Merge branch 'master' of github.com:Pylons/pyramid
2 files deleted
10 files added
131 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/ |
| | |
| | | # Wire up travis |
| | | language: python |
| | | sudo: false |
| | | |
| | | python: |
| | | - 2.6 |
| | | - 2.7 |
| | | - pypy |
| | | - 3.2 |
| | | - 3.3 |
| | | env: |
| | | - TOXENV=py26 |
| | | - TOXENV=py27 |
| | | - TOXENV=py32 |
| | | - TOXENV=py33 |
| | | - TOXENV=py34 |
| | | - TOXENV=pypy |
| | | - TOXENV=pypy3 |
| | | - TOXENV=py2-docs |
| | | - TOXENV=py3-docs |
| | | - TOXENV=py2-cover,py3-cover,coverage |
| | | |
| | | install: python setup.py dev |
| | | install: |
| | | - travis_retry pip install tox |
| | | |
| | | script: python setup.py test -q |
| | | script: |
| | | - travis_retry tox |
| | | |
| | | notifications: |
| | | email: |
| | |
| | | Next release |
| | | ============ |
| | | |
| | | 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 |
| | | https://github.com/Pylons/pyramid/issues/1297 |
| | | |
| | | - 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``. |
| | | 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 |
| | | resources relative to the package in which the ``Configurator`` was created. |
| | | This is especially useful for addons that need to load asset specs from |
| | | settings, in which case it is may be natural for a developer to define |
| | | imports or assets relative to the top-level package. |
| | | See https://github.com/Pylons/pyramid/pull/1337 |
| | | |
| | | - Added line numbers to the log formatters in the scaffolds to assist with |
| | | debugging. See https://github.com/Pylons/pyramid/pull/1326 |
| | | |
| | | - Add new HTTP exception objects for status codes |
| | | ``428 Precondition Required``, ``429 Too Many Requests`` and |
| | | ``431 Request Header Fields Too Large`` in ``pyramid.httpexceptions``. |
| | | See https://github.com/Pylons/pyramid/pull/1372/files |
| | | |
| | | - The ``pshell`` script will now load a ``PYTHONSTARTUP`` file if one is |
| | | defined in the environment prior to launching the interpreter. |
| | | See https://github.com/Pylons/pyramid/pull/1448 |
| | | |
| | | - Make it simple to define notfound and forbidden views that wish to use |
| | | the default exception-response view but with altered predicates and other |
| | | configuration options. The ``view`` argument is now optional in |
| | | ``config.add_notfound_view`` and ``config.add_forbidden_view``.. |
| | | See https://github.com/Pylons/pyramid/issues/494 |
| | | |
| | | - Greatly improve the readability of the ``pcreate`` shell script output. |
| | | See https://github.com/Pylons/pyramid/pull/1453 |
| | | |
| | | - Improve robustness to timing attacks in the ``AuthTktCookieHelper`` and |
| | | the ``SignedCookieSessionFactory`` classes by using the stdlib's |
| | | ``hmac.compare_digest`` if it is available (such as Python 2.7.7+ and 3.3+). |
| | | See https://github.com/Pylons/pyramid/pull/1457 |
| | | |
| | | - Assets can now be overidden by an absolute path on the filesystem when using |
| | | the ``config.override_asset`` API. This makes it possible to fully support |
| | | serving up static content from a mutable directory while still being able |
| | | to use the ``request.static_url`` API and ``config.add_static_view``. |
| | | Previously it was not possible to use ``config.add_static_view`` with an |
| | | absolute path **and** generate urls to the content. This change replaces |
| | | the call, ``config.add_static_view('/abs/path', 'static')``, with |
| | | ``config.add_static_view('myapp:static', 'static')`` and |
| | | ``config.override_asset(to_override='myapp:static/', |
| | | override_with='/abs/path/')``. The ``myapp:static`` asset spec is completely |
| | | made up and does not need to exist - it is used for generating urls |
| | | via ``request.static_url('myapp:static/foo.png')``. |
| | | See https://github.com/Pylons/pyramid/issues/1252 |
| | | |
| | | - Added ``pyramid.config.Configurator.set_response_factory`` and the |
| | | ``response_factory`` keyword argument to the ``Configurator`` for defining |
| | | a factory that will return a custom ``Response`` class. |
| | | See https://github.com/Pylons/pyramid/pull/1499 |
| | | |
| | | - Allow an iterator to be returned from a renderer. Previously it was only |
| | | possible to return bytes or unicode. |
| | | See https://github.com/Pylons/pyramid/pull/1417 |
| | | |
| | | - ``pserve`` can now take a ``-b`` or ``--browser`` option to open the server |
| | | URL in a web browser. See https://github.com/Pylons/pyramid/pull/1533 |
| | | |
| | | - Overall improvments for the ``proutes`` command. Added ``--format`` and |
| | | ``--glob`` arguments to the command, introduced the ``method`` |
| | | column for displaying available request methods, and improved the ``view`` |
| | | output by showing the module instead of just ``__repr__``. |
| | | See https://github.com/Pylons/pyramid/pull/1488 |
| | | |
| | | - 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 |
| | | |
| | | - The ``append_slash`` argument of ```Configurator().add_notfound_view()`` will |
| | | now accept anything that implements the ``IResponse`` interface and will use |
| | | that as the response class instead of the default ``HTTPFound``. See |
| | | https://github.com/Pylons/pyramid/pull/1610 |
| | | |
| | | 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``. |
| | | See https://github.com/Pylons/pyramid/pull/1320 |
| | | |
| | | - Fix an issue whereby predicates would be resolved as maybe_dotted in the |
| | | introspectable but not when passed for registration. This would mean that |
| | | add_route_predicate for example can not take a string and turn it into the |
| | | actual callable function. |
| | | ``add_route_predicate`` for example can not take a string and turn it into |
| | | the actual callable function. |
| | | See https://github.com/Pylons/pyramid/pull/1306 |
| | | |
| | | - Fix ``pyramid.testing.setUp`` to return a ``Configurator`` with a proper |
| | | package. Previously it was not possible to do package-relative includes |
| | | using the returned ``Configurator`` during testing. There is now a |
| | | ``package`` argument that can override this behavior as well. |
| | | See https://github.com/Pylons/pyramid/pull/1322 |
| | | |
| | | - Fix an issue where a ``pyramid.response.FileResponse`` may apply a charset |
| | | where it does not belong. See https://github.com/Pylons/pyramid/pull/1251 |
| | |
| | | type, unlike any previous version of Python. See |
| | | https://github.com/Pylons/pyramid/issues/1360 for more information. |
| | | |
| | | - ``pcreate`` now normalizes the package name by converting hyphens to |
| | | underscores. See https://github.com/Pylons/pyramid/pull/1376 |
| | | |
| | | - Fix an issue with the final response/finished callback being unable to |
| | | add another callback to the list. See |
| | | https://github.com/Pylons/pyramid/pull/1373 |
| | | |
| | | - Fix a failing unittest caused by differing mimetypes across various OSs. |
| | | See https://github.com/Pylons/pyramid/issues/1405 |
| | | |
| | | - Fix route generation for static view asset specifications having no path. |
| | | See https://github.com/Pylons/pyramid/pull/1377 |
| | | |
| | | - 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. |
| | | 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 |
| | | ------------ |
| | | |
| | | - Renamed the ``principal`` argument to ``pyramid.security.remember()`` to |
| | | ``userid`` in order to clarify its intended purpose. |
| | | See https://github.com/Pylons/pyramid/pull/1399 |
| | | |
| | | Docs |
| | | ---- |
| | | |
| | | - Moved the documentation for ``accept`` on ``Configurator.add_view`` to no |
| | | longer be part of the predicate list. See |
| | | https://github.com/Pylons/pyramid/issues/1391 for a bug report stating |
| | | ``not_`` was failing on ``accept``. Discussion with @mcdonc led to the |
| | | conclusion that it should not be documented as a predicate. |
| | | See https://github.com/Pylons/pyramid/pull/1487 for this PR |
| | | |
| | | - Removed logging configuration from Quick Tutorial ini files except for |
| | | scaffolding- and logging-related chapters to avoid needing to explain it too |
| | |
| | | - Clarify a previously-implied detail of the ``ISession.invalidate`` API |
| | | documentation. |
| | | |
| | | - Improve and clarify the documentation on what Pyramid defines as a |
| | | ``principal`` and a ``userid`` in its security APIs. |
| | | See https://github.com/Pylons/pyramid/pull/1399 |
| | | |
| | | Scaffolds |
| | | --------- |
| | | |
| | |
| | | - Amit Mane, 2014/01/23 |
| | | |
| | | - Fenton Travers, 2014/05/06 |
| | | |
| | | - Randall Leeds, 2014/11/11 |
| | | |
| | | - Hugo Branquinho, 2014/11/25 |
| | | |
| | | - Adrian Teng, 2014/12/17 |
| | | |
| | | - Ilja Everila, 2015/02/05 |
| | | |
| | | - Geoffrey T. Dairiki, 2015/02/06 |
| | | |
| | | - David Glick, 2015/02/12 |
| | | |
| | | - Donald Stufft, 2015/03/15 |
| | |
| | | $ cd hack-on-pyramid |
| | | # Configure remotes such that you can pull changes from the Pyramid |
| | | # repository into your local repository. |
| | | $ git remote add upstream https://github.com:Pylons/pyramid.git |
| | | $ git remote add upstream https://github.com/Pylons/pyramid.git |
| | | # fetch and merge changes from upstream into master |
| | | $ git fetch upstream |
| | | $ git merge upstream/master |
| | |
| | | ``hacking`` that you can then fire up like so: |
| | | |
| | | cd env27/hacking |
| | | ../bin/python setup.py develop |
| | | ../bin/pserve development.ini |
| | | |
| | | Adding Features |
| | |
| | | ------------- |
| | | |
| | | - 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``. |
| | | |
| | |
| | | - Make test suite pass on 32-bit systems; closes #286. closes #306. |
| | | See also https://github.com/Pylons/pyramid/issues/286 |
| | | |
| | | - The ``pryamid.view.view_config`` decorator did not accept a ``match_params`` |
| | | - The ``pyramid.view.view_config`` decorator did not accept a ``match_params`` |
| | | predicate argument. See https://github.com/Pylons/pyramid/pull/308 |
| | | |
| | | - The AuthTktCookieHelper could potentially generate Unicode headers |
| | |
| | | Pyramid |
| | | ======= |
| | | |
| | | .. image:: https://travis-ci.org/Pylons/pyramid.png?branch=master |
| | | :target: https://travis-ci.org/Pylons/pyramid |
| | | |
| | | .. image:: https://readthedocs.org/projects/pyramid/badge/?version=master |
| | | :target: http://docs.pylonsproject.org/projects/pyramid/en/master/ |
| | | :alt: Master Documentation Status |
| | | |
| | | .. image:: https://readthedocs.org/projects/pyramid/badge/?version=latest |
| | | :target: http://docs.pylonsproject.org/projects/pyramid/en/latest/ |
| | | :alt: Latest Documentation Status |
| | | |
| | | Pyramid is a small, fast, down-to-earth, open source Python web framework. |
| | | It makes real-world web application development and |
| | | deployment more fun, more predictable, and more productive. |
| | |
| | | - Copy relevant changes (delta bug fixes) from CHANGES.txt to |
| | | docs/whatsnew-X.X (if it's a major release). |
| | | |
| | | - update README.rst to use correct versions of badges and URLs according to |
| | | each branch and context, i.e., RTD "latest" == GitHub/Travis "1.x-branch". |
| | | |
| | | - Make sure docs render OK:: |
| | | |
| | | $ cd docs |
| | |
| | | - 1.7: Change ``pyramid.authentication.AuthTktAuthenticationPolicy`` default |
| | | ``hashalg`` to ``sha512``. |
| | | |
| | | - 1.8 Remove set_request_property. |
| | | - 1.8: Remove set_request_property. |
| | | |
| | | - 1.9: Remove extra code enabling ``pyramid.security.remember(principal=...)`` |
| | | and force use of ``userid``. |
| | | |
| | | Probably Bad Ideas |
| | | ------------------ |
New file |
| | |
| | | <?xml version="1.0"?> |
| | | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="91 11 424 533" width="424pt" height="533pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2014-11-23 07:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker" viewBox="-4 -4 10 8" markerWidth="10" markerHeight="8" color="#191919"><g><path d="M 5 0 L -3 -3 L 0 0 L 0 0 L -3 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Request Processing</title><rect fill="white" width="576" height="733"/><g><title>no exceptions</title><path d="M 155 444.75674 C 155 450.64061 155 486.2592 155 502.71617" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99999 322.33334 C 154.99999 327.72413 155 337.74646 155 346.1775" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99999 245.22768 C 154.99999 250.5417 154.99999 257.93189 154.99999 265.10145" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99995 198.62203 C 154.99995 203.74682 154.99998 209.1909 154.99999 215.28222" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="102.16667" y="45.183037" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="45.183037" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 50.455358)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="4.7596016" y="10" textLength="88.92578">middleware ingress </tspan></text><rect x="102.16667" y="96.183037" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="96.183037" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 101.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.983723" y="10" textLength="61.69922">tween ingress</tspan></text><rect x="102.16667" y="222.18304" width="105.666664" height="22.544641" fill="#d2ffd0"/><rect x="102.16667" y="222.18304" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 227.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="28.660969" y="10" textLength="38.344727">traversal</tspan></text><rect x="238.83336" y="247.18304" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="238.83336" y="247.18304" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 252.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.424641" y="10" textLength="62.817383">ContextFound</tspan></text><rect x="102.16667" y="422.2121" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="422.2121" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 427.48442)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="18.094563" y="10" textLength="59.47754">tween egress</tspan></text><rect x="239" y="445.2359" width="105.666664" height="22.544641" fill="#fed153"/><rect x="239" y="445.2359" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244 450.50821)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="5.3113594" y="10" textLength="85.043945">response callbacks</tspan></text><rect x="239" y="497.2359" width="105.666664" height="22.544641" fill="#fed153"/><rect x="239" y="497.2359" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244 502.5082)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="8.6463203" y="10" textLength="5">fi</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="13.64632" y="10" textLength="73.374023">nished callbacks</tspan></text><rect x="102.16667" y="509.61795" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="509.61795" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 514.89027)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="5.8704414" y="10" textLength="83.92578">middleware egress</tspan></text><path d="M 155 67.72768 C 155 73.048893 155 81.55558 155 89.2853" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 155 119.22768 C 155 124.62026 154.99997 133.48763 154.99996 141.38632" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="375.5" y="391" width="105.666664" height="22.544642" fill="#dfbeff"/><rect x="375.5" y="391" width="105.666664" height="22.544642" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(380.5 396.27232)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.702961" y="10" textLength="62.260742">BeforeRender</tspan></text><text transform="translate(233.5 20)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".31445312" y="11" textLength="115.371094">Request Processing</tspan></text><path d="M 375.99995 42.910746 L 498.66662 42.910746 C 501.42805 42.910746 503.66662 45.149323 503.66662 47.910746 L 503.66662 222 C 503.66662 224.76142 501.42805 227 498.66662 227 L 375.99995 227 C 373.23853 227 370.99995 224.76142 370.99995 222 L 370.99995 47.910746 C 370.99995 45.149323 373.23853 42.910746 375.99995 42.910746 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(375.99995 42.910746)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="0" y="10" textLength="35.55664">Legend</tspan></text><rect x="383.66662" y="63.908513" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="383.66662" y="63.908513" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 69.180834)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="35.601887" y="10" textLength="24.46289">event</tspan></text><rect x="383.66662" y="186.58226" width="105.666664" height="22.544641" fill="#fed153"/><rect x="383.66662" y="186.58226" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 191.85458)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="29.769367" y="10" textLength="36.12793">callback</tspan></text><rect x="383.66662" y="158.54998" width="105.666664" height="22.544641" fill="#ffff6c"/><rect x="383.66662" y="158.54998" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 163.8223)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.83089" y="10" textLength="20.004883">view</tspan></text><rect x="383.66662" y="91.94079" width="105.666664" height="33.089283" fill="#a4cfff"/><rect x="383.66662" y="91.94079" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 96.48543)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="11.148762" y="10" textLength="76.14746">external process </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="2.8162422" y="22" textLength="90.03418">(middleware, tween)</tspan></text><rect x="383.66662" y="130.51771" width="105.666664" height="22.544641" fill="#d2ffd0"/><rect x="383.66662" y="130.51771" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 135.79003)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="12.537922" y="10" textLength="70.59082">internal process</tspan></text><line x1="154.99999" y1="258.44082" x2="238.83336" y2="258.45536" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="102.16667" y="353.07515" width="105.666664" height="33.089283" fill="#ffff6c"/><rect x="102.16667" y="353.07515" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 363.61979)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.205402" y="10" textLength="57.25586">view pipeline</tspan></text><path d="M 155 386.66443 C 155 392.17252 155 405.5052 155 415.30935" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="239.33336" y1="285.57838" x2="207.66667" y2="353.07515" stroke="#c1c1c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/><line x1="238.75001" y1="430.80676" x2="207.66667" y2="385.656" stroke="#c1c1c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/><rect x="102.16666" y="305.0893" width="105.666664" height="17.244049" fill="#d2ffd0"/><rect x="102.16666" y="305.0893" width="105.666664" height="17.244049" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16666 307.71132)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="24.764484" y="10" textLength="46.137695">predicates</tspan></text><rect x="102.16666" y="272" width="105.666664" height="33.089294" fill="#d2ffd0"/><rect x="102.16666" y="272" width="105.666664" height="33.089294" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16666 282.54465)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="21.707844" y="10" textLength="52.250977">view lookup</tspan></text><rect x="102.166606" y="181.37798" width="105.666695" height="17.244049" fill="#d2ffd0"/><rect x="102.166606" y="181.37798" width="105.666695" height="17.244049" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.166606 184)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="11.978855" y="10" textLength="71.708984">route predicates</tspan></text><rect x="102.166606" y="148.28869" width="105.666695" height="33.089294" fill="#d2ffd0"/><rect x="102.166606" y="148.28869" width="105.666695" height="33.089294" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.166606 158.83333)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="18.001804" y="10" textLength="20.004883">URL</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.640476" y="10" textLength="40.024414"> dispatch</tspan></text><rect x="239.8334" y="117.3192" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="239.8334" y="117.3192" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244.8334 122.59152)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.207844" y="10" textLength="57.250977">NewRequest</tspan></text><line x1="154.99999" y1="128.68025" x2="239.8334" y2="128.59152" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="238.83336" y="471.2262" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="238.83336" y="471.2262" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 476.49852)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="15.316242" y="10" textLength="65.03418">NewResponse</tspan></text><line x1="155" y1="470.25295" x2="238.33861" y2="482.42625" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="238.75001" y="322.26348" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="322.26348" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 325.5915)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="1.9812813" y="10" textLength="91.7041">view mapper ingress</tspan></text><rect x="238.75001" y="341.36561" width="105.666664" height="33.089283" fill="#ffff6c"/><rect x="238.75001" y="341.36561" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 351.91025)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.83089" y="10" textLength="20.004883">view</tspan></text><rect x="238.75001" y="374.901" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="374.901" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 378.22901)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="3.0921211" y="10" textLength="89.48242">view mapper egress</tspan></text><rect x="238.75001" y="393.55704" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="393.55704" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 396.88507)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="8.9173164" y="10" textLength="77.83203">response adapter</tspan></text><rect x="238.75001" y="303.65604" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="303.65604" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 306.98407)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="6.702961" y="10" textLength="82.26074">decorators ingress</tspan></text><rect x="238.75001" y="412.1507" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="412.1507" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 415.47873)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="7.813801" y="10" textLength="80.039062">decorators egress</tspan></text><rect x="238.83336" y="285" width="105.66669" height="18.656048" fill="#ffffa3"/><rect x="238.83336" y="285" width="105.66669" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 288.32802)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.202972" y="10" textLength="57.260742">authorization</tspan></text><line x1="155" y1="482.12575" x2="238.52297" y2="508.3584" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><line x1="155" y1="459.27668" x2="238.50027" y2="456.52468" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><line x1="344.41667" y1="402.88507" x2="375.5" y2="402.27232" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/></g></g></svg> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| | | <plist version="1.0"> |
| | | <dict> |
| | | <key>ActiveLayerIndex</key> |
| | | <integer>0</integer> |
| | | <key>ApplicationVersion</key> |
| | | <array> |
| | | <string>com.omnigroup.OmniGrafflePro</string> |
| | | <string>139.18.0.187838</string> |
| | | </array> |
| | | <key>AutoAdjust</key> |
| | | <true/> |
| | | <key>BackgroundGraphic</key> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{0, 0}, {576, 733}}</string> |
| | | <key>Class</key> |
| | | <string>SolidGraphic</string> |
| | | <key>ID</key> |
| | | <integer>2</integer> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | </dict> |
| | | <key>BaseZoom</key> |
| | | <integer>0</integer> |
| | | <key>CanvasOrigin</key> |
| | | <string>{0, 0}</string> |
| | | <key>ColumnAlign</key> |
| | | <integer>1</integer> |
| | | <key>ColumnSpacing</key> |
| | | <real>36</real> |
| | | <key>CreationDate</key> |
| | | <string>2014-12-01 08:25:13 +0000</string> |
| | | <key>Creator</key> |
| | | <string>Steve Piercy</string> |
| | | <key>DisplayScale</key> |
| | | <string>1 0/72 in = 1 0/72 in</string> |
| | | <key>GraphDocumentVersion</key> |
| | | <integer>8</integer> |
| | | <key>GraphicsList</key> |
| | | <array> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169413</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169414</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165903727232, 501.05557886759294}</string> |
| | | <string>{202.04165903727232, 528.77776209513161}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169412</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666686, 528.77776209513161}, {195.24998474121094, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169413</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.999449</string> |
| | | <key>g</key> |
| | | <string>0.743511</string> |
| | | <key>r</key> |
| | | <string>0.872276</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Return the |
| | | \b response}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666657, 471.55557886759294}, {195.24998474121094, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169412</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.422927</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>1</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Invoke the |
| | | \b view callable |
| | | \b0 ,\ |
| | | which returns a |
| | | \b response}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{291.21562524160186, 379.55555343627816}, {26, 24}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>FitText</key> |
| | | <string>YES</string> |
| | | <key>Flow</key> |
| | | <string>Resize</string> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169411</integer> |
| | | <key>Line</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169410</integer> |
| | | <key>Offset</key> |
| | | <real>7.3333320617675781</real> |
| | | <key>Position</key> |
| | | <real>0.4865129292011261</real> |
| | | <key>RotationType</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc |
| | | |
| | | \f0\fs24 \cf0 No}</string> |
| | | </dict> |
| | | <key>Wrap</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{34.791667904111534, 0}</string> |
| | | <string>{-33.999994913736998, 0}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169409</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169410</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{280.85416589389337, 398.88888549804574}</string> |
| | | <string>{327.47912214508739, 398.88888549804574}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169404</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{327.47912214508739, 384.38888549804574}, {156.62496948242188, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169409</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.756045</string> |
| | | <key>g</key> |
| | | <string>0.75004</string> |
| | | <key>r</key> |
| | | <string>0.994455</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Return the |
| | | \b Forbidden View}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{175.11595161998204, 438.9999954213917}, {30, 24}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>FitText</key> |
| | | <string>YES</string> |
| | | <key>Flow</key> |
| | | <string>Resize</string> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169408</integer> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc |
| | | |
| | | \f0\fs24 \cf0 Yes}</string> |
| | | </dict> |
| | | <key>Wrap</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169412</integer> |
| | | <key>Info</key> |
| | | <integer>2</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169407</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165267944353, 437.33333079020139}</string> |
| | | <string>{202.04165903727204, 471.55557886759294}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169404</integer> |
| | | <key>Info</key> |
| | | <integer>1</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{171.708317756653, 329.24978243601743}, {30, 24}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>FitText</key> |
| | | <string>YES</string> |
| | | <key>Flow</key> |
| | | <string>Resize</string> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169406</integer> |
| | | <key>Line</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169405</integer> |
| | | <key>Offset</key> |
| | | <real>-15.333334922790527</real> |
| | | <key>Position</key> |
| | | <real>0.45895844697952271</real> |
| | | <key>RotationType</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc |
| | | |
| | | \f0\fs24 \cf0 Yes}</string> |
| | | </dict> |
| | | <key>Wrap</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169404</integer> |
| | | <key>Info</key> |
| | | <integer>2</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169405</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165267944353, 326.72223360222029}</string> |
| | | <string>{202.04165267944353, 360.44446818033811}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>3</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{123.72916793823259, 360.44446818033811}, {156.62496948242188, 76.888862609863281}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169404</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Diamond</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.422927</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>1</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Current user has |
| | | \b authorization |
| | | \b0 to invoke the view callable?}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{283.07625736262997, 281.88889694213805}, {26, 24}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>FitText</key> |
| | | <string>YES</string> |
| | | <key>Flow</key> |
| | | <string>Resize</string> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169403</integer> |
| | | <key>Line</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169402</integer> |
| | | <key>Offset</key> |
| | | <real>7.3333320617675781</real> |
| | | <key>Position</key> |
| | | <real>0.4865129292011261</real> |
| | | <key>RotationType</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc |
| | | |
| | | \f0\fs24 \cf0 No}</string> |
| | | </dict> |
| | | <key>Wrap</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{34.791667904111534, 0}</string> |
| | | <string>{-33.999994913736998, 0}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169401</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169402</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{265.20833208871704, 301.22222900390562}</string> |
| | | <string>{327.47911580403627, 301.22222900390562}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>3</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{327.47911580403627, 286.72222900390562}, {156.62496948242188, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169401</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.756045</string> |
| | | <key>g</key> |
| | | <string>0.75004</string> |
| | | <key>r</key> |
| | | <string>0.994455</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Return the |
| | | \b Not Found View}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>3</integer> |
| | | <key>Info</key> |
| | | <integer>2</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169400</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165903727255, 251}</string> |
| | | <string>{202.04165776570633, 276.22223154703772}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169393</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{139.37498982747391, 276.22223154703778}, {125.33333587646484, 50}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>3</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Diamond</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.422927</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>1</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 View callable found?}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169393</integer> |
| | | <key>Info</key> |
| | | <integer>2</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169396</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165903727255, 196.77777862548834}</string> |
| | | <string>{202.04165903727255, 222}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169392</integer> |
| | | <key>Info</key> |
| | | <integer>1</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169392</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169395</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165903727255, 142.55555725097662}</string> |
| | | <string>{202.04165903727255, 167.77777862548834}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169391</integer> |
| | | <key>Info</key> |
| | | <integer>1</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Class</key> |
| | | <string>LineGraphic</string> |
| | | <key>ControlPoints</key> |
| | | <array> |
| | | <string>{0, 6.9840087890625}</string> |
| | | <string>{0, -9}</string> |
| | | </array> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>w</key> |
| | | <string>0</string> |
| | | </dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>Head</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>169391</integer> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169385</integer> |
| | | <key>Points</key> |
| | | <array> |
| | | <string>{202.04165903727255, 82.666667938232479}</string> |
| | | <string>{202.04165903727255, 107.88888931274418}</string> |
| | | </array> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Bezier</key> |
| | | <true/> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.0980392</string> |
| | | <key>g</key> |
| | | <string>0.0980392</string> |
| | | <key>r</key> |
| | | <string>0.0980392</string> |
| | | </dict> |
| | | <key>HeadArrow</key> |
| | | <string>SharpArrow</string> |
| | | <key>Legacy</key> |
| | | <true/> |
| | | <key>LineType</key> |
| | | <integer>1</integer> |
| | | <key>TailArrow</key> |
| | | <string>0</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Tail</key> |
| | | <dict> |
| | | <key>ID</key> |
| | | <integer>19</integer> |
| | | <key>Info</key> |
| | | <integer>1</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666708, 222}, {195.24998474121094, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169393</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.815377</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>0.820561</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Look up a |
| | | \b view callable |
| | | \b0 in the |
| | | \b registry |
| | | \b0 using the |
| | | \b context |
| | | \b0 and |
| | | \b view name}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666708, 167.77777862548834}, {195.24998474121094, 29}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169392</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.815377</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>0.820561</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\b\fs20 \cf0 Traversal |
| | | \b0 locates\ |
| | | the |
| | | \b context |
| | | \b0 and |
| | | \b view name}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666708, 107.88888931274418}, {195.24998474121094, 34.666667938232422}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>169391</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.815377</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>0.820561</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Traverse the model graph\ |
| | | from the |
| | | \b root |
| | | \b0 using the |
| | | \b path}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{104.41666666666708, 48.000000000000043}, {195.24998474121094, 34.666667938232422}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>ID</key> |
| | | <integer>19</integer> |
| | | <key>Magnets</key> |
| | | <array> |
| | | <string>{0, 1}</string> |
| | | <string>{0, -1}</string> |
| | | <string>{1, 0}</string> |
| | | <string>{-1, 0}</string> |
| | | </array> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Color</key> |
| | | <dict> |
| | | <key>b</key> |
| | | <string>0.815377</string> |
| | | <key>g</key> |
| | | <string>1</string> |
| | | <key>r</key> |
| | | <string>0.820561</string> |
| | | </dict> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc |
| | | |
| | | \f0\fs20 \cf0 Obtain a root object from the |
| | | \b root factory}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | </dict> |
| | | <dict> |
| | | <key>Bounds</key> |
| | | <string>{{229.04165903727255, 20.000000000000934}, {90, 14}}</string> |
| | | <key>Class</key> |
| | | <string>ShapedGraphic</string> |
| | | <key>FitText</key> |
| | | <string>YES</string> |
| | | <key>Flow</key> |
| | | <string>Resize</string> |
| | | <key>FontInfo</key> |
| | | <dict> |
| | | <key>Font</key> |
| | | <string>Helvetica</string> |
| | | <key>Size</key> |
| | | <real>12</real> |
| | | </dict> |
| | | <key>ID</key> |
| | | <integer>169390</integer> |
| | | <key>Shape</key> |
| | | <string>Rectangle</string> |
| | | <key>Style</key> |
| | | <dict> |
| | | <key>fill</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>shadow</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | <key>stroke</key> |
| | | <dict> |
| | | <key>Draws</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </dict> |
| | | <key>Text</key> |
| | | <dict> |
| | | <key>Pad</key> |
| | | <integer>0</integer> |
| | | <key>Text</key> |
| | | <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 |
| | | \cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} |
| | | {\colortbl;\red255\green255\blue255;} |
| | | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc |
| | | |
| | | \f0\b\fs24 \cf0 <%Canvas%>}</string> |
| | | <key>VerticalPad</key> |
| | | <integer>0</integer> |
| | | </dict> |
| | | <key>Wrap</key> |
| | | <string>NO</string> |
| | | </dict> |
| | | </array> |
| | | <key>GridInfo</key> |
| | | <dict/> |
| | | <key>GuidesLocked</key> |
| | | <string>NO</string> |
| | | <key>GuidesVisible</key> |
| | | <string>YES</string> |
| | | <key>HPages</key> |
| | | <integer>1</integer> |
| | | <key>ImageCounter</key> |
| | | <integer>1</integer> |
| | | <key>KeepToScale</key> |
| | | <false/> |
| | | <key>Layers</key> |
| | | <array> |
| | | <dict> |
| | | <key>Lock</key> |
| | | <string>NO</string> |
| | | <key>Name</key> |
| | | <string>Layer 1</string> |
| | | <key>Print</key> |
| | | <string>YES</string> |
| | | <key>View</key> |
| | | <string>YES</string> |
| | | </dict> |
| | | </array> |
| | | <key>LayoutInfo</key> |
| | | <dict> |
| | | <key>Animate</key> |
| | | <string>NO</string> |
| | | <key>circoMinDist</key> |
| | | <real>18</real> |
| | | <key>circoSeparation</key> |
| | | <real>0.0</real> |
| | | <key>layoutEngine</key> |
| | | <string>dot</string> |
| | | <key>neatoSeparation</key> |
| | | <real>0.0</real> |
| | | <key>twopiSeparation</key> |
| | | <real>0.0</real> |
| | | </dict> |
| | | <key>LinksVisible</key> |
| | | <string>NO</string> |
| | | <key>MagnetsVisible</key> |
| | | <string>NO</string> |
| | | <key>MasterSheets</key> |
| | | <array/> |
| | | <key>ModificationDate</key> |
| | | <string>2014-12-01 09:19:51 +0000</string> |
| | | <key>Modifier</key> |
| | | <string>Steve Piercy</string> |
| | | <key>NotesVisible</key> |
| | | <string>NO</string> |
| | | <key>Orientation</key> |
| | | <integer>2</integer> |
| | | <key>OriginVisible</key> |
| | | <string>NO</string> |
| | | <key>PageBreaks</key> |
| | | <string>YES</string> |
| | | <key>PrintInfo</key> |
| | | <dict> |
| | | <key>NSBottomMargin</key> |
| | | <array> |
| | | <string>float</string> |
| | | <string>41</string> |
| | | </array> |
| | | <key>NSHorizonalPagination</key> |
| | | <array> |
| | | <string>coded</string> |
| | | <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string> |
| | | </array> |
| | | <key>NSLeftMargin</key> |
| | | <array> |
| | | <string>float</string> |
| | | <string>18</string> |
| | | </array> |
| | | <key>NSPaperSize</key> |
| | | <array> |
| | | <string>size</string> |
| | | <string>{612, 792}</string> |
| | | </array> |
| | | <key>NSPrintReverseOrientation</key> |
| | | <array> |
| | | <string>int</string> |
| | | <string>0</string> |
| | | </array> |
| | | <key>NSRightMargin</key> |
| | | <array> |
| | | <string>float</string> |
| | | <string>18</string> |
| | | </array> |
| | | <key>NSTopMargin</key> |
| | | <array> |
| | | <string>float</string> |
| | | <string>18</string> |
| | | </array> |
| | | </dict> |
| | | <key>PrintOnePage</key> |
| | | <false/> |
| | | <key>ReadOnly</key> |
| | | <string>NO</string> |
| | | <key>RowAlign</key> |
| | | <integer>1</integer> |
| | | <key>RowSpacing</key> |
| | | <real>36</real> |
| | | <key>SheetTitle</key> |
| | | <string>Pyramid Router</string> |
| | | <key>SmartAlignmentGuidesActive</key> |
| | | <string>YES</string> |
| | | <key>SmartDistanceGuidesActive</key> |
| | | <string>YES</string> |
| | | <key>UniqueID</key> |
| | | <integer>1</integer> |
| | | <key>UseEntirePage</key> |
| | | <false/> |
| | | <key>VPages</key> |
| | | <integer>1</integer> |
| | | <key>WindowInfo</key> |
| | | <dict> |
| | | <key>CurrentSheet</key> |
| | | <integer>0</integer> |
| | | <key>ExpandedCanvases</key> |
| | | <array> |
| | | <dict> |
| | | <key>name</key> |
| | | <string>Pyramid Router</string> |
| | | </dict> |
| | | </array> |
| | | <key>Frame</key> |
| | | <string>{{96, 20}, {1076, 1286}}</string> |
| | | <key>ListView</key> |
| | | <false/> |
| | | <key>OutlineWidth</key> |
| | | <integer>142</integer> |
| | | <key>RightSidebar</key> |
| | | <true/> |
| | | <key>ShowRuler</key> |
| | | <true/> |
| | | <key>Sidebar</key> |
| | | <true/> |
| | | <key>SidebarWidth</key> |
| | | <integer>120</integer> |
| | | <key>VisibleRegion</key> |
| | | <string>{{8, -10}, {532, 754.66666666666663}}</string> |
| | | <key>Zoom</key> |
| | | <real>1.5</real> |
| | | <key>ZoomValues</key> |
| | | <array> |
| | | <array> |
| | | <string>Pyramid Router</string> |
| | | <real>1.5</real> |
| | | <real>1</real> |
| | | </array> |
| | | </array> |
| | | </dict> |
| | | </dict> |
| | | </plist> |
New file |
| | |
| | | <?xml version="1.0"?> |
| | | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> |
| | | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="93 11 403 558" width="403pt" height="558pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2014-12-01 09:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker" viewBox="-4 -4 10 8" markerWidth="10" markerHeight="8" color="#191919"><g><path d="M 5 0 L -3 -3 L 0 0 L 0 0 L -3 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Pyramid Router</title><rect fill="white" width="576" height="733"/><g><title>Layer 1</title><text transform="translate(229.04166 20)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".32226562" y="11" textLength="89.35547">Pyramid Router</tspan></text><rect x="104.416667" y="48" width="195.24998" height="34.666668" fill="#d2ffd0"/><rect x="104.416667" y="48" width="195.24998" height="34.666668" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 59.333334)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x=".088371277" y="10" textLength="129.51172">Obtain a root object from the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="129.60009" y="10" textLength="55.561523">root factory</tspan></text><rect x="104.416667" y="107.88889" width="195.24998" height="34.666668" fill="#d2ffd0"/><rect x="104.416667" y="107.88889" width="195.24998" height="34.666668" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 113.22222)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="35.557121" y="10" textLength="6.1083984">T</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="41.29931" y="10" textLength="108.393555">raverse the model graph</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="29.551262" y="22" textLength="39.458008">from the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="69.00927" y="22" textLength="19.438477">root</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="88.447746" y="22" textLength="46.142578"> using the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="134.590324" y="22" textLength="21.108398">path</tspan></text><rect x="104.416667" y="167.77778" width="195.24998" height="29" fill="#d2ffd0"/><rect x="104.416667" y="167.77778" width="195.24998" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 170.27778)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="53.428215" y="10" textLength="6.1083984">T</tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="58.98974" y="10" textLength="38.36914">raversal</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="97.35888" y="10" textLength="34.46289"> locates</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="30.093254" y="22" textLength="16.6796875">the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="46.772942" y="22" textLength="35.561523">context</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="82.334465" y="22" textLength="22.241211"> and </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="104.575676" y="22" textLength="50.581055">view name</tspan></text><rect x="104.416667" y="222" width="195.24998" height="29" fill="#d2ffd0"/><rect x="104.416667" y="222" width="195.24998" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 224.5)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="5.3471603" y="10" textLength="46.704102">Look up a </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="52.051262" y="10" textLength="61.14746">view callable</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="113.19872" y="10" textLength="30.019531"> in the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="143.21825" y="10" textLength="36.68457">registry</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="179.90282" y="10" textLength="2.7783203"> </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.750969" y="22" textLength="43.364258">using the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="60.115227" y="22" textLength="35.561523">context</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="95.67675" y="22" textLength="22.241211"> and </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="117.91796" y="22" textLength="50.581055">view name</tspan></text><path d="M 202.04166 82.66667 C 202.04166 87.86648 202.04166 94.31586 202.04166 100.98615" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 202.04166 142.55556 C 202.04166 147.75537 202.04166 154.20475 202.04166 160.87504" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 202.04166 196.77778 C 202.04166 201.97759 202.04166 208.42697 202.04166 215.09726" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 202.04166 276.22223 L 264.70833 301.22223 L 202.04166 326.22223 L 139.37499 301.22223 Z" fill="#ffff6c"/><path d="M 202.04166 276.22223 L 264.70833 301.22223 L 202.04166 326.22223 L 139.37499 301.22223 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(161.29499 288.72223)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="12.905763" y="10" textLength="6.669922">V</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.399903" y="10" textLength="54.472656">iew callable </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="26.707032" y="22" textLength="30.585938">found?</tspan></text><path d="M 202.04166 251 C 202.04166 256.19981 202.04166 262.6492 202.04166 269.31949" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="327.47912" y="286.72223" width="156.62497" height="29" fill="#fec0c1"/><rect x="327.47912" y="286.72223" width="156.62497" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(332.47912 295.22223)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="10.89061" y="10" textLength="49.472656">Return the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="60.363266" y="10" textLength="59.42871">Not Found V</tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="119.616196" y="10" textLength="16.118164">iew</tspan></text><path d="M 265.20833 301.22223 C 297.4314 301.22223 294.2168 301.22223 320.5783 301.22223" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(288.07626 286.8889)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x=".33007812" y="11" textLength="15.339844">No</tspan></text><path d="M 202.04165 360.44447 L 280.35414 398.8889 L 202.04165 437.33333 L 123.72917 398.8889 Z" fill="#ffff6c"/><path d="M 202.04165 360.44447 L 280.35414 398.8889 L 202.04165 437.33333 L 123.72917 398.8889 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(149.87354 380.12)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.495594" y="10" textLength="77.25586">Current user has </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x=".9462776" y="22" textLength="62.773438">authorization</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="63.719715" y="22" textLength="45.581055"> to invoke </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="14.26659" y="34" textLength="78.935547">the view callable?</tspan></text><path d="M 202.04165 326.72223 C 202.04165 332.1894 202.04165 344.2467 202.04165 353.54354" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(176.70832 334.24978)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x=".21191406" y="11" textLength="8.0039062">Y</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="7.114258" y="11" textLength="12.673828">es</tspan></text><path d="M 202.04165 437.33333 C 202.04165 442.81278 202.04166 455.21977 202.04166 464.65762" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(180.11595 444)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".21191406" y="11" textLength="8.0039062">Y</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.114258" y="11" textLength="12.673828">es</tspan></text><rect x="327.47912" y="384.38889" width="156.62497" height="29" fill="#fec0c1"/><rect x="327.47912" y="384.38889" width="156.62497" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(332.47912 392.88889)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="11.439926" y="10" textLength="49.472656">Return the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="60.912582" y="10" textLength="58.330078">Forbidden V</tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="119.06688" y="10" textLength="16.118164">iew</tspan></text><path d="M 280.85417 398.88889 C 312.9685 398.88889 296.55343 398.88889 320.57617 398.88889" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(296.21563 384.55555)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".33007812" y="11" textLength="15.339844">No</tspan></text><rect x="104.416667" y="471.55558" width="195.24998" height="29" fill="#ffff6c"/><rect x="104.416667" y="471.55558" width="195.24998" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 474.05558)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="36.201653" y="10" textLength="48.920898">Invoke the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="85.12255" y="10" textLength="61.14746">view callable</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="146.27001" y="10" textLength="2.7783203">,</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="35.100578" y="22" textLength="70.585938">which returns a </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="105.686516" y="22" textLength="44.46289">response</tspan></text><rect x="104.416667" y="528.77776" width="195.24998" height="29" fill="#dfbeff"/><rect x="104.416667" y="528.77776" width="195.24998" height="29" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(109.416667 537.27776)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="45.65722" y="10" textLength="49.472656">Return the </tspan><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="95.129875" y="10" textLength="44.46289">response</tspan></text><path d="M 202.04166 501.05558 C 202.04166 506.35088 202.04166 514.3792 202.04166 521.8749" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg> |
| | |
| | | Subproject commit 3bec9280a6cedb15e97e5899021aa8d723c25388 |
| | | Subproject commit 382cba80fbd6a7424818d17ec63ca520e485f108 |
| | |
| | | are being used. |
| | | |
| | | .. autoclass:: not_ |
| | | |
| | | .. attribute:: PHASE0_CONFIG |
| | | .. attribute:: PHASE1_CONFIG |
| | | .. attribute:: PHASE2_CONFIG |
| | | .. attribute:: PHASE3_CONFIG |
| | |
| | | |
| | | .. automodule:: pyramid.exceptions |
| | | |
| | | .. autoclass:: BadCSRFToken |
| | | .. autoexception:: BadCSRFToken |
| | | |
| | | .. autoclass:: PredicateMismatch |
| | | .. autoexception:: PredicateMismatch |
| | | |
| | | .. autoclass:: Forbidden |
| | | .. autoexception:: Forbidden |
| | | |
| | | .. autoclass:: NotFound |
| | | .. autoexception:: NotFound |
| | | |
| | | .. autoclass:: ConfigurationError |
| | | .. autoexception:: ConfigurationError |
| | | |
| | | .. autoclass:: URLDecodeError |
| | | .. autoexception:: URLDecodeError |
| | |
| | | |
| | | .. autofunction:: exception_response |
| | | |
| | | .. autoclass:: HTTPException |
| | | .. autoexception:: HTTPException |
| | | |
| | | .. autoclass:: HTTPOk |
| | | .. autoexception:: HTTPOk |
| | | |
| | | .. autoclass:: HTTPRedirection |
| | | .. autoexception:: HTTPRedirection |
| | | |
| | | .. autoclass:: HTTPError |
| | | .. autoexception:: HTTPError |
| | | |
| | | .. autoclass:: HTTPClientError |
| | | .. autoexception:: HTTPClientError |
| | | |
| | | .. autoclass:: HTTPServerError |
| | | .. autoexception:: HTTPServerError |
| | | |
| | | .. autoclass:: HTTPCreated |
| | | .. autoexception:: HTTPCreated |
| | | |
| | | .. autoclass:: HTTPAccepted |
| | | .. autoexception:: HTTPAccepted |
| | | |
| | | .. autoclass:: HTTPNonAuthoritativeInformation |
| | | .. autoexception:: HTTPNonAuthoritativeInformation |
| | | |
| | | .. autoclass:: HTTPNoContent |
| | | .. autoexception:: HTTPNoContent |
| | | |
| | | .. autoclass:: HTTPResetContent |
| | | .. autoexception:: HTTPResetContent |
| | | |
| | | .. autoclass:: HTTPPartialContent |
| | | .. autoexception:: HTTPPartialContent |
| | | |
| | | .. autoclass:: HTTPMultipleChoices |
| | | .. autoexception:: HTTPMultipleChoices |
| | | |
| | | .. autoclass:: HTTPMovedPermanently |
| | | .. autoexception:: HTTPMovedPermanently |
| | | |
| | | .. autoclass:: HTTPFound |
| | | .. autoexception:: HTTPFound |
| | | |
| | | .. autoclass:: HTTPSeeOther |
| | | .. autoexception:: HTTPSeeOther |
| | | |
| | | .. autoclass:: HTTPNotModified |
| | | .. autoexception:: HTTPNotModified |
| | | |
| | | .. autoclass:: HTTPUseProxy |
| | | .. autoexception:: HTTPUseProxy |
| | | |
| | | .. autoclass:: HTTPTemporaryRedirect |
| | | .. autoexception:: HTTPTemporaryRedirect |
| | | |
| | | .. autoclass:: HTTPBadRequest |
| | | .. autoexception:: HTTPBadRequest |
| | | |
| | | .. autoclass:: HTTPUnauthorized |
| | | .. autoexception:: HTTPUnauthorized |
| | | |
| | | .. autoclass:: HTTPPaymentRequired |
| | | .. autoexception:: HTTPPaymentRequired |
| | | |
| | | .. autoclass:: HTTPForbidden |
| | | .. autoexception:: HTTPForbidden |
| | | |
| | | .. autoclass:: HTTPNotFound |
| | | .. autoexception:: HTTPNotFound |
| | | |
| | | .. autoclass:: HTTPMethodNotAllowed |
| | | .. autoexception:: HTTPMethodNotAllowed |
| | | |
| | | .. autoclass:: HTTPNotAcceptable |
| | | .. autoexception:: HTTPNotAcceptable |
| | | |
| | | .. autoclass:: HTTPProxyAuthenticationRequired |
| | | .. autoexception:: HTTPProxyAuthenticationRequired |
| | | |
| | | .. autoclass:: HTTPRequestTimeout |
| | | .. autoexception:: HTTPRequestTimeout |
| | | |
| | | .. autoclass:: HTTPConflict |
| | | .. autoexception:: HTTPConflict |
| | | |
| | | .. autoclass:: HTTPGone |
| | | .. autoexception:: HTTPGone |
| | | |
| | | .. autoclass:: HTTPLengthRequired |
| | | .. autoexception:: HTTPLengthRequired |
| | | |
| | | .. autoclass:: HTTPPreconditionFailed |
| | | .. autoexception:: HTTPPreconditionFailed |
| | | |
| | | .. autoclass:: HTTPRequestEntityTooLarge |
| | | .. autoexception:: HTTPRequestEntityTooLarge |
| | | |
| | | .. autoclass:: HTTPRequestURITooLong |
| | | .. autoexception:: HTTPRequestURITooLong |
| | | |
| | | .. autoclass:: HTTPUnsupportedMediaType |
| | | .. autoexception:: HTTPUnsupportedMediaType |
| | | |
| | | .. autoclass:: HTTPRequestRangeNotSatisfiable |
| | | .. autoexception:: HTTPRequestRangeNotSatisfiable |
| | | |
| | | .. autoclass:: HTTPExpectationFailed |
| | | .. autoexception:: HTTPExpectationFailed |
| | | |
| | | .. autoclass:: HTTPUnprocessableEntity |
| | | .. autoexception:: HTTPUnprocessableEntity |
| | | |
| | | .. autoclass:: HTTPLocked |
| | | .. autoexception:: HTTPLocked |
| | | |
| | | .. autoclass:: HTTPFailedDependency |
| | | .. autoexception:: HTTPFailedDependency |
| | | |
| | | .. autoclass:: HTTPInternalServerError |
| | | .. autoexception:: HTTPInternalServerError |
| | | |
| | | .. autoclass:: HTTPNotImplemented |
| | | .. autoexception:: HTTPNotImplemented |
| | | |
| | | .. autoclass:: HTTPBadGateway |
| | | .. autoexception:: HTTPBadGateway |
| | | |
| | | .. autoclass:: HTTPServiceUnavailable |
| | | .. autoexception:: HTTPServiceUnavailable |
| | | |
| | | .. autoclass:: HTTPGatewayTimeout |
| | | .. autoexception:: HTTPGatewayTimeout |
| | | |
| | | .. autoclass:: HTTPVersionNotSupported |
| | | .. autoexception:: HTTPVersionNotSupported |
| | | |
| | | .. autoclass:: HTTPInsufficientStorage |
| | | .. autoexception:: HTTPInsufficientStorage |
New file |
| | |
| | | .. _html_api_documentation: |
| | | |
| | | API Documentation |
| | | ================= |
| | | |
| | | Comprehensive reference material for every public API exposed by :app:`Pyramid`: |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | * |
| | |
| | | .. autointerface:: IRenderer |
| | | :members: |
| | | |
| | | .. autointerface:: IResponseFactory |
| | | :members: |
| | | |
| | | .. autointerface:: IViewMapperFactory |
| | | :members: |
| | | |
| | |
| | | .. autointerface:: IResourceURL |
| | | :members: |
| | | |
| | | .. autointerface:: ICacheBuster |
| | | :members: |
| | |
| | | accessed as ``request.registry.settings`` or |
| | | ``config.registry.settings`` in a typical Pyramid application. |
| | | |
| | | .. attribute:: package_name |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | When a registry is set up (or created) by a :term:`Configurator`, this |
| | | attribute will be the shortcut for |
| | | :attr:`pyramid.config.Configurator.package_name`. |
| | | |
| | | This attribute is often accessed as ``request.registry.package_name`` or |
| | | ``config.registry.package_name`` or ``config.package_name`` |
| | | in a typical Pyramid application. |
| | | |
| | | .. attribute:: introspector |
| | | |
| | | .. versionadded:: 1.3 |
| | |
| | | |
| | | .. versionadded:: 1.5 |
| | | |
| | | A property which returns the userid of the currently authenticated user |
| | | or ``None`` if there is no :term:`authentication policy` in effect or |
| | | there is no currently authenticated user. This differs from |
| | | :attr:`~pyramid.request.Request.unauthenticated_userid`, because the |
| | | effective authentication policy will have ensured that a record |
| | | associated with the userid exists in persistent storage; if it has |
| | | not, this value will be ``None``. |
| | | A property which returns the :term:`userid` of the currently |
| | | authenticated user or ``None`` if there is no :term:`authentication |
| | | policy` in effect or there is no currently authenticated user. This |
| | | differs from :attr:`~pyramid.request.Request.unauthenticated_userid`, |
| | | because the effective authentication policy will have ensured that a |
| | | record associated with the :term:`userid` exists in persistent storage; |
| | | if it has not, this value will be ``None``. |
| | | |
| | | .. attribute:: unauthenticated_userid |
| | | |
| | | .. versionadded:: 1.5 |
| | | |
| | | A property which returns a value which represents the *claimed* (not |
| | | verified) user id of the credentials present in the request. ``None`` if |
| | | there is no :term:`authentication policy` in effect or there is no user |
| | | data associated with the current request. This differs from |
| | | :attr:`~pyramid.request.Request.authenticated_userid`, because the |
| | | effective authentication policy will not ensure that a record associated |
| | | with the userid exists in persistent storage. Even if the userid |
| | | does not exist in persistent storage, this value will be the value |
| | | of the userid *claimed* by the request data. |
| | | verified) :term:`userid` of the credentials present in the |
| | | request. ``None`` if there is no :term:`authentication policy` in effect |
| | | or there is no user data associated with the current request. This |
| | | differs from :attr:`~pyramid.request.Request.authenticated_userid`, |
| | | because the effective authentication policy will not ensure that a |
| | | record associated with the :term:`userid` exists in persistent storage. |
| | | Even if the :term:`userid` does not exist in persistent storage, this |
| | | value will be the value of the :term:`userid` *claimed* by the request |
| | | data. |
| | | |
| | | .. attribute:: effective_principals |
| | | |
| | | .. versionadded:: 1.5 |
| | | |
| | | A property which returns the list of 'effective' :term:`principal` |
| | | identifiers for this request. This will include the userid of the |
| | | currently authenticated user if a user is currently authenticated. If no |
| | | :term:`authentication policy` is in effect, this will return a sequence |
| | | containing only the :attr:`pyramid.security.Everyone` principal. |
| | | identifiers for this request. This list typically includes the |
| | | :term:`userid` of the currently authenticated user if a user is |
| | | currently authenticated, but this depends on the |
| | | :term:`authentication policy` in effect. If no :term:`authentication |
| | | policy` is in effect, this will return a sequence containing only the |
| | | :attr:`pyramid.security.Everyone` principal. |
| | | |
| | | .. method:: invoke_subrequest(request, use_tweens=False) |
| | | |
| | |
| | | that used as ``request.GET``, ``request.POST``, and ``request.params``), |
| | | see :class:`pyramid.interfaces.IMultiDict`. |
| | | |
| | | .. autofunction:: apply_request_extensions(request) |
| | |
| | | |
| | | .. autofunction:: forget |
| | | |
| | | .. autofunction:: remember |
| | | .. autofunction:: remember(request, userid, **kwargs) |
| | | |
| | | Authorization API Functions |
| | | --------------------------- |
| | |
| | | :members: |
| | | :inherited-members: |
| | | |
| | | .. autoclass:: PathSegmentCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: PathSegmentMd5CacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringMd5CacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringConstantCacheBuster |
| | | :members: |
| | |
| | | 'http://docs.pylonsproject.org/projects/deform/en/latest', |
| | | None), |
| | | 'sqla': ('http://docs.sqlalchemy.org/en/latest', None), |
| | | 'who': ('http://docs.repoze.org/who/latest', None), |
| | | 'who': ('http://repozewho.readthedocs.org/en/latest', None), |
| | | 'python': ('http://docs.python.org', None), |
| | | 'python3': ('http://docs.python.org/3', None), |
| | | 'tstring': |
| | |
| | | An object which, provided a :term:`WSGI` environment as a single |
| | | positional argument, returns a Pyramid-compatible request. |
| | | |
| | | response factory |
| | | An object which, provided a :term:`request` as a single positional |
| | | argument, returns a Pyramid-compatible response. See |
| | | :class:`pyramid.interfaces.IResponseFactory`. |
| | | |
| | | response |
| | | An object returned by a :term:`view callable` that represents response |
| | | data returned to the requesting user agent. It must implement the |
| | |
| | | :term:`authorization policy`. |
| | | |
| | | principal |
| | | A *principal* is a string or unicode object representing a userid |
| | | or a group id. It is provided by an :term:`authentication |
| | | policy`. For example, if a user had the user id "bob", and Bob |
| | | was part of two groups named "group foo" and "group bar", the |
| | | request might have information attached to it that would |
| | | indicate that Bob was represented by three principals: "bob", |
| | | "group foo" and "group bar". |
| | | A *principal* is a string or unicode object representing an |
| | | entity, typically a user or group. Principals are provided by an |
| | | :term:`authentication policy`. For example, if a user had the |
| | | :term:`userid` `"bob"`, and was part of two groups named `"group foo"` |
| | | and "group bar", the request might have information attached to |
| | | it that would indicate that Bob was represented by three |
| | | principals: `"bob"`, `"group foo"` and `"group bar"`. |
| | | |
| | | userid |
| | | A *userid* is a string or unicode object used to identify and |
| | | authenticate a real-world user (or client). A userid is |
| | | supplied to an :term:`authentication policy` in order to discover |
| | | the user's :term:`principals <principal>`. The default behavior |
| | | of the authentication policies :app:`Pyramid` provides is to |
| | | return the user's userid as a principal, but this is not strictly |
| | | necessary in custom policies that define their principals differently. |
| | | |
| | | authorization policy |
| | | An authorization policy in :app:`Pyramid` terms is a bit of |
| | |
| | | made. For example the word "java" might be translated |
| | | differently if the translation domain is "programming-languages" |
| | | than would be if the translation domain was "coffee". A |
| | | translation domain is represnted by a collection of ``.mo`` files |
| | | translation domain is represented by a collection of ``.mo`` files |
| | | within one or more :term:`translation directory` directories. |
| | | |
| | | Translation Context |
| | | A string representing the "context" in which a translation was |
| | | made within a given :term:`translation domain`. See the gettext |
| | | documentation, `11.2.5 Using contexts for solving ambiguities |
| | | <https://www.gnu.org/software/gettext/manual/gettext.html#Contexts>`_ |
| | | for more information. |
| | | |
| | | Translator |
| | | A callable which receives a :term:`translation string` and returns a |
| | | translated Unicode object for the purposes of internationalization. A |
| | |
| | | tutorials/wiki/index.rst |
| | | tutorials/modwsgi/index.rst |
| | | |
| | | .. _html_api_documentation: |
| | | |
| | | API Documentation |
| | | ================= |
| | | |
| | |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | api/index |
| | | api/* |
| | | |
| | | Change History |
| | |
| | | request = testing.DummyRequest() |
| | | info = my_view(request) |
| | | self.assertEqual(info['project'], 'MyProject') |
| | | |
| | | class ViewIntegrationTests(unittest.TestCase): |
| | | def setUp(self): |
| | | """ This sets up the application registry with the |
| | | registrations your application declares in its ``includeme`` |
| | | function. |
| | | """ |
| | | self.config = testing.setUp() |
| | | self.config.include('myproject') |
| | | |
| | | def tearDown(self): |
| | | """ Clear out the application registry """ |
| | | testing.tearDown() |
| | | |
| | | def test_my_view(self): |
| | | from myproject.views import my_view |
| | | request = testing.DummyRequest() |
| | | result = my_view(request) |
| | | self.assertEqual(result.status, '200 OK') |
| | | body = result.app_iter[0] |
| | | self.assertTrue('Welcome to' in body) |
| | | self.assertEqual(len(result.headerlist), 2) |
| | | self.assertEqual(result.headerlist[0], |
| | | ('Content-Type', 'text/html; charset=UTF-8')) |
| | | self.assertEqual(result.headerlist[1], ('Content-Length', |
| | | str(len(body)))) |
| | | |
| | | class FunctionalTests(unittest.TestCase): |
| | | def setUp(self): |
| | | from myproject import main |
| | | app = main({}) |
| | | from webtest import TestApp |
| | | self.testapp = TestApp(app) |
| | | |
| | | def test_root(self): |
| | | res = self.testapp.get('/', status=200) |
| | | self.assertTrue('Pyramid' in res.body) |
| | |
| | | import os |
| | | """Setup for the MyProject package. |
| | | |
| | | """ |
| | | import os |
| | | from setuptools import setup, find_packages |
| | | |
| | | here = os.path.abspath(os.path.dirname(__file__)) |
| | | with open(os.path.join(here, 'README.txt')) as f: |
| | | README = f.read() |
| | | with open(os.path.join(here, 'CHANGES.txt')) as f: |
| | | CHANGES = f.read() |
| | | |
| | | requires = [ |
| | | HERE = os.path.abspath(os.path.dirname(__file__)) |
| | | |
| | | |
| | | with open(os.path.join(HERE, 'README.txt')) as fp: |
| | | README = fp.read() |
| | | |
| | | |
| | | with open(os.path.join(HERE, 'CHANGES.txt')) as fp: |
| | | CHANGES = fp.read() |
| | | |
| | | |
| | | REQUIRES = [ |
| | | 'pyramid', |
| | | 'pyramid_chameleon', |
| | | 'pyramid_debugtoolbar', |
| | | 'waitress', |
| | | ] |
| | | |
| | | TESTS_REQUIRE = [ |
| | | 'webtest' |
| | | ] |
| | | |
| | | setup(name='MyProject', |
| | |
| | | description='MyProject', |
| | | long_description=README + '\n\n' + CHANGES, |
| | | classifiers=[ |
| | | "Programming Language :: Python", |
| | | "Framework :: Pyramid", |
| | | "Topic :: Internet :: WWW/HTTP", |
| | | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", |
| | | ], |
| | | 'Programming Language :: Python', |
| | | 'Framework :: Pyramid', |
| | | 'Topic :: Internet :: WWW/HTTP', |
| | | 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', |
| | | ], |
| | | author='', |
| | | author_email='', |
| | | url='', |
| | |
| | | packages=find_packages(), |
| | | include_package_data=True, |
| | | zip_safe=False, |
| | | install_requires=requires, |
| | | tests_require=requires, |
| | | test_suite="myproject", |
| | | install_requires=REQUIRES, |
| | | tests_require=TESTS_REQUIRE, |
| | | test_suite='myproject', |
| | | entry_points="""\ |
| | | [paste.app_factory] |
| | | main = myproject:main |
| | | """, |
| | | ) |
| | | """) |
| | |
| | | ``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is |
| | | a URL), while keeping static media package-internal and served by the |
| | | development webserver during development (if the ``name`` argument to |
| | | :meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). To |
| | | create such a circumstance, we suggest using the |
| | | :attr:`pyramid.registry.Registry.settings` API in conjunction with a setting |
| | | in the application ``.ini`` file named ``media_location``. Then set the |
| | | value of ``media_location`` to either a prefix or a URL depending on whether |
| | | the application is being run in development or in production (use a different |
| | | ``.ini`` file for production than you do for development). This is just a |
| | | suggestion for a pattern; any setting name other than ``media_location`` |
| | | could be used. |
| | | :meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). |
| | | |
| | | For example, we may define a :ref:`custom setting <adding_a_custom_setting>` |
| | | named ``media_location`` which we can set to an external URL in production |
| | | when our assets are hosted on a CDN. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | media_location = settings.get('media_location', 'static') |
| | | |
| | | config = Configurator(settings=settings) |
| | | config.add_static_view(path='myapp:static', name=media_location) |
| | | |
| | | Now we can optionally define the setting in our ini file: |
| | | |
| | | .. code-block:: ini |
| | | :linenos: |
| | | |
| | | # production.ini |
| | | [app:main] |
| | | use = egg:myapp#main |
| | | |
| | | media_location = http://static.example.com/ |
| | | |
| | | It is also possible to serve assets that live outside of the source by |
| | | referring to an absolute path on the filesystem. There are two ways to |
| | | accomplish this. |
| | | |
| | | First, :meth:`~pyramid.config.Configurator.add_static_view` |
| | | supports taking an absolute path directly instead of an asset spec. This works |
| | | as expected, looking in the file or folder of files and serving them up at |
| | | some URL within your application or externally. Unfortunately, this technique |
| | | has a drawback that it is not possible to use the |
| | | :meth:`~pyramid.request.Request.static_url` method to generate URLs, since it |
| | | works based on an asset spec. |
| | | |
| | | The second approach, available in Pyramid 1.6+, uses the asset overriding |
| | | APIs described in the :ref:`overriding_assets_section` section. It is then |
| | | possible to configure a "dummy" package which then serves its file or folder |
| | | from an absolute path. |
| | | |
| | | .. code-block:: python |
| | | |
| | | config.add_static_view(path='myapp:static_images', name='static') |
| | | config.override_asset(to_override='myapp:static_images/', |
| | | override_with='/abs/path/to/images/') |
| | | |
| | | From this configuration it is now possible to use |
| | | :meth:`~pyramid.request.Request.static_url` to generate URLs to the data |
| | | in the folder by doing something like |
| | | ``request.static_url('myapp:static_images/foo.png')``. While it is not |
| | | necessary that the ``static_images`` file or folder actually exist in the |
| | | ``myapp`` package, it is important that the ``myapp`` portion points to a |
| | | valid package. If the folder does exist then the overriden folder is given |
| | | priority if the file's name exists in both locations. |
| | | |
| | | .. index:: |
| | | single: Cache Busting |
| | | |
| | | .. _cache_busting: |
| | | |
| | | Cache Busting |
| | | ------------- |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | In order to maximize performance of a web application, you generally want to |
| | | limit the number of times a particular client requests the same static asset. |
| | | Ideally a client would cache a particular static asset "forever", requiring |
| | | it to be sent to the client a single time. The HTTP protocol allows you to |
| | | send headers with an HTTP response that can instruct a client to cache a |
| | | particular asset for an amount of time. As long as the client has a copy of |
| | | the asset in its cache and that cache hasn't expired, the client will use the |
| | | cached copy rather than request a new copy from the server. The drawback to |
| | | sending cache headers to the client for a static asset is that at some point |
| | | the static asset may change, and then you'll want the client to load a new copy |
| | | of the asset. Under normal circumstances you'd just need to wait for the |
| | | client's cached copy to expire before they get the new version of the static |
| | | resource. |
| | | |
| | | A commonly used workaround to this problem is a technique known as "cache |
| | | busting". Cache busting schemes generally involve generating a URL for a |
| | | static asset that changes when the static asset changes. This way headers can |
| | | be sent along with the static asset instructing the client to cache the asset |
| | | for a very long time. When a static asset is changed, the URL used to refer to |
| | | it in a web page also changes, so the client sees it as a new resource and |
| | | requests a copy, regardless of any caching policy set for the resource's old |
| | | URL. |
| | | |
| | | :app:`Pyramid` can be configured to produce cache busting URLs for static |
| | | assets by passing the optional argument, ``cachebust`` to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=True) |
| | | |
| | | Setting the ``cachebust`` argument instructs :app:`Pyramid` to use a cache |
| | | busting scheme which adds the md5 checksum for a static asset as a path segment |
| | | in the asset's URL: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | js_url = request.static_url('mypackage:folder/static/js/myapp.js') |
| | | # Returns: 'http://www.example.com/static/c9658b3c0a314a1ca21e5988e662a09e/js/myapp.js` |
| | | |
| | | When the asset changes, so will its md5 checksum, and therefore so will its |
| | | URL. Supplying the ``cachebust`` argument also causes the static view to set |
| | | headers instructing clients to cache the asset for ten years, unless the |
| | | ``max_cache_age`` argument is also passed, in which case that value is used. |
| | | |
| | | .. note:: |
| | | |
| | | md5 checksums are cached in RAM so if you change a static resource without |
| | | restarting your application, you may still generate URLs with a stale md5 |
| | | checksum. |
| | | |
| | | Disabling the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | It can be useful in some situations (e.g. development) to globally disable all |
| | | configured cache busters without changing calls to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`. To do this set the |
| | | ``PYRAMID_PREVENT_CACHEBUST`` environment variable or the |
| | | ``pyramid.prevent_cachebust`` configuration value to a true value. |
| | | |
| | | Customizing the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Revisiting from the previous section: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=True) |
| | | |
| | | Setting ``cachebust`` to ``True`` instructs :app:`Pyramid` to use a default |
| | | cache busting implementation that should work for many situations. The |
| | | ``cachebust`` may be set to any object that implements the interface, |
| | | :class:`~pyramid.interfaces.ICacheBuster`. The above configuration is exactly |
| | | equivalent to: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.static import PathSegmentMd5CacheBuster |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=PathSegmentMd5CacheBuster()) |
| | | |
| | | :app:`Pyramid` includes a handful of ready to use cache buster implementations: |
| | | :class:`~pyramid.static.PathSegmentMd5CacheBuster`, which inserts an md5 |
| | | checksum token in the path portion of the asset's URL, |
| | | :class:`~pyramid.static.QueryStringMd5CacheBuster`, which adds an md5 checksum |
| | | token to the query string of the asset's URL, and |
| | | :class:`~pyramid.static.QueryStringConstantCacheBuster`, which adds an |
| | | arbitrary token you provide to the query string of the asset's URL. |
| | | |
| | | In order to implement your own cache buster, you can write your own class from |
| | | 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 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 PathSegmentCacheBuster |
| | | |
| | | 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 |
| | | get the current commit's SHA1 to use as the cache bust token. |
| | | """ |
| | | def __init__(self): |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | self.sha1 = subprocess.check_output( |
| | | ['git', 'rev-parse', 'HEAD'], |
| | | cwd=here).strip() |
| | | |
| | | def tokenize(self, pathspec): |
| | | return self.sha1 |
| | | |
| | | Choosing a Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The default cache buster implementation, |
| | | :class:`~pyramid.static.PathSegmentMd5CacheBuster`, works very well assuming |
| | | that you're using :app:`Pyramid` to serve your static assets. The md5 checksum |
| | | is fine grained enough that browsers should only request new versions of |
| | | specific assets that have changed. Many caching HTTP proxies will fail to |
| | | cache a resource if the URL contains a query string. In general, therefore, |
| | | you should prefer a cache busting strategy which modifies the path segment to |
| | | a strategy which adds a query string. |
| | | |
| | | It is possible, however, that your static assets are being served by another |
| | | web server or externally on a CDN. In these cases modifying the path segment |
| | | for a static asset URL would cause the external service to fail to find the |
| | | asset, causing your customer to get a 404. In these cases you would need to |
| | | fall back to a cache buster which adds a query string. It is even possible |
| | | that there isn't a copy of your static assets available to the :app:`Pyramid` |
| | | application, so a cache busting implementation that generates md5 checksums |
| | | would fail since it can't access the assets. In such a case, |
| | | :class:`~pyramid.static.QueryStringConstantCacheBuster` is a reasonable |
| | | fallback. The following code would set up a cachebuster that just uses the |
| | | time at start up as a cachebust token: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | import time |
| | | from pyramid.static import QueryStringConstantCacheBuster |
| | | |
| | | config.add_static_view( |
| | | name='http://mycdn.example.com/', |
| | | path='mypackage:static', |
| | | cachebust=QueryStringConstantCacheBuster(str(time.time()))) |
| | | |
| | | .. index:: |
| | | single: static assets view |
| | |
| | | :func:`pkg_resources.get_resource_string` APIs will obtain an overridden file |
| | | when an override is used. |
| | | |
| | | As of Pyramid 1.6, it is also possible to override an asset by supplying an |
| | | absolute path to a file or directory. This may be useful if the assets are |
| | | not distributed as part of a Python package. |
| | |
| | | |
| | | .. code-block:: text |
| | | |
| | | $ $VENV/bin starter/development.ini#main |
| | | $ $VENV/bin/pshell starter/development.ini#main |
| | | Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) |
| | | [GCC 4.4.3] on linux2 |
| | | Type "help" for more information. |
| | |
| | | :linenos: |
| | | |
| | | $ $VENV/bin/proutes development.ini |
| | | Name Pattern View |
| | | ---- ------- ---- |
| | | home / <function my_view> |
| | | home2 / <function my_view> |
| | | another /another None |
| | | static/ static/*subpath <static_view object> |
| | | catchall /*subpath <function static_view> |
| | | Name Pattern View |
| | | ---- ------- ---- |
| | | debugtoolbar /_debug_toolbar/*subpath <wsgiapp> * |
| | | __static/ /static/*subpath dummy_starter:static/ * |
| | | __static2/ /static2/*subpath /var/www/static/ * |
| | | __pdt_images/ /pdt_images/*subpath pyramid_debugtoolbar:static/img/ * |
| | | a / <unknown> * |
| | | no_view_attached / <unknown> * |
| | | route_and_view_attached / app1.standard_views.route_and_view_attached * |
| | | method_conflicts /conflicts app1.standard_conflicts <route mismatch> |
| | | multiview /multiview app1.standard_views.multiview GET,PATCH |
| | | not_post /not_post app1.standard_views.multview !POST,* |
| | | |
| | | ``proutes`` generates a table with three columns: *Name*, *Pattern*, |
| | | ``proutes`` generates a table with four columns: *Name*, *Pattern*, *Method*, |
| | | and *View*. The items listed in the |
| | | Name column are route names, the items listed in the Pattern column are route |
| | | patterns, and the items listed in the View column are representations of the |
| | | view callable that will be invoked when a request matches the associated |
| | | route pattern. The view column may show ``None`` if no associated view |
| | | route pattern. The view column may show ``<unknown>`` if no associated view |
| | | callable could be found. If no routes are configured within your |
| | | application, nothing will be printed to the console when ``proutes`` |
| | | is executed. |
| | | |
| | | It is convenient when using the ``proutes`` often to configure which columns |
| | | and the order you would like to view them. To facilitate this, ``proutes`` will |
| | | look for a special ``[proutes]`` section in your INI file and use those as |
| | | defaults. |
| | | |
| | | For example you may remove request method and place the view first: |
| | | |
| | | .. code-block:: text |
| | | :linenos: |
| | | |
| | | [proutes] |
| | | format = view |
| | | name |
| | | pattern |
| | | |
| | | You can also separate the formats with commas or spaces: |
| | | |
| | | .. code-block:: text |
| | | :linenos: |
| | | |
| | | [proutes] |
| | | format = view name pattern |
| | | |
| | | [proutes] |
| | | format = view, name, pattern |
| | | |
| | | If you want to temporarily configure the columns and order there is the |
| | | ``--format`` which is a comma separated list of columns you want to include. The |
| | | current available formats are ``name``, ``pattern``, ``view``, and ``method``. |
| | | |
| | | |
| | | .. index:: |
| | | pair: tweens; printing |
| | | single: ptweens |
| | |
| | | referred to within this documentation as "configuration"; you are configuring |
| | | :app:`Pyramid` to call the code that makes up your application. |
| | | |
| | | .. seealso:: |
| | | For information on ``.ini`` files for Pyramid applications see the |
| | | :ref:`startup_chapter` chapter. |
| | | |
| | | There are two ways to configure a :app:`Pyramid` application: |
| | | :term:`imperative configuration` and :term:`declarative configuration`. Both |
| | | are described below. |
| | |
| | | single: reload settings |
| | | single: default_locale_name |
| | | single: environment variables |
| | | single: Mako environment settings |
| | | single: ini file settings |
| | | single: PasteDeploy settings |
| | | |
| | |
| | | +=================================+==================================+ |
| | | | ``PYRAMID_PREVENT_HTTP_CACHE`` | ``pyramid.prevent_http_cache`` | |
| | | | | or ``prevent_http_cache`` | |
| | | | | | |
| | | | | | |
| | | +---------------------------------+----------------------------------+ |
| | | |
| | | Preventing Cache Busting |
| | | ------------------------ |
| | | |
| | | Prevent the ``cachebust`` static view configuration argument from having any |
| | | effect globally in this process when this value is true. No cache buster will |
| | | be configured or used when this is true. |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | .. seealso:: |
| | | |
| | | See also :ref:`cache_busting`. |
| | | |
| | | +---------------------------------+----------------------------------+ |
| | | | Environment Variable Name | Config File Setting Name | |
| | | +=================================+==================================+ |
| | | | ``PYRAMID_PREVENT_CACHEBUST`` | ``pyramid.prevent_cachebust`` | |
| | | | | or ``prevent_cachebust`` | |
| | | | | | |
| | | | | | |
| | | +---------------------------------+----------------------------------+ |
| | |
| | | config = Configurator(settings=settings) |
| | | |
| | | It is fine to use both or either form. |
| | | |
| | | .. _mako_template_renderer_settings: |
| | | |
| | | Mako Template Render Settings |
| | | ----------------------------- |
| | | |
| | | Mako derives additional settings to configure its template renderer that |
| | | should be set when using it. Many of these settings are optional and only need |
| | | to be set if they should be different from the default. The Mako Template |
| | | Renderer uses a subclass of Mako's `template lookup |
| | | <http://www.makotemplates.org/docs/usage.html#usage_lookup>`_ and accepts |
| | | several arguments to configure it. |
| | | |
| | | Mako Directories |
| | | ~~~~~~~~~~~~~~~~ |
| | | |
| | | The value(s) supplied here are passed in as the template directories. They |
| | | should be in :term:`asset specification` format, for example: |
| | | ``my.package:templates``. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.directories`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Module Directory |
| | | ~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The value supplied here tells Mako where to store compiled Mako templates. If |
| | | omitted, compiled templates will be stored in memory. This value should be an |
| | | absolute path, for example: ``%(here)s/data/templates`` would use a directory |
| | | called ``data/templates`` in the same parent directory as the INI file. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.module_directory`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Input Encoding |
| | | ~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The encoding that Mako templates are assumed to have. By default this is set |
| | | to ``utf-8``. If you wish to use a different template encoding, this value |
| | | should be changed accordingly. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.input_encoding`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Error Handler |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | |
| | | A callable (or a :term:`dotted Python name` which names a callable) which is |
| | | called whenever Mako compile or runtime exceptions occur. The callable is |
| | | passed the current context as well as the exception. If the callable returns |
| | | True, the exception is considered to be handled, else it is re-raised after |
| | | the function completes. Is used to provide custom error-rendering functions. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.error_handler`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Default Filters |
| | | ~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | List of string filter names that will be applied to all Mako expressions. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.default_filters`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Import |
| | | ~~~~~~~~~~~ |
| | | |
| | | String list of Python statements, typically individual "import" lines, which |
| | | will be placed into the module level preamble of all generated Python modules. |
| | | |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.imports`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | |
| | | Mako Strict Undefined |
| | | ~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | ``true`` or ``false``, representing the "strict undefined" behavior of Mako |
| | | (see `Mako Context Variables |
| | | <http://www.makotemplates.org/docs/runtime.html#context-variables>`_). By |
| | | default, this is ``false``. |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.strict_undefined`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Mako Preprocessor |
| | | ~~~~~~~~~~~~~~~~~ |
| | | |
| | | .. versionadded:: 1.1 |
| | | |
| | | A callable (or a :term:`dotted Python name` which names a callable) which is |
| | | called to preprocess the source before the template is called. The callable |
| | | will be passed the full template source before it is parsed. The return |
| | | result of the callable will be used as the template source code. |
| | | |
| | | |
| | | +-----------------------------+ |
| | | | Config File Setting Name | |
| | | +=============================+ |
| | | | ``mako.preprocessor`` | |
| | | | | |
| | | | | |
| | | | | |
| | | +-----------------------------+ |
| | | |
| | | Examples |
| | | -------- |
| | |
| | | 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 |
| | |
| | | the property |
| | | |
| | | .. index:: |
| | | single: response factory |
| | | |
| | | .. _changing_the_response_factory: |
| | | |
| | | Changing the Response Factory |
| | | ------------------------------- |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | Whenever :app:`Pyramid` returns a response from a view it creates a |
| | | :term:`response` object. By default, an instance of the |
| | | :class:`pyramid.response.Response` class is created to represent the response |
| | | object. |
| | | |
| | | The factory that :app:`Pyramid` uses to create a response object instance can be |
| | | changed by passing a :class:`pyramid.interfaces.IResponseFactory` argument to |
| | | the constructor of the :term:`configurator`. This argument can be either a |
| | | callable or a :term:`dotted Python name` representing a callable. |
| | | |
| | | The factory takes a single positional argument, which is a :term:`Request` |
| | | object. The argument may be ``None``. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.response import Response |
| | | |
| | | class MyResponse(Response): |
| | | pass |
| | | |
| | | config = Configurator(response_factory=lambda r: MyResponse()) |
| | | |
| | | If you're doing imperative configuration, and you'd rather do it after you've |
| | | already constructed a :term:`configurator` it can also be registered via the |
| | | :meth:`pyramid.config.Configurator.set_response_factory` method: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.config import Configurator |
| | | from pyramid.response import Response |
| | | |
| | | class MyResponse(Response): |
| | | pass |
| | | |
| | | config = Configurator() |
| | | config.set_response_factory(lambda r: MyResponse()) |
| | | |
| | | .. index:: |
| | | single: before render event |
| | | single: adding renderer globals |
| | | |
| | |
| | | :class:`pyramid.response.Response` object in any capacity at all, you'll have |
| | | to make sure the object implements every attribute and method outlined in |
| | | :class:`pyramid.interfaces.IResponse` and you'll have to ensure that it uses |
| | | ``zope.interface.implementer(IResponse)`` as a class decoratoror. |
| | | ``zope.interface.implementer(IResponse)`` as a class decorator. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pryamid.static import static_view |
| | | from pyramid.static import static_view |
| | | |
| | | www = static_view('mypackage:static', use_subpath=True) |
| | | |
| | |
| | | |
| | | $ cd /place/where/myapplication/setup.py/lives |
| | | $ mkdir -p myapplication/locale |
| | | $ $VENV/bin/pot-create src > myapplication/locale/myapplication.pot |
| | | $ $VENV/bin/pot-create -o myapplication/locale/myapplication.pot src |
| | | |
| | | The message catalog ``.pot`` template will end up in: |
| | | |
| | |
| | | $ cd /place/where/myapplication/setup.py/lives |
| | | $ cd myapplication/locale |
| | | $ mkdir -p es/LC_MESSAGES |
| | | $ msginit -l es es/LC_MESSAGES/myapplication.po |
| | | $ msginit -l es -o es/LC_MESSAGES/myapplication.po |
| | | |
| | | This will create a new the message catalog ``.po`` file will in: |
| | | This will create a new message catalog ``.po`` file in: |
| | | |
| | | ``myapplication/locale/es/LC_MESSAGES/myapplication.po``. |
| | | |
| | |
| | | .. code-block:: text |
| | | |
| | | $ cd /place/where/myapplication/setup.py/lives |
| | | $ msgfmt myapplication/locale/*/LC_MESSAGES/*.po |
| | | $ msgfmt -o myapplication/locale/es/LC_MESSAGES/myapplication.mo myapplication/locale/es/LC_MESSAGES/myapplication.po |
| | | |
| | | This will create a ``.mo`` file for each ``.po`` file in your |
| | | application. As long as the :term:`translation directory` in which |
| | | the ``.mo`` file ends up in is configured into your application, these |
| | | the ``.mo`` file ends up in is configured into your application (see :ref:`adding_a_translation_directory`), these |
| | | translations will be available to :app:`Pyramid`. |
| | | |
| | | .. index:: |
| | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.threadlocal import get_current_registry |
| | | settings = get_current_registry().settings |
| | | languages = settings['available_languages'].split() |
| | | from pyramid.settings import aslist |
| | | |
| | | def my_locale_negotiator(request): |
| | | languages = aslist(request.registry.settings['available_languages']) |
| | | # ... |
| | | |
| | | This is only a suggestion. You can create your own "available |
| | | languages" configuration scheme as necessary. |
| | |
| | | ``subscriber`` |
| | | |
| | | The subscriber callable object (the resolution of the ``subscriber`` |
| | | argument passed to ``add_susbcriber``). |
| | | argument passed to ``add_subscriber``). |
| | | |
| | | ``interfaces`` |
| | | |
| | |
| | | ``predicates`` |
| | | |
| | | The predicate objects created as the result of passing predicate arguments |
| | | to ``add_susbcriber`` |
| | | to ``add_subscriber`` |
| | | |
| | | ``derived_predicates`` |
| | | |
| | | Wrappers around the predicate objects created as the result of passing |
| | | predicate arguments to ``add_susbcriber`` (to be used when predicates take |
| | | predicate arguments to ``add_subscriber`` (to be used when predicates take |
| | | only one value but must be passed more than one). |
| | | |
| | | ``response adapters`` |
| | |
| | | The :class:`pyramid.interfaces.IRendererInfo` object which represents |
| | | this template's renderer. |
| | | |
| | | ``view mapper`` |
| | | ``view mappers`` |
| | | |
| | | Each introspectable in the ``permissions`` category represents a call to |
| | | Each introspectable in the ``view mappers`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_view` that has an explicit |
| | | ``mapper`` argument to *or* a call to |
| | | :meth:`pyramid.config.Configurator.set_view_mapper`; each will have |
| | |
| | | |
| | | ``translation directories`` |
| | | |
| | | Each introspectable in the ``asset overrides`` category represents an |
| | | individual element in a ``specs`` argument passed to |
| | | Each introspectable in the ``translation directories`` category represents |
| | | an individual element in a ``specs`` argument passed to |
| | | :meth:`pyramid.config.Configurator.add_translation_dirs`; each will have |
| | | the following data. |
| | | |
| | |
| | | |
| | | ``type`` |
| | | |
| | | ``implict`` or ``explicit`` as a string. |
| | | ``implicit`` or ``explicit`` as a string. |
| | | |
| | | ``under`` |
| | | |
| | |
| | | a third-party scaffold which does not create these files, the |
| | | configuration information in this chapter may not be applicable. |
| | | |
| | | .. index: |
| | | pair: settings; logging |
| | | pair: .ini; logging |
| | | pair: logging; configuration |
| | | |
| | | .. _logging_config: |
| | | |
| | | Logging Configuration |
| | |
| | | [logger_myapp] |
| | | level = DEBUG |
| | | handlers = |
| | | qualname = helloworld |
| | | qualname = myapp |
| | | |
| | | All of the child loggers of the ``myapp`` logger will inherit the ``DEBUG`` |
| | | level unless they're explicitly set differently. Meaning the ``myapp.views``, |
| | | ``myapp.models`` (and all your app's modules') loggers by default have an |
| | | effective level of ``DEBUG`` too. |
| | | |
| | | For more advanced filtering, the logging module provides a `Filter |
| | | <http://docs.python.org/lib/node423.html>`_ object; however it cannot be used |
| | | directly from the configuration file. |
| | | For more advanced filtering, the logging module provides a |
| | | :class:`logging.Filter` object; however it cannot be used directly from the |
| | | configuration file. |
| | | |
| | | Advanced Configuration |
| | | Advanced Configuration |
| | | ---------------------- |
| | | |
| | | To capture log output to a separate file, use a `FileHandler |
| | | <http://docs.python.org/lib/node412.html>`_ (or a `RotatingFileHandler |
| | | <http://docs.python.org/lib/node413.html>`_): |
| | | To capture log output to a separate file, use :class:`logging.FileHandler` (or |
| | | :class:`logging.handlers.RotatingFileHandler`): |
| | | |
| | | .. code-block:: ini |
| | | |
| | |
| | | in its `documentation |
| | | <http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_. |
| | | |
| | | .. index:: |
| | | single: TransLogger |
| | | single: middleware; TransLogger |
| | | pair: configuration; middleware |
| | | single: settings; middleware |
| | | pair: .ini; middleware |
| | | |
| | | .. _request_logging_with_pastes_translogger: |
| | | |
| | | Request Logging with Paste's TransLogger |
| | | ---------------------------------------- |
| | | |
| | | Paste provides the `TransLogger |
| | | <http://pythonpaste.org/modules/translogger.html>`_ :term:`middleware` for |
| | | logging requests using the `Apache Combined Log Format |
| | | <http://httpd.apache.org/docs/2.2/logs.html#combined>`_. TransLogger combined |
| | | with a FileHandler can be used to create an ``access.log`` file similar to |
| | | Apache's. |
| | | The term:`WSGI` design is modular. Waitress logs error conditions, debugging |
| | | output, etc., but not web traffic. For web traffic logging Paste provides the |
| | | `TransLogger <http://pythonpaste.org/modules/translogger.html>`_ |
| | | :term:`middleware`. TransLogger produces logs in the `Apache Combined Log |
| | | Format <http://httpd.apache.org/docs/2.2/logs.html#combined>`_. But |
| | | TransLogger does not write to files, the Python logging system must be |
| | | configured to do this. The Python :class:`logging.FileHandler` logging |
| | | handler can be used alongside TransLogger to create an ``access.log`` file |
| | | similar to Apache's. |
| | | |
| | | Like any standard :term:`middleware` with a Paste entry point, TransLogger can |
| | | be configured to wrap your application using ``.ini`` file syntax. First, |
| | |
| | | app = TransLogger(app, setup_console_handler=False) |
| | | return app |
| | | |
| | | TransLogger will automatically setup a logging handler to the console when |
| | | called with no arguments, so it 'just works' in environments that don't |
| | | configure logging. Since we've configured our own logging handlers, we need |
| | | to disable that option via ``setup_console_handler = False``. |
| | | |
| | | .. note:: |
| | | TransLogger will automatically setup a logging handler to the console when |
| | | called with no arguments, so it 'just works' in environments that don't |
| | | configure logging. Since our logging handlers are configured we disable |
| | | the automation via ``setup_console_handler = False``. |
| | | |
| | | With the filter in place, TransLogger's logger (named the ``wsgi`` logger) will |
| | | propagate its log messages to the parent logger (the root logger), sending |
| | |
| | | "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.6) Gecko/20070725 |
| | | Firefox/2.0.0.6" |
| | | |
| | | To direct TransLogger to an ``access.log`` FileHandler, we need to add that |
| | | FileHandler to the list of handlers (named ``accesslog``), and ensure that the |
| | | ``wsgi`` logger is configured and uses this handler accordingly: |
| | | To direct TransLogger to an ``access.log`` FileHandler, we need the following |
| | | to add a FileHandler (named ``accesslog``) to the list of handlers, and ensure |
| | | that the ``wsgi`` logger is configured and uses this handler accordingly: |
| | | |
| | | .. code-block:: ini |
| | | |
| | |
| | | |
| | | [logger_wsgi] |
| | | level = INFO |
| | | handlers = handler_accesslog |
| | | handlers = accesslog |
| | | qualname = wsgi |
| | | propagate = 0 |
| | | |
| | |
| | | Finally, there's no need to use the ``generic`` formatter with TransLogger as |
| | | TransLogger itself provides all the information we need. We'll use a |
| | | formatter that passes-through the log messages as is. Add a new formatter |
| | | called ``accesslog`` by include the following in your configuration file: |
| | | called ``accesslog`` by including the following in your configuration file: |
| | | |
| | | .. code-block:: ini |
| | | |
| | |
| | | [formatter_accesslog] |
| | | format = %(message)s |
| | | |
| | | Then wire this new ``accesslog`` formatter into the FileHandler: |
| | | |
| | | Finally alter the existing configuration to wire this new |
| | | ``accesslog`` formatter into the FileHandler: |
| | | |
| | | .. code-block:: ini |
| | | |
| | |
| | | Request Processing |
| | | ================== |
| | | |
| | | .. image:: ../_static/pyramid_request_processing.* |
| | | :alt: Request Processing |
| | | |
| | | Once a :app:`Pyramid` application is up and running, it is ready to accept |
| | | requests and return responses. What happens from the time a :term:`WSGI` |
| | | request enters a :app:`Pyramid` application through to the point that |
| | |
| | | |
| | | #. The :term:`thread local` stack is popped. |
| | | |
| | | .. image:: router.png |
| | | .. image:: ../_static/pyramid_router.* |
| | | :alt: Pyramid Router |
| | | |
| | | This is a very high-level overview that leaves out various details. For more |
| | | detail about subsystems invoked by the :app:`Pyramid` router such as |
| | |
| | | Security |
| | | ======== |
| | | |
| | | :app:`Pyramid` provides an optional declarative authorization system |
| | | that can prevent a :term:`view` from being invoked based on an |
| | | :app:`Pyramid` provides an optional, declarative, security system. |
| | | Security in :app:`Pyramid` is separated into authentication and |
| | | authorization. The two systems communicate via :term:`principal` |
| | | identifiers. Authentication is merely the mechanism by which credentials |
| | | provided in the :term:`request` are resolved to one or more |
| | | :term:`principal` identifiers. These identifiers represent the users and |
| | | groups that are in effect during the request. Authorization then determines |
| | | access based on the :term:`principal` identifiers, the requested |
| | | :term:`permission`, and a :term:`context`. |
| | | |
| | | The :app:`Pyramid` authorization system |
| | | can prevent a :term:`view` from being invoked based on an |
| | | :term:`authorization policy`. Before a view is invoked, the |
| | | authorization system can use the credentials in the :term:`request` |
| | | along with the :term:`context` resource to determine if access will be |
| | | allowed. Here's how it works at a high level: |
| | | |
| | | - A user may or may not have previously visited the application and |
| | | supplied authentication credentials, including a :term:`userid`. If |
| | | so, the application may have called |
| | | :func:`pyramid.security.remember` to remember these. |
| | | |
| | | - A :term:`request` is generated when a user visits the application. |
| | | |
| | |
| | | context as well as other attributes of the request. |
| | | |
| | | - If an :term:`authentication policy` is in effect, it is passed the |
| | | request; it returns some number of :term:`principal` identifiers. |
| | | request. It will return some number of :term:`principal` identifiers. |
| | | To do this, the policy would need to determine the authenticated |
| | | :term:`userid` present in the request. |
| | | |
| | | - If an :term:`authorization policy` is in effect and the :term:`view |
| | | configuration` associated with the view callable that was found has |
| | |
| | | |
| | | - If the authorization policy denies access, the view callable is not |
| | | invoked; instead the :term:`forbidden view` is invoked. |
| | | |
| | | Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly |
| | | separates authentication and authorization. Authentication is merely the |
| | | mechanism by which credentials provided in the :term:`request` are |
| | | resolved to one or more :term:`principal` identifiers. These identifiers |
| | | represent the users and groups in effect during the request. |
| | | Authorization then determines access based on the :term:`principal` |
| | | identifiers, the :term:`view callable` being invoked, and the |
| | | :term:`context` resource. |
| | | |
| | | Authorization is enabled by modifying your application to include an |
| | | :term:`authentication policy` and :term:`authorization policy`. |
| | |
| | | |
| | | The above configuration enables a policy which compares the value of an "auth |
| | | ticket" cookie passed in the request's environment which contains a reference |
| | | to a single :term:`principal` against the principals present in any |
| | | to a single :term:`userid` and matches that userid's |
| | | :term:`principals <principal>` against the principals present in any |
| | | :term:`ACL` found in the resource tree when attempting to call some |
| | | :term:`view`. |
| | | |
| | |
| | | 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: |
| | |
| | | """ An object representing a Pyramid authentication policy. """ |
| | | |
| | | def authenticated_userid(self, request): |
| | | """ Return the authenticated userid or ``None`` if no |
| | | authenticated userid can be found. This method of the policy |
| | | should ensure that a record exists in whatever persistent store is |
| | | used related to the user (the user should not have been deleted); |
| | | if a record associated with the current id does not exist in a |
| | | persistent store, it should return ``None``.""" |
| | | """ Return the authenticated :term:`userid` or ``None`` if |
| | | no authenticated userid can be found. This method of the |
| | | policy should ensure that a record exists in whatever |
| | | persistent store is used related to the user (the user |
| | | should not have been deleted); if a record associated with |
| | | the current id does not exist in a persistent store, it |
| | | should return ``None``. |
| | | |
| | | """ |
| | | |
| | | def unauthenticated_userid(self, request): |
| | | """ Return the *unauthenticated* userid. This method performs the |
| | | same duty as ``authenticated_userid`` but is permitted to return the |
| | | userid based only on data present in the request; it needn't (and |
| | | shouldn't) check any persistent store to ensure that the user record |
| | | related to the request userid exists.""" |
| | | """ Return the *unauthenticated* userid. This method |
| | | performs the same duty as ``authenticated_userid`` but is |
| | | permitted to return the userid based only on data present |
| | | in the request; it needn't (and shouldn't) check any |
| | | persistent store to ensure that the user record related to |
| | | the request userid exists. |
| | | |
| | | This method is intended primarily a helper to assist the |
| | | ``authenticated_userid`` method in pulling credentials out |
| | | of the request data, abstracting away the specific headers, |
| | | query strings, etc that are used to authenticate the request. |
| | | |
| | | """ |
| | | |
| | | def effective_principals(self, request): |
| | | """ Return a sequence representing the effective principals |
| | | including the userid and any groups belonged to by the current |
| | | user, including 'system' groups such as |
| | | ``pyramid.security.Everyone`` and |
| | | ``pyramid.security.Authenticated``. """ |
| | | typically including the :term:`userid` and any groups belonged |
| | | to by the current user, always including 'system' groups such |
| | | as ``pyramid.security.Everyone`` and |
| | | ``pyramid.security.Authenticated``. |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | """ |
| | | |
| | | def remember(self, request, userid, **kw): |
| | | """ Return a set of headers suitable for 'remembering' the |
| | | principal named ``principal`` when set in a response. An |
| | | individual authentication policy and its consumers can decide |
| | | on the composition and meaning of **kw. """ |
| | | |
| | | :term:`userid` named ``userid`` when set in a response. An |
| | | individual authentication policy and its consumers can |
| | | decide on the composition and meaning of **kw. |
| | | |
| | | """ |
| | | |
| | | def forget(self, request): |
| | | """ Return a set of headers suitable for 'forgetting' the |
| | | current user on subsequent requests. """ |
| | | current user on subsequent requests. |
| | | |
| | | """ |
| | | |
| | | 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:: |
| | |
| | | tampered with. |
| | | |
| | | You can configure this session factory in your :app:`Pyramid` application |
| | | by using the :meth:`pyramid.config.Configurator.set_session_factory`` method. |
| | | by using the :meth:`pyramid.config.Configurator.set_session_factory` method. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | In request handling code, you can check the presence and validity of a CSRF |
| | | token with :func:`pyramid.session.check_csrf_token(request)``. If the token is |
| | | token with :func:`pyramid.session.check_csrf_token`. If the token is |
| | | valid, it will return ``True``, otherwise it will raise ``HTTPBadRequest``. |
| | | Optionally, you can specify ``raises=False`` to have the check return ``False`` |
| | | instead of raising an exception. |
| | |
| | | |
| | | .. index:: |
| | | single: startup process |
| | | pair: settings; .ini |
| | | |
| | | The Startup Process |
| | | ------------------- |
| | |
| | | The server serves the application, and the application is running, waiting |
| | | to receive requests. |
| | | |
| | | .. seealso:: |
| | | Logging configuration is described in the :ref:`logging_chapter` |
| | | chapter. There, in :ref:`request_logging_with_pastes_translogger`, |
| | | you will also find an example of how to configure |
| | | :term:`middleware` to add pre-packaged functionality to your |
| | | application. |
| | | |
| | | .. index:: |
| | | pair: settings; deployment |
| | | single: custom settings |
| | |
| | | we're using a Chameleon renderer, it means "relative to the directory in |
| | | which the file which defines the view configuration lives". In this case, |
| | | this is the directory containing the file that defines the ``my_view`` |
| | | function. View-configuration-relative asset specifications work only |
| | | in Chameleon, not in Mako templates. |
| | | function. |
| | | |
| | | Similar renderer configuration can be done imperatively. See |
| | | :ref:`views_which_use_a_renderer`. |
| | |
| | | test. See the :ref:`testing_module` API for information about the extra |
| | | arguments supported by these functions. |
| | | |
| | | If you also want to make :func:`~pyramid.threadlocal.get_current_request` return something |
| | | other than ``None`` during the course of a single test, you can pass a |
| | | If you also want to make :func:`~pyramid.threadlocal.get_current_request` |
| | | return something other than ``None`` during the course of a single test, you |
| | | can pass a |
| | | :term:`request` object into the :func:`pyramid.testing.setUp` within the |
| | | ``setUp`` method of your test: |
| | | |
| | |
| | | -------------------------- |
| | | |
| | | In :app:`Pyramid`, a *unit test* typically relies on "mock" or "dummy" |
| | | implementations to give the code under test only enough context to run. |
| | | implementations to give the code under test enough context to run. |
| | | |
| | | "Integration testing" implies another sort of testing. In the context of a |
| | | :app:`Pyramid` integration test, the test logic tests the functionality of |
| | | some code *and* its integration with the rest of the :app:`Pyramid` |
| | | :app:`Pyramid` integration test, the test logic exercises the functionality of |
| | | the code under test *and* its integration with the rest of the :app:`Pyramid` |
| | | framework. |
| | | |
| | | In :app:`Pyramid` applications that are plugins to Pyramid, you can create an |
| | | integration test by including its ``includeme`` function via |
| | | :meth:`pyramid.config.Configurator.include` in the test's setup code. This |
| | | causes the entire :app:`Pyramid` environment to be set up and torn down as if |
| | | your application was running "for real". This is a heavy-hammer way of |
| | | making sure that your tests have enough context to run properly, and it tests |
| | | your code's integration with the rest of :app:`Pyramid`. |
| | | Creating an integration test for a :app:`Pyramid` application usually means |
| | | invoking the application's ``includeme`` function via |
| | | :meth:`pyramid.config.Configurator.include` within the test's setup code. This |
| | | causes the entire :app:`Pyramid` environment to be set up, simulating what |
| | | happens when your application is run "for real". This is a heavy-hammer way of |
| | | making sure that your tests have enough context to run properly, and tests your |
| | | code's integration with the rest of :app:`Pyramid`. |
| | | |
| | | Let's demonstrate this by showing an integration test for a view. The below |
| | | test assumes that your application's package name is ``myapp``, and that |
| | | there is a ``views`` module in the app with a function with the name |
| | | ``my_view`` in it that returns the response 'Welcome to this application' |
| | | after accessing some values that require a fully set up environment. |
| | | .. seealso:: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | See also :ref:`including_configuration` |
| | | |
| | | import unittest |
| | | Let's demonstrate this by showing an integration test for a view. |
| | | |
| | | from pyramid import testing |
| | | Given the following view definition, which assumes that your application's |
| | | :term:`package` name is ``myproject``, and within that :term:`package` there |
| | | exists a module ``views``, which in turn contains a :term:`view` function named |
| | | ``my_view``: |
| | | |
| | | class ViewIntegrationTests(unittest.TestCase): |
| | | def setUp(self): |
| | | """ This sets up the application registry with the |
| | | registrations your application declares in its ``includeme`` |
| | | function. |
| | | """ |
| | | import myapp |
| | | self.config = testing.setUp() |
| | | self.config.include('myapp') |
| | | .. literalinclude:: MyProject/myproject/views.py |
| | | :linenos: |
| | | :lines: 1-6 |
| | | :language: python |
| | | |
| | | def tearDown(self): |
| | | """ Clear out the application registry """ |
| | | testing.tearDown() |
| | | You'd then create a ``tests`` module within your ``myproject`` package, |
| | | containing the following test code: |
| | | |
| | | def test_my_view(self): |
| | | from myapp.views import my_view |
| | | request = testing.DummyRequest() |
| | | result = my_view(request) |
| | | self.assertEqual(result.status, '200 OK') |
| | | body = result.app_iter[0] |
| | | self.assertTrue('Welcome to' in body) |
| | | self.assertEqual(len(result.headerlist), 2) |
| | | self.assertEqual(result.headerlist[0], |
| | | ('Content-Type', 'text/html; charset=UTF-8')) |
| | | self.assertEqual(result.headerlist[1], ('Content-Length', |
| | | str(len(body)))) |
| | | .. literalinclude:: MyProject/myproject/tests.py |
| | | :linenos: |
| | | :pyobject: ViewIntegrationTests |
| | | :language: python |
| | | |
| | | Unless you cannot avoid it, you should prefer writing unit tests that use the |
| | | :class:`~pyramid.config.Configurator` API to set up the right "mock" |
| | | registrations rather than creating an integration test. Unit tests will run |
| | | faster (because they do less for each test) and the result of a unit test is |
| | | usually easier to make assertions about. |
| | | Writing unit tests that use the :class:`~pyramid.config.Configurator` API to |
| | | set up the right "mock" registrations is often preferred to creating |
| | | integration tests. Unit tests will run faster (because they do less for each |
| | | test) and are usually easier to reason about. |
| | | |
| | | .. index:: |
| | | single: functional tests |
| | |
| | | |
| | | Functional tests test your literal application. |
| | | |
| | | The below test assumes that your application's package name is ``myapp``, and |
| | | that there is a view that returns an HTML body when the root URL is invoked. |
| | | It further assumes that you've added a ``tests_require`` dependency on the |
| | | ``WebTest`` package within your ``setup.py`` file. :term:`WebTest` is a |
| | | functional testing package written by Ian Bicking. |
| | | In Pyramid, functional tests are typically written using the :term:`WebTest` |
| | | package, which provides APIs for invoking HTTP(S) requests to your application. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | Regardless of which testing :term:`package` you use, ensure to add a |
| | | ``tests_require`` dependency on that package to to your application's |
| | | ``setup.py`` file: |
| | | |
| | | import unittest |
| | | .. literalinclude:: MyProject/setup.py |
| | | :linenos: |
| | | :emphasize-lines: 26-28,48 |
| | | :language: python |
| | | |
| | | class FunctionalTests(unittest.TestCase): |
| | | def setUp(self): |
| | | from myapp import main |
| | | app = main({}) |
| | | from webtest import TestApp |
| | | self.testapp = TestApp(app) |
| | | Assuming your :term:`package` is named ``myproject``, which contains a |
| | | ``views`` module, which in turn contains a :term:`view` function ``my_view`` |
| | | that returns a HTML body when the root URL is invoked: |
| | | |
| | | def test_root(self): |
| | | res = self.testapp.get('/', status=200) |
| | | self.assertTrue('Pyramid' in res.body) |
| | | .. literalinclude:: MyProject/myproject/views.py |
| | | :linenos: |
| | | :language: python |
| | | |
| | | When this test is run, each test creates a "real" WSGI application using the |
| | | ``main`` function in your ``myapp.__init__`` module and uses :term:`WebTest` |
| | | to wrap that WSGI application. It assigns the result to ``self.testapp``. |
| | | In the test named ``test_root``, we use the testapp's ``get`` method to |
| | | invoke the root URL. We then assert that the returned HTML has the string |
| | | ``Pyramid`` in it. |
| | | Then the following example functional test (shown below) demonstrates invoking |
| | | the :term:`view` shown above: |
| | | |
| | | .. literalinclude:: MyProject/myproject/tests.py |
| | | :linenos: |
| | | :pyobject: FunctionalTests |
| | | :language: python |
| | | |
| | | When this test is run, each test method creates a "real" :term:`WSGI` |
| | | application using the ``main`` function in your ``myproject.__init__`` module, |
| | | using :term:`WebTest` to wrap that WSGI application. It assigns the result to |
| | | ``self.testapp``. In the test named ``test_root``. The ``TestApp``'s ``get`` |
| | | method is used to invoke the root URL. Finally, an assertion is made that the |
| | | returned HTML contains the text ``MyProject``. |
| | | |
| | | See the :term:`WebTest` documentation for further information about the |
| | | methods available to a :class:`webtest.app.TestApp` instance. |
| | |
| | | :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. |
| | | |
| | |
| | | application, this view will be invoked if the value of ``PATH_INFO`` does not |
| | | already end in a slash, and if the value of ``PATH_INFO`` *plus* a slash |
| | | matches any route's pattern. In this case it does an HTTP redirect to the |
| | | slash-appended ``PATH_INFO``. |
| | | slash-appended ``PATH_INFO``. In addition you may pass anything that implements |
| | | :class:`pyramid.interfaces.IResponse` which will then be used in place of the |
| | | default class (:class:`pyramid.httpexceptions.HTTPFound`). |
| | | |
| | | Let's use an example. If the following routes are configured in your |
| | | application: |
| | |
| | | |
| | | ``match_param`` |
| | | This param may be either a single string of the format "key=value" or a |
| | | dict of key/value pairs. |
| | | tuple containing one or more of these strings. |
| | | |
| | | This argument ensures that the view will only be called when the |
| | | :term:`request` has key/value pairs in its :term:`matchdict` that equal |
| | |
| | | hand side of the expression (``edit``) for the view to "match" the current |
| | | request. |
| | | |
| | | If the ``match_param`` is a dict, every key/value pair must match for the |
| | | predicate to pass. |
| | | If the ``match_param`` is a tuple, every key/value pair must match |
| | | for the predicate to pass. |
| | | |
| | | If ``match_param`` is not supplied, the view will be invoked without |
| | | consideration of the keys and values in ``request.matchdict``. |
| | |
| | | req = urllib2.Request('http://localhost:6543/', json_payload, headers) |
| | | resp = urllib2.urlopen(req) |
| | | |
| | | If you are doing Cross-origin resource sharing (CORS), then the standard |
| | | requires the browser to do a pre-flight HTTP OPTIONS request. The easiest way |
| | | to handling this is adding an extra ``view_config`` for the same route, with |
| | | ``request_method`` set to ``OPTIONS``, and setting the desired response header |
| | | before returning. You can find examples of response headers here_. |
| | | |
| | | .. _here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests |
| | | |
| | | .. index:: |
| | | single: cleaning up after request |
| | | |
| | |
| | | need *logging*. |
| | | |
| | | Fortunately Pyramid uses the normal Python approach to logging. The |
| | | scaffold generated in your ``development.ini`` a number of lines that |
| | | scaffold generated in your ``development.ini`` has a number of lines that |
| | | configure the logging for you to some reasonable defaults. You then see |
| | | messages sent by Pyramid (for example, when a new request comes in). |
| | | |
| | |
| | | Analysis |
| | | ======== |
| | | |
| | | ``pyramid_debugtoolbar`` is a full-fledged Python package, |
| | | available on PyPI just like thousands of other Python packages. Thus we |
| | | start by installing the ``pyramid_debugtoolbar`` package into our |
| | | virtual environment using normal Python package installation commands. |
| | | ``pyramid_debugtoolbar`` is a full-fledged Python package, available on PyPI |
| | | just like thousands of other Python packages. Thus we start by installing the |
| | | ``pyramid_debugtoolbar`` package into our virtual environment using normal |
| | | Python package installation commands. |
| | | |
| | | The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on, |
| | | which means we need to include its add-on configuration into our web |
| | | application. We could do this with imperative configuration in |
| | | ``tutorial/__init__.py`` by using ``config.include``. Pyramid also |
| | | supports wiring in add-on configuration via our ``development.ini`` |
| | | using ``pyramid.includes``. We use this to load the configuration for |
| | | the debugtoolbar. |
| | | The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on, which |
| | | means we need to include its add-on configuration into our web application. We |
| | | could do this with imperative configuration in ``tutorial/__init__.py`` by |
| | | using ``config.include``. Pyramid also supports wiring in add-on configuration |
| | | via our ``development.ini`` using ``pyramid.includes``. We use this to load |
| | | the configuration for the debugtoolbar. |
| | | |
| | | You'll now see an attractive button on the right side of |
| | | your browser, which you may click to provide introspective access to debugging |
| | | information in a new browser tab. Even better, if your web application |
| | | generates an error, |
| | | you will see a nice traceback on the screen. When you want to disable |
| | | this toolbar, no need to change code: you can remove it from |
| | | ``pyramid.includes`` in the relevant ``.ini`` configuration file (thus |
| | | showing why configuration files are handy.) |
| | | You'll now see an attractive button on the right side of your browser, which |
| | | you may click to provide introspective access to debugging information in a |
| | | new browser tab. Even better, if your web application generates an error, you |
| | | will see a nice traceback on the screen. When you want to disable this |
| | | toolbar, no need to change code: you can remove it from ``pyramid.includes`` |
| | | in the relevant ``.ini`` configuration file (thus showing why configuration |
| | | files are handy.) |
| | | |
| | | Note injects a small amount of html/css into your app just before the closing |
| | | ``</body>`` tag in order to display itself. If you |
| | | start to experience otherwise inexplicable client-side weirdness, you can shut |
| | | it off by commenting out the ``pyramid_debugtoolbar`` line in |
| | | ``pyramid.includes`` temporarily. |
| | | Note that the toolbar injects a small amount of html/css into your app just |
| | | before the closing ``</body>`` tag in order to display itself. If you start to |
| | | experience otherwise inexplicable client-side weirdness, you can shut it off |
| | | by commenting out the ``pyramid_debugtoolbar`` line in ``pyramid.includes`` |
| | | temporarily. |
| | | |
| | | .. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`. |
| | | |
| | |
| | | it is located. We point at the package, then the path inside the package. |
| | | |
| | | We just need to include a call to ``add_static_view`` to make that |
| | | directory available at a URL. For Pyramid-specific pages, |
| | | directory available at a URL. For Pyramid-specific packages, |
| | | Pyramid provides a facility (``config.include()``) which even makes |
| | | that unnecessary for consumers of a package. (Deform is not specific to |
| | | Pyramid.) |
| | |
| | | $ $VENV/bin/python setup.py develop |
| | | $ $VENV/bin/easy_install webtest |
| | | |
| | | #. Let's extend ``unit_testing/tutorial/tests.py`` to include a |
| | | #. Let's extend ``functional_testing/tutorial/tests.py`` to include a |
| | | functional test: |
| | | |
| | | .. literalinclude:: functional_testing/tutorial/tests.py |
| | | :linenos: |
| | | |
| | | Be sure this file is not executable, or ``nosetests`` may not |
| | | include your tests. |
| | | |
| | | #. Now run the tests: |
| | | |
| | | .. code-block:: bash |
| | |
| | | Extra Credit |
| | | ============ |
| | | |
| | | #. Why do our functional tests use ``b''``? |
| | | #. Why do our functional tests use ``b''``? |
| | |
| | | #. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect |
| | | :term:`view` code to a particular URL :term:`route`. |
| | | |
| | | #. *Lines 6-7*. Implement the view code that generates the |
| | | #. *Lines 6-8*. Implement the view code that generates the |
| | | :term:`response`. |
| | | |
| | | #. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP |
| | |
| | | :ref:`configuration <configuration_narr>` distinct from code. |
| | | This approach is optional, but its presence makes it distinct from |
| | | other Python web frameworks. It taps into Python's ``setuptools`` |
| | | library, which establishes conventions for how Python projects can be |
| | | installed and provide "entry points". Pyramid uses an entry point to |
| | | let a Pyramid application it where to find the WSGI app. |
| | | library, which establishes conventions for installing and providing |
| | | "entry points" for Python projects. Pyramid uses an entry point to |
| | | let a Pyramid application know where to find the WSGI app. |
| | | |
| | | Objectives |
| | | ========== |
| | |
| | | Steps |
| | | ===== |
| | | |
| | | #. In this step let's start by installing the ``pyramid_jinja2`` |
| | | add-on, the copying the ``view_class`` step's directory: |
| | | #. In this step let's start by copying the ``view_class`` step's |
| | | directory, and then installing the ``pyramid_jinja2`` add-on. |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | |
| | | .. literalinclude:: jinja2/tutorial/home.jinja2 |
| | | :language: html |
| | | |
| | | #. Get the ``pyramid.includes`` into the functional test setup in |
| | | ``jinja2/tutorial/tests.py``: |
| | | |
| | | .. literalinclude:: jinja2/tutorial/tests.py |
| | | :linenos: |
| | | |
| | | #. Now run the tests: |
| | | |
| | |
| | | Our view code stayed largely the same. We simply changed the file |
| | | extension on the renderer. For the template, the syntax for Chameleon |
| | | and Jinja2's basic variable insertion is very similar. |
| | | |
| | | Our functional tests don't have ``development.ini`` so they needed the |
| | | ``pyramid.includes`` to be setup in the test setup. |
| | | |
| | | Extra Credit |
| | | ============ |
| | |
| | | class TutorialFunctionalTests(unittest.TestCase): |
| | | def setUp(self): |
| | | from tutorial import main |
| | | |
| | | settings = { |
| | | 'pyramid.includes': [ |
| | | 'pyramid_jinja2' |
| | | ] |
| | | } |
| | | app = main({}, **settings) |
| | | app = main({}) |
| | | from webtest import TestApp |
| | | |
| | | self.testapp = TestApp(app) |
| | |
| | | need *logging*. |
| | | |
| | | Fortunately Pyramid uses the normal Python approach to logging. The |
| | | scaffold generated, in your ``development.ini``, a number of lines that |
| | | scaffold generated, in your ``development.ini``, has a number of lines that |
| | | configure the logging for you to some reasonable defaults. You then see |
| | | messages sent by Pyramid (for example, when a new request comes in.) |
| | | |
| | |
| | | |
| | | |
| | | @view_defaults(route_name='hello') |
| | | class TutorialViews: |
| | | class TutorialViews(object): |
| | | def __init__(self, request): |
| | | self.request = request |
| | | self.view_name = 'TutorialViews' |
| | |
| | | def hello(self): |
| | | return {'page_title': 'Hello View'} |
| | | |
| | | # Posting to /home via the "Edit" submit button |
| | | # Posting to /howdy/first/last via the "Edit" submit button |
| | | @view_config(request_method='POST', renderer='edit.pt') |
| | | def edit(self): |
| | | new_name = self.request.params['new_name'] |
| | | return {'page_title': 'Edit View', 'new_name': new_name} |
| | | |
| | | # Posting to /home via the "Delete" submit button |
| | | # Posting to /howdy/first/last via the "Delete" submit button |
| | | @view_config(request_method='POST', request_param='form.delete', |
| | | renderer='delete.pt') |
| | | def delete(self): |
| | |
| | | head of ``tutorial/tutorial/views.py``: |
| | | |
| | | .. literalinclude:: src/authorization/tutorial/views.py |
| | | :lines: 6-13,15-17 |
| | | :lines: 6-17 |
| | | :linenos: |
| | | :emphasize-lines: 3,6-9,11 |
| | | :emphasize-lines: 3,6-11 |
| | | :language: python |
| | | |
| | | (Only the highlighted lines, with other necessary modifications, |
| | |
| | | We'll eventually be adding security to our application. The components we'll |
| | | use to do this are below. |
| | | |
| | | - USERS, a dictionary mapping usernames to their |
| | | - USERS, a dictionary mapping :term:`userids <userid>` to their |
| | | corresponding passwords. |
| | | |
| | | - GROUPS, a dictionary mapping usernames to a |
| | | - GROUPS, a dictionary mapping :term:`userids <userid>` to a |
| | | list of groups to which they belong to. |
| | | |
| | | - ``groupfinder``, an *authorization callback* that looks up |
| | |
| | | We'll eventually be adding security to our application. The components we'll |
| | | use to do this are below. |
| | | |
| | | - USERS, a dictionary mapping users names to their corresponding passwords. |
| | | - USERS, a dictionary mapping users names (the user's :term:`userids |
| | | <userid>`) to their corresponding passwords. |
| | | |
| | | - GROUPS, a dictionary mapping user names to a list of groups they belong to. |
| | | |
| | |
| | | from codecs import utf_8_encode |
| | | import hashlib |
| | | import base64 |
| | | import datetime |
| | | import re |
| | | import time as time_mod |
| | | import warnings |
| | |
| | | effective_principals.extend(groups) |
| | | return effective_principals |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | """ Store the ``principal`` as ``repoze.who.userid``. |
| | | def remember(self, request, userid, **kw): |
| | | """ Store the ``userid`` as ``repoze.who.userid``. |
| | | |
| | | The identity to authenticated to :mod:`repoze.who` |
| | | will contain the given principal as ``userid``, and |
| | | will contain the given userid as ``userid``, and |
| | | provide all keyword arguments as additional identity |
| | | keys. Useful keys could be ``max_age`` or ``userdata``. |
| | | """ |
| | |
| | | return [] |
| | | environ = request.environ |
| | | identity = kw |
| | | identity['repoze.who.userid'] = principal |
| | | identity['repoze.who.userid'] = userid |
| | | return identifier.remember(environ, identity) |
| | | |
| | | def forget(self, request): |
| | |
| | | """ The ``REMOTE_USER`` value found within the ``environ``.""" |
| | | return request.environ.get(self.environ_key) |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | def remember(self, request, userid, **kw): |
| | | """ A no-op. The ``REMOTE_USER`` does not provide a protocol for |
| | | remembering the user. This will be application-specific and can |
| | | be done somewhere else or in a subclass.""" |
| | |
| | | if result: |
| | | return result['userid'] |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | def remember(self, request, userid, **kw): |
| | | """ Accepts the following kw args: ``max_age=<int-seconds>, |
| | | ``tokens=<sequence-of-ascii-strings>``. |
| | | |
| | |
| | | the response. |
| | | |
| | | """ |
| | | return self.cookie.remember(request, principal, **kw) |
| | | return self.cookie.remember(request, userid, **kw) |
| | | |
| | | def forget(self, request): |
| | | """ A list of headers which will delete appropriate cookies.""" |
| | |
| | | If the ticket cannot be parsed, a ``BadTicket`` exception will be raised |
| | | with an explanation. |
| | | """ |
| | | ticket = ticket.strip('"') |
| | | ticket = native_(ticket).strip('"') |
| | | digest_size = hashlib.new(hashalg).digest_size * 2 |
| | | digest = ticket[:digest_size] |
| | | try: |
| | |
| | | |
| | | if reissue and not hasattr(request, '_authtkt_reissued'): |
| | | if ( (now - timestamp) > self.reissue_time ): |
| | | # work around https://github.com/Pylons/pyramid/issues#issue/108 |
| | | # See https://github.com/Pylons/pyramid/issues#issue/108 |
| | | tokens = list(filter(None, tokens)) |
| | | headers = self.remember(request, userid, max_age=self.max_age, |
| | | tokens=tokens) |
| | |
| | | self.userid_key = prefix + 'userid' |
| | | self.debug = debug |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | """ Store a principal in the session.""" |
| | | request.session[self.userid_key] = principal |
| | | def remember(self, request, userid, **kw): |
| | | """ Store a userid in the session.""" |
| | | request.session[self.userid_key] = userid |
| | | return [] |
| | | |
| | | def forget(self, request): |
| | | """ Remove the stored principal from the session.""" |
| | | """ Remove the stored userid from the session.""" |
| | | if self.userid_key in request.session: |
| | | del request.session[self.userid_key] |
| | | return [] |
| | |
| | | if credentials: |
| | | return credentials[0] |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | def remember(self, request, userid, **kw): |
| | | """ A no-op. Basic authentication does not provide a protocol for |
| | | remembering the user. Credentials are sent on every request. |
| | | |
| | |
| | | import sys |
| | | import types |
| | | |
| | | if platform.system() == 'Windows': # pragma: no cover |
| | | if platform.system() == 'Windows': # pragma: no cover |
| | | WIN = True |
| | | else: # pragma: no cover |
| | | else: # pragma: no cover |
| | | WIN = False |
| | | |
| | | try: # pragma: no cover |
| | | try: # pragma: no cover |
| | | import __pypy__ |
| | | PYPY = True |
| | | except: # pragma: no cover |
| | | except: # pragma: no cover |
| | | __pypy__ = None |
| | | PYPY = False |
| | | |
| | | try: |
| | | import cPickle as pickle |
| | | except ImportError: # pragma: no cover |
| | | except ImportError: # pragma: no cover |
| | | import pickle |
| | | |
| | | # True if we are running on Python 3. |
| | | PY3 = sys.version_info[0] == 3 |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | string_types = str, |
| | | integer_types = int, |
| | | class_types = type, |
| | |
| | | ``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 |
| | |
| | | from urllib import unquote as url_unquote |
| | | from urllib import urlencode as url_encode |
| | | from urllib2 import urlopen as url_open |
| | | |
| | | def url_unquote_text(v, encoding='utf-8', errors='replace'): # pragma: no cover |
| | | v = url_unquote(v) |
| | | return v.decode(encoding, errors) |
| | | |
| | | def url_unquote_native(v, encoding='utf-8', errors='replace'): # pragma: no cover |
| | | return native_(url_unquote_text(v, encoding, errors)) |
| | | |
| | | |
| | | if PY3: # pragma: no cover |
| | | |
| | | if PY3: # pragma: no cover |
| | | import builtins |
| | | exec_ = getattr(builtins, "exec") |
| | | |
| | | |
| | | def reraise(tp, value, tb=None): |
| | | if value is None: |
| | |
| | | raise value.with_traceback(tb) |
| | | raise value |
| | | |
| | | |
| | | del builtins |
| | | |
| | | else: # pragma: no cover |
| | | else: # pragma: no cover |
| | | def exec_(code, globs=None, locs=None): |
| | | """Execute code in a namespace.""" |
| | | if globs is None: |
| | |
| | | locs = globs |
| | | exec("""exec code in globs, locs""") |
| | | |
| | | |
| | | exec_("""def reraise(tp, value, tb=None): |
| | | raise tp, value, tb |
| | | """) |
| | | |
| | | |
| | | if PY3: # pragma: no cover |
| | | if PY3: # pragma: no cover |
| | | def iteritems_(d): |
| | | return d.items() |
| | | |
| | | def itervalues_(d): |
| | | return d.values() |
| | | |
| | | def iterkeys_(d): |
| | | return d.keys() |
| | | else: # pragma: no cover |
| | | else: # pragma: no cover |
| | | def iteritems_(d): |
| | | return d.iteritems() |
| | | |
| | | def itervalues_(d): |
| | | return d.itervalues() |
| | | |
| | | def iterkeys_(d): |
| | | 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 |
| | |
| | | else: |
| | | 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 |
| | | |
| | | 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 |
| | | 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): |
| | | return unquote_to_bytes(bytestring).decode('latin-1') |
| | | else: |
| | | from urlparse import unquote as unquote_to_bytes |
| | | |
| | | def unquote_bytes_to_wsgi(bytestring): |
| | | return unquote_to_bytes(bytestring) |
| | | |
| | | |
| | | def is_bound_method(ob): |
| | | return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None |
| | | |
| | | # 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): |
| | | """ |
| | | This consistently verifies that the callable is bound to a |
| | | class. |
| | | """ |
| | | is_bound = is_bound_method(fn) |
| | | |
| | | if not is_bound and inspect.isroutine(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 |
| | | return True |
| | | elif inspect.ismethod(fn): |
| | | return True |
| | | |
| | | return False |
| | |
| | | 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, |
| | |
| | | which accept a ``renderer`` argument, into absolute paths. If ``None`` |
| | | is passed (the default), the package is assumed to be the Python package |
| | | in which the *caller* of the ``Configurator`` constructor lives. |
| | | |
| | | If the ``root_package`` is passed, it will propagate through the |
| | | configuration hierarchy as a way for included packages to locate |
| | | resources relative to the package in which the main ``Configurator`` was |
| | | created. If ``None`` is passed (the default), the ``root_package`` will |
| | | be derived from the ``package`` argument. The ``package`` attribute is |
| | | always pointing at the package being included when using :meth:`.include`, |
| | | whereas the ``root_package`` does not change. |
| | | |
| | | If the ``settings`` argument is passed, it should be a Python dictionary |
| | | representing the :term:`deployment settings` for this application. These |
| | |
| | | See :ref:`changing_the_request_factory`. By default it is ``None``, |
| | | which means use the default request factory. |
| | | |
| | | If ``response_factory`` is passed, it should be a :term:`response |
| | | factory` implementation or a :term:`dotted Python name` to the same. |
| | | See :ref:`changing_the_response_factory`. By default it is ``None``, |
| | | which means use the default response factory. |
| | | |
| | | If ``default_permission`` is passed, it should be a |
| | | :term:`permission` string to be used as the default permission for |
| | | all view configuration registrations performed against this |
| | |
| | | configurations which do not explicitly declare a permission will |
| | | always be executable by entirely anonymous users (any |
| | | authorization policy in effect is ignored). |
| | | |
| | | |
| | | .. seealso:: |
| | | |
| | | See also :ref:`setting_a_default_permission`. |
| | |
| | | |
| | | .. versionadded:: 1.3 |
| | | The ``introspection`` argument. |
| | | |
| | | .. versionadded:: 1.6 |
| | | The ``root_package`` argument. |
| | | The ``response_factory`` argument. |
| | | """ |
| | | manager = manager # for testing injection |
| | | venusian = venusian # for testing injection |
| | |
| | | debug_logger=None, |
| | | locale_negotiator=None, |
| | | request_factory=None, |
| | | response_factory=None, |
| | | default_permission=None, |
| | | session_factory=None, |
| | | default_view_mapper=None, |
| | |
| | | exceptionresponse_view=default_exceptionresponse_view, |
| | | route_prefix=None, |
| | | introspection=True, |
| | | root_package=None, |
| | | ): |
| | | if package is None: |
| | | package = caller_package() |
| | | if root_package is None: |
| | | root_package = package |
| | | name_resolver = DottedNameResolver(package) |
| | | self.name_resolver = name_resolver |
| | | self.package_name = name_resolver.get_package_name() |
| | | self.package = name_resolver.get_package() |
| | | self.root_package = root_package |
| | | self.registry = registry |
| | | self.autocommit = autocommit |
| | | self.route_prefix = route_prefix |
| | |
| | | debug_logger=debug_logger, |
| | | locale_negotiator=locale_negotiator, |
| | | request_factory=request_factory, |
| | | response_factory=response_factory, |
| | | default_permission=default_permission, |
| | | session_factory=session_factory, |
| | | default_view_mapper=default_view_mapper, |
| | |
| | | debug_logger=None, |
| | | locale_negotiator=None, |
| | | request_factory=None, |
| | | response_factory=None, |
| | | default_permission=None, |
| | | session_factory=None, |
| | | default_view_mapper=None, |
| | |
| | | if request_factory: |
| | | self.set_request_factory(request_factory) |
| | | |
| | | if response_factory: |
| | | self.set_response_factory(response_factory) |
| | | |
| | | if default_permission: |
| | | self.set_default_permission(default_permission) |
| | | |
| | |
| | | _registry.registerSelfAdapter = registerSelfAdapter |
| | | |
| | | # API |
| | | |
| | | |
| | | def _get_introspector(self): |
| | | introspector = getattr(self.registry, 'introspector', _marker) |
| | | if introspector is _marker: |
| | |
| | | configurator = self.__class__( |
| | | registry=self.registry, |
| | | package=package_of(module), |
| | | root_package=self.root_package, |
| | | autocommit=self.autocommit, |
| | | route_prefix=route_prefix, |
| | | ) |
| | |
| | | configurator = self.__class__( |
| | | registry=self.registry, |
| | | package=package, |
| | | root_package=self.root_package, |
| | | autocommit=self.autocommit, |
| | | route_prefix=self.route_prefix, |
| | | introspection=self.introspection, |
| | |
| | | class ActionState(object): |
| | | def __init__(self): |
| | | # NB "actions" is an API, dep'd upon by pyramid_zcml's load_zcml func |
| | | self.actions = [] |
| | | self.actions = [] |
| | | self._seen_files = set() |
| | | |
| | | def processSpec(self, spec): |
| | |
| | | >>> 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'] |
| | |
| | | ConfigurationExecutionError(t, v, info), |
| | | tb) |
| | | finally: |
| | | del t, v, tb |
| | | del t, v, tb |
| | | |
| | | 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): |
| | |
| | | ) |
| | | |
| | | global_registries = WeakOrderedSet() |
| | | |
| | |
| | | Adds a subscriber predicate factory. The associated subscriber |
| | | predicate can later be named as a keyword argument to |
| | | :meth:`pyramid.config.Configurator.add_subscriber` in the |
| | | ``**predicates`` anonyous keyword argument dictionary. |
| | | ``**predicates`` anonymous keyword argument dictionary. |
| | | |
| | | ``name`` should be the name of the predicate. It must be a valid |
| | | Python identifier (it will be used as a ``**predicates`` keyword |
| | |
| | | import os |
| | | import pkg_resources |
| | | import sys |
| | | |
| | |
| | | return result |
| | | return pkg_resources.DefaultProvider.resource_listdir( |
| | | self, resource_name) |
| | | |
| | | |
| | | |
| | | @implementer(IPackageOverrides) |
| | | class PackageOverrides(object): |
| | | # pkg_resources arg in kw args below for testing |
| | |
| | | # optional)... |
| | | # A __loader__ attribute is basically metadata, and setuptools |
| | | # uses it as such. |
| | | package.__loader__ = self |
| | | package.__loader__ = self |
| | | # we call register_loader_type for every instantiation of this |
| | | # class; that's OK, it's idempotent to do it more than once. |
| | | pkg_resources.register_loader_type(self.__class__, OverrideProvider) |
| | | self.overrides = [] |
| | | self.overridden_package_name = package.__name__ |
| | | |
| | | def insert(self, path, package, prefix): |
| | | def insert(self, path, source): |
| | | if not path or path.endswith('/'): |
| | | override = DirectoryOverride(path, package, prefix) |
| | | override = DirectoryOverride(path, source) |
| | | else: |
| | | override = FileOverride(path, package, prefix) |
| | | override = FileOverride(path, source) |
| | | self.overrides.insert(0, override) |
| | | return override |
| | | |
| | | def search_path(self, resource_name): |
| | | def filtered_sources(self, resource_name): |
| | | for override in self.overrides: |
| | | o = override(resource_name) |
| | | if o is not None: |
| | | package, name = o |
| | | yield package, name |
| | | yield o |
| | | |
| | | def get_filename(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_filename(package, rname) |
| | | for source, path in self.filtered_sources(resource_name): |
| | | result = source.get_filename(path) |
| | | if result is not None: |
| | | return result |
| | | |
| | | def get_stream(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_stream(package, rname) |
| | | for source, path in self.filtered_sources(resource_name): |
| | | result = source.get_stream(path) |
| | | if result is not None: |
| | | return result |
| | | |
| | | def get_string(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_string(package, rname) |
| | | for source, path in self.filtered_sources(resource_name): |
| | | result = source.get_string(path) |
| | | if result is not None: |
| | | return result |
| | | |
| | | def has_resource(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | for source, path in self.filtered_sources(resource_name): |
| | | if source.exists(path): |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_isdir(package, rname) |
| | | for source, path in self.filtered_sources(resource_name): |
| | | result = source.isdir(path) |
| | | if result is not None: |
| | | return result |
| | | |
| | | def listdir(self, resource_name): |
| | | for package, rname in self.search_path(resource_name): |
| | | if pkg_resources.resource_exists(package, rname): |
| | | return pkg_resources.resource_listdir(package, rname) |
| | | for source, path in self.filtered_sources(resource_name): |
| | | result = source.listdir(path) |
| | | if result is not None: |
| | | return result |
| | | |
| | | @property |
| | | def real_loader(self): |
| | |
| | | """ See IPEP302Loader. |
| | | """ |
| | | return self.real_loader.get_source(fullname) |
| | | |
| | | |
| | | |
| | | class DirectoryOverride: |
| | | def __init__(self, path, package, prefix): |
| | | def __init__(self, path, source): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | self.pathlen = len(self.path) |
| | | self.source = source |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name.startswith(self.path): |
| | | name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) |
| | | return self.package, name |
| | | new_path = resource_name[self.pathlen:] |
| | | return self.source, new_path |
| | | |
| | | class FileOverride: |
| | | def __init__(self, path, package, prefix): |
| | | def __init__(self, path, source): |
| | | self.path = path |
| | | self.package = package |
| | | self.prefix = prefix |
| | | self.source = source |
| | | |
| | | def __call__(self, resource_name): |
| | | if resource_name == self.path: |
| | | return self.package, self.prefix |
| | | return self.source, '' |
| | | |
| | | |
| | | class PackageAssetSource(object): |
| | | """ |
| | | An asset source relative to a package. |
| | | |
| | | If this asset source is a file, then we expect the ``prefix`` to point |
| | | to the new name of the file, and the incoming ``resource_name`` will be |
| | | the empty string, as returned by the ``FileOverride``. |
| | | |
| | | """ |
| | | 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): |
| | | return '%s%s' % (self.prefix, resource_name) |
| | | |
| | | def get_filename(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | 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.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.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.pkg_name, path): |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | 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.pkg_name, path): |
| | | return pkg_resources.resource_listdir(self.pkg_name, path) |
| | | |
| | | |
| | | class FSAssetSource(object): |
| | | """ |
| | | An asset source relative to a path in the filesystem. |
| | | |
| | | """ |
| | | def __init__(self, prefix): |
| | | self.prefix = prefix |
| | | |
| | | def get_filename(self, resource_name): |
| | | if resource_name: |
| | | path = os.path.join(self.prefix, resource_name) |
| | | else: |
| | | path = self.prefix |
| | | |
| | | if os.path.exists(path): |
| | | return path |
| | | |
| | | def get_stream(self, resource_name): |
| | | path = self.get_filename(resource_name) |
| | | if path is not None: |
| | | return open(path, 'rb') |
| | | |
| | | def get_string(self, resource_name): |
| | | stream = self.get_stream(resource_name) |
| | | if stream is not None: |
| | | with stream: |
| | | return stream.read() |
| | | |
| | | def exists(self, resource_name): |
| | | path = self.get_filename(resource_name) |
| | | if path is not None: |
| | | return True |
| | | |
| | | def isdir(self, resource_name): |
| | | path = self.get_filename(resource_name) |
| | | if path is not None: |
| | | return os.path.isdir(path) |
| | | |
| | | def listdir(self, resource_name): |
| | | path = self.get_filename(resource_name) |
| | | if path is not None: |
| | | return os.listdir(path) |
| | | |
| | | |
| | | class AssetsConfiguratorMixin(object): |
| | | def _override(self, package, path, override_package, override_prefix, |
| | | def _override(self, package, path, override_source, |
| | | PackageOverrides=PackageOverrides): |
| | | pkg_name = package.__name__ |
| | | override_pkg_name = override_package.__name__ |
| | | override = self.registry.queryUtility(IPackageOverrides, name=pkg_name) |
| | | if override is None: |
| | | override = PackageOverrides(package) |
| | | self.registry.registerUtility(override, IPackageOverrides, |
| | | name=pkg_name) |
| | | override.insert(path, override_pkg_name, override_prefix) |
| | | override.insert(path, override_source) |
| | | |
| | | @action_method |
| | | def override_asset(self, to_override, override_with, _override=None): |
| | | """ Add a :app:`Pyramid` asset override to the current |
| | | configuration state. |
| | | |
| | | ``to_override`` is a :term:`asset specification` to the |
| | | ``to_override`` is an :term:`asset specification` to the |
| | | asset being overridden. |
| | | |
| | | ``override_with`` is a :term:`asset specification` to the |
| | | asset that is performing the override. |
| | | ``override_with`` is an :term:`asset specification` to the |
| | | asset that is performing the override. This may also be an absolute |
| | | path. |
| | | |
| | | See :ref:`assets_chapter` for more |
| | | information about asset overrides.""" |
| | | if to_override == override_with: |
| | | raise ConfigurationError('You cannot override an asset with itself') |
| | | raise ConfigurationError( |
| | | 'You cannot override an asset with itself') |
| | | |
| | | package = to_override |
| | | path = '' |
| | | if ':' in to_override: |
| | | package, path = to_override.split(':', 1) |
| | | |
| | | override_package = override_with |
| | | override_prefix = '' |
| | | if ':' in override_with: |
| | | override_package, override_prefix = override_with.split(':', 1) |
| | | |
| | | # *_isdir = override is package or directory |
| | | overridden_isdir = path=='' or path.endswith('/') |
| | | override_isdir = override_prefix=='' or override_prefix.endswith('/') |
| | | overridden_isdir = path == '' or path.endswith('/') |
| | | |
| | | if os.path.isabs(override_with): |
| | | override_source = FSAssetSource(override_with) |
| | | if not os.path.exists(override_with): |
| | | raise ConfigurationError( |
| | | 'Cannot override asset with an absolute path that does ' |
| | | 'not exist') |
| | | override_isdir = os.path.isdir(override_with) |
| | | override_package = None |
| | | override_prefix = override_with |
| | | else: |
| | | override_package = override_with |
| | | override_prefix = '' |
| | | if ':' in override_with: |
| | | override_package, override_prefix = override_with.split(':', 1) |
| | | |
| | | __import__(override_package) |
| | | to_package = sys.modules[override_package] |
| | | override_source = PackageAssetSource(to_package, override_prefix) |
| | | |
| | | override_isdir = ( |
| | | override_prefix == '' or |
| | | override_with.endswith('/') |
| | | ) |
| | | |
| | | if overridden_isdir and (not override_isdir): |
| | | raise ConfigurationError( |
| | |
| | | |
| | | def register(): |
| | | __import__(package) |
| | | __import__(override_package) |
| | | from_package = sys.modules[package] |
| | | to_package = sys.modules[override_package] |
| | | override(from_package, path, to_package, override_prefix) |
| | | override(from_package, path, override_source) |
| | | |
| | | intr = self.introspectable( |
| | | 'asset overrides', |
| | |
| | | from zope.deprecation import deprecate |
| | | from zope.deprecation import deprecated |
| | | from zope.interface import implementer |
| | | |
| | | from pyramid.interfaces import ( |
| | | IDefaultRootFactory, |
| | | IRequestFactory, |
| | | IResponseFactory, |
| | | IRequestExtensions, |
| | | IRootFactory, |
| | | ISessionFactory, |
| | |
| | | |
| | | from pyramid.util import ( |
| | | action_method, |
| | | InstancePropertyMixin, |
| | | get_callable_name, |
| | | InstancePropertyHelper, |
| | | ) |
| | | |
| | | |
| | | class FactoriesConfiguratorMixin(object): |
| | | @action_method |
| | |
| | | factory = self.maybe_dotted(factory) |
| | | if factory is None: |
| | | factory = DefaultRootFactory |
| | | |
| | | def register(): |
| | | self.registry.registerUtility(factory, IRootFactory) |
| | | self.registry.registerUtility(factory, IDefaultRootFactory) # b/c |
| | | self.registry.registerUtility(factory, IDefaultRootFactory) # b/c |
| | | |
| | | intr = self.introspectable('root factories', |
| | | None, |
| | |
| | | intr['factory'] = factory |
| | | self.action(IRootFactory, register, introspectables=(intr,)) |
| | | |
| | | _set_root_factory = set_root_factory # bw compat |
| | | _set_root_factory = set_root_factory # bw compat |
| | | |
| | | @action_method |
| | | def set_session_factory(self, factory): |
| | |
| | | achieve the same purpose. |
| | | """ |
| | | factory = self.maybe_dotted(factory) |
| | | |
| | | def register(): |
| | | self.registry.registerUtility(factory, ISessionFactory) |
| | | intr = self.introspectable('session factory', None, |
| | |
| | | can be used to achieve the same purpose. |
| | | """ |
| | | factory = self.maybe_dotted(factory) |
| | | |
| | | def register(): |
| | | self.registry.registerUtility(factory, IRequestFactory) |
| | | intr = self.introspectable('request factory', None, |
| | |
| | | 'request factory') |
| | | intr['factory'] = factory |
| | | self.action(IRequestFactory, register, introspectables=(intr,)) |
| | | |
| | | @action_method |
| | | def set_response_factory(self, factory): |
| | | """ The object passed as ``factory`` should be an object (or a |
| | | :term:`dotted Python name` which refers to an object) which |
| | | will be used by the :app:`Pyramid` as the default response |
| | | objects. The factory should conform to the |
| | | :class:`pyramid.interfaces.IResponseFactory` interface. |
| | | |
| | | .. note:: |
| | | |
| | | Using the ``response_factory`` argument to the |
| | | :class:`pyramid.config.Configurator` constructor |
| | | can be used to achieve the same purpose. |
| | | """ |
| | | factory = self.maybe_dotted(factory) |
| | | |
| | | def register(): |
| | | self.registry.registerUtility(factory, IResponseFactory) |
| | | |
| | | intr = self.introspectable('response factory', None, |
| | | self.object_description(factory), |
| | | 'response factory') |
| | | intr['factory'] = factory |
| | | self.action(IResponseFactory, register, introspectables=(intr,)) |
| | | |
| | | @action_method |
| | | def add_request_method(self, |
| | |
| | | |
| | | 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__ |
| | | else: |
| | | name = get_callable_name(name) |
| | | |
| | | def register(): |
| | | exts = self.registry.queryUtility(IRequestExtensions) |
| | |
| | | introspectables=(intr,)) |
| | | |
| | | @action_method |
| | | @deprecate('set_request_propery() is deprecated as of Pyramid 1.5; use ' |
| | | 'add_request_method() with the property=True argument instead') |
| | | def set_request_property(self, callable, name=None, reify=False): |
| | | """ Add a property to the request object. |
| | | |
| | |
| | | self.add_request_method( |
| | | callable, name=name, property=not reify, reify=reify) |
| | | |
| | | deprecated( |
| | | set_request_property, |
| | | 'set_request_propery() is deprecated as of Pyramid 1.5; use ' |
| | | 'add_request_method() with the property=True argument instead') |
| | | |
| | | |
| | | @implementer(IRequestExtensions) |
| | | class _RequestExtensions(object): |
| | | def __init__(self): |
| | | self.descriptors = {} |
| | | self.methods = {} |
| | | |
| | |
| | | |
| | | .. versionadded:: 1.1 |
| | | |
| | | accept |
| | | |
| | | This value represents a match query for one or more mimetypes in the |
| | | ``Accept`` HTTP request header. If this value is specified, it must |
| | | be in one of the following forms: a mimetype match token in the form |
| | | ``text/plain``, a wildcard mimetype match token in the form |
| | | ``text/*`` or a match-all wildcard mimetype match token in the form |
| | | ``*/*``. If any of the forms matches the ``Accept`` header of the |
| | | request, or if the ``Accept`` header isn't set at all in the request, |
| | | this will match the current route. If this does not match the |
| | | ``Accept`` header of the request, route matching continues. |
| | | |
| | | Predicate Arguments |
| | | |
| | | pattern |
| | |
| | | case of the header name is not significant. If this |
| | | predicate returns ``False``, route matching continues. |
| | | |
| | | accept |
| | | |
| | | This value represents a match query for one or more |
| | | mimetypes in the ``Accept`` HTTP request header. If this |
| | | value is specified, it must be in one of the following |
| | | forms: a mimetype match token in the form ``text/plain``, a |
| | | wildcard mimetype match token in the form ``text/*`` or a |
| | | match-all wildcard mimetype match token in the form ``*/*``. |
| | | If any of the forms matches the ``Accept`` header of the |
| | | request, or if the ``Accept`` header isn't set at all in the |
| | | request, this predicate will be true. If this predicate |
| | | returns ``False``, route matching continues. |
| | | |
| | | effective_principals |
| | | |
| | | If specified, this value should be a :term:`principal` identifier or |
| | |
| | | # check for an external route; an external route is one which is |
| | | # is a full url (e.g. 'http://example.com/{id}') |
| | | parsed = urlparse.urlparse(pattern) |
| | | external_url = pattern |
| | | |
| | | if parsed.hostname: |
| | | pattern = parsed.path |
| | | |
| | |
| | | intr['pregenerator'] = pregenerator |
| | | intr['static'] = static |
| | | intr['use_global_views'] = use_global_views |
| | | |
| | | if static is True: |
| | | intr['external_url'] = external_url |
| | | |
| | | introspectables.append(intr) |
| | | |
| | | if factory: |
| | |
| | | |
| | | def add_settings(self, settings=None, **kw): |
| | | """Augment the :term:`deployment settings` with one or more |
| | | key/value pairs. |
| | | key/value pairs. |
| | | |
| | | You may pass a dictionary:: |
| | | |
| | |
| | | config_prevent_http_cache) |
| | | eff_prevent_http_cache = asbool(eget('PYRAMID_PREVENT_HTTP_CACHE', |
| | | config_prevent_http_cache)) |
| | | config_prevent_cachebust = self.get('prevent_cachebust', '') |
| | | config_prevent_cachebust = self.get('pyramid.prevent_cachebust', |
| | | config_prevent_cachebust) |
| | | eff_prevent_cachebust = asbool(eget('PYRAMID_PREVENT_CACHEBUST', |
| | | config_prevent_cachebust)) |
| | | |
| | | update = { |
| | | 'debug_authorization': eff_debug_all or eff_debug_auth, |
| | |
| | | 'reload_assets':eff_reload_all or eff_reload_assets, |
| | | 'default_locale_name':eff_locale_name, |
| | | 'prevent_http_cache':eff_prevent_http_cache, |
| | | 'prevent_cachebust':eff_prevent_cachebust, |
| | | |
| | | 'pyramid.debug_authorization': eff_debug_all or eff_debug_auth, |
| | | 'pyramid.debug_notfound': eff_debug_all or eff_debug_notfound, |
| | |
| | | 'pyramid.reload_assets':eff_reload_all or eff_reload_assets, |
| | | 'pyramid.default_locale_name':eff_locale_name, |
| | | 'pyramid.prevent_http_cache':eff_prevent_http_cache, |
| | | 'pyramid.prevent_cachebust':eff_prevent_cachebust, |
| | | } |
| | | |
| | | self.update(update) |
| | |
| | | |
| | | from pyramid.compat import ( |
| | | bytes_, |
| | | getargspec, |
| | | is_nonstr_iter, |
| | | ) |
| | | |
| | |
| | | return False |
| | | |
| | | try: |
| | | argspec = inspect.getargspec(fn) |
| | | argspec = getargspec(fn) |
| | | except TypeError: |
| | | return False |
| | | |
| | |
| | | ) |
| | | |
| | | from pyramid import renderers |
| | | from pyramid.static import PathSegmentMd5CacheBuster |
| | | |
| | | from pyramid.compat import ( |
| | | string_types, |
| | |
| | | url_quote, |
| | | WIN, |
| | | is_bound_method, |
| | | is_nonstr_iter |
| | | is_unbound_method, |
| | | is_nonstr_iter, |
| | | ) |
| | | |
| | | from pyramid.encode import ( |
| | | quote_plus, |
| | | urlencode, |
| | | ) |
| | | |
| | | from pyramid.exceptions import ( |
| | | ConfigurationError, |
| | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPForbidden, |
| | | HTTPNotFound, |
| | | default_exceptionresponse_view, |
| | | ) |
| | | |
| | | from pyramid.registry import ( |
| | |
| | | raise PredicateMismatch( |
| | | 'predicate mismatch for view %s (%s)' % ( |
| | | view_name, predicate.text())) |
| | | return view(context, request) |
| | | return view(context, request) |
| | | def checker(context, request): |
| | | return all((predicate(context, request) for predicate in |
| | | preds)) |
| | |
| | | |
| | | def _rendered_view(self, view, view_renderer): |
| | | def rendered_view(context, request): |
| | | renderer = view_renderer |
| | | result = view(context, request) |
| | | if result.__class__ is Response: # potential common case |
| | | response = result |
| | |
| | | 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: |
| | |
| | | self.attr = kw.get('attr') |
| | | |
| | | def __call__(self, view): |
| | | if is_unbound_method(view) and self.attr is None: |
| | | raise ConfigurationError(( |
| | | 'Unbound method calls are not supported, please set the class ' |
| | | 'as your `view` and the method as your `attr`' |
| | | )) |
| | | |
| | | if inspect.isclass(view): |
| | | view = self.map_class(view) |
| | | else: |
| | |
| | | very useful for 'civilians' who are just developing stock Pyramid |
| | | applications. Pay no attention to the man behind the curtain. |
| | | |
| | | accept |
| | | |
| | | This value represents a match query for one or more mimetypes in the |
| | | ``Accept`` HTTP request header. If this value is specified, it must |
| | | be in one of the following forms: a mimetype match token in the form |
| | | ``text/plain``, a wildcard mimetype match token in the form |
| | | ``text/*`` or a match-all wildcard mimetype match token in the form |
| | | ``*/*``. If any of the forms matches the ``Accept`` header of the |
| | | request, or if the ``Accept`` header isn't set at all in the request, |
| | | this will match the current view. If this does not match the |
| | | ``Accept`` header of the request, view matching continues. |
| | | |
| | | Predicate Arguments |
| | | |
| | | name |
| | |
| | | |
| | | request_param |
| | | |
| | | This value can be any string or any sequence of strings. A view |
| | | declaration with this argument ensures that the view will only be |
| | | This value can be any string or any sequence of strings. A view |
| | | declaration with this argument ensures that the view will only be |
| | | called when the :term:`request` has a key in the ``request.params`` |
| | | dictionary (an HTTP ``GET`` or ``POST`` variable) that has a |
| | | name which matches the supplied value (if the value is a string) |
| | |
| | | This is useful for detecting AJAX requests issued from |
| | | jQuery, Prototype and other Javascript libraries. |
| | | |
| | | accept |
| | | |
| | | The value of this argument represents a match query for one |
| | | or more mimetypes in the ``Accept`` HTTP request header. If |
| | | this value is specified, it must be in one of the following |
| | | forms: a mimetype match token in the form ``text/plain``, a |
| | | wildcard mimetype match token in the form ``text/*`` or a |
| | | match-all wildcard mimetype match token in the form ``*/*``. |
| | | If any of the forms matches the ``Accept`` header of the |
| | | request, this predicate will be true. |
| | | |
| | | header |
| | | |
| | | This value represents an HTTP header name or a header |
| | |
| | | |
| | | Note that using this feature requires a :term:`session factory` to |
| | | have been configured. |
| | | |
| | | |
| | | .. versionadded:: 1.4a2 |
| | | |
| | | physical_path |
| | |
| | | This value should be a sequence of references to custom |
| | | predicate callables. Use custom predicates when no set of |
| | | predefined predicates do what you need. Custom predicates |
| | | can be combined with predefined predicates as necessary. |
| | | can be combined with predefined predicates as necessary. |
| | | Each custom predicate callable should accept two arguments: |
| | | ``context`` and ``request`` and should return either |
| | | ``True`` or ``False`` after doing arbitrary evaluation of |
| | |
| | | DeprecationWarning, |
| | | stacklevel=4 |
| | | ) |
| | | |
| | | |
| | | view = self.maybe_dotted(view) |
| | | context = self.maybe_dotted(context) |
| | | for_ = self.maybe_dotted(for_) |
| | |
| | | view_desc = self.object_description(view) |
| | | |
| | | tmpl_intr = None |
| | | |
| | | |
| | | view_intr = self.introspectable('views', |
| | | discriminator, |
| | | view_desc, |
| | |
| | | predlist = self.get_predlist('view') |
| | | |
| | | def register(permission=permission, renderer=renderer): |
| | | # the discrim_func above is guaranteed to have been called already |
| | | order = view_intr['order'] |
| | | preds = view_intr['predicates'] |
| | | phash = view_intr['phash'] |
| | | request_iface = IRequest |
| | | if route_name is not None: |
| | | request_iface = self.registry.queryUtility(IRouteRequest, |
| | |
| | | wrapper=None, |
| | | route_name=None, |
| | | request_type=None, |
| | | request_method=None, |
| | | request_method=None, |
| | | request_param=None, |
| | | containment=None, |
| | | xhr=None, |
| | |
| | | |
| | | config.add_forbidden_view(forbidden) |
| | | |
| | | If ``view`` argument is not provided, the view callable defaults to |
| | | :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. |
| | | |
| | | All arguments have the same meaning as |
| | | :meth:`pyramid.config.Configurator.add_view` and each predicate |
| | | argument restricts the set of circumstances under which this notfound |
| | | argument restricts the set of circumstances under which this forbidden |
| | | view will be invoked. Unlike |
| | | :meth:`pyramid.config.Configurator.add_view`, this method will raise |
| | | an exception if passed ``name``, ``permission``, ``context``, |
| | |
| | | '%s may not be used as an argument to add_forbidden_view' |
| | | % arg |
| | | ) |
| | | |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | | |
| | | settings = dict( |
| | | view=view, |
| | | context=HTTPForbidden, |
| | |
| | | containment=containment, |
| | | xhr=xhr, |
| | | accept=accept, |
| | | header=header, |
| | | header=header, |
| | | path_info=path_info, |
| | | custom_predicates=custom_predicates, |
| | | decorator=decorator, |
| | |
| | | return self.add_view(**settings) |
| | | |
| | | set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias |
| | | |
| | | |
| | | @viewdefaults |
| | | @action_method |
| | | def add_notfound_view( |
| | |
| | | wrapper=None, |
| | | route_name=None, |
| | | request_type=None, |
| | | request_method=None, |
| | | request_method=None, |
| | | request_param=None, |
| | | containment=None, |
| | | xhr=None, |
| | |
| | | |
| | | config.add_notfound_view(notfound) |
| | | |
| | | If ``view`` argument is not provided, the view callable defaults to |
| | | :func:`~pyramid.httpexceptions.default_exceptionresponse_view`. |
| | | |
| | | All arguments except ``append_slash`` have the same meaning as |
| | | :meth:`pyramid.config.Configurator.add_view` and each predicate |
| | | argument restricts the set of circumstances under which this notfound |
| | |
| | | Pyramid will return the result of the view callable provided as |
| | | ``view``, as normal. |
| | | |
| | | If the argument provided as ``append_slash`` is not a boolean but |
| | | instead implements :class:`~pyramid.interfaces.IResponse`, the |
| | | append_slash logic will behave as if ``append_slash=True`` was passed, |
| | | but the provided class will be used as the response class instead of |
| | | the default :class:`~pyramid.httpexceptions.HTTPFound` response class |
| | | when a redirect is performed. For example: |
| | | |
| | | .. code-block:: python |
| | | |
| | | from pyramid.httpexceptions import HTTPMovedPermanently |
| | | config.add_notfound_view(append_slash=HTTPMovedPermanently) |
| | | |
| | | The above means that a redirect to a slash-appended route will be |
| | | attempted, but instead of :class:`~pyramid.httpexceptions.HTTPFound` |
| | | being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will |
| | | be used` for the redirect response if a slash-appended route is found. |
| | | |
| | | .. versionchanged:: 1.6 |
| | | .. versionadded:: 1.3 |
| | | """ |
| | | for arg in ('name', 'permission', 'context', 'for_', 'http_cache'): |
| | |
| | | '%s may not be used as an argument to add_notfound_view' |
| | | % arg |
| | | ) |
| | | |
| | | |
| | | if view is None: |
| | | view = default_exceptionresponse_view |
| | | |
| | | settings = dict( |
| | | view=view, |
| | | context=HTTPNotFound, |
| | |
| | | containment=containment, |
| | | xhr=xhr, |
| | | accept=accept, |
| | | header=header, |
| | | header=header, |
| | | path_info=path_info, |
| | | custom_predicates=custom_predicates, |
| | | decorator=decorator, |
| | |
| | | settings.update(predicates) |
| | | if append_slash: |
| | | view = self._derive_view(view, attr=attr, renderer=renderer) |
| | | view = AppendSlashNotFoundViewFactory(view) |
| | | if IResponse.implementedBy(append_slash): |
| | | view = AppendSlashNotFoundViewFactory( |
| | | view, redirect_class=append_slash, |
| | | ) |
| | | else: |
| | | view = AppendSlashNotFoundViewFactory(view) |
| | | settings['view'] = view |
| | | else: |
| | | settings['attr'] = attr |
| | |
| | | ``Expires`` and ``Cache-Control`` headers for static assets served. |
| | | Note that this argument has no effect when the ``name`` is a *url |
| | | prefix*. By default, this argument is ``None``, meaning that no |
| | | particular Expires or Cache-Control headers are set in the response. |
| | | particular Expires or Cache-Control headers are set in the response, |
| | | unless ``cachebust`` is specified. |
| | | |
| | | The ``cachebust`` keyword argument may be set to cause |
| | | :meth:`~pyramid.request.Request.static_url` to use cache busting when |
| | | generating URLs. See :ref:`cache_busting` for general information |
| | | about cache busting. The value of the ``cachebust`` argument may be |
| | | ``True``, in which case a default cache busting implementation is used. |
| | | The value of the ``cachebust`` argument may also be an object which |
| | | implements :class:`~pyramid.interfaces.ICacheBuster`. See the |
| | | :mod:`~pyramid.static` module for some implementations. If the |
| | | ``cachebust`` argument is provided, the default for ``cache_max_age`` |
| | | is modified to be ten years. ``cache_max_age`` may still be explicitly |
| | | provided to override this default. |
| | | |
| | | The ``permission`` keyword argument is used to specify the |
| | | :term:`permission` required by a user to execute the static view. By |
| | |
| | | |
| | | @implementer(IStaticURLInfo) |
| | | class StaticURLInfo(object): |
| | | # Indirection for testing |
| | | _default_cachebust = PathSegmentMd5CacheBuster |
| | | |
| | | def _get_registrations(self, registry): |
| | | try: |
| | |
| | | registry = request.registry |
| | | except AttributeError: # bw compat (for tests) |
| | | registry = get_current_registry() |
| | | for (url, spec, route_name) in self._get_registrations(registry): |
| | | registrations = self._get_registrations(registry) |
| | | for (url, spec, route_name, cachebust) in registrations: |
| | | if path.startswith(spec): |
| | | subpath = path[len(spec):] |
| | | if WIN: # pragma: no cover |
| | | subpath = subpath.replace('\\', '/') # windows |
| | | if cachebust: |
| | | subpath, kw = cachebust(subpath, kw) |
| | | if url is None: |
| | | kw['subpath'] = subpath |
| | | return request.route_url(route_name, **kw) |
| | |
| | | sep = os.sep |
| | | else: |
| | | sep = '/' |
| | | if not spec.endswith(sep): |
| | | if not spec.endswith(sep) and not spec.endswith(':'): |
| | | spec = spec + sep |
| | | |
| | | # we also make sure the name ends with a slash, purely as a |
| | |
| | | # make sure it ends with a slash |
| | | name = name + '/' |
| | | |
| | | if config.registry.settings.get('pyramid.prevent_cachebust'): |
| | | cb = None |
| | | else: |
| | | cb = extra.pop('cachebust', None) |
| | | if cb is True: |
| | | cb = self._default_cachebust() |
| | | if cb: |
| | | def cachebust(subpath, kw): |
| | | subpath_tuple = tuple(subpath.split('/')) |
| | | subpath_tuple, kw = cb.pregenerate( |
| | | spec + subpath, subpath_tuple, kw) |
| | | return '/'.join(subpath_tuple), kw |
| | | else: |
| | | cachebust = None |
| | | |
| | | if url_parse(name).netloc: |
| | | # it's a URL |
| | | # url, spec, route_name |
| | |
| | | else: |
| | | # it's a view name |
| | | url = None |
| | | cache_max_age = extra.pop('cache_max_age', None) |
| | | ten_years = 10 * 365 * 24 * 60 * 60 # more or less |
| | | default = ten_years if cb else None |
| | | cache_max_age = extra.pop('cache_max_age', default) |
| | | |
| | | # create a view |
| | | cb_match = getattr(cb, 'match', None) |
| | | view = static_view(spec, cache_max_age=cache_max_age, |
| | | use_subpath=True) |
| | | use_subpath=True, cachebust_match=cb_match) |
| | | |
| | | # Mutate extra to allow factory, etc to be passed through here. |
| | | # Treat permission specially because we'd like to default to |
| | |
| | | registrations.pop(idx) |
| | | |
| | | # url, spec, route_name |
| | | registrations.append((url, spec, route_name)) |
| | | registrations.append((url, spec, route_name, cachebust)) |
| | | |
| | | intr = config.introspectable('static views', |
| | | name, |
| | |
| | | intr['spec'] = spec |
| | | |
| | | config.action(None, callable=register, introspectables=(intr,)) |
| | | |
| | | |
| | |
| | | import functools |
| | | |
| | | |
| | | class reify(object): |
| | | """ Use as a class method decorator. It operates almost exactly like the |
| | | Python ``@property`` decorator, but it puts the result of the method it |
| | |
| | | """ |
| | | def __init__(self, wrapped): |
| | | self.wrapped = wrapped |
| | | try: |
| | | self.__doc__ = wrapped.__doc__ |
| | | except: # pragma: no cover |
| | | pass |
| | | functools.update_wrapper(self, wrapped) |
| | | |
| | | def __get__(self, inst, objtype=None): |
| | | if inst is None: |
| | |
| | | * 422 - HTTPUnprocessableEntity |
| | | * 423 - HTTPLocked |
| | | * 424 - HTTPFailedDependency |
| | | * 428 - HTTPPreconditionRequired |
| | | * 429 - HTTPTooManyRequests |
| | | * 431 - HTTPRequestHeaderFieldsTooLarge |
| | | HTTPServerError |
| | | * 500 - HTTPInternalServerError |
| | | * 501 - HTTPNotImplemented |
| | |
| | | subclass of :class:`~HTTPClientError` |
| | | |
| | | This indicates that the server is unable to process the contained |
| | | instructions. Only for WebDAV. |
| | | instructions. |
| | | |
| | | May be used to notify the client that their JSON/XML is well formed, but |
| | | not correct for the current request. |
| | | |
| | | See RFC4918 section 11 for more information. |
| | | |
| | | code: 422, title: Unprocessable Entity |
| | | """ |
| | |
| | | """ |
| | | subclass of :class:`~HTTPClientError` |
| | | |
| | | This indicates that the resource is locked. Only for WebDAV |
| | | This indicates that the resource is locked. |
| | | |
| | | code: 423, title: Locked |
| | | """ |
| | |
| | | |
| | | This indicates that the method could not be performed because the |
| | | requested action depended on another action and that action failed. |
| | | Only for WebDAV. |
| | | |
| | | code: 424, title: Failed Dependency |
| | | """ |
| | |
| | | 'The method could not be performed because the requested ' |
| | | 'action dependended on another action and that action failed') |
| | | |
| | | class HTTPPreconditionRequired(HTTPClientError): |
| | | """ |
| | | subclass of :class:`~HTTPClientError` |
| | | |
| | | This indicates that the origin server requires the |
| | | request to be conditional. |
| | | |
| | | Its typical use is to avoid the "lost update" problem, where a client |
| | | GETs a resource's state, modifies it, and PUTs it back to the server, |
| | | when meanwhile a third party has modified the state on the server, |
| | | leading to a conflict. By requiring requests to be conditional, the |
| | | server can assure that clients are working with the correct copies. |
| | | |
| | | RFC 6585.3 |
| | | |
| | | code: 428, title: Precondition Required |
| | | """ |
| | | code = 428 |
| | | title = 'Precondition Required' |
| | | explanation = ( |
| | | 'The origin server requires the request to be conditional.') |
| | | |
| | | class HTTPTooManyRequests(HTTPClientError): |
| | | """ |
| | | subclass of :class:`~HTTPClientError` |
| | | |
| | | This indicates that the user has sent too many |
| | | requests in a given amount of time ("rate limiting"). |
| | | |
| | | RFC 6585.4 |
| | | |
| | | code: 429, title: Too Many Requests |
| | | """ |
| | | code = 429 |
| | | title = 'Too Many Requests' |
| | | explanation = ( |
| | | 'The action could not be performed because there were too ' |
| | | 'many requests by the client.') |
| | | |
| | | class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): |
| | | """ |
| | | subclass of :class:`~HTTPClientError` |
| | | |
| | | This indicates that the server is unwilling to process |
| | | the request because its header fields are too large. The request MAY |
| | | be resubmitted after reducing the size of the request header fields. |
| | | |
| | | RFC 6585.5 |
| | | |
| | | code: 431, title: Request Header Fields Too Large |
| | | """ |
| | | code = 431 |
| | | title = 'Request Header Fields Too Large' |
| | | explanation = ( |
| | | 'The requests header fields were too large.') |
| | | |
| | | ############################################################ |
| | | ## 5xx Server Error |
| | | ############################################################ |
| | |
| | | """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 IAuthenticationPolicy(Interface): |
| | | """ An object representing a Pyramid authentication policy. """ |
| | | |
| | | def authenticated_userid(request): |
| | | """ Return the authenticated userid or ``None`` if no authenticated |
| | | userid can be found. This method of the policy should ensure that a |
| | | record exists in whatever persistent store is used related to the |
| | | user (the user should not have been deleted); if a record associated |
| | | with the current id does not exist in a persistent store, it should |
| | | return ``None``.""" |
| | | """ Return the authenticated :term:`userid` or ``None`` if |
| | | no authenticated userid can be found. This method of the |
| | | policy should ensure that a record exists in whatever |
| | | persistent store is used related to the user (the user |
| | | should not have been deleted); if a record associated with |
| | | the current id does not exist in a persistent store, it |
| | | should return ``None``. |
| | | |
| | | """ |
| | | |
| | | def unauthenticated_userid(request): |
| | | """ Return the *unauthenticated* userid. This method performs the |
| | | same duty as ``authenticated_userid`` but is permitted to return the |
| | | userid based only on data present in the request; it needn't (and |
| | | shouldn't) check any persistent store to ensure that the user record |
| | | related to the request userid exists.""" |
| | | """ Return the *unauthenticated* userid. This method |
| | | performs the same duty as ``authenticated_userid`` but is |
| | | permitted to return the userid based only on data present |
| | | in the request; it needn't (and shouldn't) check any |
| | | persistent store to ensure that the user record related to |
| | | the request userid exists. |
| | | |
| | | This method is intended primarily a helper to assist the |
| | | ``authenticated_userid`` method in pulling credentials out |
| | | of the request data, abstracting away the specific headers, |
| | | query strings, etc that are used to authenticate the request. |
| | | |
| | | """ |
| | | |
| | | def effective_principals(request): |
| | | """ Return a sequence representing the effective principals |
| | | including the userid and any groups belonged to by the current |
| | | user, including 'system' groups such as Everyone and |
| | | Authenticated. """ |
| | | typically including the :term:`userid` and any groups belonged |
| | | to by the current user, always including 'system' groups such |
| | | as ``pyramid.security.Everyone`` and |
| | | ``pyramid.security.Authenticated``. |
| | | |
| | | def remember(request, principal, **kw): |
| | | """ |
| | | |
| | | def remember(request, userid, **kw): |
| | | """ Return a set of headers suitable for 'remembering' the |
| | | principal named ``principal`` when set in a response. An |
| | | individual authentication policy and its consumers can decide |
| | | on the composition and meaning of ``**kw.`` """ |
| | | :term:`userid` named ``userid`` when set in a response. An |
| | | individual authentication policy and its consumers can |
| | | decide on the composition and meaning of ``**kw``. |
| | | |
| | | """ |
| | | |
| | | def forget(request): |
| | | """ Return a set of headers suitable for 'forgetting' the |
| | | current user on subsequent requests. """ |
| | | current user on subsequent requests. |
| | | |
| | | """ |
| | | |
| | | class IAuthorizationPolicy(Interface): |
| | | """ An object representing a Pyramid authorization policy. """ |
| | |
| | | """ Generate a URL for the given path """ |
| | | |
| | | class IResponseFactory(Interface): |
| | | """ A utility which generates a response factory """ |
| | | def __call__(): |
| | | """ Return a response factory (e.g. a callable that returns an object |
| | | implementing IResponse, e.g. :class:`pyramid.response.Response`). It |
| | | should accept all the arguments that the Pyramid Response class |
| | | accepts.""" |
| | | """ A utility which generates a response """ |
| | | def __call__(request): |
| | | """ Return a response object implementing IResponse, |
| | | e.g. :class:`pyramid.response.Response`). It should handle the |
| | | case when ``request`` is ``None``.""" |
| | | |
| | | 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 |
| | |
| | | pregenerator = Attribute('This attribute should either be ``None`` or ' |
| | | 'a callable object implementing the ' |
| | | '``IRoutePregenerator`` interface') |
| | | |
| | | |
| | | def match(path): |
| | | """ |
| | | If the ``path`` passed to this function can be matched by the |
| | |
| | | # <__main__.Fudge object at 0x1cda890> |
| | | # <object object at 0x7fa678f3e2a0> <object object at 0x7fa678f3e2a0> |
| | | # <__main__.Another object at 0x1cda850> |
| | | |
| | | |
| | | def virtual_root(): |
| | | """ Return the virtual root related to a request and the |
| | | current context""" |
| | |
| | | |
| | | def get_code(fullname): |
| | | """ Return the code object for the module identified by 'fullname'. |
| | | |
| | | |
| | | Return 'None' if it's a built-in or extension module. |
| | | |
| | | |
| | | If the loader doesn't have the code object but it does have the source |
| | | code, return the compiled source code. |
| | | |
| | |
| | | |
| | | def get_source(fullname): |
| | | """ Return the source code for the module identified by 'fullname'. |
| | | |
| | | |
| | | Return a string, using newline characters for line endings, or None |
| | | if the source is not available. |
| | | |
| | | |
| | | Raise ImportError if the module can't be found by the importer at all. |
| | | """ |
| | | |
| | | def get_filename(fullname): |
| | | """ Return the value of '__file__' if the named module was loaded. |
| | | |
| | | |
| | | If the module is not found, raise ImportError. |
| | | """ |
| | | |
| | |
| | | class IPredicateList(Interface): |
| | | """ Interface representing a predicate list """ |
| | | |
| | | class ICacheBuster(Interface): |
| | | """ |
| | | Instances of ``ICacheBuster`` may be provided as arguments to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`. Instances of |
| | | ``ICacheBuster`` provide mechanisms for generating a cache bust token for |
| | | a static asset, modifying a static asset URL to include a cache bust token, |
| | | and, optionally, unmodifying a static asset URL in order to look up an |
| | | asset. See :ref:`cache_busting`. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | 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 ``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 |
| | | :meth:`~pyramid.request.Request.route_url` for URL generation. The |
| | | return value should be a two-tuple of ``(subpath, kw)`` which are |
| | | versions of the same arguments modified to include the cachebust token |
| | | in the generated URL. |
| | | """ |
| | | |
| | | def match(subpath): |
| | | """ |
| | | Performs the logical inverse of |
| | | :meth:`~pyramid.interfaces.ICacheBuster.pregenerate` by taking a |
| | | subpath from a cache busted URL and removing the cache bust token, so |
| | | that :app:`Pyramid` can find the underlying asset. |
| | | |
| | | ``subpath`` is the subpath portion of the URL for an incoming request |
| | | for a static asset. The return value should be the same tuple with the |
| | | cache busting token elided. |
| | | |
| | | If the cache busting scheme in use doesn't specifically modify the path |
| | | portion of the generated URL (e.g. it adds a query string), a method |
| | | which implements this interface may not be necessary. It is |
| | | permissible for an instance of |
| | | :class:`~pyramid.interfaces.ICacheBuster` to omit this method. |
| | | """ |
| | | |
| | | # configuration phases: a lower phase number means the actions associated |
| | | # with this phase will be executed earlier than those with later phase |
| | | # numbers. The default phase number is 0, FTR. |
| | | |
| | | PHASE0_CONFIG = -30 |
| | | PHASE1_CONFIG = -20 |
| | | PHASE2_CONFIG = -10 |
| | | |
| | | PHASE3_CONFIG = 0 |
| | |
| | | value = package.__name__ |
| | | else: |
| | | value = package.__name__ + value |
| | | return pkg_resources.EntryPoint.parse( |
| | | 'x=%s' % value).load(False) |
| | | # Calling EntryPoint.load with an argument is deprecated. |
| | | # See https://pythonhosted.org/setuptools/history.html#id8 |
| | | ep = pkg_resources.EntryPoint.parse('x=%s' % value) |
| | | if hasattr(ep, 'resolve'): |
| | | # setuptools>=10.2 |
| | | return ep.resolve() # pragma: NO COVER |
| | | else: |
| | | return ep.load(False) # pragma: NO COVER |
| | | |
| | | def _zope_dottedname_style(self, value, package): |
| | | """ package.module.attr style """ |
| | |
| | | from zope.interface.registry import Components |
| | | |
| | | from pyramid.compat import text_ |
| | | from pyramid.decorator import reify |
| | | |
| | | from pyramid.interfaces import ( |
| | | ISettings, |
| | |
| | | # defeat bool determination via dict.__len__ |
| | | return True |
| | | |
| | | @reify |
| | | def package_name(self): |
| | | return self.__name__ |
| | | |
| | | def registerSubscriptionAdapter(self, *arg, **kw): |
| | | result = Components.registerSubscriptionAdapter(self, *arg, **kw) |
| | | self.has_listeners = True |
| | |
| | | import contextlib |
| | | import json |
| | | import os |
| | | |
| | |
| | | from pyramid.interfaces import ( |
| | | IJSONAdapter, |
| | | IRendererFactory, |
| | | IResponseFactory, |
| | | IRendererInfo, |
| | | ) |
| | | |
| | |
| | | |
| | | from pyramid.path import caller_package |
| | | |
| | | from pyramid.response import Response |
| | | from pyramid.response import _get_response_factory |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | # API |
| | |
| | | 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``. |
| | |
| | | When you've done this, the JSON renderer will be able to serialize |
| | | instances of the ``Foo`` class when they're encountered in your view |
| | | results.""" |
| | | |
| | | |
| | | self.components.registerAdapter(adapter, (type_or_iface,), |
| | | IJSONAdapter) |
| | | |
| | |
| | | response.content_type = 'application/json' |
| | | default = self._make_default(request) |
| | | return self.serializer(value, default=default, **self.kw) |
| | | |
| | | |
| | | return _render |
| | | |
| | | def _make_default(self, request): |
| | |
| | | class JSONP(JSON): |
| | | """ `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper |
| | | which implements a hybrid json/jsonp renderer. JSONP is useful for |
| | | making cross-domain AJAX requests. |
| | | making cross-domain AJAX requests. |
| | | |
| | | Configure a JSONP renderer using the |
| | | :meth:`pyramid.config.Configurator.add_renderer` API at application |
| | |
| | | |
| | | config = Configurator() |
| | | config.add_renderer('jsonp', JSONP(param_name='callback', indent=4)) |
| | | |
| | | |
| | | .. versionchanged:: 1.4 |
| | | The ability of this class to accept a ``**kw`` in its constructor. |
| | | |
| | |
| | | ``self.param_name`` is present in request.GET; otherwise returns |
| | | plain-JSON encoded string with content-type ``application/json``""" |
| | | def _render(value, system): |
| | | request = system['request'] |
| | | request = system.get('request') |
| | | default = self._make_default(request) |
| | | val = self.serializer(value, default=default, **self.kw) |
| | | callback = request.GET.get(self.param_name) |
| | | if callback is None: |
| | | ct = 'application/json' |
| | | body = val |
| | | else: |
| | | ct = 'application/javascript' |
| | | body = '%s(%s);' % (callback, val) |
| | | response = request.response |
| | | if response.content_type == response.default_content_type: |
| | | response.content_type = ct |
| | | ct = 'application/json' |
| | | body = val |
| | | if request is not None: |
| | | callback = request.GET.get(self.param_name) |
| | | if callback is not None: |
| | | ct = 'application/javascript' |
| | | body = '%s(%s);' % (callback, val) |
| | | response = request.response |
| | | if response.content_type == response.default_content_type: |
| | | response.content_type = ct |
| | | return body |
| | | return _render |
| | | |
| | |
| | | if response is None: |
| | | # request is None or request is not a pyramid.response.Response |
| | | registry = self.registry |
| | | response_factory = registry.queryUtility(IResponseFactory, |
| | | default=Response) |
| | | |
| | | response = response_factory() |
| | | response_factory = _get_response_factory(registry) |
| | | response = response_factory(request) |
| | | |
| | | if result is not None: |
| | | if isinstance(result, text_type): |
| | | response.text = result |
| | | elif isinstance(result, bytes): |
| | | response.body = result |
| | | elif hasattr(result, '__iter__'): |
| | | response.app_iter = result |
| | | else: |
| | | response.body = result |
| | | |
| | |
| | | |
| | | @property |
| | | def settings(self): |
| | | return get_current_registry().settings or {} |
| | | return {} |
| | | |
| | | def render_view(self, request, value, view, context): |
| | | return value |
| | | |
| | | def render(self, value, system_values, request=None): |
| | | return value |
| | | |
| | | |
| | | def render_to_response(self, value, system_values, request=None): |
| | | return value |
| | | |
| | | def clone(self, name=None, package=None, registry=None): |
| | | return self |
| | | |
| | | |
| | | null_renderer = NullRendererHelper() |
| | |
| | | from collections import deque |
| | | import json |
| | | |
| | | from zope.interface import implementer |
| | |
| | | |
| | | from pyramid.interfaces import ( |
| | | IRequest, |
| | | IRequestExtensions, |
| | | IResponse, |
| | | ISessionFactory, |
| | | IResponseFactory, |
| | | ) |
| | | |
| | | from pyramid.compat import ( |
| | | text_, |
| | | bytes_, |
| | | native_, |
| | | iteritems_, |
| | | ) |
| | | |
| | | from pyramid.decorator import reify |
| | | from pyramid.i18n import LocalizerRequestMixin |
| | | from pyramid.response import Response |
| | | from pyramid.response import Response, _get_response_factory |
| | | from pyramid.security import ( |
| | | AuthenticationAPIMixin, |
| | | AuthorizationAPIMixin, |
| | | ) |
| | | from pyramid.url import URLMethodsMixin |
| | | from pyramid.util import InstancePropertyMixin |
| | | from pyramid.util import ( |
| | | InstancePropertyHelper, |
| | | InstancePropertyMixin, |
| | | ) |
| | | |
| | | class TemplateContext(object): |
| | | pass |
| | | |
| | | class CallbackMethodsMixin(object): |
| | | response_callbacks = () |
| | | finished_callbacks = () |
| | | response_callbacks = None |
| | | finished_callbacks = None |
| | | def add_response_callback(self, callback): |
| | | """ |
| | | Add a callback to the set of callbacks to be called by the |
| | |
| | | """ |
| | | |
| | | callbacks = self.response_callbacks |
| | | if not callbacks: |
| | | callbacks = [] |
| | | if callbacks is None: |
| | | callbacks = deque() |
| | | callbacks.append(callback) |
| | | self.response_callbacks = callbacks |
| | | |
| | | def _process_response_callbacks(self, response): |
| | | callbacks = self.response_callbacks |
| | | while callbacks: |
| | | callback = callbacks.pop(0) |
| | | callback = callbacks.popleft() |
| | | callback(self, response) |
| | | |
| | | def add_finished_callback(self, callback): |
| | |
| | | """ |
| | | |
| | | callbacks = self.finished_callbacks |
| | | if not callbacks: |
| | | callbacks = [] |
| | | if callbacks is None: |
| | | callbacks = deque() |
| | | callbacks.append(callback) |
| | | self.finished_callbacks = callbacks |
| | | |
| | | def _process_finished_callbacks(self): |
| | | callbacks = self.finished_callbacks |
| | | while callbacks: |
| | | callback = callbacks.pop(0) |
| | | callback = callbacks.popleft() |
| | | callback(self) |
| | | |
| | | @implementer(IRequest) |
| | |
| | | right" attributes (e.g. by calling ``request.response.set_cookie()``) |
| | | within a view that uses a renderer. Mutations to this response object |
| | | will be preserved in the response sent to the client.""" |
| | | registry = self.registry |
| | | response_factory = registry.queryUtility(IResponseFactory, |
| | | default=Response) |
| | | return response_factory() |
| | | response_factory = _get_response_factory(self.registry) |
| | | return response_factory(self) |
| | | |
| | | def is_response(self, ob): |
| | | """ Return ``True`` if the object passed as ``ob`` is a valid |
| | |
| | | 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 webob import Response as _Response |
| | | from zope.interface import implementer |
| | | from pyramid.interfaces import IResponse |
| | | from pyramid.interfaces import IResponse, IResponseFactory |
| | | |
| | | |
| | | def init_mimetypes(mimetypes): |
| | | # this is a function so it can be unittested |
| | |
| | | @response_adapter(dict, list) |
| | | def myadapter(ob): |
| | | return Response(json.dumps(ob)) |
| | | |
| | | |
| | | This method will have no effect until a :term:`scan` is performed |
| | | agains the package or module which contains it, ala: |
| | | |
| | |
| | | def __call__(self, wrapped): |
| | | self.venusian.attach(wrapped, self.register, category='pyramid') |
| | | return wrapped |
| | | |
| | | |
| | | def _get_response_factory(registry): |
| | | """ Obtain a :class: `pyramid.response.Response` using the |
| | | `pyramid.interfaces.IResponseFactory`. |
| | | """ |
| | | response_factory = registry.queryUtility( |
| | | IResponseFactory, |
| | | default=lambda r: Response() |
| | | ) |
| | | |
| | | return response_factory |
| | |
| | | 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: |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | 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 |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | 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, |
| | |
| | | class PCreateCommand(object): |
| | | verbosity = 1 # required |
| | | description = "Render Pyramid scaffolding to an output directory" |
| | | usage = "usage: %prog [options] output_directory" |
| | | usage = "usage: %prog [options] -s <scaffold> output_directory" |
| | | parser = optparse.OptionParser(usage, description=description) |
| | | parser.add_option('-s', '--scaffold', |
| | | dest='scaffold_name', |
| | |
| | | def run(self): |
| | | if self.options.list: |
| | | return self.show_scaffolds() |
| | | if not self.options.scaffold_name and not self.args: |
| | | if not self.quiet: # pragma: no cover |
| | | self.parser.print_help() |
| | | self.out('') |
| | | self.show_scaffolds() |
| | | return 2 |
| | | if not self.options.scaffold_name: |
| | | self.out('You must provide at least one scaffold name') |
| | | self.out('You must provide at least one scaffold name: -s <scaffold name>') |
| | | self.out('') |
| | | self.show_scaffolds() |
| | | return 2 |
| | | if not self.args: |
| | | self.out('You must provide a project name') |
| | |
| | | args = self.args |
| | | output_dir = os.path.abspath(os.path.normpath(args[0])) |
| | | project_name = os.path.basename(os.path.split(output_dir)[1]) |
| | | pkg_name = _bad_chars_re.sub('', project_name.lower()) |
| | | pkg_name = _bad_chars_re.sub( |
| | | '', project_name.lower().replace('-', '_')) |
| | | safe_name = pkg_resources.safe_name(project_name) |
| | | egg_name = pkg_resources.to_filename(safe_name) |
| | | |
| | |
| | | |
| | | from pyramid.compat import url_unquote |
| | | from pyramid.request import Request |
| | | from pyramid.paster import get_app |
| | | from pyramid.paster import get_app, setup_logging |
| | | from pyramid.scripts.common import parse_vars |
| | | |
| | | def main(argv=sys.argv, quiet=False): |
| | |
| | | if not self.quiet: |
| | | print(msg) |
| | | |
| | | def configure_logging(self, app_spec): |
| | | setup_logging(app_spec) |
| | | |
| | | def run(self): |
| | | if not len(self.args) >= 2: |
| | | self.out('You must provide at least two arguments') |
| | | return 2 |
| | | app_spec = self.args[0] |
| | | path = self.args[1] |
| | | |
| | | self.configure_logging(app_spec) |
| | | |
| | | if not path.startswith('/'): |
| | | path = '/' + path |
| | | |
| | |
| | | import fnmatch |
| | | import optparse |
| | | import sys |
| | | import textwrap |
| | | import re |
| | | |
| | | from pyramid.paster import bootstrap |
| | | from pyramid.compat import (string_types, configparser) |
| | | from pyramid.interfaces import ( |
| | | IRouteRequest, |
| | | IViewClassifier, |
| | | IView, |
| | | ) |
| | | from pyramid.config import not_ |
| | | |
| | | from pyramid.scripts.common import parse_vars |
| | | from pyramid.static import static_view |
| | | from zope.interface import Interface |
| | | |
| | | |
| | | PAD = 3 |
| | | ANY_KEY = '*' |
| | | UNKNOWN_KEY = '<unknown>' |
| | | |
| | | |
| | | def main(argv=sys.argv, quiet=False): |
| | | command = PRoutesCommand(argv, quiet) |
| | | return command.run() |
| | | |
| | | |
| | | def _get_pattern(route): |
| | | pattern = route.pattern |
| | | |
| | | if not pattern.startswith('/'): |
| | | pattern = '/%s' % pattern |
| | | return pattern |
| | | |
| | | |
| | | def _get_print_format(fmt, max_name, max_pattern, max_view, max_method): |
| | | print_fmt = '' |
| | | max_map = { |
| | | 'name': max_name, |
| | | 'pattern': max_pattern, |
| | | 'view': max_view, |
| | | 'method': max_method, |
| | | } |
| | | sizes = [] |
| | | |
| | | for index, col in enumerate(fmt): |
| | | size = max_map[col] + PAD |
| | | print_fmt += '{{%s: <{%s}}} ' % (col, index) |
| | | sizes.append(size) |
| | | |
| | | return print_fmt.format(*sizes) |
| | | |
| | | |
| | | def _get_request_methods(route_request_methods, view_request_methods): |
| | | excludes = set() |
| | | |
| | | if route_request_methods: |
| | | route_request_methods = set(route_request_methods) |
| | | |
| | | if view_request_methods: |
| | | view_request_methods = set(view_request_methods) |
| | | |
| | | for method in view_request_methods.copy(): |
| | | if method.startswith('!'): |
| | | view_request_methods.remove(method) |
| | | excludes.add(method[1:]) |
| | | |
| | | has_route_methods = route_request_methods is not None |
| | | has_view_methods = len(view_request_methods) > 0 |
| | | has_methods = has_route_methods or has_view_methods |
| | | |
| | | if has_route_methods is False and has_view_methods is False: |
| | | request_methods = [ANY_KEY] |
| | | elif has_route_methods is False and has_view_methods is True: |
| | | request_methods = view_request_methods |
| | | elif has_route_methods is True and has_view_methods is False: |
| | | request_methods = route_request_methods |
| | | else: |
| | | request_methods = route_request_methods.intersection( |
| | | view_request_methods |
| | | ) |
| | | |
| | | request_methods = set(request_methods).difference(excludes) |
| | | |
| | | if has_methods and not request_methods: |
| | | request_methods = '<route mismatch>' |
| | | elif request_methods: |
| | | if excludes and request_methods == set([ANY_KEY]): |
| | | for exclude in excludes: |
| | | request_methods.add('!%s' % exclude) |
| | | |
| | | request_methods = ','.join(sorted(request_methods)) |
| | | |
| | | return request_methods |
| | | |
| | | |
| | | def _get_view_module(view_callable): |
| | | if view_callable is None: |
| | | return UNKNOWN_KEY |
| | | |
| | | if hasattr(view_callable, '__name__'): |
| | | if hasattr(view_callable, '__original_view__'): |
| | | original_view = view_callable.__original_view__ |
| | | else: |
| | | original_view = None |
| | | |
| | | if isinstance(original_view, static_view): |
| | | if original_view.package_name is not None: |
| | | return '%s:%s' % ( |
| | | original_view.package_name, |
| | | original_view.docroot |
| | | ) |
| | | else: |
| | | return original_view.docroot |
| | | else: |
| | | view_name = view_callable.__name__ |
| | | else: |
| | | # Currently only MultiView hits this, |
| | | # we could just not run _get_view_module |
| | | # for them and remove this logic |
| | | view_name = str(view_callable) |
| | | |
| | | view_module = '%s.%s' % ( |
| | | view_callable.__module__, |
| | | view_name, |
| | | ) |
| | | |
| | | # If pyramid wraps something in wsgiapp or wsgiapp2 decorators |
| | | # that is currently returned as pyramid.router.decorator, lets |
| | | # hack a nice name in: |
| | | if view_module == 'pyramid.router.decorator': |
| | | view_module = '<wsgiapp>' |
| | | |
| | | return view_module |
| | | |
| | | |
| | | def get_route_data(route, registry): |
| | | pattern = _get_pattern(route) |
| | | |
| | | request_iface = registry.queryUtility( |
| | | IRouteRequest, |
| | | name=route.name |
| | | ) |
| | | |
| | | route_request_methods = None |
| | | view_request_methods_order = [] |
| | | view_request_methods = {} |
| | | view_callable = None |
| | | |
| | | route_intr = registry.introspector.get( |
| | | 'routes', route.name |
| | | ) |
| | | |
| | | if request_iface is None: |
| | | return [ |
| | | (route.name, _get_pattern(route), UNKNOWN_KEY, ANY_KEY) |
| | | ] |
| | | |
| | | view_callable = registry.adapters.lookup( |
| | | (IViewClassifier, request_iface, Interface), |
| | | IView, |
| | | name='', |
| | | default=None |
| | | ) |
| | | view_module = _get_view_module(view_callable) |
| | | |
| | | # Introspectables can be turned off, so there could be a chance |
| | | # that we have no `route_intr` but we do have a route + callable |
| | | if route_intr is None: |
| | | view_request_methods[view_module] = [] |
| | | view_request_methods_order.append(view_module) |
| | | else: |
| | | if route_intr.get('static', False) is True: |
| | | return [ |
| | | (route.name, route_intr['external_url'], UNKNOWN_KEY, ANY_KEY) |
| | | ] |
| | | |
| | | |
| | | route_request_methods = route_intr['request_methods'] |
| | | view_intr = registry.introspector.related(route_intr) |
| | | |
| | | if view_intr: |
| | | for view in view_intr: |
| | | request_method = view.get('request_methods') |
| | | |
| | | if request_method is not None: |
| | | view_callable = view['callable'] |
| | | view_module = _get_view_module(view_callable) |
| | | |
| | | if view_module not in view_request_methods: |
| | | view_request_methods[view_module] = [] |
| | | view_request_methods_order.append(view_module) |
| | | |
| | | if isinstance(request_method, string_types): |
| | | request_method = (request_method,) |
| | | elif isinstance(request_method, not_): |
| | | request_method = ('!%s' % request_method.value,) |
| | | |
| | | view_request_methods[view_module].extend(request_method) |
| | | else: |
| | | if view_module not in view_request_methods: |
| | | view_request_methods[view_module] = [] |
| | | view_request_methods_order.append(view_module) |
| | | |
| | | else: |
| | | view_request_methods[view_module] = [] |
| | | view_request_methods_order.append(view_module) |
| | | |
| | | final_routes = [] |
| | | |
| | | for view_module in view_request_methods_order: |
| | | methods = view_request_methods[view_module] |
| | | request_methods = _get_request_methods( |
| | | route_request_methods, |
| | | methods |
| | | ) |
| | | |
| | | final_routes.append(( |
| | | route.name, |
| | | pattern, |
| | | view_module, |
| | | request_methods, |
| | | )) |
| | | |
| | | return final_routes |
| | | |
| | | |
| | | class PRoutesCommand(object): |
| | | description = """\ |
| | |
| | | bootstrap = (bootstrap,) |
| | | stdout = sys.stdout |
| | | usage = '%prog config_uri' |
| | | |
| | | ConfigParser = configparser.ConfigParser # testing |
| | | parser = optparse.OptionParser( |
| | | usage, |
| | | description=textwrap.dedent(description) |
| | | ) |
| | | ) |
| | | parser.add_option('-g', '--glob', |
| | | action='store', type='string', dest='glob', |
| | | default='', help='Display routes matching glob pattern') |
| | | |
| | | parser.add_option('-f', '--format', |
| | | action='store', type='string', dest='format', |
| | | default='', help=('Choose which columns to display, this ' |
| | | 'will override the format key in the ' |
| | | '[proutes] ini section')) |
| | | |
| | | def __init__(self, argv, quiet=False): |
| | | self.options, self.args = self.parser.parse_args(argv[1:]) |
| | | self.quiet = quiet |
| | | self.available_formats = [ |
| | | 'name', 'pattern', 'view', 'method' |
| | | ] |
| | | self.column_format = self.available_formats |
| | | |
| | | def validate_formats(self, formats): |
| | | invalid_formats = [] |
| | | for fmt in formats: |
| | | if fmt not in self.available_formats: |
| | | invalid_formats.append(fmt) |
| | | |
| | | msg = ( |
| | | 'You provided invalid formats %s, ' |
| | | 'Available formats are %s' |
| | | ) |
| | | |
| | | if invalid_formats: |
| | | msg = msg % (invalid_formats, self.available_formats) |
| | | self.out(msg) |
| | | return False |
| | | |
| | | return True |
| | | |
| | | def proutes_file_config(self, filename): |
| | | config = self.ConfigParser() |
| | | config.read(filename) |
| | | try: |
| | | items = config.items('proutes') |
| | | for k, v in items: |
| | | if 'format' == k: |
| | | cols = re.split(r'[,|\s|\n]*', v) |
| | | self.column_format = [x.strip() for x in cols] |
| | | |
| | | except configparser.NoSectionError: |
| | | return |
| | | |
| | | def out(self, msg): # pragma: no cover |
| | | if not self.quiet: |
| | | print(msg) |
| | | |
| | | def _get_mapper(self, registry): |
| | | from pyramid.config import Configurator |
| | | config = Configurator(registry = registry) |
| | | config = Configurator(registry=registry) |
| | | return config.get_routes_mapper() |
| | | |
| | | def out(self, msg): # pragma: no cover |
| | | if not self.quiet: |
| | | print(msg) |
| | | |
| | | def run(self, quiet=False): |
| | | if not self.args: |
| | | self.out('requires a config file argument') |
| | | return 2 |
| | | |
| | | from pyramid.interfaces import IRouteRequest |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IView |
| | | from zope.interface import Interface |
| | | config_uri = self.args[0] |
| | | |
| | | env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) |
| | | registry = env['registry'] |
| | | mapper = self._get_mapper(registry) |
| | | if mapper is not None: |
| | | routes = mapper.get_routes() |
| | | fmt = '%-15s %-30s %-25s' |
| | | if not routes: |
| | | return 0 |
| | | self.out(fmt % ('Name', 'Pattern', 'View')) |
| | | self.out( |
| | | fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) |
| | | for route in routes: |
| | | pattern = route.pattern |
| | | if not pattern.startswith('/'): |
| | | pattern = '/' + pattern |
| | | request_iface = registry.queryUtility(IRouteRequest, |
| | | name=route.name) |
| | | view_callable = None |
| | | if (request_iface is None) or (route.factory is not None): |
| | | self.out(fmt % (route.name, pattern, '<unknown>')) |
| | | else: |
| | | view_callable = registry.adapters.lookup( |
| | | (IViewClassifier, request_iface, Interface), |
| | | IView, name='', default=None) |
| | | self.out(fmt % (route.name, pattern, view_callable)) |
| | | |
| | | self.proutes_file_config(config_uri) |
| | | |
| | | if self.options.format: |
| | | columns = self.options.format.split(',') |
| | | self.column_format = [x.strip() for x in columns] |
| | | |
| | | is_valid = self.validate_formats(self.column_format) |
| | | |
| | | if is_valid is False: |
| | | return 2 |
| | | |
| | | if mapper is None: |
| | | return 0 |
| | | |
| | | max_name = len('Name') |
| | | max_pattern = len('Pattern') |
| | | max_view = len('View') |
| | | max_method = len('Method') |
| | | |
| | | routes = mapper.get_routes(include_static=True) |
| | | |
| | | if len(routes) == 0: |
| | | return 0 |
| | | |
| | | mapped_routes = [{ |
| | | 'name': 'Name', |
| | | 'pattern': 'Pattern', |
| | | 'view': 'View', |
| | | 'method': 'Method' |
| | | },{ |
| | | 'name': '----', |
| | | 'pattern': '-------', |
| | | 'view': '----', |
| | | 'method': '------' |
| | | }] |
| | | |
| | | for route in routes: |
| | | route_data = get_route_data(route, registry) |
| | | |
| | | for name, pattern, view, method in route_data: |
| | | if self.options.glob: |
| | | match = (fnmatch.fnmatch(name, self.options.glob) or |
| | | fnmatch.fnmatch(pattern, self.options.glob)) |
| | | if not match: |
| | | continue |
| | | |
| | | if len(name) > max_name: |
| | | max_name = len(name) |
| | | |
| | | if len(pattern) > max_pattern: |
| | | max_pattern = len(pattern) |
| | | |
| | | if len(view) > max_view: |
| | | max_view = len(view) |
| | | |
| | | if len(method) > max_method: |
| | | max_method = len(method) |
| | | |
| | | mapped_routes.append({ |
| | | 'name': name, |
| | | 'pattern': pattern, |
| | | 'view': view, |
| | | 'method': method |
| | | }) |
| | | |
| | | fmt = _get_print_format( |
| | | self.column_format, max_name, max_pattern, max_view, max_method |
| | | ) |
| | | |
| | | for route in mapped_routes: |
| | | self.out(fmt.format(**route)) |
| | | |
| | | return 0 |
| | | |
| | | if __name__ == '__main__': # pragma: no cover |
| | | |
| | | if __name__ == '__main__': # pragma: no cover |
| | | sys.exit(main() or 0) |
| | |
| | | import threading |
| | | import time |
| | | import traceback |
| | | import webbrowser |
| | | |
| | | from paste.deploy import loadserver |
| | | from paste.deploy import loadapp |
| | | from paste.deploy.loadwsgi import loadcontext, SERVER |
| | | |
| | | from pyramid.compat import PY3 |
| | | from pyramid.compat import WIN |
| | |
| | | from pyramid.scripts.common import parse_vars |
| | | |
| | | 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 |
| | |
| | | dest='monitor_restart', |
| | | action='store_true', |
| | | help="Auto-restart server if it dies") |
| | | parser.add_option( |
| | | '-b', '--browser', |
| | | dest='browser', |
| | | action='store_true', |
| | | help="Open a web browser to server url") |
| | | parser.add_option( |
| | | '--status', |
| | | action='store_true', |
| | |
| | | else: |
| | | msg = '' |
| | | self.out('Exiting%s (-v to see traceback)' % msg) |
| | | |
| | | if self.options.browser: |
| | | def open_browser(): |
| | | context = loadcontext(SERVER, app_spec, name=app_name, relative_to=base, |
| | | global_conf=vars) |
| | | url = 'http://127.0.0.1:{port}/'.format(**context.config()) |
| | | time.sleep(1) |
| | | webbrowser.open(url) |
| | | t = threading.Thread(target=open_browser) |
| | | t.setDaemon(True) |
| | | t.start() |
| | | |
| | | serve() |
| | | |
| | |
| | | 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 = [] |
| | |
| | | from code import interact |
| | | import optparse |
| | | import os |
| | | import sys |
| | | import textwrap |
| | | |
| | | from pyramid.compat import configparser |
| | | from pyramid.compat import exec_ |
| | | from pyramid.util import DottedNameResolver |
| | | from pyramid.paster import bootstrap |
| | | |
| | |
| | | loaded_objects = {} |
| | | object_help = {} |
| | | setup = None |
| | | pystartup = os.environ.get('PYTHONSTARTUP') |
| | | |
| | | def __init__(self, argv, quiet=False): |
| | | self.quiet = quiet |
| | |
| | | if shell is None: |
| | | shell = self.make_shell() |
| | | |
| | | if self.pystartup and os.path.isfile(self.pystartup): |
| | | with open(self.pystartup, 'rb') as fp: |
| | | exec_(fp.read().decode('utf-8'), env) |
| | | if '__builtins__' in env: |
| | | del env['__builtins__'] |
| | | |
| | | try: |
| | | shell(env, help) |
| | | finally: |
| | |
| | | Allow = 'Allow' |
| | | Deny = 'Deny' |
| | | |
| | | _marker = object() |
| | | |
| | | class AllPermissionsList(object): |
| | | """ Stand in 'permission list' to represent all permissions """ |
| | | def __iter__(self): |
| | |
| | | '"effective_principals" attribute of the Pyramid request instead.' |
| | | ) |
| | | |
| | | def remember(request, principal, **kw): |
| | | def remember(request, userid=_marker, **kw): |
| | | """ |
| | | Returns a sequence of header tuples (e.g. ``[('Set-Cookie', 'foo=abc')]``) |
| | | on this request's response. |
| | | These headers are suitable for 'remembering' a set of credentials |
| | | implied by the data passed as ``principal`` and ``*kw`` using the |
| | | implied by the data passed as ``userid`` and ``*kw`` using the |
| | | current :term:`authentication policy`. Common usage might look |
| | | like so within the body of a view function (``response`` is |
| | | assumed to be a :term:`WebOb` -style :term:`response` object |
| | | computed previously by the view code):: |
| | | computed previously by the view code): |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | always return an empty sequence. If used, the composition and |
| | | meaning of ``**kw`` must be agreed upon by the calling code and |
| | | the effective authentication policy. |
| | | |
| | | .. deprecated:: 1.6 |
| | | Renamed the ``principal`` argument to ``userid`` to clarify its |
| | | purpose. |
| | | """ |
| | | if userid is _marker: |
| | | principal = kw.pop('principal', _marker) |
| | | if principal is _marker: |
| | | raise TypeError( |
| | | 'remember() missing 1 required positional argument: ' |
| | | '\'userid\'') |
| | | else: |
| | | deprecated( |
| | | 'principal', |
| | | 'The "principal" argument was deprecated in Pyramid 1.6. ' |
| | | 'It will be removed in Pyramid 1.9. Use the "userid" ' |
| | | 'argument instead.') |
| | | userid = principal |
| | | policy = _get_authentication_policy(request) |
| | | if policy is None: |
| | | return [] |
| | | return policy.remember(request, principal, **kw) |
| | | return policy.remember(request, userid, **kw) |
| | | |
| | | def forget(request): |
| | | """ |
| | |
| | | possessed by the currently authenticated user. A common usage |
| | | might look like so within the body of a view function |
| | | (``response`` is assumed to be an :term:`WebOb` -style |
| | | :term:`response` object computed previously by the view code):: |
| | | :term:`response` object computed previously by the view code): |
| | | |
| | | from pyramid.security import forget |
| | | headers = forget(request) |
| | | response.headerlist.extend(headers) |
| | | return response |
| | | .. code-block:: python |
| | | |
| | | from pyramid.security import forget |
| | | headers = forget(request) |
| | | response.headerlist.extend(headers) |
| | | return response |
| | | |
| | | If no :term:`authentication policy` is in use, this function will |
| | | always return an empty sequence. |
| | |
| | | |
| | | .. 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 |
| | |
| | | # -*- coding: utf-8 -*- |
| | | import hashlib |
| | | import os |
| | | |
| | | from os.path import ( |
| | |
| | | HTTPMovedPermanently, |
| | | ) |
| | | |
| | | from pyramid.path import caller_package |
| | | from pyramid.path import AssetResolver, caller_package |
| | | from pyramid.response import FileResponse |
| | | from pyramid.traversal import traversal_path_info |
| | | |
| | |
| | | """ |
| | | |
| | | def __init__(self, root_dir, cache_max_age=3600, package_name=None, |
| | | use_subpath=False, index='index.html'): |
| | | use_subpath=False, index='index.html', cachebust_match=None): |
| | | # package_name is for bw compat; it is preferred to pass in a |
| | | # package-relative path as root_dir |
| | | # (e.g. ``anotherpackage:foo/static``). |
| | |
| | | self.docroot = docroot |
| | | self.norm_docroot = normcase(normpath(docroot)) |
| | | self.index = index |
| | | self.cachebust_match = cachebust_match |
| | | |
| | | def __call__(self, context, request): |
| | | if self.use_subpath: |
| | | path_tuple = request.subpath |
| | | else: |
| | | path_tuple = traversal_path_info(request.environ['PATH_INFO']) |
| | | |
| | | if self.cachebust_match: |
| | | path_tuple = self.cachebust_match(path_tuple) |
| | | path = _secure_path(path_tuple) |
| | | |
| | | if path is None: |
| | |
| | | encoded = slash.join(path_tuple) # will be unicode |
| | | return encoded |
| | | |
| | | def _generate_md5(spec): |
| | | asset = AssetResolver(None).resolve(spec) |
| | | md5 = hashlib.md5() |
| | | with asset.stream() as stream: |
| | | for block in iter(lambda: stream.read(4096), b''): |
| | | md5.update(block) |
| | | return md5.hexdigest() |
| | | |
| | | class Md5AssetTokenGenerator(object): |
| | | """ |
| | | A mixin class which provides an implementation of |
| | | :meth:`~pyramid.interfaces.ICacheBuster.target` which generates an md5 |
| | | checksum token for an asset, caching it for subsequent calls. |
| | | """ |
| | | def __init__(self): |
| | | self.token_cache = {} |
| | | |
| | | 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 |
| | | # to the dict shouldn't cause a segfault or other catastrophic failure. |
| | | # (See: http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm) |
| | | # |
| | | # We do have a race condition that could result in the same md5 |
| | | # checksum getting computed twice or more times in parallel. Since |
| | | # the program would still function just fine if this were to occur, |
| | | # the extra overhead of using locks to serialize access to the dict |
| | | # seems an unnecessary burden. |
| | | # |
| | | token = self.token_cache.get(pathspec) |
| | | if not token: |
| | | self.token_cache[pathspec] = token = _generate_md5(pathspec) |
| | | return token |
| | | |
| | | 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 |
| | | asset URL. Generated md5 checksums are cached in order to speed up |
| | | subsequent calls. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self): |
| | | super(PathSegmentMd5CacheBuster, self).__init__() |
| | | |
| | | 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. |
| | | |
| | | 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 |
| | | URL. Generated md5 checksums are cached in order to speed up subsequent |
| | | calls. |
| | | |
| | | The optional ``param`` argument determines the name of the parameter added |
| | | to the query string and defaults to ``'x'``. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, param='x'): |
| | | super(QueryStringMd5CacheBuster, self).__init__(param=param) |
| | | |
| | | 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. |
| | | |
| | | The ``token`` parameter is the token string to use for cache busting and |
| | | will be the same for every request. |
| | | |
| | | The optional ``param`` argument determines the name of the parameter added |
| | | to the query string and defaults to ``'x'``. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, token, param='x'): |
| | | super(QueryStringConstantCacheBuster, self).__init__(param=param) |
| | | self._token = token |
| | | |
| | | def tokenize(self, pathspec): |
| | | return self._token |
| | |
| | | |
| | | from pyramid.interfaces import ( |
| | | IRequest, |
| | | IResponseFactory, |
| | | ISession, |
| | | ) |
| | | |
| | |
| | | from pyramid.config import Configurator |
| | | from pyramid.decorator import reify |
| | | from pyramid.path import caller_package |
| | | from pyramid.response import Response |
| | | from pyramid.response import Response, _get_response_factory |
| | | from pyramid.registry import Registry |
| | | |
| | | from pyramid.security import ( |
| | |
| | | from pyramid.request import CallbackMethodsMixin |
| | | from pyramid.url import URLMethodsMixin |
| | | from pyramid.util import InstancePropertyMixin |
| | | |
| | | |
| | | _marker = object() |
| | | |
| | |
| | | effective_principals.extend(self.groupids) |
| | | return effective_principals |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | self.remembered = principal |
| | | def remember(self, request, userid, **kw): |
| | | self.remembered = userid |
| | | return self.remember_result |
| | | |
| | | def forget(self, request): |
| | |
| | | |
| | | @reify |
| | | def response(self): |
| | | f = self.registry.queryUtility(IResponseFactory, default=Response) |
| | | return f() |
| | | f = _get_response_factory(self.registry) |
| | | return f(self) |
| | | |
| | | have_zca = True |
| | | |
| | |
| | | self._assertRaisesBadTicket('secret', ticket, '0.0.0.0') |
| | | |
| | | def test_correct_with_user_data(self): |
| | | ticket = '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!' |
| | | ticket = text_('66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!') |
| | | result = self._callFUT('secret', ticket, '0.0.0.0') |
| | | self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) |
| | | |
| | | def test_correct_with_user_data_sha512(self): |
| | | ticket = '7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1160cab'\ |
| | | '0ec0e6888faa41eba641a18522b26f19109f3ffafb769767ba8a26d02aae'\ |
| | | 'ae56599a0000000auserid!a,b!' |
| | | ticket = text_('7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1' |
| | | '160cab0ec0e6888faa41eba641a18522b26f19109f3ffafb769767' |
| | | 'ba8a26d02aaeae56599a0000000auserid!a,b!') |
| | | result = self._callFUT('secret', ticket, '0.0.0.0', 'sha512') |
| | | self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) |
| | | |
| | | def test_ipv4(self): |
| | | ticket = 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecdade8d7'\ |
| | | '6c530000000auserid!' |
| | | ticket = text_('b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecd' |
| | | 'ade8d76c530000000auserid!') |
| | | result = self._callFUT('secret', ticket, '198.51.100.1', 'sha256') |
| | | self.assertEqual(result, (10, 'userid', [''], '')) |
| | | |
| | | def test_ipv6(self): |
| | | ticket = 'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760cd7a2f'\ |
| | | 'a4910000000auserid!' |
| | | ticket = text_('d025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760' |
| | | 'cd7a2fa4910000000auserid!') |
| | | result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256') |
| | | self.assertEqual(result, (10, 'userid', [''], '')) |
| | | pass |
New file |
| | |
| | | import unittest |
| | | from pyramid.compat import is_unbound_method |
| | | |
| | | class TestUnboundMethods(unittest.TestCase): |
| | | def test_old_style_bound(self): |
| | | self.assertFalse(is_unbound_method(OldStyle().run)) |
| | | |
| | | def test_new_style_bound(self): |
| | | self.assertFalse(is_unbound_method(NewStyle().run)) |
| | | |
| | | def test_old_style_unbound(self): |
| | | self.assertTrue(is_unbound_method(OldStyle.run)) |
| | | |
| | | def test_new_style_unbound(self): |
| | | self.assertTrue(is_unbound_method(NewStyle.run)) |
| | | |
| | | def test_normal_func_unbound(self): |
| | | def func(): return 'OK' |
| | | |
| | | self.assertFalse(is_unbound_method(func)) |
| | | |
| | | class OldStyle: |
| | | def run(self): return 'OK' |
| | | |
| | | class NewStyle(object): |
| | | def run(self): return 'OK' |
| | |
| | | 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' |
| | |
| | | import os.path |
| | | import unittest |
| | | from pyramid.testing import cleanUp |
| | | |
| | | # we use this folder |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | |
| | | class TestAssetsConfiguratorMixin(unittest.TestCase): |
| | | def _makeOne(self, *arg, **kw): |
| | |
| | | def test_override_asset_samename(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset,'a', 'a') |
| | | self.assertRaises(ConfigurationError, config.override_asset, 'a', 'a') |
| | | |
| | | def test_override_asset_directory_with_file(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo/', 'a:foo.pt') |
| | | 'a:foo/', |
| | | 'pyramid.tests.test_config.pkgs.asset:foo.pt') |
| | | |
| | | def test_override_asset_file_with_directory(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo.pt', 'a:foo/') |
| | | 'a:foo.pt', |
| | | 'pyramid.tests.test_config.pkgs.asset:templates/') |
| | | |
| | | def test_override_asset_file_with_package(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo.pt', 'a') |
| | | 'a:foo.pt', |
| | | 'pyramid.tests.test_config.pkgs.asset') |
| | | |
| | | def test_override_asset_file_with_file(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | config.override_asset( |
| | |
| | | from pyramid.tests.test_config.pkgs.asset import subpackage |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, 'templates/foo.pt') |
| | | self.assertEqual(override.override_package, subpackage) |
| | | self.assertEqual(override.override_prefix, 'templates/bar.pt') |
| | | source = override.source |
| | | self.assertTrue(isinstance(source, PackageAssetSource)) |
| | | 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) |
| | | override = DummyUnderOverride() |
| | | config.override_asset( |
| | |
| | | from pyramid.tests.test_config.pkgs.asset import subpackage |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, '') |
| | | self.assertEqual(override.override_package, subpackage) |
| | | self.assertEqual(override.override_prefix, '') |
| | | source = override.source |
| | | 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 |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | config.override_asset( |
| | |
| | | from pyramid.tests.test_config.pkgs.asset import subpackage |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, 'templates/') |
| | | self.assertEqual(override.override_package, subpackage) |
| | | self.assertEqual(override.override_prefix, 'templates/') |
| | | source = override.source |
| | | 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_package(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | config.override_asset( |
| | |
| | | from pyramid.tests.test_config.pkgs.asset import subpackage |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, 'templates/') |
| | | self.assertEqual(override.override_package, subpackage) |
| | | self.assertEqual(override.override_prefix, '') |
| | | source = override.source |
| | | 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_package_with_directory(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | config.override_asset( |
| | |
| | | from pyramid.tests.test_config.pkgs.asset import subpackage |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, '') |
| | | self.assertEqual(override.override_package, subpackage) |
| | | self.assertEqual(override.override_prefix, 'templates/') |
| | | source = override.source |
| | | 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 |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo/', |
| | | os.path.join(here, 'pkgs', 'asset', 'foo.pt')) |
| | | |
| | | def test_override_asset_file_with_absdirectory(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo.pt', |
| | | abspath) |
| | | |
| | | def test_override_asset_file_with_missing_abspath(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne() |
| | | self.assertRaises(ConfigurationError, config.override_asset, |
| | | 'a:foo.pt', |
| | | os.path.join(here, 'wont_exist')) |
| | | |
| | | def test_override_asset_file_with_absfile(self): |
| | | from pyramid.config.assets import FSAssetSource |
| | | config = self._makeOne(autocommit=True) |
| | | override = DummyUnderOverride() |
| | | abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', |
| | | 'templates', 'bar.pt') |
| | | config.override_asset( |
| | | 'pyramid.tests.test_config.pkgs.asset:templates/foo.pt', |
| | | abspath, |
| | | _override=override) |
| | | from pyramid.tests.test_config.pkgs import asset |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, 'templates/foo.pt') |
| | | source = override.source |
| | | 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) |
| | | override = DummyUnderOverride() |
| | | abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') |
| | | config.override_asset( |
| | | 'pyramid.tests.test_config.pkgs.asset:templates/', |
| | | abspath, |
| | | _override=override) |
| | | from pyramid.tests.test_config.pkgs import asset |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, 'templates/') |
| | | source = override.source |
| | | 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) |
| | | override = DummyUnderOverride() |
| | | abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') |
| | | config.override_asset( |
| | | 'pyramid.tests.test_config.pkgs.asset', |
| | | abspath, |
| | | _override=override) |
| | | from pyramid.tests.test_config.pkgs import asset |
| | | self.assertEqual(override.package, asset) |
| | | self.assertEqual(override.path, '') |
| | | source = override.source |
| | | 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') |
| | | opackage = DummyPackage('opackage') |
| | | source = DummyAssetSource() |
| | | config = self._makeOne() |
| | | config._override(package, 'path', opackage, 'oprefix', |
| | | config._override(package, 'path', source, |
| | | PackageOverrides=DummyPackageOverrides) |
| | | overrides = config.registry.queryUtility(IPackageOverrides, |
| | | name='package') |
| | | self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) |
| | | self.assertEqual(overrides.inserted, [('path', source)]) |
| | | self.assertEqual(overrides.package, package) |
| | | |
| | | def test__override_already_registered(self): |
| | | from pyramid.interfaces import IPackageOverrides |
| | | package = DummyPackage('package') |
| | | opackage = DummyPackage('opackage') |
| | | source = DummyAssetSource() |
| | | overrides = DummyPackageOverrides(package) |
| | | config = self._makeOne() |
| | | config.registry.registerUtility(overrides, IPackageOverrides, |
| | | name='package') |
| | | config._override(package, 'path', opackage, 'oprefix', |
| | | config._override(package, 'path', source, |
| | | PackageOverrides=DummyPackageOverrides) |
| | | self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) |
| | | self.assertEqual(overrides.inserted, [('path', source)]) |
| | | self.assertEqual(overrides.package, package) |
| | | |
| | | |
| | |
| | | reg.registerUtility(overrides, IPackageOverrides, name=name) |
| | | |
| | | def test_get_resource_filename_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = os.path.join(here, resource_name) |
| | | result = provider.get_resource_filename(None, resource_name) |
| | | self.assertEqual(result, expected) |
| | | |
| | | def test_get_resource_stream_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | with provider.get_resource_stream(None, resource_name) as result: |
| | | _assertBody(result.read(), os.path.join(here, resource_name)) |
| | | |
| | | def test_get_resource_string_no_overrides(self): |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | result = provider.get_resource_string(None, resource_name) |
| | | _assertBody(result, os.path.join(here, resource_name)) |
| | | |
| | |
| | | def test_get_resource_filename_override_returns_None(self): |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = os.path.join(here, resource_name) |
| | | result = provider.get_resource_filename(None, resource_name) |
| | | self.assertEqual(result, expected) |
| | |
| | | def test_get_resource_stream_override_returns_None(self): |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | with provider.get_resource_stream(None, resource_name) as result: |
| | | _assertBody(result.read(), os.path.join(here, resource_name)) |
| | | |
| | | def test_get_resource_string_override_returns_None(self): |
| | | overrides = DummyOverrides(None) |
| | | self._registerOverrides(overrides) |
| | | import os |
| | | resource_name = 'test_assets.py' |
| | | import pyramid.tests.test_config |
| | | provider = self._makeOne(pyramid.tests.test_config) |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | result = provider.get_resource_string(None, resource_name) |
| | | _assertBody(result, os.path.join(here, resource_name)) |
| | | |
| | |
| | | from pyramid.config.assets import DirectoryOverride |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= [None] |
| | | po.insert('foo/', 'package', 'bar/') |
| | | po.overrides = [None] |
| | | po.insert('foo/', DummyAssetSource()) |
| | | self.assertEqual(len(po.overrides), 2) |
| | | override = po.overrides[0] |
| | | self.assertEqual(override.__class__, DirectoryOverride) |
| | |
| | | from pyramid.config.assets import FileOverride |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= [None] |
| | | po.insert('foo.pt', 'package', 'bar.pt') |
| | | po.overrides = [None] |
| | | po.insert('foo.pt', DummyAssetSource()) |
| | | self.assertEqual(len(po.overrides), 2) |
| | | override = po.overrides[0] |
| | | self.assertEqual(override.__class__, FileOverride) |
| | |
| | | from pyramid.config.assets import DirectoryOverride |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= [None] |
| | | po.insert('', 'package', 'bar/') |
| | | po.overrides = [None] |
| | | source = DummyAssetSource() |
| | | po.insert('', source) |
| | | self.assertEqual(len(po.overrides), 2) |
| | | override = po.overrides[0] |
| | | self.assertEqual(override.__class__, DirectoryOverride) |
| | | |
| | | def test_search_path(self): |
| | | overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))] |
| | | def test_filtered_sources(self): |
| | | overrides = [ DummyOverride(None), DummyOverride('foo')] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | self.assertEqual(list(po.search_path('whatever')), |
| | | [('package', 'name')]) |
| | | po.overrides = overrides |
| | | self.assertEqual(list(po.filtered_sources('whatever')), ['foo']) |
| | | |
| | | def test_get_filename(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'test_assets.py'))] |
| | | source = DummyAssetSource(filename='foo.pt') |
| | | overrides = [ DummyOverride(None), DummyOverride((source, ''))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | expected = os.path.join(here, 'test_assets.py') |
| | | self.assertEqual(po.get_filename('whatever'), expected) |
| | | po.overrides = overrides |
| | | result = po.get_filename('whatever') |
| | | self.assertEqual(result, 'foo.pt') |
| | | self.assertEqual(source.resource_name, '') |
| | | |
| | | def test_get_filename_file_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(filename=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.get_filename('whatever'), None) |
| | | |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | def test_get_stream(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'test_assets.py'))] |
| | | source = DummyAssetSource(stream='a stream?') |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | with po.get_stream('whatever') as stream: |
| | | _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) |
| | | po.overrides = overrides |
| | | self.assertEqual(po.get_stream('whatever'), 'a stream?') |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_get_stream_file_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(stream=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.get_stream('whatever'), None) |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | def test_get_string(self): |
| | | import os |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'test_assets.py'))] |
| | | source = DummyAssetSource(string='a string') |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | _assertBody(po.get_string('whatever'), |
| | | os.path.join(here, 'test_assets.py')) |
| | | po.overrides = overrides |
| | | self.assertEqual(po.get_string('whatever'), 'a string') |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_get_string_file_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(string=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.get_string('whatever'), None) |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | def test_has_resource(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'test_assets.py'))] |
| | | source = DummyAssetSource(exists=True) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.has_resource('whatever'), True) |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_has_resource_file_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(exists=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.has_resource('whatever'), None) |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | def test_isdir_false(self): |
| | | overrides = [ DummyOverride( |
| | | ('pyramid.tests.test_config', 'test_assets.py'))] |
| | | source = DummyAssetSource(isdir=False) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.isdir('whatever'), False) |
| | | |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_isdir_true(self): |
| | | overrides = [ DummyOverride( |
| | | ('pyramid.tests.test_config', 'files'))] |
| | | source = DummyAssetSource(isdir=True) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.isdir('whatever'), True) |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_isdir_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(isdir=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.isdir('whatever'), None) |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | def test_listdir(self): |
| | | overrides = [ DummyOverride( |
| | | ('pyramid.tests.test_config', 'files'))] |
| | | source = DummyAssetSource(listdir=True) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | self.assertTrue(po.listdir('whatever')) |
| | | po.overrides = overrides |
| | | self.assertEqual(po.listdir('whatever'), True) |
| | | self.assertEqual(source.resource_name, 'foo.pt') |
| | | |
| | | def test_listdir_doesnt_exist(self): |
| | | overrides = [ DummyOverride(None), DummyOverride( |
| | | ('pyramid.tests.test_config', 'wont_exist'))] |
| | | source = DummyAssetSource(listdir=None) |
| | | overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] |
| | | package = DummyPackage('package') |
| | | po = self._makeOne(package) |
| | | po.overrides= overrides |
| | | po.overrides = overrides |
| | | self.assertEqual(po.listdir('whatever'), None) |
| | | self.assertEqual(source.resource_name, 'wont_exist') |
| | | |
| | | # PEP 302 __loader__ extensions: use the "real" __loader__, if present. |
| | | def test_get_data_pkg_has_no___loader__(self): |
| | |
| | | |
| | | def test_get_source_pkg_has___loader__(self): |
| | | package = DummyPackage('package') |
| | | loader = package.__loader__ = DummyLoader() |
| | | loader = package.__loader__ = DummyLoader() |
| | | po = self._makeOne(package) |
| | | self.assertEqual(po.get_source('whatever'), 'def foo():\n pass') |
| | | self.assertEqual(loader._got_source, 'whatever') |
| | | |
| | | class AssetSourceIntegrationTests(object): |
| | | |
| | | def test_get_filename(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.get_filename('test_assets.py'), |
| | | os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_filename_with_prefix(self): |
| | | source = self._makeOne('test_assets.py') |
| | | self.assertEqual(source.get_filename(''), |
| | | os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_filename_file_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.get_filename('wont_exist'), None) |
| | | |
| | | def test_get_stream(self): |
| | | source = self._makeOne('') |
| | | with source.get_stream('test_assets.py') as stream: |
| | | _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_stream_with_prefix(self): |
| | | source = self._makeOne('test_assets.py') |
| | | with source.get_stream('') as stream: |
| | | _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_stream_file_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.get_stream('wont_exist'), None) |
| | | |
| | | def test_get_string(self): |
| | | source = self._makeOne('') |
| | | _assertBody(source.get_string('test_assets.py'), |
| | | os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_string_with_prefix(self): |
| | | source = self._makeOne('test_assets.py') |
| | | _assertBody(source.get_string(''), |
| | | os.path.join(here, 'test_assets.py')) |
| | | |
| | | def test_get_string_file_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.get_string('wont_exist'), None) |
| | | |
| | | def test_exists(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.exists('test_assets.py'), True) |
| | | |
| | | def test_exists_with_prefix(self): |
| | | source = self._makeOne('test_assets.py') |
| | | self.assertEqual(source.exists(''), True) |
| | | |
| | | def test_exists_file_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.exists('wont_exist'), None) |
| | | |
| | | def test_isdir_false(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.isdir('test_assets.py'), False) |
| | | |
| | | def test_isdir_true(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.isdir('files'), True) |
| | | |
| | | def test_isdir_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.isdir('wont_exist'), None) |
| | | |
| | | def test_listdir(self): |
| | | source = self._makeOne('') |
| | | self.assertTrue(source.listdir('files')) |
| | | |
| | | def test_listdir_doesnt_exist(self): |
| | | source = self._makeOne('') |
| | | self.assertEqual(source.listdir('wont_exist'), None) |
| | | |
| | | class TestPackageAssetSource(AssetSourceIntegrationTests, unittest.TestCase): |
| | | |
| | | def _getTargetClass(self): |
| | | from pyramid.config.assets import PackageAssetSource |
| | | return PackageAssetSource |
| | | |
| | | def _makeOne(self, prefix, package='pyramid.tests.test_config'): |
| | | klass = self._getTargetClass() |
| | | return klass(package, prefix) |
| | | |
| | | class TestFSAssetSource(AssetSourceIntegrationTests, unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.config.assets import FSAssetSource |
| | | return FSAssetSource |
| | | |
| | | def _makeOne(self, prefix, base_prefix=here): |
| | | klass = self._getTargetClass() |
| | | return klass(os.path.join(base_prefix, prefix)) |
| | | |
| | | class TestDirectoryOverride(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.config.assets import DirectoryOverride |
| | | return DirectoryOverride |
| | | |
| | | def _makeOne(self, path, package, prefix): |
| | | def _makeOne(self, path, source): |
| | | klass = self._getTargetClass() |
| | | return klass(path, package, prefix) |
| | | return klass(path, source) |
| | | |
| | | def test_it_match(self): |
| | | o = self._makeOne('foo/', 'package', 'bar/') |
| | | source = DummyAssetSource() |
| | | o = self._makeOne('foo/', source) |
| | | result = o('foo/something.pt') |
| | | self.assertEqual(result, ('package', 'bar/something.pt')) |
| | | self.assertEqual(result, (source, 'something.pt')) |
| | | |
| | | def test_it_no_match(self): |
| | | o = self._makeOne('foo/', 'package', 'bar/') |
| | | source = DummyAssetSource() |
| | | o = self._makeOne('foo/', source) |
| | | result = o('baz/notfound.pt') |
| | | self.assertEqual(result, None) |
| | | |
| | |
| | | from pyramid.config.assets import FileOverride |
| | | return FileOverride |
| | | |
| | | def _makeOne(self, path, package, prefix): |
| | | def _makeOne(self, path, source): |
| | | klass = self._getTargetClass() |
| | | return klass(path, package, prefix) |
| | | return klass(path, source) |
| | | |
| | | def test_it_match(self): |
| | | o = self._makeOne('foo.pt', 'package', 'bar.pt') |
| | | source = DummyAssetSource() |
| | | o = self._makeOne('foo.pt', source) |
| | | result = o('foo.pt') |
| | | self.assertEqual(result, ('package', 'bar.pt')) |
| | | self.assertEqual(result, (source, '')) |
| | | |
| | | def test_it_no_match(self): |
| | | o = self._makeOne('foo.pt', 'package', 'bar.pt') |
| | | source = DummyAssetSource() |
| | | o = self._makeOne('foo.pt', source) |
| | | result = o('notfound.pt') |
| | | self.assertEqual(result, None) |
| | | |
| | |
| | | self.package = package |
| | | self.inserted = [] |
| | | |
| | | def insert(self, path, package, prefix): |
| | | self.inserted.append((path, package, prefix)) |
| | | def insert(self, path, source): |
| | | self.inserted.append((path, source)) |
| | | |
| | | class DummyPkgResources: |
| | | def __init__(self): |
| | |
| | | class DummyPackage: |
| | | def __init__(self, name): |
| | | self.__name__ = name |
| | | |
| | | class DummyAssetSource: |
| | | def __init__(self, **kw): |
| | | self.kw = kw |
| | | |
| | | def get_filename(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['filename'] |
| | | |
| | | def get_stream(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['stream'] |
| | | |
| | | def get_string(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['string'] |
| | | |
| | | def exists(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['exists'] |
| | | |
| | | def isdir(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['isdir'] |
| | | |
| | | def listdir(self, resource_name): |
| | | self.resource_name = resource_name |
| | | return self.kw['listdir'] |
| | | |
| | | class DummyLoader: |
| | | _got_data = _is_package = None |
| | |
| | | return 'def foo():\n pass' |
| | | |
| | | class DummyUnderOverride: |
| | | def __call__(self, package, path, override_package, override_prefix, |
| | | _info=''): |
| | | def __call__(self, package, path, source, _info=''): |
| | | self.package = package |
| | | self.path = path |
| | | self.override_package = override_package |
| | | self.override_prefix = override_prefix |
| | | self.source = source |
| | | |
| | | def read_(src): |
| | | with open(src, 'rb') as f: |
| | |
| | | self.assertEqual(config.registry.getUtility(IRequestFactory), |
| | | dummyfactory) |
| | | |
| | | def test_set_response_factory(self): |
| | | from pyramid.interfaces import IResponseFactory |
| | | config = self._makeOne(autocommit=True) |
| | | factory = lambda r: object() |
| | | config.set_response_factory(factory) |
| | | self.assertEqual(config.registry.getUtility(IResponseFactory), factory) |
| | | |
| | | def test_set_response_factory_dottedname(self): |
| | | from pyramid.interfaces import IResponseFactory |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_response_factory( |
| | | 'pyramid.tests.test_config.dummyfactory') |
| | | self.assertEqual(config.registry.getUtility(IResponseFactory), |
| | | dummyfactory) |
| | | |
| | | def test_set_root_factory(self): |
| | | from pyramid.interfaces import IRootFactory |
| | | config = self._makeOne() |
| | |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises(AttributeError, config.add_request_method) |
| | | |
| | | def test_add_request_method_with_text_type_name(self): |
| | | from pyramid.interfaces import IRequestExtensions |
| | | from pyramid.compat import text_, PY3 |
| | | from pyramid.exceptions import ConfigurationError |
| | | |
| | | config = self._makeOne(autocommit=True) |
| | | def boomshaka(r): pass |
| | | |
| | | def get_bad_name(): |
| | | if PY3: # pragma: nocover |
| | | name = b'La Pe\xc3\xb1a' |
| | | else: # pragma: nocover |
| | | name = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | |
| | | config.add_request_method(boomshaka, name=name) |
| | | |
| | | self.assertRaises(ConfigurationError, get_bad_name) |
| | | |
| | | class TestDeprecatedFactoriesMixinMethods(unittest.TestCase): |
| | | def setUp(self): |
| | |
| | | def tearDown(self): |
| | | from zope.deprecation import __show__ |
| | | __show__.on() |
| | | |
| | | |
| | | def _makeOne(self, *arg, **kw): |
| | | from pyramid.config import Configurator |
| | | config = Configurator(*arg, **kw) |
| | |
| | | utility = reg.getUtility(IRequestFactory) |
| | | self.assertEqual(utility, factory) |
| | | |
| | | def test_setup_registry_response_factory(self): |
| | | from pyramid.registry import Registry |
| | | from pyramid.interfaces import IResponseFactory |
| | | reg = Registry() |
| | | config = self._makeOne(reg) |
| | | factory = lambda r: object() |
| | | config.setup_registry(response_factory=factory) |
| | | self.assertEqual(reg.queryUtility(IResponseFactory), None) |
| | | config.commit() |
| | | utility = reg.getUtility(IResponseFactory) |
| | | self.assertEqual(utility, factory) |
| | | |
| | | def test_setup_registry_request_factory_dottedname(self): |
| | | from pyramid.registry import Registry |
| | | from pyramid.interfaces import IRequestFactory |
| | |
| | | "file must exist, refusing to use orphan .pyc or .pyo file).") |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_include_constant_root_package(self): |
| | | from pyramid import tests |
| | | from pyramid.tests import test_config |
| | | config = self._makeOne(root_package=tests) |
| | | results = {} |
| | | def include(config): |
| | | results['package'] = config.package |
| | | results['root_package'] = config.root_package |
| | | config.include(include) |
| | | self.assertEqual(results['root_package'], tests) |
| | | self.assertEqual(results['package'], test_config) |
| | | |
| | | def test_action_branching_kw_is_None(self): |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | 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(settings['a'], 1) |
| | | |
| | | class TestSettings(unittest.TestCase): |
| | | |
| | | |
| | | def _getTargetClass(self): |
| | | from pyramid.config.settings import Settings |
| | | return Settings |
| | |
| | | self.assertEqual(result['prevent_http_cache'], True) |
| | | self.assertEqual(result['pyramid.prevent_http_cache'], True) |
| | | |
| | | def test_prevent_cachebust(self): |
| | | settings = self._makeOne({}) |
| | | self.assertEqual(settings['prevent_cachebust'], False) |
| | | self.assertEqual(settings['pyramid.prevent_cachebust'], False) |
| | | result = self._makeOne({'prevent_cachebust':'false'}) |
| | | self.assertEqual(result['prevent_cachebust'], False) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], False) |
| | | result = self._makeOne({'prevent_cachebust':'t'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | result = self._makeOne({'prevent_cachebust':'1'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | result = self._makeOne({'pyramid.prevent_cachebust':'t'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | result = self._makeOne({}, {'PYRAMID_PREVENT_CACHEBUST':'1'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | result = self._makeOne({'prevent_cachebust':'false', |
| | | 'pyramid.prevent_cachebust':'1'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | result = self._makeOne({'prevent_cachebust':'false', |
| | | 'pyramid.prevent_cachebust':'f'}, |
| | | {'PYRAMID_PREVENT_CACHEBUST':'1'}) |
| | | self.assertEqual(result['prevent_cachebust'], True) |
| | | self.assertEqual(result['pyramid.prevent_cachebust'], True) |
| | | |
| | | def test_reload_templates(self): |
| | | settings = self._makeOne({}) |
| | | self.assertEqual(settings['reload_templates'], False) |
| | |
| | | foo = Foo() |
| | | self.assertTrue(self._callFUT(foo.method)) |
| | | |
| | | def test_function_annotations(self): |
| | | def foo(bar): |
| | | """ """ |
| | | # avoid SyntaxErrors in python2, this if effectively nop |
| | | getattr(foo, '__annotations__', {}).update({'bar': 'baz'}) |
| | | self.assertTrue(self._callFUT(foo)) |
| | | |
| | | class TestNotted(unittest.TestCase): |
| | | def _makeOne(self, predicate): |
| | | from pyramid.config.util import Notted |
| | |
| | | config.add_view(renderer='dummy.pt') |
| | | view = self._getViewCallable(config) |
| | | self.assertRaises(ValueError, view, None, None) |
| | | |
| | | |
| | | def test_add_view_with_tmpl_renderer_factory_no_renderer_factory(self): |
| | | config = self._makeOne(autocommit=True) |
| | | introspector = DummyIntrospector() |
| | |
| | | ('renderer factories', '.pt') in introspector.related[-1]) |
| | | view = self._getViewCallable(config) |
| | | self.assertTrue(b'Hello!' in view(None, None).body) |
| | | |
| | | |
| | | def test_add_view_wrapped_view_is_decorated(self): |
| | | def view(request): # request-only wrapper |
| | | """ """ |
| | |
| | | renderer=null_renderer) |
| | | self.assertRaises(ConfigurationConflictError, config.commit) |
| | | |
| | | def test_add_view_class_method_no_attr(self): |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import directlyProvides |
| | | from pyramid.exceptions import ConfigurationError |
| | | |
| | | config = self._makeOne(autocommit=True) |
| | | class DummyViewClass(object): |
| | | def run(self): pass |
| | | |
| | | def configure_view(): |
| | | config.add_view(view=DummyViewClass.run, renderer=null_renderer) |
| | | |
| | | self.assertRaises(ConfigurationError, configure_view) |
| | | |
| | | def test_derive_view_function(self): |
| | | from pyramid.renderers import null_renderer |
| | | def view(request): |
| | |
| | | result = view(None, request) |
| | | self.assertEqual(result, 'OK') |
| | | |
| | | def test_add_forbidden_view_no_view_argument(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPForbidden |
| | | config = self._makeOne(autocommit=True) |
| | | config.setup_registry() |
| | | config.add_forbidden_view() |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPForbidden), |
| | | request_iface=IRequest) |
| | | context = HTTPForbidden() |
| | | result = view(context, request) |
| | | self.assertEqual(result, context) |
| | | |
| | | def test_add_forbidden_view_allows_other_predicates(self): |
| | | from pyramid.renderers import null_renderer |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | result = view(None, request) |
| | | self.assertEqual(result, (None, request)) |
| | | |
| | | def test_add_notfound_view_no_view_argument(self): |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | config.setup_registry() |
| | | config.add_notfound_view() |
| | | request = self._makeRequest(config) |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | context = HTTPNotFound() |
| | | result = view(context, request) |
| | | self.assertEqual(result, context) |
| | | |
| | | def test_add_notfound_view_allows_other_predicates(self): |
| | | from pyramid.renderers import null_renderer |
| | | config = self._makeOne(autocommit=True) |
| | |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | from pyramid.httpexceptions import HTTPFound, HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_route('foo', '/foo/') |
| | | def view(request): return Response('OK') |
| | |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertTrue(isinstance(result, HTTPFound)) |
| | | self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') |
| | | |
| | | def test_add_notfound_view_append_slash_custom_response(self): |
| | | from pyramid.response import Response |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import implementedBy |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_route('foo', '/foo/') |
| | | def view(request): return Response('OK') |
| | | config.add_notfound_view( |
| | | view, renderer=null_renderer,append_slash=HTTPMovedPermanently |
| | | ) |
| | | request = self._makeRequest(config) |
| | | request.environ['PATH_INFO'] = '/foo' |
| | | request.query_string = 'a=1&b=2' |
| | | request.path = '/scriptname/foo' |
| | | view = self._getViewCallable(config, |
| | | ctx_iface=implementedBy(HTTPNotFound), |
| | | request_iface=IRequest) |
| | | result = view(None, request) |
| | | self.assertTrue(isinstance(result, HTTPMovedPermanently)) |
| | | self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') |
| | | |
| | | def test_add_notfound_view_with_view_defaults(self): |
| | |
| | | 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_generate_registration_miss(self): |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'spec', 'route_name'), |
| | | ('http://example.com/foo/', 'package:path/', None)] |
| | | registrations = [ |
| | | (None, 'spec', 'route_name', None), |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc', request) |
| | |
| | | |
| | | def test_generate_registration_no_registry_on_request(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/foo/', 'package:path/', None)] |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | del request.registry |
| | |
| | | |
| | | def test_generate_slash_in_name1(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/foo/', 'package:path/', None)] |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc', request) |
| | |
| | | |
| | | def test_generate_slash_in_name2(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/foo/', 'package:path/', None)] |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/', request) |
| | |
| | | |
| | | def test_generate_route_url(self): |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname/')] |
| | | registrations = [(None, 'package:path/', '__viewname/', None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | def route_url(n, **kw): |
| | | self.assertEqual(n, '__viewname/') |
| | |
| | | |
| | | def test_generate_url_unquoted_local(self): |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname/')] |
| | | registrations = [(None, 'package:path/', '__viewname/', None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | def route_url(n, **kw): |
| | | self.assertEqual(n, '__viewname/') |
| | |
| | | |
| | | def test_generate_url_quoted_remote(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None)] |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc def', request, a=1) |
| | |
| | | |
| | | def test_generate_url_with_custom_query(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None)] |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc def', request, a=1, |
| | |
| | | |
| | | def test_generate_url_with_custom_anchor(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None)] |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | |
| | | self.assertEqual(result, |
| | | 'http://example.com/abc%20def#La%20Pe%C3%B1a') |
| | | |
| | | def test_generate_url_cachebust(self): |
| | | def cachebust(subpath, kw): |
| | | kw['foo'] = 'bar' |
| | | return 'foo' + '/' + subpath, kw |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname', cachebust)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | def route_url(n, **kw): |
| | | self.assertEqual(n, '__viewname') |
| | | self.assertEqual(kw, {'subpath':'foo/abc', 'foo':'bar'}) |
| | | request.route_url = route_url |
| | | inst.generate('package:path/abc', request) |
| | | |
| | | def test_add_already_exists(self): |
| | | inst = self._makeOne() |
| | | config = self._makeConfig( |
| | | [('http://example.com/', 'package:path/', None)]) |
| | | inst.add(config, 'http://example.com', 'anotherpackage:path') |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | |
| | | def test_add_package_root(self): |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com', 'package:') |
| | | expected = [('http://example.com/', 'package:', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | |
| | | def test_add_url_withendslash(self): |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com/', 'anotherpackage:path') |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | |
| | | def test_add_url_noendslash(self): |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com', 'anotherpackage:path') |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | |
| | | def test_add_url_noscheme(self): |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, '//example.com', 'anotherpackage:path') |
| | | expected = [('//example.com/', 'anotherpackage:path/', None)] |
| | | expected = [('//example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | |
| | | def test_add_viewname(self): |
| | |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1) |
| | | expected = [(None, 'anotherpackage:path/', '__view/')] |
| | | expected = [(None, 'anotherpackage:path/', '__view/', None)] |
| | | self._assertRegistrations(config, expected) |
| | | self.assertEqual(config.route_args, ('__view/', 'view/*subpath')) |
| | | self.assertEqual(config.view_kw['permission'], NO_PERMISSION_REQUIRED) |
| | |
| | | config.route_prefix = '/abc' |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path',) |
| | | expected = [(None, 'anotherpackage:path/', '__/abc/view/')] |
| | | expected = [(None, 'anotherpackage:path/', '__/abc/view/', None)] |
| | | self._assertRegistrations(config, expected) |
| | | self.assertEqual(config.route_args, ('__/abc/view/', 'view/*subpath')) |
| | | |
| | |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, |
| | | context=DummyContext) |
| | | self.assertEqual(config.view_kw['context'], DummyContext) |
| | | |
| | | |
| | | def test_add_viewname_with_for_(self): |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | |
| | | renderer='mypackage:templates/index.pt') |
| | | self.assertEqual(config.view_kw['renderer'], |
| | | 'mypackage:templates/index.pt') |
| | | |
| | | def test_add_cachebust_default(self): |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | 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', {}) |
| | | self.assertEqual(subpath, 'some/path') |
| | | self.assertEqual(kw['x'], 'foo') |
| | | |
| | | def test_add_cachebust_prevented(self): |
| | | config = self._makeConfig() |
| | | config.registry.settings['pyramid.prevent_cachebust'] = True |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'mypackage:path', cachebust=True) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | self.assertEqual(cachebust, None) |
| | | |
| | | def test_add_cachebust_custom(self): |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'mypackage:path', |
| | | cachebust=DummyCacheBuster('foo')) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | subpath, kw = cachebust('some/path', {}) |
| | | self.assertEqual(subpath, 'some/path') |
| | | self.assertEqual(kw['x'], 'foo') |
| | | |
| | | class Test_view_description(unittest.TestCase): |
| | | def _callFUT(self, view): |
| | |
| | | |
| | | |
| | | class DummyRegistry: |
| | | pass |
| | | def __init__(self): |
| | | self.settings = {} |
| | | |
| | | from zope.interface import implementer |
| | | from pyramid.interfaces import IResponse |
| | |
| | | def __permitted__(self, context, request): |
| | | """ """ |
| | | |
| | | class DummyCacheBuster(object): |
| | | def __init__(self, token): |
| | | self.token = token |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | kw['x'] = self.token |
| | | return subpath, kw |
| | | |
| | | def parse_httpdate(s): |
| | | import datetime |
| | | # cannot use %Z, must use literal GMT; Jython honors timezone |
| | |
| | | self.assertEqual(inst.__dict__['wrapped'], 'a') |
| | | |
| | | def test___get__noinst(self): |
| | | decorator = self._makeOne(None) |
| | | def wrapped(inst): |
| | | return 'a' # pragma: no cover |
| | | decorator = self._makeOne(wrapped) |
| | | result = decorator.__get__(None) |
| | | self.assertEqual(result, decorator) |
| | | |
| | | def test___doc__copied(self): |
| | | def wrapped(inst): |
| | | """My doc""" |
| | | decorator = self._makeOne(wrapped) |
| | | self.assertEqual(decorator.__doc__, "My doc") |
| | | |
| | | def test_dunder_attrs_copied(self): |
| | | from pyramid.util import viewdefaults |
| | | decorator = self._makeOne(viewdefaults) |
| | | self.assertEqual(decorator.__doc__, viewdefaults.__doc__) |
| | | self.assertEqual(decorator.__name__, viewdefaults.__name__) |
| | | self.assertEqual(decorator.__module__, viewdefaults.__module__) |
| | | |
| | | |
| | | class Dummy(object): |
| | | pass |
| | |
| | | res = self.testapp.get('/static/.hiddenfile', status=200) |
| | | _assertBody(res.body, os.path.join(here, 'fixtures/static/.hiddenfile')) |
| | | |
| | | if defaultlocale is not None: |
| | | if defaultlocale is not None: # pragma: no cover |
| | | # These tests are expected to fail on LANG=C systems due to decode |
| | | # errors and on non-Linux systems due to git highchar handling |
| | | # vagaries |
| | |
| | | |
| | | def test_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) |
| | |
| | | registry = self._makeOne() |
| | | self.assertEqual(registry.__nonzero__(), True) |
| | | |
| | | def test_package_name(self): |
| | | package_name = 'testing' |
| | | registry = self._getTargetClass()(package_name) |
| | | self.assertEqual(registry.package_name, package_name) |
| | | |
| | | def test_registerHandler_and_notify(self): |
| | | registry = self._makeOne() |
| | | self.assertEqual(registry.has_listeners, False) |
| | |
| | | from pyramid.interfaces import IResponseFactory |
| | | class ResponseFactory(object): |
| | | pass |
| | | self.config.registry.registerUtility(ResponseFactory, IResponseFactory) |
| | | |
| | | self.config.registry.registerUtility( |
| | | lambda r: ResponseFactory(), IResponseFactory |
| | | ) |
| | | |
| | | def test_render_to_response(self): |
| | | self._registerRendererFactory() |
| | |
| | | helper = self._makeOne('loo.foo') |
| | | response = helper.render_to_response('values', {}, |
| | | request=request) |
| | | self.assertEqual(response.body[0], 'values') |
| | | self.assertEqual(response.body[1], {}) |
| | | self.assertEqual(response.app_iter[0], 'values') |
| | | self.assertEqual(response.app_iter[1], {}) |
| | | |
| | | def test_get_renderer(self): |
| | | factory = self._registerRendererFactory() |
| | |
| | | request = testing.DummyRequest() |
| | | response = 'response' |
| | | response = helper.render_view(request, response, view, context) |
| | | self.assertEqual(response.body[0], 'response') |
| | | self.assertEqual(response.body[1], |
| | | self.assertEqual(response.app_iter[0], 'response') |
| | | self.assertEqual(response.app_iter[1], |
| | | {'renderer_info': helper, |
| | | 'renderer_name': 'loo.foo', |
| | | 'request': request, |
| | |
| | | response = helper._make_response(la.encode('utf-8'), request) |
| | | self.assertEqual(response.body, la.encode('utf-8')) |
| | | |
| | | def test__make_response_result_is_iterable(self): |
| | | from pyramid.response import Response |
| | | request = testing.DummyRequest() |
| | | request.response = Response() |
| | | helper = self._makeOne('loo.foo') |
| | | la = text_('/La Pe\xc3\xb1a', 'utf-8') |
| | | response = helper._make_response([la.encode('utf-8')], request) |
| | | self.assertEqual(response.body, la.encode('utf-8')) |
| | | |
| | | def test__make_response_result_is_other(self): |
| | | self._registerResponseFactory() |
| | | request = None |
| | | helper = self._makeOne('loo.foo') |
| | | result = object() |
| | | response = helper._make_response(result, request) |
| | | self.assertEqual(response.body, result) |
| | | |
| | | def test__make_response_result_is_None_no_body(self): |
| | | from pyramid.response import Response |
| | | request = testing.DummyRequest() |
| | |
| | | class ResponseFactory(object): |
| | | def __init__(self): |
| | | pass |
| | | self.config.registry.registerUtility(ResponseFactory, IResponseFactory) |
| | | self.config.registry.registerUtility( |
| | | lambda r: ResponseFactory(), IResponseFactory |
| | | ) |
| | | request = testing.DummyRequest() |
| | | helper = self._makeOne('loo.foo') |
| | | response = helper._make_response(b'abc', request) |
| | |
| | | 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): |
| | |
| | | self.assertEqual(request.response.content_type, |
| | | 'application/json') |
| | | |
| | | def test_render_without_request(self): |
| | | renderer_factory = self._makeOne() |
| | | renderer = renderer_factory(None) |
| | | result = renderer({'a':'1'}, {}) |
| | | self.assertEqual(result, '{"a": "1"}') |
| | | |
| | | |
| | | class Dummy: |
| | | pass |
| | | |
| | | 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) |
| | | |
| | |
| | | from collections import deque |
| | | import unittest |
| | | from pyramid import testing |
| | | |
| | |
| | | |
| | | def test_add_response_callback(self): |
| | | inst = self._makeOne() |
| | | self.assertEqual(inst.response_callbacks, ()) |
| | | self.assertEqual(inst.response_callbacks, None) |
| | | def callback(request, response): |
| | | """ """ |
| | | inst.add_response_callback(callback) |
| | | self.assertEqual(inst.response_callbacks, [callback]) |
| | | self.assertEqual(list(inst.response_callbacks), [callback]) |
| | | inst.add_response_callback(callback) |
| | | self.assertEqual(inst.response_callbacks, [callback, callback]) |
| | | self.assertEqual(list(inst.response_callbacks), [callback, callback]) |
| | | |
| | | def test__process_response_callbacks(self): |
| | | inst = self._makeOne() |
| | |
| | | def callback2(request, response): |
| | | request.called2 = True |
| | | response.called2 = True |
| | | inst.response_callbacks = [callback1, callback2] |
| | | inst.add_response_callback(callback1) |
| | | inst.add_response_callback(callback2) |
| | | response = DummyResponse() |
| | | inst._process_response_callbacks(response) |
| | | self.assertEqual(inst.called1, True) |
| | | self.assertEqual(inst.called2, True) |
| | | self.assertEqual(response.called1, True) |
| | | self.assertEqual(response.called2, True) |
| | | self.assertEqual(inst.response_callbacks, []) |
| | | self.assertEqual(len(inst.response_callbacks), 0) |
| | | |
| | | def test__process_response_callback_adding_response_callback(self): |
| | | """ |
| | | When a response callback adds another callback, that new callback should still be called. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/1373 |
| | | """ |
| | | inst = self._makeOne() |
| | | def callback1(request, response): |
| | | request.called1 = True |
| | | response.called1 = True |
| | | request.add_response_callback(callback2) |
| | | def callback2(request, response): |
| | | request.called2 = True |
| | | response.called2 = True |
| | | inst.add_response_callback(callback1) |
| | | response = DummyResponse() |
| | | inst._process_response_callbacks(response) |
| | | self.assertEqual(inst.called1, True) |
| | | self.assertEqual(inst.called2, True) |
| | | self.assertEqual(response.called1, True) |
| | | self.assertEqual(response.called2, True) |
| | | self.assertEqual(len(inst.response_callbacks), 0) |
| | | |
| | | def test_add_finished_callback(self): |
| | | inst = self._makeOne() |
| | | self.assertEqual(inst.finished_callbacks, ()) |
| | | self.assertEqual(inst.finished_callbacks, None) |
| | | def callback(request): |
| | | """ """ |
| | | inst.add_finished_callback(callback) |
| | | self.assertEqual(inst.finished_callbacks, [callback]) |
| | | self.assertEqual(list(inst.finished_callbacks), [callback]) |
| | | inst.add_finished_callback(callback) |
| | | self.assertEqual(inst.finished_callbacks, [callback, callback]) |
| | | self.assertEqual(list(inst.finished_callbacks), [callback, callback]) |
| | | |
| | | def test__process_finished_callbacks(self): |
| | | inst = self._makeOne() |
| | |
| | | request.called1 = True |
| | | def callback2(request): |
| | | request.called2 = True |
| | | inst.finished_callbacks = [callback1, callback2] |
| | | inst.add_finished_callback(callback1) |
| | | inst.add_finished_callback(callback2) |
| | | inst._process_finished_callbacks() |
| | | self.assertEqual(inst.called1, True) |
| | | self.assertEqual(inst.called2, True) |
| | | self.assertEqual(inst.finished_callbacks, []) |
| | | self.assertEqual(len(inst.finished_callbacks), 0) |
| | | |
| | | def test_resource_url(self): |
| | | self._registerResourceURL() |
| | |
| | | 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 = {} |
| | |
| | | import io |
| | | import mimetypes |
| | | import os |
| | | import unittest |
| | | from pyramid import testing |
| | |
| | | def _getTargetClass(self): |
| | | from pyramid.response import Response |
| | | return Response |
| | | |
| | | |
| | | def test_implements_IResponse(self): |
| | | from pyramid.interfaces import IResponse |
| | | cls = self._getTargetClass() |
| | |
| | | r.app_iter.close() |
| | | |
| | | def test_without_content_type(self): |
| | | for suffix, content_type in ( |
| | | ('txt', 'text/plain; charset=UTF-8'), |
| | | ('xml', 'application/xml; charset=UTF-8'), |
| | | ('pdf', 'application/pdf') |
| | | ): |
| | | for suffix in ('txt', 'xml', 'pdf'): |
| | | path = self._getPath(suffix) |
| | | r = self._makeOne(path) |
| | | self.assertEqual(r.content_type, content_type.split(';')[0]) |
| | | self.assertEqual(r.headers['content-type'], content_type) |
| | | self.assertEqual(r.headers['content-type'].split(';')[0], |
| | | mimetypes.guess_type(path, strict=False)[0]) |
| | | r.app_iter.close() |
| | | |
| | | def test_python_277_bug_15207(self): |
| | |
| | | result = self._callFUT(module) |
| | | self.assertEqual(result, True) |
| | | self.assertEqual(module.initted, True) |
| | | |
| | | |
| | | def test_missing_init(self): |
| | | class DummyMimetypes(object): |
| | | pass |
| | |
| | | self.assertEqual(dummy_venusian.attached, |
| | | [(foo, dec.register, 'pyramid')]) |
| | | |
| | | |
| | | class TestGetResponseFactory(unittest.TestCase): |
| | | def test_get_factory(self): |
| | | from pyramid.registry import Registry |
| | | from pyramid.response import Response, _get_response_factory |
| | | |
| | | registry = Registry() |
| | | response = _get_response_factory(registry)(None) |
| | | self.assertTrue(isinstance(response, Response)) |
| | | |
| | | |
| | | class Dummy(object): |
| | | pass |
| | | |
| | |
| | | |
| | | def attach(self, wrapped, fn, category=None): |
| | | self.attached.append((wrapped, fn, category)) |
| | | |
| | | |
| | |
| | | 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 |
| | |
| | | def view(context, request): |
| | | def callback(request, response): |
| | | response.called_back = True |
| | | request.response_callbacks = [callback] |
| | | request.add_response_callback(callback) |
| | | return response |
| | | environ = self._makeEnviron() |
| | | self._registerView(view, '', IViewClassifier, IRequest, IContext) |
| | |
| | | def view(context, request): |
| | | def callback(request): |
| | | request.environ['called_back'] = True |
| | | request.finished_callbacks = [callback] |
| | | request.add_finished_callback(callback) |
| | | return response |
| | | environ = self._makeEnviron() |
| | | self._registerView(view, '', IViewClassifier, IRequest, IContext) |
| | |
| | | def view(context, request): |
| | | def callback(request): |
| | | request.environ['called_back'] = True |
| | | request.finished_callbacks = [callback] |
| | | request.add_finished_callback(callback) |
| | | raise NotImplementedError |
| | | environ = self._makeEnviron() |
| | | self._registerView(view, '', IViewClassifier, IRequest, IContext) |
| | |
| | | environ = self._makeEnviron() |
| | | self._registerView(view, '', IViewClassifier, None, None) |
| | | request_events = self._registerEventListener(INewRequest) |
| | | aftertraversal_events = self._registerEventListener(IContextFound) |
| | | context_found_events = self._registerEventListener(IContextFound) |
| | | response_events = self._registerEventListener(INewResponse) |
| | | router = self._makeOne() |
| | | start_response = DummyStartResponse() |
| | | result = router(environ, start_response) |
| | | self.assertEqual(len(request_events), 1) |
| | | self.assertEqual(request_events[0].request.environ, environ) |
| | | self.assertEqual(len(aftertraversal_events), 1) |
| | | self.assertEqual(aftertraversal_events[0].request.environ, environ) |
| | | self.assertEqual(len(context_found_events), 1) |
| | | self.assertEqual(context_found_events[0].request.environ, environ) |
| | | self.assertEqual(context_found_events[0].request.context, context) |
| | | self.assertEqual(len(response_events), 1) |
| | | self.assertEqual(response_events[0].response, response) |
| | | self.assertEqual(response_events[0].request.context, context) |
| | | self.assertEqual(result, response.app_iter) |
| | | |
| | | def test_call_newrequest_evllist_exc_can_be_caught_by_exceptionview(self): |
| | |
| | | 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 = {} |
| | |
| | | def __init__(self, *routes): |
| | | self.routes = routes |
| | | |
| | | def get_routes(self): |
| | | def get_routes(self, include_static=False): |
| | | return self.routes |
| | | |
| | | class DummyRoute(object): |
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 |
| | |
| | | from pyramid.scripts.pcreate import PCreateCommand |
| | | return PCreateCommand |
| | | |
| | | def _makeOne(self, *args): |
| | | def _makeOne(self, *args, **kw): |
| | | effargs = ['pcreate'] |
| | | effargs.extend(args) |
| | | cmd = self._getTargetClass()(effargs) |
| | | cmd = self._getTargetClass()(effargs, **kw) |
| | | cmd.out = self.out |
| | | return cmd |
| | | |
| | |
| | | out = self.out_.getvalue() |
| | | self.assertTrue(out.startswith('No scaffolds available')) |
| | | |
| | | def test_run_no_scaffold_no_args(self): |
| | | cmd = self._makeOne(quiet=True) |
| | | result = cmd.run() |
| | | self.assertEqual(result, 2) |
| | | |
| | | def test_run_no_scaffold_name(self): |
| | | cmd = self._makeOne() |
| | | cmd = self._makeOne('dummy') |
| | | result = cmd.run() |
| | | self.assertEqual(result, 2) |
| | | out = self.out_.getvalue() |
| | |
| | | {'project': 'Distro', 'egg': 'Distro', 'package': 'distro', |
| | | 'pyramid_version': '0.1', 'pyramid_docs_branch':'0.1-branch'}) |
| | | |
| | | def test_scaffold_with_hyphen_in_project_name(self): |
| | | import os |
| | | cmd = self._makeOne('-s', 'dummy', 'Distro-') |
| | | scaffold = DummyScaffold('dummy') |
| | | cmd.scaffolds = [scaffold] |
| | | cmd.pyramid_dist = DummyDist("0.1") |
| | | result = cmd.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual( |
| | | scaffold.output_dir, |
| | | os.path.normpath(os.path.join(os.getcwd(), 'Distro-')) |
| | | ) |
| | | self.assertEqual( |
| | | scaffold.vars, |
| | | {'project': 'Distro-', 'egg': 'Distro_', 'package': 'distro_', |
| | | 'pyramid_version': '0.1', 'pyramid_docs_branch':'0.1-branch'}) |
| | | |
| | | def test_known_scaffold_absolute_path(self): |
| | | import os |
| | | path = os.path.abspath('Distro') |
| | |
| | | self.assertEqual(self._path_info, '/') |
| | | self.assertEqual(self._spec, 'development.ini') |
| | | self.assertEqual(self._app_name, None) |
| | | |
| | | self.assertEqual(self._out, [b'abc']) |
| | | |
| | | def test_command_method_configures_logging(self): |
| | | command = self._makeOne(['', 'development.ini', '/']) |
| | | called_args = [] |
| | | |
| | | def configure_logging(app_spec): |
| | | called_args.append(app_spec) |
| | | |
| | | command.configure_logging = configure_logging |
| | | command.run() |
| | | self.assertEqual(called_args, ['development.ini']) |
| | | |
| | | |
| | | class Test_main(unittest.TestCase): |
| | | def _callFUT(self, argv): |
| | | from pyramid.scripts.prequest import main |
| | |
| | | import unittest |
| | | from pyramid.tests.test_scripts import dummy |
| | | |
| | | |
| | | class DummyIntrospector(object): |
| | | def __init__(self): |
| | | self.relations = {} |
| | | self.introspectables = {} |
| | | |
| | | def get(self, name, discrim): |
| | | pass |
| | | |
| | | |
| | | class TestPRoutesCommand(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | | from pyramid.scripts.proutes import PRoutesCommand |
| | |
| | | cmd = self._getTargetClass()([]) |
| | | cmd.bootstrap = (dummy.DummyBootstrap(),) |
| | | cmd.args = ('/foo/bar/myapp.ini#myapp',) |
| | | |
| | | return cmd |
| | | |
| | | def _makeRegistry(self): |
| | | from pyramid.registry import Registry |
| | | registry = Registry() |
| | | registry.introspector = DummyIntrospector() |
| | | return registry |
| | | |
| | | def _makeConfig(self, *arg, **kw): |
| | | from pyramid.config import Configurator |
| | | config = Configurator(*arg, **kw) |
| | | return config |
| | | |
| | | def test_good_args(self): |
| | | cmd = self._getTargetClass()([]) |
| | |
| | | route = dummy.DummyRoute('a', '/a') |
| | | mapper = dummy.DummyMapper(route) |
| | | cmd._get_mapper = lambda *arg: mapper |
| | | registry = self._makeRegistry() |
| | | cmd.bootstrap = (dummy.DummyBootstrap(registry=registry),) |
| | | L = [] |
| | | cmd.out = lambda msg: L.append(msg) |
| | | cmd.run() |
| | |
| | | route = dummy.DummyRoute('a', '/a') |
| | | mapper = dummy.DummyMapper(route) |
| | | command._get_mapper = lambda *arg: mapper |
| | | registry = self._makeRegistry() |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=registry),) |
| | | |
| | | L = [] |
| | | command.out = L.append |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>']) |
| | | self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>', '*']) |
| | | |
| | | def test_route_with_no_slash_prefix(self): |
| | | command = self._makeOne() |
| | |
| | | command._get_mapper = lambda *arg: mapper |
| | | L = [] |
| | | command.out = L.append |
| | | registry = self._makeRegistry() |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>']) |
| | | self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>', '*']) |
| | | |
| | | def test_single_route_no_views_registered(self): |
| | | from zope.interface import Interface |
| | | from pyramid.registry import Registry |
| | | from pyramid.interfaces import IRouteRequest |
| | | registry = Registry() |
| | | registry = self._makeRegistry() |
| | | |
| | | def view():pass |
| | | class IMyRoute(Interface): |
| | | pass |
| | |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None']) |
| | | self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>']) |
| | | |
| | | def test_single_route_one_view_registered(self): |
| | | from zope.interface import Interface |
| | | from pyramid.registry import Registry |
| | | from pyramid.interfaces import IRouteRequest |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IView |
| | | registry = Registry() |
| | | registry = self._makeRegistry() |
| | | |
| | | def view():pass |
| | | class IMyRoute(Interface): |
| | | pass |
| | |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split()[:3] |
| | | self.assertEqual(compare_to, ['a', '/a', '<function']) |
| | | |
| | | def test_single_route_one_view_registered_with_factory(self): |
| | | self.assertEqual( |
| | | compare_to, |
| | | ['a', '/a', 'pyramid.tests.test_scripts.test_proutes.view'] |
| | | ) |
| | | |
| | | def test_one_route_with_long_name_one_view_registered(self): |
| | | from zope.interface import Interface |
| | | from pyramid.registry import Registry |
| | | from pyramid.interfaces import IRouteRequest |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IView |
| | | registry = Registry() |
| | | registry = self._makeRegistry() |
| | | |
| | | def view():pass |
| | | |
| | | class IMyRoute(Interface): |
| | | pass |
| | | |
| | | registry.registerAdapter( |
| | | view, |
| | | (IViewClassifier, IMyRoute, Interface), |
| | | IView, '' |
| | | ) |
| | | |
| | | registry.registerUtility(IMyRoute, IRouteRequest, |
| | | name='very_long_name_123') |
| | | |
| | | command = self._makeOne() |
| | | route = dummy.DummyRoute( |
| | | 'very_long_name_123', |
| | | '/and_very_long_pattern_as_well' |
| | | ) |
| | | mapper = dummy.DummyMapper(route) |
| | | command._get_mapper = lambda *arg: mapper |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split()[:3] |
| | | self.assertEqual( |
| | | compare_to, |
| | | ['very_long_name_123', |
| | | '/and_very_long_pattern_as_well', |
| | | 'pyramid.tests.test_scripts.test_proutes.view'] |
| | | ) |
| | | |
| | | def test_single_route_one_view_registered_with_factory(self): |
| | | from zope.interface import Interface |
| | | from pyramid.interfaces import IRouteRequest |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IView |
| | | registry = self._makeRegistry() |
| | | |
| | | def view():pass |
| | | class IMyRoot(Interface): |
| | | pass |
| | |
| | | self.assertEqual(len(L), 3) |
| | | self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>']) |
| | | |
| | | def test_single_route_multiview_registered(self): |
| | | from zope.interface import Interface |
| | | from pyramid.interfaces import IRouteRequest |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IMultiView |
| | | |
| | | registry = self._makeRegistry() |
| | | |
| | | def view(): pass |
| | | |
| | | class IMyRoute(Interface): |
| | | pass |
| | | |
| | | multiview1 = dummy.DummyMultiView( |
| | | view, context='context', |
| | | view_name='a1' |
| | | ) |
| | | |
| | | registry.registerAdapter( |
| | | multiview1, |
| | | (IViewClassifier, IMyRoute, Interface), |
| | | IMultiView, '' |
| | | ) |
| | | registry.registerUtility(IMyRoute, IRouteRequest, name='a') |
| | | command = self._makeOne() |
| | | route = dummy.DummyRoute('a', '/a') |
| | | mapper = dummy.DummyMapper(route) |
| | | command._get_mapper = lambda *arg: mapper |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split()[:3] |
| | | view_module = 'pyramid.tests.test_scripts.dummy' |
| | | view_str = '<pyramid.tests.test_scripts.dummy.DummyMultiView' |
| | | final = '%s.%s' % (view_module, view_str) |
| | | |
| | | self.assertEqual( |
| | | compare_to, |
| | | ['a', '/a', final] |
| | | ) |
| | | |
| | | def test__get_mapper(self): |
| | | from pyramid.registry import Registry |
| | | from pyramid.urldispatch import RoutesMapper |
| | | command = self._makeOne() |
| | | registry = Registry() |
| | | registry = self._makeRegistry() |
| | | |
| | | result = command._get_mapper(registry) |
| | | self.assertEqual(result.__class__, RoutesMapper) |
| | | |
| | | |
| | | def test_one_route_all_methods_view_only_post(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method='POST' |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', 'POST' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_one_route_only_post_view_all_methods(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b', request_method='POST') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', 'POST' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_one_route_only_post_view_post_and_get(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b', request_method='POST') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=('POST', 'GET') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', 'POST' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_route_request_method_mismatch(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b', request_method='POST') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method='GET' |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', |
| | | '<route', 'mismatch>' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_route_static_views(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_static_view('static', 'static', cache_max_age=3600) |
| | | config.add_static_view(name='static2', path='/var/www/static') |
| | | config.add_static_view( |
| | | name='pyramid_scaffold', |
| | | path='pyramid:scaffolds/starter/+package+/static' |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 5) |
| | | |
| | | expected = [ |
| | | ['__static/', '/static/*subpath', |
| | | 'pyramid.tests.test_scripts:static/', '*'], |
| | | ['__static2/', '/static2/*subpath', '/var/www/static/', '*'], |
| | | ['__pyramid_scaffold/', '/pyramid_scaffold/*subpath', |
| | | 'pyramid:scaffolds/starter/+package+/static/', '*'], |
| | | ] |
| | | |
| | | for index, line in enumerate(L[2:]): |
| | | data = line.split() |
| | | self.assertEqual(data, expected[index]) |
| | | |
| | | def test_route_no_view(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b', request_method='POST') |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | '<unknown>', |
| | | 'POST', |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_route_as_wsgiapp(self): |
| | | from pyramid.wsgi import wsgiapp2 |
| | | |
| | | config1 = self._makeConfig(autocommit=True) |
| | | def view1(context, request): return 'view1' |
| | | config1.add_route('foo', '/a/b', request_method='POST') |
| | | config1.add_view(view=view1, route_name='foo') |
| | | |
| | | config2 = self._makeConfig(autocommit=True) |
| | | config2.add_route('foo', '/a/b', request_method='POST') |
| | | config2.add_view( |
| | | wsgiapp2(config1.make_wsgi_app()), |
| | | route_name='foo', |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config2.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | '<wsgiapp>', |
| | | 'POST', |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_route_is_get_view_request_method_not_post(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b', request_method='GET') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', |
| | | 'GET' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_view_request_method_not_post(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', |
| | | '!POST,*' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_view_glob(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | def view2(context, request): return 'view2' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | config.add_route('bar', '/b/a') |
| | | config.add_view( |
| | | route_name='bar', |
| | | view=view2, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | command.options.glob = '*foo*' |
| | | |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', '/a/b', |
| | | 'pyramid.tests.test_scripts.test_proutes.view1', |
| | | '!POST,*' |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | def test_good_format(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | command.options.glob = '*foo*' |
| | | command.options.format = 'method,name' |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = ['!POST,*', 'foo'] |
| | | |
| | | self.assertEqual(compare_to, expected) |
| | | self.assertEqual(L[0].split(), ['Method', 'Name']) |
| | | |
| | | def test_bad_format(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | command.options.glob = '*foo*' |
| | | command.options.format = 'predicates,name,pattern' |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | expected = ( |
| | | "You provided invalid formats ['predicates'], " |
| | | "Available formats are ['name', 'pattern', 'view', 'method']" |
| | | ) |
| | | result = command.run() |
| | | self.assertEqual(result, 2) |
| | | self.assertEqual(L[0], expected) |
| | | |
| | | def test_config_format_ini_newlines(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | config_factory = dummy.DummyConfigParserFactory() |
| | | command.ConfigParser = config_factory |
| | | config_factory.items = [('format', 'method\nname')] |
| | | |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = ['!POST,*', 'foo'] |
| | | |
| | | self.assertEqual(compare_to, expected) |
| | | self.assertEqual(L[0].split(), ['Method', 'Name']) |
| | | |
| | | def test_config_format_ini_spaces(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | config_factory = dummy.DummyConfigParserFactory() |
| | | command.ConfigParser = config_factory |
| | | config_factory.items = [('format', 'method name')] |
| | | |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = ['!POST,*', 'foo'] |
| | | |
| | | self.assertEqual(compare_to, expected) |
| | | self.assertEqual(L[0].split(), ['Method', 'Name']) |
| | | |
| | | def test_config_format_ini_commas(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | from pyramid.config import not_ |
| | | |
| | | def view1(context, request): return 'view1' |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', '/a/b') |
| | | config.add_view( |
| | | route_name='foo', |
| | | view=view1, |
| | | renderer=nr, |
| | | request_method=not_('POST') |
| | | ) |
| | | |
| | | command = self._makeOne() |
| | | |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | config_factory = dummy.DummyConfigParserFactory() |
| | | command.ConfigParser = config_factory |
| | | config_factory.items = [('format', 'method,name')] |
| | | |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = ['!POST,*', 'foo'] |
| | | |
| | | self.assertEqual(compare_to, expected) |
| | | self.assertEqual(L[0].split(), ['Method', 'Name']) |
| | | |
| | | def test_static_routes_included_in_list(self): |
| | | from pyramid.renderers import null_renderer as nr |
| | | |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_route('foo', 'http://example.com/bar.aspx', static=True) |
| | | |
| | | command = self._makeOne() |
| | | L = [] |
| | | command.out = L.append |
| | | command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),) |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(len(L), 3) |
| | | compare_to = L[-1].split() |
| | | expected = [ |
| | | 'foo', 'http://example.com/bar.aspx', |
| | | '<unknown>', '*', |
| | | ] |
| | | self.assertEqual(compare_to, expected) |
| | | |
| | | class Test_main(unittest.TestCase): |
| | | def _callFUT(self, argv): |
| | | from pyramid.scripts.proutes import main |
| | |
| | | def test_it(self): |
| | | result = self._callFUT(['proutes']) |
| | | self.assertEqual(result, 2) |
| | | |
| | |
| | | import unittest |
| | | |
| | | from pyramid.compat import PY3 |
| | | if PY3: # pragma: no cover |
| | | if PY3: |
| | | import builtins as __builtin__ |
| | | else: |
| | | import __builtin__ |
| | |
| | | import os |
| | | import unittest |
| | | from pyramid.tests.test_scripts import dummy |
| | | |
| | |
| | | self.options.python_shell = '' |
| | | self.options.setup = None |
| | | cmd.options = self.options |
| | | # default to None to prevent side-effects from running tests in |
| | | # unknown environments |
| | | cmd.pystartup = None |
| | | return cmd |
| | | |
| | | def test_make_default_shell(self): |
| | |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | def test_command_loads_pythonstartup(self): |
| | | command = self._makeOne() |
| | | command.pystartup = ( |
| | | os.path.abspath( |
| | | os.path.join( |
| | | os.path.dirname(__file__), |
| | | 'pystartup.txt'))) |
| | | shell = dummy.DummyShell() |
| | | command.run(shell) |
| | | self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') |
| | | self.assertEqual(shell.env, { |
| | | 'app':self.bootstrap.app, 'root':self.bootstrap.root, |
| | | 'registry':self.bootstrap.registry, |
| | | 'request':self.bootstrap.request, |
| | | 'root_factory':self.bootstrap.root_factory, |
| | | 'foo':1, |
| | | }) |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | class Test_main(unittest.TestCase): |
| | | def _callFUT(self, argv): |
| | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | def _callFUT(self, *arg): |
| | | def _callFUT(self, *arg, **kwarg): |
| | | from pyramid.security import remember |
| | | return remember(*arg) |
| | | return remember(*arg, **kwarg) |
| | | |
| | | def test_no_authentication_policy(self): |
| | | request = _makeRequest() |
| | |
| | | _registerAuthenticationPolicy(registry, 'yo') |
| | | result = self._callFUT(request, 'me') |
| | | self.assertEqual(result, [('X-Pyramid-Test', 'me')]) |
| | | |
| | | def test_with_deprecated_principal_arg(self): |
| | | request = _makeRequest() |
| | | registry = request.registry |
| | | _registerAuthenticationPolicy(registry, 'yo') |
| | | result = self._callFUT(request, principal='me') |
| | | self.assertEqual(result, [('X-Pyramid-Test', 'me')]) |
| | | |
| | | def test_with_missing_arg(self): |
| | | request = _makeRequest() |
| | | registry = request.registry |
| | | _registerAuthenticationPolicy(registry, 'yo') |
| | | self.assertRaises(TypeError, lambda: self._callFUT(request)) |
| | | |
| | | class TestForget(unittest.TestCase): |
| | | def setUp(self): |
| | |
| | | def authenticated_userid(self, request): |
| | | return self.result |
| | | |
| | | def remember(self, request, principal, **kw): |
| | | headers = [(_TEST_HEADER, principal)] |
| | | def remember(self, request, userid, **kw): |
| | | headers = [(_TEST_HEADER, userid)] |
| | | self._header_remembered = headers[0] |
| | | return headers |
| | | |
| | |
| | | result = wrapper(session, 'a') |
| | | self.assertEqual(result, 1) |
| | | callbacks = request.response_callbacks |
| | | self.assertEqual(len(callbacks), 0) |
| | | if callbacks is not None: self.assertEqual(len(callbacks), 0) |
| | | |
| | | class Test_manage_changed(unittest.TestCase): |
| | | def _makeOne(self, wrapped): |
| | |
| | | if kw is not None: |
| | | environ.update(kw) |
| | | return Request(environ=environ) |
| | | |
| | | |
| | | def test_ctor_defaultargs(self): |
| | | inst = self._makeOne('package:resource_name') |
| | | self.assertEqual(inst.package_name, 'package') |
| | |
| | | def test_resource_is_file(self): |
| | | inst = self._makeOne('pyramid.tests:fixtures/static') |
| | | request = self._makeRequest({'PATH_INFO':'/index.html'}) |
| | | context = DummyContext() |
| | | response = inst(context, request) |
| | | self.assertTrue(b'<html>static</html>' in response.body) |
| | | |
| | | def test_cachebust_match(self): |
| | | inst = self._makeOne('pyramid.tests:fixtures/static') |
| | | inst.cachebust_match = lambda subpath: subpath[1:] |
| | | request = self._makeRequest({'PATH_INFO':'/foo/index.html'}) |
| | | context = DummyContext() |
| | | response = inst(context, request) |
| | | self.assertTrue(b'<html>static</html>' in response.body) |
| | |
| | | if kw is not None: |
| | | environ.update(kw) |
| | | return Request(environ=environ) |
| | | |
| | | |
| | | def test_ctor_defaultargs(self): |
| | | inst = self._makeOne('package:resource_name') |
| | | self.assertEqual(inst.package_name, 'package') |
| | |
| | | context = DummyContext() |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | self.assertRaises(HTTPNotFound, inst, context, request) |
| | | |
| | | |
| | | def test_oob_os_sep(self): |
| | | import os |
| | | inst = self._makeOne('pyramid.tests:fixtures/static') |
| | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | self.assertRaises(HTTPNotFound, inst, context, request) |
| | | |
| | | class TestMd5AssetTokenGenerator(unittest.TestCase): |
| | | _fspath = None |
| | | _tmp = None |
| | | |
| | | @property |
| | | def fspath(self): |
| | | if self._fspath: |
| | | return self._fspath |
| | | |
| | | import os |
| | | import tempfile |
| | | self._tmp = tmp = tempfile.mkdtemp() |
| | | self._fspath = os.path.join(tmp, 'test.txt') |
| | | return self._fspath |
| | | |
| | | def tearDown(self): |
| | | import shutil |
| | | if self._tmp: |
| | | shutil.rmtree(self._tmp) |
| | | |
| | | def _makeOne(self): |
| | | from pyramid.static import Md5AssetTokenGenerator as cls |
| | | return cls() |
| | | |
| | | def test_package_resource(self): |
| | | 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().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | def test_cache(self): |
| | | fut = self._makeOne().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | # md5 shouldn't change because we've cached it |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Sorry for the convenience.") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | class TestPathSegmentMd5CacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self): |
| | | from pyramid.static import PathSegmentMd5CacheBuster as cls |
| | | inst = cls() |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual(fut('foo', ('bar',), 'kw'), (('foo', 'bar'), 'kw')) |
| | | |
| | | def test_match(self): |
| | | fut = self._makeOne().match |
| | | self.assertEqual(fut(('foo', 'bar')), ('bar',)) |
| | | |
| | | class TestQueryStringMd5CacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self, param=None): |
| | | from pyramid.static import QueryStringMd5CacheBuster as cls |
| | | if param: |
| | | inst = cls(param) |
| | | else: |
| | | inst = cls() |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'x': 'foo'}})) |
| | | |
| | | def test_pregenerate_change_param(self): |
| | | fut = self._makeOne('y').pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'y': 'foo'}})) |
| | | |
| | | def test_pregenerate_query_is_already_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': [('a', 'b')]}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | def test_pregenerate_query_is_tuple_of_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': (('a', 'b'),)}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | class TestQueryStringConstantCacheBuster(TestQueryStringMd5CacheBuster): |
| | | |
| | | def _makeOne(self, param=None): |
| | | from pyramid.static import QueryStringConstantCacheBuster as cls |
| | | if param: |
| | | inst = cls('foo', param) |
| | | else: |
| | | inst = cls('foo') |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'x': 'foo'}})) |
| | | |
| | | def test_pregenerate_change_param(self): |
| | | fut = self._makeOne('y').pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'y': 'foo'}})) |
| | | |
| | | def test_pregenerate_query_is_already_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': [('a', 'b')]}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | def test_pregenerate_query_is_tuple_of_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': (('a', 'b'),)}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | class DummyContext: |
| | | pass |
| | | |
| | |
| | | def test_add_response_callback(self): |
| | | request = self._makeOne() |
| | | request.add_response_callback(1) |
| | | self.assertEqual(request.response_callbacks, [1]) |
| | | self.assertEqual(list(request.response_callbacks), [1]) |
| | | |
| | | def test_registry_is_config_registry_when_setup_is_called_after_ctor(self): |
| | | # see https://github.com/Pylons/pyramid/issues/165 |
| | |
| | | registry = Registry('this_test') |
| | | class ResponseFactory(object): |
| | | pass |
| | | registry.registerUtility(ResponseFactory, IResponseFactory) |
| | | registry.registerUtility( |
| | | lambda r: ResponseFactory(), IResponseFactory |
| | | ) |
| | | request = self._makeOne() |
| | | request.registry = registry |
| | | resp = request.response |
| | |
| | | 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' |
| | |
| | | import unittest |
| | | 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() |
| | | |
| | | class Foo(cls): |
| | | pass |
| | | return Foo() |
| | |
| | | foo.set_property(lambda _: 2, name='x', reify=True) |
| | | self.assertEqual(1, foo.x) |
| | | |
| | | def test__make_property(self): |
| | | from pyramid.decorator import reify |
| | | cls = self._getTargetClass() |
| | | name, fn = cls._make_property(lambda x: 1, name='x', reify=True) |
| | | self.assertEqual(name, 'x') |
| | | self.assertTrue(isinstance(fn, reify)) |
| | | |
| | | def test__set_properties_with_iterable(self): |
| | | foo = self._makeOne() |
| | | x = foo._make_property(lambda _: 1, name='x', reify=True) |
| | | y = foo._make_property(lambda _: 2, name='y') |
| | | foo._set_properties([x, y]) |
| | | self.assertEqual(1, foo.x) |
| | | self.assertEqual(2, foo.y) |
| | | |
| | | def test__set_properties_with_dict(self): |
| | | foo = self._makeOne() |
| | | x_name, x_fn = foo._make_property(lambda _: 1, name='x', reify=True) |
| | | y_name, y_fn = foo._make_property(lambda _: 2, name='y') |
| | | foo._set_properties({x_name: x_fn, y_name: y_fn}) |
| | | self.assertEqual(1, foo.x) |
| | | self.assertEqual(2, foo.y) |
| | | |
| | | def test__set_extensions(self): |
| | | inst = self._makeOne() |
| | | def foo(self, result): |
| | | return result |
| | | n, bar = inst._make_property(lambda _: 'bar', name='bar') |
| | | class Extensions(object): |
| | | def __init__(self): |
| | | self.methods = {'foo':foo} |
| | | self.descriptors = {'bar':bar} |
| | | extensions = Extensions() |
| | | inst._set_extensions(extensions) |
| | | self.assertEqual(inst.bar, 'bar') |
| | | self.assertEqual(inst.foo('abc'), 'abc') |
| | | |
| | | class Test_WeakOrderedSet(unittest.TestCase): |
| | | def _makeOne(self): |
| | | from pyramid.config import WeakOrderedSet |
| | |
| | | self.assertEqual(list(wos), []) |
| | | self.assertEqual(wos.last, None) |
| | | |
| | | class Test_strings_differ(unittest.TestCase): |
| | | def _callFUT(self, *args, **kw): |
| | | from pyramid.util import strings_differ |
| | | return strings_differ(*args, **kw) |
| | | |
| | | def test_it(self): |
| | | self.assertFalse(self._callFUT(b'foo', b'foo')) |
| | | self.assertTrue(self._callFUT(b'123', b'345')) |
| | | self.assertTrue(self._callFUT(b'1234', b'123')) |
| | | self.assertTrue(self._callFUT(b'123', b'1234')) |
| | | |
| | | def test_it_with_internal_comparator(self): |
| | | result = self._callFUT(b'foo', b'foo', compare_digest=None) |
| | | self.assertFalse(result) |
| | | |
| | | result = self._callFUT(b'123', b'abc', compare_digest=None) |
| | | self.assertTrue(result) |
| | | |
| | | def test_it_with_external_comparator(self): |
| | | class DummyComparator(object): |
| | | called = False |
| | | def __init__(self, ret_val): |
| | | self.ret_val = ret_val |
| | | |
| | | def __call__(self, a, b): |
| | | self.called = True |
| | | return self.ret_val |
| | | |
| | | dummy_compare = DummyComparator(True) |
| | | result = self._callFUT(b'foo', b'foo', compare_digest=dummy_compare) |
| | | self.assertTrue(dummy_compare.called) |
| | | self.assertFalse(result) |
| | | |
| | | dummy_compare = DummyComparator(False) |
| | | result = self._callFUT(b'123', b'345', compare_digest=dummy_compare) |
| | | self.assertTrue(dummy_compare.called) |
| | | self.assertTrue(result) |
| | | |
| | | dummy_compare = DummyComparator(False) |
| | | result = self._callFUT(b'abc', b'abc', compare_digest=dummy_compare) |
| | | self.assertTrue(dummy_compare.called) |
| | | self.assertTrue(result) |
| | | |
| | | class Test_object_description(unittest.TestCase): |
| | | def _callFUT(self, object): |
| | | from pyramid.util import object_description |
| | |
| | | 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): |
| | |
| | | self.assertEqual( |
| | | self._callFUT(inst), |
| | | "object %s" % str(inst)) |
| | | |
| | | |
| | | def test_shortened_repr(self): |
| | | inst = ['1'] * 1000 |
| | | self.assertEqual( |
| | |
| | | def _getTargetClass(self): |
| | | from pyramid.util import ActionInfo |
| | | return ActionInfo |
| | | |
| | | |
| | | def _makeOne(self, filename, lineno, function, linerepr): |
| | | return self._getTargetClass()(filename, lineno, function, linerepr) |
| | | |
| | |
| | | "Line 0 of file filename:\n linerepr ") |
| | | |
| | | |
| | | class TestCallableName(unittest.TestCase): |
| | | def test_valid_ascii(self): |
| | | from pyramid.util import get_callable_name |
| | | from pyramid.compat import text_, PY3 |
| | | |
| | | if PY3: # pragma: nocover |
| | | name = b'hello world' |
| | | else: # pragma: nocover |
| | | name = text_(b'hello world', 'utf-8') |
| | | |
| | | self.assertEqual(get_callable_name(name), 'hello world') |
| | | |
| | | def test_invalid_ascii(self): |
| | | from pyramid.util import get_callable_name |
| | | from pyramid.compat import text_, PY3 |
| | | from pyramid.exceptions import ConfigurationError |
| | | |
| | | def get_bad_name(): |
| | | if PY3: # pragma: nocover |
| | | name = b'La Pe\xc3\xb1a' |
| | | else: # pragma: nocover |
| | | name = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | |
| | | get_callable_name(name) |
| | | |
| | | self.assertRaises(ConfigurationError, get_bad_name) |
| | | |
| | | |
| | | def dummyfunc(): pass |
| | | |
| | | |
| | | class Dummy(object): |
| | | pass |
| | |
| | | """ |
| | | |
| | | |
| | | 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 |
| | |
| | | named portion in the generated URL. For example, if you pass |
| | | ``_host='foo.com'``, and the URL that would have been generated |
| | | without the host replacement is ``http://example.com/a``, the result |
| | | will be ``https://foo.com/a``. |
| | | will be ``http://foo.com/a``. |
| | | |
| | | Note that if ``_scheme`` is passed as ``https``, and ``_port`` is not |
| | | passed, the ``_port`` value is assumed to have been passed as |
| | |
| | | portion in the generated URL. For example, if you pass |
| | | ``host='foo.com'``, and the URL that would have been generated |
| | | without the host replacement is ``http://example.com/a``, the result |
| | | will be ``https://foo.com/a``. |
| | | will be ``http://foo.com/a``. |
| | | |
| | | If ``scheme`` is passed as ``https``, and an explicit ``port`` is not |
| | | passed, the ``port`` value is assumed to have been passed as ``443``. |
| | |
| | | class RoutesMapper(object): |
| | | def __init__(self): |
| | | self.routelist = [] |
| | | self.static_routes = [] |
| | | |
| | | self.routes = {} |
| | | |
| | | def has_routes(self): |
| | | return bool(self.routelist) |
| | | |
| | | def get_routes(self): |
| | | def get_routes(self, include_static=False): |
| | | if include_static is True: |
| | | return self.routelist + self.static_routes |
| | | |
| | | return self.routelist |
| | | |
| | | def get_route(self, name): |
| | |
| | | oldroute = self.routes[name] |
| | | if oldroute in self.routelist: |
| | | self.routelist.remove(oldroute) |
| | | |
| | | route = Route(name, pattern, factory, predicates, pregenerator) |
| | | if not static: |
| | | self.routelist.append(route) |
| | | else: |
| | | self.static_routes.append(route) |
| | | |
| | | self.routes[name] = route |
| | | return route |
| | | |
| | |
| | | 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') |
| | |
| | | import functools |
| | | try: |
| | | # py2.7.7+ and py3.3+ have native comparison support |
| | | from hmac import compare_digest |
| | | except ImportError: # pragma: nocover |
| | | compare_digest = None |
| | | import inspect |
| | | import traceback |
| | | import weakref |
| | |
| | | string_types, |
| | | text_, |
| | | PY3, |
| | | native_ |
| | | ) |
| | | |
| | | from pyramid.interfaces import IActionInfo |
| | |
| | | |
| | | _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. |
| | |
| | | raise ValueError('cannot reify a property') |
| | | elif name is not None: |
| | | fn = lambda this: callable(this) |
| | | fn.__name__ = name |
| | | fn.__name__ = get_callable_name(name) |
| | | fn.__doc__ = callable.__doc__ |
| | | else: |
| | | name = callable.__name__ |
| | |
| | | |
| | | 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) |
| | | self._set_properties(extensions.descriptors) |
| | | @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]) |
| | | |
| | | 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. |
| | |
| | | oid = self._order[-1] |
| | | return self._items[oid]() |
| | | |
| | | def strings_differ(string1, string2): |
| | | def strings_differ(string1, string2, compare_digest=compare_digest): |
| | | """Check whether two strings differ while avoiding timing attacks. |
| | | |
| | | This function returns True if the given strings differ and False |
| | |
| | | |
| | | http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf |
| | | |
| | | .. versionchanged:: 1.6 |
| | | Support :func:`hmac.compare_digest` if it is available (Python 2.7.7+ |
| | | and Python 3.3+). |
| | | |
| | | """ |
| | | if len(string1) != len(string2): |
| | | return True |
| | | len_eq = len(string1) == len(string2) |
| | | if len_eq: |
| | | invalid_bits = 0 |
| | | left = string1 |
| | | else: |
| | | invalid_bits = 1 |
| | | left = string2 |
| | | right = string2 |
| | | |
| | | invalid_bits = 0 |
| | | for a, b in zip(string1, string2): |
| | | invalid_bits += a != b |
| | | |
| | | if compare_digest is not None: |
| | | invalid_bits += not compare_digest(left, right) |
| | | else: |
| | | for a, b in zip(left, right): |
| | | invalid_bits += a != b |
| | | return invalid_bits != 0 |
| | | |
| | | def object_description(object): |
| | |
| | | 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, ')') |
| | |
| | | wrapper.__docobj__ = wrapped |
| | | return wrapper |
| | | |
| | | |
| | | def get_callable_name(name): |
| | | """ |
| | | Verifies that the ``name`` is ascii and will raise a ``ConfigurationError`` |
| | | if it is not. |
| | | """ |
| | | try: |
| | | return native_(name, 'ascii') |
| | | except (UnicodeEncodeError, UnicodeDecodeError): |
| | | msg = ( |
| | | '`name="%s"` is invalid. `name` must be ascii because it is ' |
| | | 'used on __name__ of the method' |
| | | ) |
| | | raise ConfigurationError(msg % name) |
| | |
| | | .. deprecated:: 1.3 |
| | | |
| | | """ |
| | | def __init__(self, notfound_view=None): |
| | | def __init__(self, notfound_view=None, redirect_class=HTTPFound): |
| | | if notfound_view is None: |
| | | notfound_view = default_exceptionresponse_view |
| | | self.notfound_view = notfound_view |
| | | self.redirect_class = redirect_class |
| | | |
| | | def __call__(self, context, request): |
| | | path = decode_path_info(request.environ['PATH_INFO'] or '/') |
| | |
| | | qs = request.query_string |
| | | if qs: |
| | | qs = '?' + qs |
| | | return HTTPFound(location=request.path+'/'+qs) |
| | | return self.redirect_class(location=request.path+'/'+qs) |
| | | return self.notfound_view(context, request) |
| | | |
| | | append_slash_notfound_view = AppendSlashNotFoundViewFactory() |
| | |
| | | redirect to the URL implied by the route; if it does not, Pyramid will |
| | | return the result of the view callable provided as ``view``, as normal. |
| | | |
| | | If the argument provided as ``append_slash`` is not a boolean but |
| | | instead implements :class:`~pyramid.interfaces.IResponse`, the |
| | | append_slash logic will behave as if ``append_slash=True`` was passed, |
| | | but the provided class will be used as the response class instead of |
| | | the default :class:`~pyramid.httpexceptions.HTTPFound` response class |
| | | when a redirect is performed. For example: |
| | | |
| | | .. code-block:: python |
| | | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPMovedPermanently, |
| | | HTTPNotFound |
| | | ) |
| | | |
| | | @notfound_view_config(append_slash=HTTPMovedPermanently) |
| | | def aview(request): |
| | | return HTTPNotFound('not found') |
| | | |
| | | The above means that a redirect to a slash-appended route will be |
| | | attempted, but instead of :class:`~pyramid.httpexceptions.HTTPFound` |
| | | being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will |
| | | be used` for the redirect response if a slash-appended route is found. |
| | | |
| | | .. versionchanged:: 1.6 |
| | | |
| | | See :ref:`changing_the_notfound_view` for detailed usage information. |
| | | |
| | | """ |
| | |
| | | |
| | | @forbidden_view_config() |
| | | def forbidden(request): |
| | | return Response('You are not allowed', status='401 Unauthorized') |
| | | return Response('You are not allowed', status='403 Forbidden') |
| | | |
| | | All arguments passed to this function have the same meaning as |
| | | :meth:`pyramid.view.view_config` and each predicate argument restricts |
| | |
| | | Sphinx >= 1.2.3 |
| | | repoze.sphinx.autointerface |
| | | repoze.lru |
| | | pylons_sphinx_latesturl |
| | | |
| | |
| | | match=^test |
| | | where=pyramid |
| | | nocapture=1 |
| | | cover-package=pyramid |
| | | cover-erase=1 |
| | | |
| | | [aliases] |
| | | dev = develop easy_install pyramid[testing] |
| | |
| | | tests_require.append('zope.component>=3.11.0') |
| | | |
| | | docs_extras = [ |
| | | 'Sphinx', |
| | | 'Sphinx >= 1.2.3', |
| | | 'docutils', |
| | | 'repoze.sphinx.autointerface', |
| | | ] |
| | |
| | | ] |
| | | |
| | | setup(name='pyramid', |
| | | version='1.6dev', |
| | | version='1.6.dev0', |
| | | description='The Pyramid Web Framework, a Pylons project', |
| | | long_description=README + '\n\n' + CHANGES, |
| | | classifiers=[ |
| | |
| | | [tox] |
| | | envlist = |
| | | py26,py27,py32,py33,py34,pypy,cover |
| | | envlist = |
| | | py26,py27,py32,py33,py34,pypy,pypy3, |
| | | {py2,py3}-docs, |
| | | {py2,py3}-cover,coverage |
| | | |
| | | [testenv] |
| | | commands = |
| | | python setup.py dev |
| | | python setup.py 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 |
| | | 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: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 = |
| | | python setup.py dev |
| | | python setup.py nosetests --with-xunit --with-xcoverage |
| | | coverage erase |
| | | coverage combine |
| | | coverage xml |
| | | coverage report --show-missing --fail-under=100 |
| | | deps = |
| | | nosexcover |
| | | |
| | | # we separate coverage into its own testenv because a) "last run wins" wrt |
| | | # cobertura jenkins reporting and b) pypy and jython can't handle any |
| | | # combination of versions of coverage and nosexcover that i can find. |
| | | |
| | | coverage |
| | | setenv = |
| | | COVERAGE_FILE=.coverage |