Michael Merickel
2018-10-15 bda1306749c62ef4f11cfe567ed7d56c8ad94240
Merge pull request #3388 from mmerickel/black

format source using black
2 files added
181 files modified
20825 ■■■■■ changed files
.flake8 18 ●●●●● patch | view | raw | blame | history
.travis.yml 4 ●●●● patch | view | raw | blame | history
MANIFEST.in 3 ●●●● patch | view | raw | blame | history
pyproject.toml 31 ●●●●● patch | view | raw | blame | history
setup.cfg 53 ●●●●● patch | view | raw | blame | history
setup.py 85 ●●●● patch | view | raw | blame | history
src/pyramid/asset.py 19 ●●●● patch | view | raw | blame | history
src/pyramid/authentication.py 352 ●●●●● patch | view | raw | blame | history
src/pyramid/authorization.py 42 ●●●●● patch | view | raw | blame | history
src/pyramid/compat.py 65 ●●●● patch | view | raw | blame | history
src/pyramid/config/__init__.py 308 ●●●●● patch | view | raw | blame | history
src/pyramid/config/adapters.py 77 ●●●● patch | view | raw | blame | history
src/pyramid/config/assets.py 67 ●●●●● patch | view | raw | blame | history
src/pyramid/config/factories.py 95 ●●●●● patch | view | raw | blame | history
src/pyramid/config/i18n.py 31 ●●●●● patch | view | raw | blame | history
src/pyramid/config/predicates.py 1 ●●●● patch | view | raw | blame | history
src/pyramid/config/rendering.py 31 ●●●●● patch | view | raw | blame | history
src/pyramid/config/routes.py 148 ●●●●● patch | view | raw | blame | history
src/pyramid/config/security.py 110 ●●●●● patch | view | raw | blame | history
src/pyramid/config/settings.py 4 ●●●● patch | view | raw | blame | history
src/pyramid/config/testing.py 58 ●●●●● patch | view | raw | blame | history
src/pyramid/config/tweens.py 47 ●●●● patch | view | raw | blame | history
src/pyramid/config/util.py 41 ●●●●● patch | view | raw | blame | history
src/pyramid/config/views.py 574 ●●●●● patch | view | raw | blame | history
src/pyramid/config/zca.py 4 ●●● patch | view | raw | blame | history
src/pyramid/csrf.py 68 ●●●● patch | view | raw | blame | history
src/pyramid/decorator.py 2 ●●● patch | view | raw | blame | history
src/pyramid/encode.py 7 ●●●● patch | view | raw | blame | history
src/pyramid/events.py 37 ●●●● patch | view | raw | blame | history
src/pyramid/exceptions.py 22 ●●●●● patch | view | raw | blame | history
src/pyramid/httpexceptions.py 336 ●●●● patch | view | raw | blame | history
src/pyramid/i18n.py 77 ●●●●● patch | view | raw | blame | history
src/pyramid/interfaces.py 374 ●●●● patch | view | raw | blame | history
src/pyramid/location.py 5 ●●●●● patch | view | raw | blame | history
src/pyramid/paster.py 7 ●●●● patch | view | raw | blame | history
src/pyramid/path.py 41 ●●●● patch | view | raw | blame | history
src/pyramid/predicates.py 57 ●●●●● patch | view | raw | blame | history
src/pyramid/registry.py 70 ●●●●● patch | view | raw | blame | history
src/pyramid/renderers.py 109 ●●●●● patch | view | raw | blame | history
src/pyramid/request.py 53 ●●●● patch | view | raw | blame | history
src/pyramid/resource.py 9 ●●●●● patch | view | raw | blame | history
src/pyramid/response.py 46 ●●●●● patch | view | raw | blame | history
src/pyramid/router.py 63 ●●●● patch | view | raw | blame | history
src/pyramid/scaffolds/__init__.py 17 ●●●● patch | view | raw | blame | history
src/pyramid/scaffolds/copydir.py 169 ●●●● patch | view | raw | blame | history
src/pyramid/scaffolds/template.py 51 ●●●●● patch | view | raw | blame | history
src/pyramid/scaffolds/tests.py 28 ●●●●● patch | view | raw | blame | history
src/pyramid/scripting.py 26 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/common.py 6 ●●●● patch | view | raw | blame | history
src/pyramid/scripts/pcreate.py 188 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/pdistreport.py 31 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/prequest.py 71 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/proutes.py 132 ●●●● patch | view | raw | blame | history
src/pyramid/scripts/pserve.py 107 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/pshell.py 78 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/ptweens.py 42 ●●●●● patch | view | raw | blame | history
src/pyramid/scripts/pviews.py 76 ●●●● patch | view | raw | blame | history
src/pyramid/security.py 108 ●●●●● patch | view | raw | blame | history
src/pyramid/session.py 99 ●●●● patch | view | raw | blame | history
src/pyramid/settings.py 3 ●●●●● patch | view | raw | blame | history
src/pyramid/static.py 81 ●●●● patch | view | raw | blame | history
src/pyramid/testing.py 145 ●●●●● patch | view | raw | blame | history
src/pyramid/threadlocal.py 12 ●●●● patch | view | raw | blame | history
src/pyramid/traversal.py 158 ●●●●● patch | view | raw | blame | history
src/pyramid/tweens.py 3 ●●●●● patch | view | raw | blame | history
src/pyramid/url.py 93 ●●●● patch | view | raw | blame | history
src/pyramid/urldispatch.py 78 ●●●●● patch | view | raw | blame | history
src/pyramid/util.py 69 ●●●●● patch | view | raw | blame | history
src/pyramid/view.py 109 ●●●●● patch | view | raw | blame | history
src/pyramid/viewderivers.py 175 ●●●●● patch | view | raw | blame | history
src/pyramid/wsgi.py 2 ●●●●● patch | view | raw | blame | history
tests/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/pkgs/ccbugapp/__init__.py 8 ●●●● patch | view | raw | blame | history
tests/pkgs/conflictapp/__init__.py 9 ●●●● patch | view | raw | blame | history
tests/pkgs/conflictapp/included.py 5 ●●●● patch | view | raw | blame | history
tests/pkgs/defpermbugapp/__init__.py 34 ●●●●● patch | view | raw | blame | history
tests/pkgs/eventonly/__init__.py 15 ●●●● patch | view | raw | blame | history
tests/pkgs/exceptionviewapp/__init__.py 56 ●●●●● patch | view | raw | blame | history
tests/pkgs/exceptionviewapp/models.py 6 ●●●● patch | view | raw | blame | history
tests/pkgs/exceptionviewapp/views.py 9 ●●●● patch | view | raw | blame | history
tests/pkgs/fixtureapp/__init__.py 10 ●●●●● patch | view | raw | blame | history
tests/pkgs/fixtureapp/models.py 3 ●●●● patch | view | raw | blame | history
tests/pkgs/fixtureapp/subpackage/__init__.py 2 ●●● patch | view | raw | blame | history
tests/pkgs/fixtureapp/views.py 5 ●●●●● patch | view | raw | blame | history
tests/pkgs/forbiddenapp/__init__.py 36 ●●●●● patch | view | raw | blame | history
tests/pkgs/forbiddenview/__init__.py 12 ●●●●● patch | view | raw | blame | history
tests/pkgs/hybridapp/__init__.py 81 ●●●● patch | view | raw | blame | history
tests/pkgs/hybridapp/views.py 10 ●●●●● patch | view | raw | blame | history
tests/pkgs/includeapp1/root.py 3 ●●●● patch | view | raw | blame | history
tests/pkgs/includeapp1/three.py 7 ●●●●● patch | view | raw | blame | history
tests/pkgs/includeapp1/two.py 4 ●●● patch | view | raw | blame | history
tests/pkgs/notfoundview/__init__.py 9 ●●●● patch | view | raw | blame | history
tests/pkgs/permbugapp/__init__.py 31 ●●●●● patch | view | raw | blame | history
tests/pkgs/rendererscanapp/__init__.py 5 ●●●●● patch | view | raw | blame | history
tests/pkgs/rendererscanapp/two/__init__.py 4 ●●●● patch | view | raw | blame | history
tests/pkgs/restbugapp/__init__.py 32 ●●●●● patch | view | raw | blame | history
tests/pkgs/restbugapp/views.py 6 ●●●●● patch | view | raw | blame | history
tests/pkgs/static_abspath/__init__.py 4 ●●●● patch | view | raw | blame | history
tests/pkgs/static_assetspec/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/pkgs/static_routeprefix/__init__.py 2 ●●● patch | view | raw | blame | history
tests/pkgs/staticpermapp/__init__.py 35 ●●●●● patch | view | raw | blame | history
tests/pkgs/subrequestapp/__init__.py 10 ●●●● patch | view | raw | blame | history
tests/pkgs/viewdecoratorapp/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/pkgs/viewdecoratorapp/views/views.py 11 ●●●● patch | view | raw | blame | history
tests/pkgs/wsgiapp2app/__init__.py 3 ●●●●● patch | view | raw | blame | history
tests/test_asset.py 10 ●●●● patch | view | raw | blame | history
tests/test_authentication.py 792 ●●●●● patch | view | raw | blame | history
tests/test_authorization.py 154 ●●●●● patch | view | raw | blame | history
tests/test_compat.py 12 ●●●● patch | view | raw | blame | history
tests/test_config/__init__.py 27 ●●●● patch | view | raw | blame | history
tests/test_config/path/scanerror/__init__.py 2 ●●●●● patch | view | raw | blame | history
tests/test_config/pkgs/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/asset/__init__.py 2 ●●●●● patch | view | raw | blame | history
tests/test_config/pkgs/asset/subpackage/__init__.py 2 ●●● patch | view | raw | blame | history
tests/test_config/pkgs/scanextrakw/__init__.py 5 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/__init__.py 51 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/another.py 44 ●●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/pod/notinit.py 1 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/subpackage/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/subpackage/notinit.py 1 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py 1 ●●●● patch | view | raw | blame | history
tests/test_config/pkgs/selfscan/__init__.py 3 ●●●●● patch | view | raw | blame | history
tests/test_config/pkgs/selfscan/another.py 2 ●●● patch | view | raw | blame | history
tests/test_config/test_adapters.py 122 ●●●● patch | view | raw | blame | history
tests/test_config/test_assets.py 344 ●●●●● patch | view | raw | blame | history
tests/test_config/test_factories.py 86 ●●●● patch | view | raw | blame | history
tests/test_config/test_i18n.py 129 ●●●●● patch | view | raw | blame | history
tests/test_config/test_init.py 1420 ●●●●● patch | view | raw | blame | history
tests/test_config/test_rendering.py 23 ●●●●● patch | view | raw | blame | history
tests/test_config/test_routes.py 55 ●●●●● patch | view | raw | blame | history
tests/test_config/test_security.py 54 ●●●● patch | view | raw | blame | history
tests/test_config/test_settings.py 319 ●●●●● patch | view | raw | blame | history
tests/test_config/test_testing.py 76 ●●●●● patch | view | raw | blame | history
tests/test_config/test_tweens.py 312 ●●●●● patch | view | raw | blame | history
tests/test_config/test_util.py 275 ●●●●● patch | view | raw | blame | history
tests/test_config/test_views.py 1767 ●●●● patch | view | raw | blame | history
tests/test_csrf.py 52 ●●●●● patch | view | raw | blame | history
tests/test_decorator.py 4 ●●●● patch | view | raw | blame | history
tests/test_docs.py 1 ●●●● patch | view | raw | blame | history
tests/test_encode.py 27 ●●●●● patch | view | raw | blame | history
tests/test_events.py 137 ●●●● patch | view | raw | blame | history
tests/test_exceptions.py 33 ●●●● patch | view | raw | blame | history
tests/test_httpexceptions.py 97 ●●●● patch | view | raw | blame | history
tests/test_i18n.py 135 ●●●●● patch | view | raw | blame | history
tests/test_integration.py 371 ●●●●● patch | view | raw | blame | history
tests/test_location.py 29 ●●●●● patch | view | raw | blame | history
tests/test_paster.py 47 ●●●● patch | view | raw | blame | history
tests/test_path.py 204 ●●●● patch | view | raw | blame | history
tests/test_predicates.py 128 ●●●●● patch | view | raw | blame | history
tests/test_registry.py 185 ●●●●● patch | view | raw | blame | history
tests/test_renderers.py 237 ●●●●● patch | view | raw | blame | history
tests/test_request.py 241 ●●●●● patch | view | raw | blame | history
tests/test_response.py 86 ●●●● patch | view | raw | blame | history
tests/test_router.py 568 ●●●● patch | view | raw | blame | history
tests/test_scaffolds/test_copydir.py 426 ●●●●● patch | view | raw | blame | history
tests/test_scaffolds/test_init.py 7 ●●●●● patch | view | raw | blame | history
tests/test_scaffolds/test_template.py 69 ●●●●● patch | view | raw | blame | history
tests/test_scripting.py 27 ●●●● patch | view | raw | blame | history
tests/test_scripts/dummy.py 48 ●●●● patch | view | raw | blame | history
tests/test_scripts/test_common.py 3 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_pcreate.py 212 ●●●● patch | view | raw | blame | history
tests/test_scripts/test_pdistreport.py 63 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_prequest.py 134 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_proutes.py 267 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_pserve.py 70 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_pshell.py 269 ●●●●● patch | view | raw | blame | history
tests/test_scripts/test_ptweens.py 17 ●●●● patch | view | raw | blame | history
tests/test_scripts/test_pviews.py 272 ●●●●● patch | view | raw | blame | history
tests/test_security.py 136 ●●●● patch | view | raw | blame | history
tests/test_session.py 147 ●●●● patch | view | raw | blame | history
tests/test_settings.py 14 ●●●● patch | view | raw | blame | history
tests/test_static.py 198 ●●●●● patch | view | raw | blame | history
tests/test_testing.py 199 ●●●●● patch | view | raw | blame | history
tests/test_threadlocal.py 18 ●●●● patch | view | raw | blame | history
tests/test_traversal.py 276 ●●●●● patch | view | raw | blame | history
tests/test_tweens.py 22 ●●●●● patch | view | raw | blame | history
tests/test_url.py 793 ●●●●● patch | view | raw | blame | history
tests/test_urldispatch.py 460 ●●●●● patch | view | raw | blame | history
tests/test_util.py 373 ●●●●● patch | view | raw | blame | history
tests/test_view.py 460 ●●●● patch | view | raw | blame | history
tests/test_viewderivers.py 847 ●●●● patch | view | raw | blame | history
tests/test_wsgi.py 27 ●●●●● patch | view | raw | blame | history
tox.ini 18 ●●●● patch | view | raw | blame | history
.flake8
New file
@@ -0,0 +1,18 @@
[flake8]
ignore =
    # E203: whitespace before ':' (black fails to be PEP8 compliant)
    E203
    # E731: do not assign a lambda expression, use a def
    E731
    # W503: line break before binary operator (flake8 is not PEP8 compliant)
    W503
exclude =
    src/pyramid/compat.py
    src/pyramid/scaffolds/alchemy
    src/pyramid/scaffolds/starter
    src/pyramid/scaffolds/zodb
    tests/fixtures
    tests/pkgs
    tests/test_config/pkgs
    tests/test_config/path
show-source = True
.travis.yml
@@ -16,11 +16,11 @@
          env: TOXENV=pypy
        - python: pypy3
          env: TOXENV=pypy3
        - python: 3.5
        - python: 3.6
          env: TOXENV=py2-cover,py3-cover,coverage
        - python: 3.5
          env: TOXENV=docs
        - python: 3.5
        - python: 3.6
          env: TOXENV=lint
        - python: 3.7
          env: TOXENV=py37
MANIFEST.in
@@ -8,7 +8,8 @@
include CONTRIBUTORS.txt LICENSE.txt COPYRIGHT.txt
include contributing.md RELEASING.txt
include .coveragerc tox.ini appveyor.yml .travis.yml rtd.txt
include .coveragerc .flake8 setup.cfg pyproject.toml
include tox.ini appveyor.yml .travis.yml rtd.txt
graft .github
include HACKING.txt hacking-tox.ini
pyproject.toml
New file
@@ -0,0 +1,31 @@
[build-system]
requires = ["setuptools", "wheel"]
[tool.black]
line-length = 79
skip-string-normalization = true
py36 = false
exclude = '''
/(
    \.git
  | \.mypy_cache
  | \.tox
  | \.venv
  | \.pytest_cache
  | dist
  | build
  | docs
  | src/pyramid/scaffolds/alchemy
  | src/pyramid/scaffolds/starter
  | src/pyramid/scaffolds/zodb
)/
'''
 # This next section only exists for people that have their editors
# automatically call isort, black already sorts entries on its own when run.
[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
line_length = 79
setup.cfg
@@ -16,59 +16,6 @@
[metadata]
license_file = LICENSE.txt
[flake8]
ignore =
    # E121: continuation line under-indented for hanging indent
    E121,
    # E123: closing bracket does not match indentation of opening bracket's line
    E123,
    # E125: continuation line with same indent as next logical line
    E125,
    # E127: continuation line over-indented for visual indent
    E127,
    # E128: continuation line under-indented for visual indent
    E128,
    # E129: visually indented line with same indent as next logical line
    E129,
    # E201: whitespace after '('
    E201,
    # E202: whitespace before ')'
    E202,
    # E231: missing whitespace after ',', ';', or ':'
    E231,
    # E261: at least two spaces before inline comment
    E261,
    # E262: inline comment should start with '# '
    E262,
    # E265: block comment should start with '# '
    E265,
    # E266: too many leading '#' for block comment
    E266,
    # E301: expected 1 blank line, found 0
    E301,
    # E302: expected 2 blank lines, found 0
    E302,
    # E303: too many blank lines (3)
    E303,
    # E305: expected 2 blank lines after class or function definition, found 1
    E305,
    # E306: expected 1 blank line before a nested definition, found 0
    E306,
    # E501: line too long (82 > 79 characters)
    E501,
    # E731: do not assign a lambda expression, use a def
    E731,
    # W291: trailing whitespace
    W291,
    # W293: blank line contains whitespace
    W293,
    # W391: blank line at end of file
    W391,
    # W503: line break before binary operator
    W503
exclude = pyramid/tests/,pyramid/compat.py,pyramid/resource.py
show-source = True
[check-manifest]
ignore =
    .gitignore
setup.py
@@ -55,46 +55,47 @@
    'virtualenv',  # for scaffolding tests
]
setup(name='pyramid',
      version='1.10.dev0',
      description='The Pyramid Web Framework, a Pylons project',
      long_description=README + '\n\n' + CHANGES,
      classifiers=[
          "Development Status :: 6 - Mature",
          "Intended Audience :: Developers",
          "Programming Language :: Python",
          "Programming Language :: Python :: 2.7",
          "Programming Language :: Python :: 3",
          "Programming Language :: Python :: 3.4",
          "Programming Language :: Python :: 3.5",
          "Programming Language :: Python :: 3.6",
          "Programming Language :: Python :: 3.7",
          "Programming Language :: Python :: Implementation :: CPython",
          "Programming Language :: Python :: Implementation :: PyPy",
          "Framework :: Pyramid",
          "Topic :: Internet :: WWW/HTTP",
          "Topic :: Internet :: WWW/HTTP :: WSGI",
          "License :: Repoze Public License",
      ],
      keywords='web wsgi pylons pyramid',
      author="Chris McDonough, Agendaless Consulting",
      author_email="pylons-discuss@googlegroups.com",
      url="https://trypyramid.com",
      license="BSD-derived (http://www.repoze.org/LICENSE.txt)",
      packages=find_packages('src', exclude=['tests']),
      package_dir={'': 'src'},
      include_package_data=True,
      zip_safe=False,
      python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
      install_requires=install_requires,
      extras_require={
          ':python_version<"3.2"': ['repoze.lru >= 0.4'],
          'testing': testing_extras,
          'docs': docs_extras,
      },
      tests_require=tests_require,
      test_suite="tests",
      entry_points="""\
setup(
    name='pyramid',
    version='1.10.dev0',
    description='The Pyramid Web Framework, a Pylons project',
    long_description=README + '\n\n' + CHANGES,
    classifiers=[
        "Development Status :: 6 - Mature",
        "Intended Audience :: Developers",
        "Programming Language :: Python",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.4",
        "Programming Language :: Python :: 3.5",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
        "Programming Language :: Python :: Implementation :: CPython",
        "Programming Language :: Python :: Implementation :: PyPy",
        "Framework :: Pyramid",
        "Topic :: Internet :: WWW/HTTP",
        "Topic :: Internet :: WWW/HTTP :: WSGI",
        "License :: Repoze Public License",
    ],
    keywords='web wsgi pylons pyramid',
    author="Chris McDonough, Agendaless Consulting",
    author_email="pylons-discuss@googlegroups.com",
    url="https://trypyramid.com",
    license="BSD-derived (http://www.repoze.org/LICENSE.txt)",
    packages=find_packages('src', exclude=['tests']),
    package_dir={'': 'src'},
    include_package_data=True,
    zip_safe=False,
    python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
    install_requires=install_requires,
    extras_require={
        ':python_version<"3.2"': ['repoze.lru >= 0.4'],
        'testing': testing_extras,
        'docs': docs_extras,
    },
    tests_require=tests_require,
    test_suite="tests",
    entry_points="""\
        [pyramid.scaffold]
        starter=pyramid.scaffolds:StarterProjectTemplate
        zodb=pyramid.scaffolds:ZODBProjectTemplate
@@ -113,5 +114,5 @@
        [paste.server_runner]
        wsgiref = pyramid.scripts.pserve:wsgiref_server_runner
        cherrypy = pyramid.scripts.pserve:cherrypy_server_runner
      """
      )
      """,
)
src/pyramid/asset.py
@@ -3,14 +3,12 @@
from pyramid.compat import string_types
from pyramid.path import (
    package_path,
    package_name,
    )
from pyramid.path import package_path, package_name
def resolve_asset_spec(spec, pname='__main__'):
    if pname and not isinstance(pname, string_types):
        pname = pname.__name__ # as package
        pname = pname.__name__  # as package
    if os.path.isabs(spec):
        return None, spec
    filename = spec
@@ -20,6 +18,7 @@
        pname, filename = None, spec
    return pname, filename
def asset_spec_from_abspath(abspath, package):
    """ Try to convert an absolute path to a resource in a package to
    a resource specification if possible; otherwise return the
@@ -28,10 +27,13 @@
        return abspath
    pp = package_path(package) + os.path.sep
    if abspath.startswith(pp):
        relpath = abspath[len(pp):]
        return '%s:%s' % (package_name(package),
                          relpath.replace(os.path.sep, '/'))
        relpath = abspath[len(pp) :]
        return '%s:%s' % (
            package_name(package),
            relpath.replace(os.path.sep, '/'),
        )
    return abspath
# bw compat only; use pyramid.path.AssetResolver().resolve(spec).abspath()
def abspath_from_asset_spec(spec, pname='__main__'):
@@ -41,4 +43,3 @@
    if pname is None:
        return filename
    return pkg_resources.resource_filename(pname, filename)
src/pyramid/authentication.py
@@ -21,17 +21,11 @@
    bytes_,
    ascii_native_,
    native_,
    )
)
from pyramid.interfaces import (
    IAuthenticationPolicy,
    IDebugLogger,
    )
from pyramid.interfaces import IAuthenticationPolicy, IDebugLogger
from pyramid.security import (
    Authenticated,
    Everyone,
    )
from pyramid.security import Authenticated, Everyone
from pyramid.util import strings_differ
from pyramid.util import SimpleSerializer
@@ -74,36 +68,41 @@
            debug and self._log(
                'call to unauthenticated_userid returned None; returning None',
                'authenticated_userid',
                request)
                request,
            )
            return None
        if self._clean_principal(userid) is None:
            debug and self._log(
                ('use of userid %r is disallowed by any built-in Pyramid '
                 'security policy, returning None' % userid),
                (
                    'use of userid %r is disallowed by any built-in Pyramid '
                    'security policy, returning None' % userid
                ),
                'authenticated_userid',
                request)
                request,
            )
            return None
        if self.callback is None:
            debug and self._log(
                'there was no groupfinder callback; returning %r' % (userid,),
                'authenticated_userid',
                request)
                request,
            )
            return userid
        callback_ok = self.callback(userid, request)
        if callback_ok is not None: # is not None!
        if callback_ok is not None:  # is not None!
            debug and self._log(
                'groupfinder callback returned %r; returning %r' % (
                    callback_ok, userid),
                'groupfinder callback returned %r; returning %r'
                % (callback_ok, userid),
                'authenticated_userid',
                request
                )
                request,
            )
            return userid
        debug and self._log(
            'groupfinder callback returned None; returning None',
            'authenticated_userid',
            request
            )
            request,
        )
    def effective_principals(self, request):
        """ A list of effective principals derived from request.
@@ -134,42 +133,45 @@
        if userid is None:
            debug and self._log(
                'unauthenticated_userid returned %r; returning %r' % (
                    userid, effective_principals),
                'unauthenticated_userid returned %r; returning %r'
                % (userid, effective_principals),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        if self._clean_principal(userid) is None:
            debug and self._log(
                ('unauthenticated_userid returned disallowed %r; returning %r '
                 'as if it was None' % (userid, effective_principals)),
                (
                    'unauthenticated_userid returned disallowed %r; returning '
                    '%r as if it was None' % (userid, effective_principals)
                ),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        if self.callback is None:
            debug and self._log(
                'groupfinder callback is None, so groups is []',
                'effective_principals',
                request)
                request,
            )
            groups = []
        else:
            groups = self.callback(userid, request)
            debug and self._log(
                'groupfinder callback returned %r as groups' % (groups,),
                'effective_principals',
                request)
                request,
            )
        if groups is None: # is None!
        if groups is None:  # is None!
            debug and self._log(
                'returning effective principals: %r' % (
                    effective_principals,),
                'returning effective principals: %r' % (effective_principals,),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        effective_principals.append(Authenticated)
@@ -177,10 +179,9 @@
        effective_principals.extend(groups)
        debug and self._log(
            'returning effective principals: %r' % (
                effective_principals,),
            'returning effective principals: %r' % (effective_principals,),
            'effective_principals',
            request
            request,
        )
        return effective_principals
@@ -241,7 +242,8 @@
            self.debug and self._log(
                'repoze.who identity is None, returning None',
                'authenticated_userid',
                request)
                request,
            )
            return None
        userid = identity['repoze.who.userid']
@@ -250,21 +252,25 @@
            self.debug and self._log(
                'repoze.who.userid is None, returning None' % userid,
                'authenticated_userid',
                request)
                request,
            )
            return None
        if self._clean_principal(userid) is None:
            self.debug and self._log(
                ('use of userid %r is disallowed by any built-in Pyramid '
                 'security policy, returning None' % userid),
                (
                    'use of userid %r is disallowed by any built-in Pyramid '
                    'security policy, returning None' % userid
                ),
                'authenticated_userid',
                request)
                request,
            )
            return None
        if self.callback is None:
            return userid
        if self.callback(identity, request) is not None: # is not None!
        if self.callback(identity, request) is not None:  # is not None!
            return userid
    def unauthenticated_userid(self, request):
@@ -292,11 +298,13 @@
        if identity is None:
            self.debug and self._log(
                ('repoze.who identity was None; returning %r' %
                 effective_principals),
                (
                    'repoze.who identity was None; returning %r'
                    % effective_principals
                ),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        if self.callback is None:
@@ -304,33 +312,39 @@
        else:
            groups = self.callback(identity, request)
        if groups is None: # is None!
        if groups is None:  # is None!
            self.debug and self._log(
                ('security policy groups callback returned None; returning %r' %
                 effective_principals),
                (
                    'security policy groups callback returned None; returning '
                    '%r' % effective_principals
                ),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        userid = identity['repoze.who.userid']
        if userid is None:
            self.debug and self._log(
                ('repoze.who.userid was None; returning %r' %
                 effective_principals),
                (
                    'repoze.who.userid was None; returning %r'
                    % effective_principals
                ),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        if self._clean_principal(userid) is None:
            self.debug and self._log(
                ('unauthenticated_userid returned disallowed %r; returning %r '
                 'as if it was None' % (userid, effective_principals)),
                (
                    'unauthenticated_userid returned disallowed %r; returning '
                    '%r as if it was None' % (userid, effective_principals)
                ),
                'effective_principals',
                request
                )
                request,
            )
            return effective_principals
        effective_principals.append(Authenticated)
@@ -366,6 +380,7 @@
            return []
        identity = self._get_identity(request)
        return identifier.forget(request.environ, identity)
@implementer(IAuthenticationPolicy)
class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
@@ -418,6 +433,7 @@
        forgetting the user. This will be application-specific and can
        be done somewhere else or in a subclass."""
        return []
@implementer(IAuthenticationPolicy)
class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
@@ -580,30 +596,31 @@
    .. versionchanged:: 1.10
       Added the ``samesite`` option and made the default ``'Lax'``.
    Objects of this class implement the interface described by
    :class:`pyramid.interfaces.IAuthenticationPolicy`.
    """
    def __init__(self,
                 secret,
                 callback=None,
                 cookie_name='auth_tkt',
                 secure=False,
                 include_ip=False,
                 timeout=None,
                 reissue_time=None,
                 max_age=None,
                 path="/",
                 http_only=False,
                 wild_domain=True,
                 debug=False,
                 hashalg='sha512',
                 parent_domain=False,
                 domain=None,
                 samesite='Lax',
                 ):
    def __init__(
        self,
        secret,
        callback=None,
        cookie_name='auth_tkt',
        secure=False,
        include_ip=False,
        timeout=None,
        reissue_time=None,
        max_age=None,
        path="/",
        http_only=False,
        wild_domain=True,
        debug=False,
        hashalg='sha512',
        parent_domain=False,
        domain=None,
        samesite='Lax',
    ):
        self.cookie = AuthTktCookieHelper(
            secret,
            cookie_name=cookie_name,
@@ -619,7 +636,7 @@
            parent_domain=parent_domain,
            domain=domain,
            samesite=samesite,
            )
        )
        self.callback = callback
        self.debug = debug
@@ -643,11 +660,14 @@
        """ A list of headers which will delete appropriate cookies."""
        return self.cookie.forget(request)
def b64encode(v):
    return base64.b64encode(bytes_(v)).strip().replace(b'\n', b'')
def b64decode(v):
    return base64.b64decode(bytes_(v))
# this class licensed under the MIT license (stolen from Paste)
class AuthTicket(object):
@@ -670,9 +690,18 @@
    """
    def __init__(self, secret, userid, ip, tokens=(), user_data='',
                 time=None, cookie_name='auth_tkt', secure=False,
                 hashalg='md5'):
    def __init__(
        self,
        secret,
        userid,
        ip,
        tokens=(),
        user_data='',
        time=None,
        cookie_name='auth_tkt',
        secure=False,
        hashalg='md5',
    ):
        self.secret = secret
        self.userid = userid
        self.ip = ip
@@ -688,16 +717,26 @@
    def digest(self):
        return calculate_digest(
            self.ip, self.time, self.secret, self.userid, self.tokens,
            self.user_data, self.hashalg)
            self.ip,
            self.time,
            self.secret,
            self.userid,
            self.tokens,
            self.user_data,
            self.hashalg,
        )
    def cookie_value(self):
        v = '%s%08x%s!' % (self.digest(), int(self.time),
                           url_quote(self.userid))
        v = '%s%08x%s!' % (
            self.digest(),
            int(self.time),
            url_quote(self.userid),
        )
        if self.tokens:
            v += self.tokens + '!'
        v += self.user_data
        return v
# this class licensed under the MIT license (stolen from Paste)
class BadTicket(Exception):
@@ -706,9 +745,11 @@
    determine what the expected digest should have been, expected is set.
    This should not be shown by default, but can be useful for debugging.
    """
    def __init__(self, msg, expected=None):
        self.expected = expected
        Exception.__init__(self, msg)
# this function licensed under the MIT license (stolen from Paste)
def parse_ticket(secret, ticket, ip, hashalg='md5'):
@@ -722,37 +763,41 @@
    digest_size = hashlib.new(hashalg).digest_size * 2
    digest = ticket[:digest_size]
    try:
        timestamp = int(ticket[digest_size:digest_size + 8], 16)
        timestamp = int(ticket[digest_size : digest_size + 8], 16)
    except ValueError as e:
        raise BadTicket('Timestamp is not a hex integer: %s' % e)
    try:
        userid, data = ticket[digest_size + 8:].split('!', 1)
        userid, data = ticket[digest_size + 8 :].split('!', 1)
    except ValueError:
        raise BadTicket('userid is not followed by !')
    userid = url_unquote(userid)
    if '!' in data:
        tokens, user_data = data.split('!', 1)
    else: # pragma: no cover (never generated)
    else:  # pragma: no cover (never generated)
        # @@: Is this the right order?
        tokens = ''
        user_data = data
    expected = calculate_digest(ip, timestamp, secret,
                                userid, tokens, user_data, hashalg)
    expected = calculate_digest(
        ip, timestamp, secret, userid, tokens, user_data, hashalg
    )
    # Avoid timing attacks (see
    # http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf)
    if strings_differ(expected, digest):
        raise BadTicket('Digest signature is not correct',
                        expected=(expected, digest))
        raise BadTicket(
            'Digest signature is not correct', expected=(expected, digest)
        )
    tokens = tokens.split(',')
    return (timestamp, userid, tokens, user_data)
# this function licensed under the MIT license (stolen from Paste)
def calculate_digest(ip, timestamp, secret, userid, tokens, user_data,
                     hashalg='md5'):
def calculate_digest(
    ip, timestamp, secret, userid, tokens, user_data, hashalg='md5'
):
    secret = bytes_(secret, 'utf-8')
    userid = bytes_(userid, 'utf-8')
    tokens = bytes_(tokens, 'utf-8')
@@ -767,23 +812,28 @@
        # encode_ip_timestamp not required, left in for backwards compatibility
        ip_timestamp = encode_ip_timestamp(ip, timestamp)
    hash_obj.update(ip_timestamp + secret + userid + b'\0' +
            tokens + b'\0' + user_data)
    hash_obj.update(
        ip_timestamp + secret + userid + b'\0' + tokens + b'\0' + user_data
    )
    digest = hash_obj.hexdigest()
    hash_obj2 = hashlib.new(hashalg)
    hash_obj2.update(bytes_(digest) + secret)
    return hash_obj2.hexdigest()
# this function licensed under the MIT license (stolen from Paste)
def encode_ip_timestamp(ip, timestamp):
    ip_chars = ''.join(map(chr, map(int, ip.split('.'))))
    t = int(timestamp)
    ts = ((t & 0xff000000) >> 24,
          (t & 0xff0000) >> 16,
          (t & 0xff00) >> 8,
          t & 0xff)
    ts = (
        (t & 0xFF000000) >> 24,
        (t & 0xFF0000) >> 16,
        (t & 0xFF00) >> 8,
        t & 0xFF,
    )
    ts_chars = ''.join(map(chr, ts))
    return bytes_(ip_chars + ts_chars)
class AuthTktCookieHelper(object):
    """
@@ -792,41 +842,43 @@
    :class:`pyramid.authentication.AuthTktAuthenticationPolicy` for the
    meanings of the constructor arguments.
    """
    parse_ticket = staticmethod(parse_ticket) # for tests
    AuthTicket = AuthTicket # for tests
    BadTicket = BadTicket # for tests
    now = None # for tests
    parse_ticket = staticmethod(parse_ticket)  # for tests
    AuthTicket = AuthTicket  # for tests
    BadTicket = BadTicket  # for tests
    now = None  # for tests
    userid_type_decoders = {
        'int':int,
        'unicode':lambda x: utf_8_decode(x)[0], # bw compat for old cookies
        'int': int,
        'unicode': lambda x: utf_8_decode(x)[0],  # bw compat for old cookies
        'b64unicode': lambda x: utf_8_decode(b64decode(x))[0],
        'b64str': lambda x: b64decode(x),
        }
    }
    userid_type_encoders = {
        int: ('int', str),
        long: ('int', str),
        text_type: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
        binary_type: ('b64str', lambda x: b64encode(x)),
        }
    }
    def __init__(self,
                 secret,
                 cookie_name='auth_tkt',
                 secure=False,
                 include_ip=False,
                 timeout=None,
                 reissue_time=None,
                 max_age=None,
                 http_only=False,
                 path="/",
                 wild_domain=True,
                 hashalg='md5',
                 parent_domain=False,
                 domain=None,
                 samesite='Lax',
                 ):
    def __init__(
        self,
        secret,
        cookie_name='auth_tkt',
        secure=False,
        include_ip=False,
        timeout=None,
        reissue_time=None,
        max_age=None,
        http_only=False,
        path="/",
        wild_domain=True,
        hashalg='md5',
        parent_domain=False,
        domain=None,
        samesite='Lax',
    ):
        serializer = SimpleSerializer()
@@ -845,7 +897,9 @@
        self.secure = secure
        self.include_ip = include_ip
        self.timeout = timeout if timeout is None else int(timeout)
        self.reissue_time = reissue_time if reissue_time is None else int(reissue_time)
        self.reissue_time = (
            reissue_time if reissue_time is None else int(reissue_time)
        )
        self.max_age = max_age if max_age is None else int(max_age)
        self.wild_domain = wild_domain
        self.parent_domain = parent_domain
@@ -893,16 +947,17 @@
        try:
            timestamp, userid, tokens, user_data = self.parse_ticket(
                self.secret, cookie, remote_addr, self.hashalg)
                self.secret, cookie, remote_addr, self.hashalg
            )
        except self.BadTicket:
            return None
        now = self.now # service tests
        now = self.now  # service tests
        if now is None:
            now = time_mod.time()
        if self.timeout and ( (timestamp + self.timeout) < now ):
        if self.timeout and ((timestamp + self.timeout) < now):
            # the auth_tkt data has expired
            return None
@@ -910,7 +965,7 @@
        user_data_info = user_data.split('|')
        for datum in filter(None, user_data_info):
            if datum.startswith(userid_typename):
                userid_type = datum[len(userid_typename):]
                userid_type = datum[len(userid_typename) :]
                decoder = self.userid_type_decoders.get(userid_type)
                if decoder:
                    userid = decoder(userid)
@@ -918,15 +973,18 @@
        reissue = self.reissue_time is not None
        if reissue and not hasattr(request, '_authtkt_reissued'):
            if ( (now - timestamp) > self.reissue_time ):
            if (now - timestamp) > self.reissue_time:
                # 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)
                headers = self.remember(
                    request, userid, max_age=self.max_age, tokens=tokens
                )
                def reissue_authtkt(request, response):
                    if not hasattr(request, '_authtkt_reissue_revoked'):
                        for k, v in headers:
                            response.headerlist.append((k, v))
                request.add_response_callback(reissue_authtkt)
                request._authtkt_reissued = True
@@ -986,8 +1044,9 @@
                "userid is of type {}, and is not supported by the "
                "AuthTktAuthenticationPolicy. Explicitly converting to string "
                "and storing as base64. Subsequent requests will receive a "
                "string as the userid, it will not be decoded back to the type "
                "provided.".format(type(userid)), RuntimeWarning
                "string as the userid, it will not be decoded back to the "
                "type provided.".format(type(userid)),
                RuntimeWarning,
            )
            encoding, encoder = self.userid_type_encoders.get(text_type)
            userid = str(userid)
@@ -1018,11 +1077,12 @@
            user_data=user_data,
            cookie_name=self.cookie_name,
            secure=self.secure,
            hashalg=self.hashalg
            )
            hashalg=self.hashalg,
        )
        cookie_value = ticket.cookie_value()
        return self._get_cookies(request, cookie_value, max_age)
@implementer(IAuthenticationPolicy)
class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
@@ -1094,8 +1154,8 @@
    ``realm``
       Default: ``"Realm"``.  The Basic Auth Realm string.  Usually displayed to
       the user by the browser in the login dialog.
       Default: ``"Realm"``.  The Basic Auth Realm string.  Usually displayed
       to the user by the browser in the login dialog.
    ``debug``
@@ -1123,6 +1183,7 @@
                return response
            return HTTPForbidden()
    """
    def __init__(self, check, realm='Realm', debug=False):
        self.check = check
        self.realm = realm
@@ -1158,7 +1219,8 @@
HTTPBasicCredentials = namedtuple(
    'HTTPBasicCredentials', ['username', 'password'])
    'HTTPBasicCredentials', ['username', 'password']
)
def extract_http_basic_credentials(request):
@@ -1183,7 +1245,7 @@
    try:
        authbytes = b64decode(auth.strip())
    except (TypeError, binascii.Error): # can't decode
    except (TypeError, binascii.Error):  # can't decode
        return None
    # try utf-8 first, then latin-1; see discussion in
@@ -1195,7 +1257,7 @@
    try:
        username, password = auth.split(':', 1)
    except ValueError: # not enough values to unpack
    except ValueError:  # not enough values to unpack
        return None
    return HTTPBasicCredentials(username, password)
src/pyramid/authorization.py
@@ -6,13 +6,8 @@
from pyramid.compat import is_nonstr_iter
from pyramid.security import (
    ACLAllowed,
    ACLDenied,
    Allow,
    Deny,
    Everyone,
    )
from pyramid.security import ACLAllowed, ACLDenied, Allow, Deny, Everyone
@implementer(IAuthorizationPolicy)
class ACLAuthorizationPolicy(object):
@@ -90,20 +85,19 @@
                        ace_permissions = [ace_permissions]
                    if permission in ace_permissions:
                        if ace_action == Allow:
                            return ACLAllowed(ace, acl, permission,
                                              principals, location)
                            return ACLAllowed(
                                ace, acl, permission, principals, location
                            )
                        else:
                            return ACLDenied(ace, acl, permission,
                                             principals, location)
                            return ACLDenied(
                                ace, acl, permission, principals, location
                            )
        # default deny (if no ACL in lineage at all, or if none of the
        # principals were mentioned in any ACE we found)
        return ACLDenied(
            '<default deny>',
            acl,
            permission,
            principals,
            context)
            '<default deny>', acl, permission, principals, context
        )
    def principals_allowed_by_permission(self, context, permission):
        """ Return the set of principals explicitly granted the
@@ -132,14 +126,14 @@
                    if ace_principal not in denied_here:
                        allowed_here.add(ace_principal)
                if (ace_action == Deny) and (permission in ace_permissions):
                        denied_here.add(ace_principal)
                        if ace_principal == Everyone:
                            # clear the entire allowed set, as we've hit a
                            # deny of Everyone ala (Deny, Everyone, ALL)
                            allowed = set()
                            break
                        elif ace_principal in allowed:
                            allowed.remove(ace_principal)
                    denied_here.add(ace_principal)
                    if ace_principal == Everyone:
                        # clear the entire allowed set, as we've hit a
                        # deny of Everyone ala (Deny, Everyone, ALL)
                        allowed = set()
                        break
                    elif ace_principal in allowed:
                        allowed.remove(ace_principal)
            allowed.update(allowed_here)
src/pyramid/compat.py
@@ -7,8 +7,9 @@
try:  # pragma: no cover
    import __pypy__
    PYPY = True
except:  # pragma: no cover
except BaseException:  # pragma: no cover
    __pypy__ = None
    PYPY = False
@@ -27,19 +28,20 @@
PY3 = sys.version_info[0] == 3
if PY2:
    string_types = basestring,
    string_types = (basestring,)
    integer_types = (int, long)
    class_types = (type, types.ClassType)
    text_type = unicode
    binary_type = str
    long = long
else:
    string_types = str,
    integer_types = int,
    class_types = type,
    string_types = (str,)
    integer_types = (int,)
    class_types = (type,)
    text_type = str
    binary_type = bytes
    long = int
def text_(s, encoding='latin-1', errors='strict'):
    """ If ``s`` is an instance of ``binary_type``, return
@@ -48,6 +50,7 @@
        return s.decode(encoding, errors)
    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``"""
@@ -55,16 +58,22 @@
        return s.encode(encoding, errors)
    return s
if PY2:
    def ascii_native_(s):
        if isinstance(s, text_type):
            s = s.encode('ascii')
        return str(s)
else:
    def ascii_native_(s):
        if isinstance(s, text_type):
            s = s.encode('ascii')
        return str(s, 'ascii', 'strict')
ascii_native_.__doc__ = """
Python 3: If ``s`` is an instance of ``text_type``, return
@@ -76,19 +85,24 @@
if PY2:
    def native_(s, encoding='latin-1', errors='strict'):
        """ If ``s`` is an instance of ``text_type``, return
        ``s.encode(encoding, errors)``, otherwise return ``str(s)``"""
        if isinstance(s, text_type):
            return s.encode(encoding, errors)
        return str(s)
else:
    def native_(s, encoding='latin-1', errors='strict'):
        """ If ``s`` is an instance of ``text_type``, return
        ``s``, otherwise return ``str(s, encoding, errors)``"""
        if isinstance(s, text_type):
            return s
        return str(s, encoding, errors)
native_.__doc__ = """
Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
@@ -106,25 +120,34 @@
    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
    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
    def url_unquote_native(
        v, encoding='utf-8', errors='replace'
    ):  # pragma: no cover
        return native_(url_unquote_text(v, encoding, errors))
else:
    from urllib import parse
    urlparse = parse
    from urllib.parse import quote as url_quote
    from urllib.parse import quote_plus as url_quote_plus
    from urllib.parse import unquote as url_unquote
    from urllib.parse import urlencode as url_encode
    from urllib.request import urlopen as url_open
    url_unquote_text = url_unquote
    url_unquote_native = url_unquote
if PY2:  # pragma: no cover
    def exec_(code, globs=None, locs=None):
        """Execute code in a namespace."""
        if globs is None:
@@ -137,12 +160,15 @@
            locs = globs
        exec("""exec code in globs, locs""")
    exec_("""def reraise(tp, value, tb=None):
    exec_(
        """def reraise(tp, value, tb=None):
    raise tp, value, tb
""")
"""
    )
else:  # pragma: no cover
    import builtins
    exec_ = getattr(builtins, "exec")
    def reraise(tp, value, tb=None):
@@ -156,6 +182,7 @@
if PY2:  # pragma: no cover
    def iteritems_(d):
        return d.iteritems()
@@ -164,7 +191,10 @@
    def iterkeys_(d):
        return d.iterkeys()
else:  # pragma: no cover
    def iteritems_(d):
        return d.items()
@@ -178,17 +208,24 @@
if PY2:
    map_ = map
else:
    def map_(*arg):
        return list(map(*arg))
if PY2:
    def is_nonstr_iter(v):
        return hasattr(v, '__iter__')
else:
    def is_nonstr_iter(v):
        if isinstance(v, str):
            return False
        return hasattr(v, '__iter__')
if PY2:
    im_func = 'im_func'
@@ -227,21 +264,27 @@
import json
if PY2:
    def decode_path_info(path):
        return path.decode('utf-8')
else:
    # see PEP 3333 for why we encode WSGI PATH_INFO to latin-1 before
    # decoding it to utf-8
    def decode_path_info(path):
        return path.encode('latin-1').decode('utf-8')
if PY2:
    from urlparse import unquote as unquote_to_bytes
    def unquote_bytes_to_wsgi(bytestring):
        return unquote_to_bytes(bytestring)
else:
    # see PEP 3333 for why we decode the path to latin-1
    # 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):
@@ -250,6 +293,7 @@
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 PY2:
@@ -262,6 +306,7 @@
else:
    from itertools import zip_longest
def is_unbound_method(fn):
    """
    This consistently verifies that the callable is bound to a
src/pyramid/config/__init__.py
@@ -17,17 +17,13 @@
    PHASE1_CONFIG,
    PHASE2_CONFIG,
    PHASE3_CONFIG,
    )
)
from pyramid.asset import resolve_asset_spec
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import (
    text_,
    reraise,
    string_types,
    )
from pyramid.compat import text_, reraise, string_types
from pyramid.events import ApplicationCreated
@@ -35,21 +31,13 @@
    ConfigurationConflictError,
    ConfigurationError,
    ConfigurationExecutionError,
    )
)
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.path import (
    caller_package,
    package_of,
    )
from pyramid.path import caller_package, package_of
from pyramid.registry import (
    Introspectable,
    Introspector,
    Registry,
    undefer,
    )
from pyramid.registry import Introspectable, Introspector, Registry, undefer
from pyramid.router import Router
@@ -57,17 +45,9 @@
from pyramid.threadlocal import manager
from pyramid.util import (
    WeakOrderedSet,
    object_description,
    )
from pyramid.util import WeakOrderedSet, object_description
from pyramid.config.util import (
    ActionInfo,
    PredicateList,
    action_method,
    not_,
)
from pyramid.config.util import ActionInfo, PredicateList, action_method, not_
from pyramid.config.adapters import AdaptersConfiguratorMixin
from pyramid.config.assets import AssetsConfiguratorMixin
@@ -94,6 +74,7 @@
PHASE2_CONFIG = PHASE2_CONFIG  # api
PHASE3_CONFIG = PHASE3_CONFIG  # api
class Configurator(
    TestingConfiguratorMixin,
    TweensConfiguratorMixin,
@@ -107,7 +88,7 @@
    SettingsConfiguratorMixin,
    FactoriesConfiguratorMixin,
    AdaptersConfiguratorMixin,
    ):
):
    """
    A Configurator is used to configure a :app:`Pyramid`
    :term:`application registry`.
@@ -284,8 +265,9 @@
       ``with``-statement to make threadlocal configuration available for
       further configuration with an implicit commit.
    """
    manager = manager # for testing injection
    venusian = venusian # for testing injection
    manager = manager  # for testing injection
    venusian = venusian  # for testing injection
    _ainfo = None
    basepath = None
    includepath = ()
@@ -294,27 +276,28 @@
    introspectable = Introspectable
    inspect = inspect
    def __init__(self,
                 registry=None,
                 package=None,
                 settings=None,
                 root_factory=None,
                 authentication_policy=None,
                 authorization_policy=None,
                 renderers=None,
                 debug_logger=None,
                 locale_negotiator=None,
                 request_factory=None,
                 response_factory=None,
                 default_permission=None,
                 session_factory=None,
                 default_view_mapper=None,
                 autocommit=False,
                 exceptionresponse_view=default_exceptionresponse_view,
                 route_prefix=None,
                 introspection=True,
                 root_package=None,
                 ):
    def __init__(
        self,
        registry=None,
        package=None,
        settings=None,
        root_factory=None,
        authentication_policy=None,
        authorization_policy=None,
        renderers=None,
        debug_logger=None,
        locale_negotiator=None,
        request_factory=None,
        response_factory=None,
        default_permission=None,
        session_factory=None,
        default_view_mapper=None,
        autocommit=False,
        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:
@@ -345,23 +328,24 @@
                session_factory=session_factory,
                default_view_mapper=default_view_mapper,
                exceptionresponse_view=exceptionresponse_view,
                )
            )
    def setup_registry(self,
                       settings=None,
                       root_factory=None,
                       authentication_policy=None,
                       authorization_policy=None,
                       renderers=None,
                       debug_logger=None,
                       locale_negotiator=None,
                       request_factory=None,
                       response_factory=None,
                       default_permission=None,
                       session_factory=None,
                       default_view_mapper=None,
                       exceptionresponse_view=default_exceptionresponse_view,
                       ):
    def setup_registry(
        self,
        settings=None,
        root_factory=None,
        authentication_policy=None,
        authorization_policy=None,
        renderers=None,
        debug_logger=None,
        locale_negotiator=None,
        request_factory=None,
        response_factory=None,
        default_permission=None,
        session_factory=None,
        default_view_mapper=None,
        exceptionresponse_view=default_exceptionresponse_view,
    ):
        """ When you pass a non-``None`` ``registry`` argument to the
        :term:`Configurator` constructor, no initial setup is performed
        against the registry.  This is because the registry you pass in may
@@ -404,12 +388,15 @@
        if exceptionresponse_view is not None:
            exceptionresponse_view = self.maybe_dotted(exceptionresponse_view)
            self.add_view(exceptionresponse_view, context=IExceptionResponse)
            self.add_view(exceptionresponse_view,context=WebobWSGIHTTPException)
            self.add_view(
                exceptionresponse_view, context=WebobWSGIHTTPException
            )
        # commit below because:
        #
        # - the default exceptionresponse_view requires the superdefault view
        #   mapper, so we need to configure it before adding default_view_mapper
        #   mapper, so we need to configure it before adding
        #   default_view_mapper
        #
        # - superdefault renderers should be overrideable without requiring
        #   the user to commit before calling config.add_renderer
@@ -424,7 +411,7 @@
        # automatic conflict resolution.
        if authentication_policy and not authorization_policy:
            authorization_policy = ACLAuthorizationPolicy() # default
            authorization_policy = ACLAuthorizationPolicy()  # default
        if authorization_policy:
            self.set_authorization_policy(authorization_policy)
@@ -468,7 +455,7 @@
    def _make_spec(self, path_or_spec):
        package, filename = resolve_asset_spec(path_or_spec, self.package_name)
        if package is None:
            return filename # absolute filename
            return filename  # absolute filename
        return '%s:%s' % (package, filename)
    def _fix_registry(self):
@@ -480,38 +467,55 @@
        _registry = self.registry
        if not hasattr(_registry, 'notify'):
            def notify(*events):
                [ _ for _ in _registry.subscribers(events, None) ]
                [_ for _ in _registry.subscribers(events, None)]
            _registry.notify = notify
        if not hasattr(_registry, 'has_listeners'):
            _registry.has_listeners = True
        if not hasattr(_registry, 'queryAdapterOrSelf'):
            def queryAdapterOrSelf(object, interface, default=None):
                if not interface.providedBy(object):
                    return _registry.queryAdapter(object, interface,
                                                  default=default)
                    return _registry.queryAdapter(
                        object, interface, default=default
                    )
                return object
            _registry.queryAdapterOrSelf = queryAdapterOrSelf
        if not hasattr(_registry, 'registerSelfAdapter'):
            def registerSelfAdapter(required=None, provided=None,
                                    name=empty, info=empty, event=True):
                return _registry.registerAdapter(lambda x: x,
                                                 required=required,
                                                 provided=provided, name=name,
                                                 info=info, event=event)
            def registerSelfAdapter(
                required=None,
                provided=None,
                name=empty,
                info=empty,
                event=True,
            ):
                return _registry.registerAdapter(
                    lambda x: x,
                    required=required,
                    provided=provided,
                    name=name,
                    info=info,
                    event=event,
                )
            _registry.registerSelfAdapter = registerSelfAdapter
        if not hasattr(_registry, '_lock'):
            _registry._lock = threading.Lock()
        if not hasattr(_registry, '_clear_view_lookup_cache'):
            def _clear_view_lookup_cache():
                _registry._view_lookup_cache = {}
            _registry._clear_view_lookup_cache = _clear_view_lookup_cache
            _registry._clear_view_lookup_cache = _clear_view_lookup_cache
    # API
@@ -530,7 +534,7 @@
    introspector = property(
        _get_introspector, _set_introspector, _del_introspector
        )
    )
    def get_predlist(self, name):
        predlist = self.registry.queryUtility(IPredicateList, name=name)
@@ -539,30 +543,41 @@
            self.registry.registerUtility(predlist, IPredicateList, name=name)
        return predlist
    def _add_predicate(self, type, name, factory, weighs_more_than=None,
                       weighs_less_than=None):
    def _add_predicate(
        self, type, name, factory, weighs_more_than=None, weighs_less_than=None
    ):
        factory = self.maybe_dotted(factory)
        discriminator = ('%s option' % type, name)
        intr = self.introspectable(
            '%s predicates' % type,
            discriminator,
            '%s predicate named %s' % (type, name),
            '%s predicate' % type)
            '%s predicate' % type,
        )
        intr['name'] = name
        intr['factory'] = factory
        intr['weighs_more_than'] = weighs_more_than
        intr['weighs_less_than'] = weighs_less_than
        def register():
            predlist = self.get_predlist(type)
            predlist.add(name, factory, weighs_more_than=weighs_more_than,
                         weighs_less_than=weighs_less_than)
        self.action(discriminator, register, introspectables=(intr,),
                    order=PHASE1_CONFIG) # must be registered early
            predlist.add(
                name,
                factory,
                weighs_more_than=weighs_more_than,
                weighs_less_than=weighs_less_than,
            )
        self.action(
            discriminator,
            register,
            introspectables=(intr,),
            order=PHASE1_CONFIG,
        )  # must be registered early
    @property
    def action_info(self):
        info = self.info # usually a ZCML action (ParserInfo) if self.info
        info = self.info  # usually a ZCML action (ParserInfo) if self.info
        if not info:
            # Try to provide more accurate info for conflict reports
            if self._ainfo:
@@ -571,8 +586,16 @@
                info = ActionInfo(None, 0, '', '')
        return info
    def action(self, discriminator, callable=None, args=(), kw=None, order=0,
               introspectables=(), **extra):
    def action(
        self,
        discriminator,
        callable=None,
        args=(),
        kw=None,
        order=0,
        introspectables=(),
        **extra
    ):
        """ Register an action which will be executed when
        :meth:`pyramid.config.Configurator.commit` is called (or executed
        immediately if ``autocommit`` is ``True``).
@@ -607,7 +630,7 @@
        """
        # catch nonhashable discriminators here; most unit tests use
        # autocommit=False, which won't catch unhashable discriminators
        assert hash(discriminator)
        assert hash(discriminator)
        if kw is None:
            kw = {}
@@ -645,8 +668,8 @@
                    info=action_info,
                    includepath=self.includepath,
                    introspectables=introspectables,
                    )
                )
            )
            self.action_state.action(**action)
    def _get_action_state(self):
@@ -663,7 +686,7 @@
    action_state = property(_get_action_state, _set_action_state)
    _ctx = action_state # bw compat
    _ctx = action_state  # bw compat
    def commit(self):
        """
@@ -687,7 +710,7 @@
            self.action_state.execute_actions(introspector=self.introspector)
        finally:
            self.end()
        self.action_state = ActionState() # old actions have been processed
        self.action_state = ActionState()  # old actions have been processed
    def include(self, callable, route_prefix=None):
        """Include a configuration callable, to support imperative
@@ -800,17 +823,18 @@
                c = getattr(module, 'includeme')
            except AttributeError:
                raise ConfigurationError(
                    "module %r has no attribute 'includeme'" % (module.__name__)
                    )
                    "module %r has no attribute 'includeme'"
                    % (module.__name__)
                )
        spec = module.__name__ + ':' + c.__name__
        sourcefile = self.inspect.getsourcefile(c)
        if sourcefile is None:
            raise ConfigurationError(
                'No source file for module %r (.py file must exist, '
                'refusing to use orphan .pyc or .pyo file).' % module.__name__)
                'refusing to use orphan .pyc or .pyo file).' % module.__name__
            )
        if action_state.processSpec(spec):
            with self.route_prefix_context(route_prefix):
@@ -820,7 +844,7 @@
                    root_package=self.root_package,
                    autocommit=self.autocommit,
                    route_prefix=self.route_prefix,
                    )
                )
                configurator.basepath = os.path.dirname(sourcefile)
                configurator.includepath = self.includepath + (spec,)
@@ -886,7 +910,7 @@
            autocommit=self.autocommit,
            route_prefix=self.route_prefix,
            introspection=self.introspection,
            )
        )
        configurator.basepath = self.basepath
        configurator.includepath = self.includepath
        configurator.info = self.info
@@ -914,7 +938,7 @@
            return relative_spec
        return self._make_spec(relative_spec)
    absolute_resource_spec = absolute_asset_spec # b/w compat forever
    absolute_resource_spec = absolute_asset_spec  # b/w compat forever
    def begin(self, request=_marker):
        """ Indicate that application or test configuration has begun.
@@ -940,7 +964,7 @@
                request = current['request']
            else:
                request = None
        self.manager.push({'registry':self.registry, 'request':request})
        self.manager.push({'registry': self.registry, 'request': request})
    def end(self):
        """ Indicate that application or test configuration has ended.
@@ -961,8 +985,9 @@
            self.commit()
    # this is *not* an action method (uses caller_package)
    def scan(self, package=None, categories=None, onerror=None, ignore=None,
             **kw):
    def scan(
        self, package=None, categories=None, onerror=None, ignore=None, **kw
    ):
        """Scan a Python package and any of its subpackages for objects
        marked with :term:`configuration decoration` such as
        :class:`pyramid.view.view_config`.  Any decorated object found will
@@ -1021,7 +1046,7 @@
        """
        package = self.maybe_dotted(package)
        if package is None: # pragma: no cover
        if package is None:  # pragma: no cover
            package = caller_package()
        ctorkw = {'config': self}
@@ -1029,8 +1054,9 @@
        scanner = self.venusian.Scanner(**ctorkw)
        scanner.scan(package, categories=categories, onerror=onerror,
                     ignore=ignore)
        scanner.scan(
            package, categories=categories, onerror=onerror, ignore=ignore
        )
    def make_wsgi_app(self):
        """ Commits any pending configuration statements, sends a
@@ -1079,8 +1105,18 @@
        self._seen_files.add(spec)
        return True
    def action(self, discriminator, callable=None, args=(), kw=None, order=0,
               includepath=(), info=None, introspectables=(), **extra):
    def action(
        self,
        discriminator,
        callable=None,
        args=(),
        kw=None,
        order=0,
        includepath=(),
        info=None,
        introspectables=(),
        **extra
    ):
        """Add an action with the given discriminator, callable and arguments
        """
        if kw is None:
@@ -1096,8 +1132,8 @@
                info=info,
                order=order,
                introspectables=introspectables,
                )
            )
        )
        self.actions.append(action)
    def execute_actions(self, clear=True, introspector=None):
@@ -1179,8 +1215,7 @@
                if self.actions:
                    all_actions.extend(self.actions)
                    action_iter = resolveConflicts(
                        self.actions,
                        state=conflict_state,
                        self.actions, state=conflict_state
                    )
                    self.actions = []
@@ -1203,9 +1238,11 @@
                except Exception:
                    t, v, tb = sys.exc_info()
                    try:
                        reraise(ConfigurationExecutionError,
                                ConfigurationExecutionError(t, v, info),
                                tb)
                        reraise(
                            ConfigurationExecutionError,
                            ConfigurationExecutionError(t, v, info),
                            tb,
                        )
                    finally:
                        del t, v, tb
@@ -1290,9 +1327,12 @@
        # error out if we went backward in order
        if state.min_order is not None and order < state.min_order:
            r = ['Actions were added to order={0} after execution had moved '
                 'on to order={1}. Conflicting actions: '
                 .format(order, state.min_order)]
            r = [
                'Actions were added to order={0} after execution had moved '
                'on to order={1}. Conflicting actions: '.format(
                    order, state.min_order
                )
            ]
            for i, action in actiongroup:
                for line in str(action['info']).rstrip().split('\n'):
                    r.append("  " + line)
@@ -1348,8 +1388,10 @@
                # if the new action conflicts with the resolved action then
                # note the conflict, otherwise drop the action as it's
                # effectively overriden by the previous action
                if (includepath[:len(basepath)] != basepath or
                        includepath == basepath):
                if (
                    includepath[: len(basepath)] != basepath
                    or includepath == basepath
                ):
                    L = conflicts.setdefault(discriminator, [baseinfo])
                    L.append(action['info'])
@@ -1360,8 +1402,10 @@
            for _, action in rest:
                includepath = action['includepath']
                # Test whether path is a prefix of opath
                if (includepath[:len(basepath)] != basepath or  # not a prefix
                        includepath == basepath):
                if (
                    includepath[: len(basepath)] != basepath
                    or includepath == basepath  # not a prefix
                ):
                    L = conflicts.setdefault(discriminator, [baseinfo])
                    L.append(action['info'])
@@ -1389,8 +1433,14 @@
def expand_action_tuple(
    discriminator, callable=None, args=(), kw=None, includepath=(),
    info=None, order=0, introspectables=(),
    discriminator,
    callable=None,
    args=(),
    kw=None,
    includepath=(),
    info=None,
    order=0,
    introspectables=(),
):
    if kw is None:
        kw = {}
@@ -1403,7 +1453,7 @@
        info=info,
        order=order,
        introspectables=introspectables,
        )
    )
global_registries = WeakOrderedSet()
src/pyramid/config/adapters.py
@@ -4,11 +4,7 @@
from zope.interface import Interface
from pyramid.interfaces import (
    IResponse,
    ITraverser,
    IResourceURL,
    )
from pyramid.interfaces import IResponse, ITraverser, IResourceURL
from pyramid.util import takes_one_arg
@@ -53,33 +49,33 @@
            predlist = self.get_predlist('subscriber')
            order, preds, phash = predlist.make(self, **predicates)
            derived_predicates = [ self._derive_predicate(p) for p in preds ]
            derived_predicates = [self._derive_predicate(p) for p in preds]
            derived_subscriber = self._derive_subscriber(
                subscriber,
                derived_predicates,
                )
                subscriber, derived_predicates
            )
            intr.update(
                {'phash':phash,
                 'order':order,
                 'predicates':preds,
                 'derived_predicates':derived_predicates,
                 'derived_subscriber':derived_subscriber,
                 }
                )
                {
                    'phash': phash,
                    'order': order,
                    'predicates': preds,
                    'derived_predicates': derived_predicates,
                    'derived_subscriber': derived_subscriber,
                }
            )
            self.registry.registerHandler(derived_subscriber, iface)
        intr = self.introspectable(
            'subscribers',
            id(subscriber),
            self.object_description(subscriber),
            'subscriber'
            )
            'subscriber',
        )
        intr['subscriber'] = subscriber
        intr['interfaces'] = iface
        self.action(None, register, introspectables=(intr,))
        return subscriber
@@ -87,8 +83,10 @@
        derived_predicate = predicate
        if eventonly(predicate):
            def derived_predicate(*arg):
                return predicate(arg[0])
            # seems pointless to try to fix __doc__, __module__, etc as
            # predicate will invariably be an instance
@@ -98,8 +96,10 @@
        derived_subscriber = subscriber
        if eventonly(subscriber):
            def derived_subscriber(*arg):
                return subscriber(arg[0])
            if hasattr(subscriber, '__name__'):
                update_wrapper(derived_subscriber, subscriber)
@@ -132,10 +132,11 @@
            update_wrapper(subscriber_wrapper, subscriber)
        return subscriber_wrapper
    @action_method
    def add_subscriber_predicate(self, name, factory, weighs_more_than=None,
                                 weighs_less_than=None):
    def add_subscriber_predicate(
        self, name, factory, weighs_more_than=None, weighs_less_than=None
    ):
        """
        .. versionadded:: 1.4
@@ -159,8 +160,8 @@
            name,
            factory,
            weighs_more_than=weighs_more_than,
            weighs_less_than=weighs_less_than
            )
            weighs_less_than=weighs_less_than,
        )
    @action_method
    def add_response_adapter(self, adapter, type_or_iface):
@@ -178,18 +179,21 @@
        See :ref:`using_iresponse` for more information."""
        adapter = self.maybe_dotted(adapter)
        type_or_iface = self.maybe_dotted(type_or_iface)
        def register():
            reg = self.registry
            if adapter is None:
                reg.registerSelfAdapter((type_or_iface,), IResponse)
            else:
                reg.registerAdapter(adapter, (type_or_iface,), IResponse)
        discriminator = (IResponse, type_or_iface)
        intr = self.introspectable(
            'response adapters',
            discriminator,
            self.object_description(adapter),
            'response adapter')
            'response adapter',
        )
        intr['adapter'] = adapter
        intr['type'] = type_or_iface
        self.action(discriminator, register, introspectables=(intr,))
@@ -255,17 +259,19 @@
        """
        iface = self.maybe_dotted(iface)
        adapter = self.maybe_dotted(adapter)
        def register(iface=iface):
            if iface is None:
                iface = Interface
            self.registry.registerAdapter(adapter, (iface,), ITraverser)
        discriminator = ('traverser', iface)
        intr = self.introspectable(
            'traversers',
            'traversers',
            discriminator,
            'traverser for %r' % iface,
            'traverser',
            )
        )
        intr['adapter'] = adapter
        intr['iface'] = iface
        self.action(discriminator, register, introspectables=(intr,))
@@ -303,24 +309,25 @@
        """
        adapter = self.maybe_dotted(adapter)
        resource_iface = self.maybe_dotted(resource_iface)
        def register(resource_iface=resource_iface):
            if resource_iface is None:
                resource_iface = Interface
            self.registry.registerAdapter(
                adapter,
                (resource_iface, Interface),
                IResourceURL,
                )
                adapter, (resource_iface, Interface), IResourceURL
            )
        discriminator = ('resource url adapter', resource_iface)
        intr = self.introspectable(
            'resource url adapters',
            'resource url adapters',
            discriminator,
            'resource url adapter for resource iface %r' % resource_iface,
            'resource url adapter',
            )
        )
        intr['adapter'] = adapter
        intr['resource_iface'] = resource_iface
        self.action(discriminator, register, introspectables=(intr,))
def eventonly(callee):
    return takes_one_arg(callee, argname='event')
src/pyramid/config/assets.py
@@ -4,15 +4,13 @@
from zope.interface import implementer
from pyramid.interfaces import (
    IPackageOverrides,
    PHASE1_CONFIG,
)
from pyramid.interfaces import IPackageOverrides, PHASE1_CONFIG
from pyramid.exceptions import ConfigurationError
from pyramid.threadlocal import get_current_registry
from pyramid.config.util import action_method
class OverrideProvider(pkg_resources.DefaultProvider):
    def __init__(self, module):
@@ -35,7 +33,8 @@
            if filename is not None:
                return filename
        return pkg_resources.DefaultProvider.get_resource_filename(
            self, manager, resource_name)
            self, manager, resource_name
        )
    def get_resource_stream(self, manager, resource_name):
        """ Return a readable file-like object for resource_name."""
@@ -45,7 +44,8 @@
            if stream is not None:
                return stream
        return pkg_resources.DefaultProvider.get_resource_stream(
            self, manager, resource_name)
            self, manager, resource_name
        )
    def get_resource_string(self, manager, resource_name):
        """ Return a string containing the contents of resource_name."""
@@ -55,7 +55,8 @@
            if string is not None:
                return string
        return pkg_resources.DefaultProvider.get_resource_string(
            self, manager, resource_name)
            self, manager, resource_name
        )
    def has_resource(self, resource_name):
        overrides = self._get_overrides()
@@ -63,8 +64,7 @@
            result = overrides.has_resource(resource_name)
            if result is not None:
                return result
        return pkg_resources.DefaultProvider.has_resource(
            self, resource_name)
        return pkg_resources.DefaultProvider.has_resource(self, resource_name)
    def resource_isdir(self, resource_name):
        overrides = self._get_overrides()
@@ -73,7 +73,8 @@
            if result is not None:
                return result
        return pkg_resources.DefaultProvider.resource_isdir(
            self, resource_name)
            self, resource_name
        )
    def resource_listdir(self, resource_name):
        overrides = self._get_overrides()
@@ -82,7 +83,8 @@
            if result is not None:
                return result
        return pkg_resources.DefaultProvider.resource_listdir(
            self, resource_name)
            self, resource_name
        )
@implementer(IPackageOverrides)
@@ -193,8 +195,9 @@
    def __call__(self, resource_name):
        if resource_name.startswith(self.path):
            new_path = resource_name[self.pathlen:]
            new_path = resource_name[self.pathlen :]
            return self.source, new_path
class FileOverride:
    def __init__(self, path, source):
@@ -215,6 +218,7 @@
    the empty string, as returned by the ``FileOverride``.
    """
    def __init__(self, package, prefix):
        self.package = package
        if hasattr(package, '__name__'):
@@ -262,6 +266,7 @@
    An asset source relative to a path in the filesystem.
    """
    def __init__(self, prefix):
        self.prefix = prefix
@@ -305,14 +310,16 @@
class AssetsConfiguratorMixin(object):
    def _override(self, package, path, override_source,
                  PackageOverrides=PackageOverrides):
    def _override(
        self, package, path, override_source, PackageOverrides=PackageOverrides
    ):
        pkg_name = 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)
            self.registry.registerUtility(
                override, IPackageOverrides, name=pkg_name
            )
        override.insert(path, override_source)
    @action_method
@@ -331,7 +338,8 @@
        information about asset overrides."""
        if to_override == override_with:
            raise ConfigurationError(
                'You cannot override an asset with itself')
                'You cannot override an asset with itself'
            )
        package = to_override
        path = ''
@@ -346,7 +354,8 @@
            if not os.path.exists(override_with):
                raise ConfigurationError(
                    'Cannot override asset with an absolute path that does '
                    'not exist')
                    'not exist'
                )
            override_isdir = os.path.isdir(override_with)
            override_package = None
            override_prefix = override_with
@@ -360,22 +369,23 @@
            to_package = sys.modules[override_package]
            override_source = PackageAssetSource(to_package, override_prefix)
            override_isdir = (
                override_prefix == '' or
                override_with.endswith('/')
            override_isdir = override_prefix == '' or override_with.endswith(
                '/'
            )
        if overridden_isdir and (not override_isdir):
            raise ConfigurationError(
                'A directory cannot be overridden with a file (put a '
                'slash at the end of override_with if necessary)')
                'slash at the end of override_with if necessary)'
            )
        if (not overridden_isdir) and override_isdir:
            raise ConfigurationError(
                'A file cannot be overridden with a directory (put a '
                'slash at the end of to_override if necessary)')
                'slash at the end of to_override if necessary)'
            )
        override = _override or self._override # test jig
        override = _override or self._override  # test jig
        def register():
            __import__(package)
@@ -387,10 +397,11 @@
            (package, override_package, path, override_prefix),
            '%s -> %s' % (to_override, override_with),
            'asset override',
            )
        )
        intr['to_override'] = to_override
        intr['override_with'] = override_with
        self.action(None, register, introspectables=(intr,),
                    order=PHASE1_CONFIG)
        self.action(
            None, register, introspectables=(intr,), order=PHASE1_CONFIG
        )
    override_resource = override_asset # bw compat
    override_resource = override_asset  # bw compat
src/pyramid/config/factories.py
@@ -8,17 +8,15 @@
    IRequestExtensions,
    IRootFactory,
    ISessionFactory,
    )
)
from pyramid.router import default_execution_policy
from pyramid.traversal import DefaultRootFactory
from pyramid.util import (
    get_callable_name,
    InstancePropertyHelper,
    )
from pyramid.util import get_callable_name, InstancePropertyHelper
from pyramid.config.util import action_method
class FactoriesConfiguratorMixin(object):
    @action_method
@@ -41,10 +39,12 @@
            self.registry.registerUtility(factory, IRootFactory)
            self.registry.registerUtility(factory, IDefaultRootFactory)  # b/c
        intr = self.introspectable('root factories',
                                   None,
                                   self.object_description(factory),
                                   'root factory')
        intr = self.introspectable(
            'root factories',
            None,
            self.object_description(factory),
            'root factory',
        )
        intr['factory'] = factory
        self.action(IRootFactory, register, introspectables=(intr,))
@@ -67,9 +67,13 @@
        def register():
            self.registry.registerUtility(factory, ISessionFactory)
        intr = self.introspectable('session factory', None,
                                   self.object_description(factory),
                                   'session factory')
        intr = self.introspectable(
            'session factory',
            None,
            self.object_description(factory),
            'session factory',
        )
        intr['factory'] = factory
        self.action(ISessionFactory, register, introspectables=(intr,))
@@ -97,9 +101,13 @@
        def register():
            self.registry.registerUtility(factory, IRequestFactory)
        intr = self.introspectable('request factory', None,
                                   self.object_description(factory),
                                   'request factory')
        intr = self.introspectable(
            'request factory',
            None,
            self.object_description(factory),
            'request factory',
        )
        intr['factory'] = factory
        self.action(IRequestFactory, register, introspectables=(intr,))
@@ -122,18 +130,19 @@
        def register():
            self.registry.registerUtility(factory, IResponseFactory)
        intr = self.introspectable('response factory', None,
                                   self.object_description(factory),
                                   'response factory')
        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,
                           callable=None,
                           name=None,
                           property=False,
                           reify=False):
    def add_request_method(
        self, callable=None, name=None, property=False, reify=False
    ):
        """ Add a property or method to the request object.
        When adding a method to the request, ``callable`` may be any
@@ -177,7 +186,8 @@
        property = property or reify
        if property:
            name, callable = InstancePropertyHelper.make_property(
                callable, name=name, reify=reify)
                callable, name=name, reify=reify
            )
        elif name is None:
            name = callable.__name__
        else:
@@ -196,23 +206,31 @@
        if callable is None:
            self.action(('request extensions', name), None)
        elif property:
            intr = self.introspectable('request extensions', name,
                                       self.object_description(callable),
                                       'request property')
            intr = self.introspectable(
                'request extensions',
                name,
                self.object_description(callable),
                'request property',
            )
            intr['callable'] = callable
            intr['property'] = True
            intr['reify'] = reify
            self.action(('request extensions', name), register,
                        introspectables=(intr,))
            self.action(
                ('request extensions', name), register, introspectables=(intr,)
            )
        else:
            intr = self.introspectable('request extensions', name,
                                       self.object_description(callable),
                                       'request method')
            intr = self.introspectable(
                'request extensions',
                name,
                self.object_description(callable),
                'request method',
            )
            intr['callable'] = callable
            intr['property'] = False
            intr['reify'] = False
            self.action(('request extensions', name), register,
                        introspectables=(intr,))
            self.action(
                ('request extensions', name), register, introspectables=(intr,)
            )
    @action_method
    def set_execution_policy(self, policy):
@@ -231,9 +249,12 @@
        def register():
            self.registry.registerUtility(policy, IExecutionPolicy)
        intr = self.introspectable('execution policy', None,
                                   self.object_description(policy),
                                   'execution policy')
        intr = self.introspectable(
            'execution policy',
            None,
            self.object_description(policy),
            'execution policy',
        )
        intr['policy'] = policy
        self.action(IExecutionPolicy, register, introspectables=(intr,))
src/pyramid/config/i18n.py
@@ -1,12 +1,10 @@
from pyramid.interfaces import (
    ILocaleNegotiator,
    ITranslationDirectories,
    )
from pyramid.interfaces import ILocaleNegotiator, ITranslationDirectories
from pyramid.exceptions import ConfigurationError
from pyramid.path import AssetResolver
from pyramid.config.util import action_method
class I18NConfiguratorMixin(object):
    @action_method
@@ -30,11 +28,16 @@
           :class:`pyramid.config.Configurator` constructor can be used to
           achieve the same purpose.
        """
        def register():
            self._set_locale_negotiator(negotiator)
        intr = self.introspectable('locale negotiator', None,
                                   self.object_description(negotiator),
                                   'locale negotiator')
        intr = self.introspectable(
            'locale negotiator',
            None,
            self.object_description(negotiator),
            'locale negotiator',
        )
        intr['negotiator'] = negotiator
        self.action(ILocaleNegotiator, register, introspectables=(intr,))
@@ -97,10 +100,15 @@
                asset = resolver.resolve(spec)
                directory = asset.abspath()
                if not asset.isdir():
                    raise ConfigurationError('"%s" is not a directory' %
                                            directory)
                intr = self.introspectable('translation directories', directory,
                                        spec, 'translation directory')
                    raise ConfigurationError(
                        '"%s" is not a directory' % directory
                    )
                intr = self.introspectable(
                    'translation directories',
                    directory,
                    spec,
                    'translation directory',
                )
                intr['directory'] = directory
                intr['spec'] = spec
                introspectables.append(intr)
@@ -117,4 +125,3 @@
                    tdirs.insert(0, directory)
        self.action(None, register, introspectables=introspectables)
src/pyramid/config/predicates.py
@@ -1,2 +1,3 @@
import zope.deprecation
zope.deprecation.moved('pyramid.predicates', 'Pyramid 1.10')
src/pyramid/config/rendering.py
@@ -1,7 +1,4 @@
from pyramid.interfaces import (
    IRendererFactory,
    PHASE1_CONFIG,
    )
from pyramid.interfaces import IRendererFactory, PHASE1_CONFIG
from pyramid import renderers
from pyramid.config.util import action_method
@@ -9,13 +6,14 @@
DEFAULT_RENDERERS = (
    ('json', renderers.json_renderer_factory),
    ('string', renderers.string_renderer_factory),
    )
)
class RenderingConfiguratorMixin(object):
    def add_default_renderers(self):
        for name, renderer in DEFAULT_RENDERERS:
            self.add_renderer(name, renderer)
    @action_method
    def add_renderer(self, name, factory):
        """
@@ -36,16 +34,23 @@
        # as a name
        if not name:
            name = ''
        def register():
            self.registry.registerUtility(factory, IRendererFactory, name=name)
        intr = self.introspectable('renderer factories',
                                   name,
                                   self.object_description(factory),
                                   'renderer factory')
        intr = self.introspectable(
            'renderer factories',
            name,
            self.object_description(factory),
            'renderer factory',
        )
        intr['factory'] = factory
        intr['name'] = name
        # we need to register renderers early (in phase 1) because they are
        # used during view configuration (which happens in phase 3)
        self.action((IRendererFactory, name), register, order=PHASE1_CONFIG,
                    introspectables=(intr,))
        self.action(
            (IRendererFactory, name),
            register,
            order=PHASE1_CONFIG,
            introspectables=(intr,),
        )
src/pyramid/config/routes.py
@@ -7,16 +7,13 @@
    IRouteRequest,
    IRoutesMapper,
    PHASE2_CONFIG,
    )
)
from pyramid.exceptions import ConfigurationError
from pyramid.request import route_request_iface
from pyramid.urldispatch import RoutesMapper
from pyramid.util import (
    as_sorted_tuple,
    is_nonstr_iter,
)
from pyramid.util import as_sorted_tuple, is_nonstr_iter
import pyramid.predicates
@@ -26,26 +23,29 @@
    predvalseq,
)
class RoutesConfiguratorMixin(object):
    @action_method
    def add_route(self,
                  name,
                  pattern=None,
                  factory=None,
                  for_=None,
                  header=None,
                  xhr=None,
                  accept=None,
                  path_info=None,
                  request_method=None,
                  request_param=None,
                  traverse=None,
                  custom_predicates=(),
                  use_global_views=False,
                  path=None,
                  pregenerator=None,
                  static=False,
                  **predicates):
    def add_route(
        self,
        name,
        pattern=None,
        factory=None,
        for_=None,
        header=None,
        xhr=None,
        accept=None,
        path_info=None,
        request_method=None,
        request_param=None,
        traverse=None,
        custom_predicates=(),
        use_global_views=False,
        path=None,
        pregenerator=None,
        static=False,
        **predicates
    ):
        """ Add a :term:`route configuration` to the current
        configuration state, as well as possibly a :term:`view
        configuration` to be used to specify a :term:`view callable`
@@ -158,8 +158,8 @@
             For backwards compatibility purposes (as of :app:`Pyramid` 1.0), a
             ``path`` keyword argument passed to this function will be used to
             represent the pattern value if the ``pattern`` argument is
             ``None``.  If both ``path`` and ``pattern`` are passed, ``pattern``
             wins.
             ``None``.  If both ``path`` and ``pattern`` are passed,
             ``pattern`` wins.
        xhr
@@ -297,27 +297,31 @@
        """
        if custom_predicates:
            warnings.warn(
                ('The "custom_predicates" argument to Configurator.add_route '
                 'is deprecated as of Pyramid 1.5.  Use '
                 '"config.add_route_predicate" and use the registered '
                 'route predicate as a predicate argument to add_route '
                 'instead. See "Adding A Third Party View, Route, or '
                 'Subscriber Predicate" in the "Hooks" chapter of the '
                 'documentation for more information.'),
                (
                    'The "custom_predicates" argument to '
                    'Configurator.add_route is deprecated as of Pyramid 1.5. '
                    'Use "config.add_route_predicate" and use the registered '
                    'route predicate as a predicate argument to add_route '
                    'instead. See "Adding A Third Party View, Route, or '
                    'Subscriber Predicate" in the "Hooks" chapter of the '
                    'documentation for more information.'
                ),
                DeprecationWarning,
                stacklevel=3
                )
                stacklevel=3,
            )
        if accept is not None:
            if not is_nonstr_iter(accept):
                if '*' in accept:
                    warnings.warn(
                        ('Passing a media range to the "accept" argument of '
                         'Configurator.add_route is deprecated as of Pyramid '
                         '1.10. Use a list of explicit media types.'),
                        (
                            'Passing a media range to the "accept" argument '
                            'of Configurator.add_route is deprecated as of '
                            'Pyramid 1.10. Use a list of explicit media types.'
                        ),
                        DeprecationWarning,
                        stacklevel=3,
                        )
                    )
                # XXX switch this to False when range support is dropped
                accept = [normalize_accept_offer(accept, allow_range=True)]
@@ -347,15 +351,16 @@
            pattern = parsed.path
            original_pregenerator = pregenerator
            def external_url_pregenerator(request, elements, kw):
                if '_app_url' in kw:
                    raise ValueError(
                        'You cannot generate a path to an external route '
                        'pattern via request.route_path nor pass an _app_url '
                        'to request.route_url when generating a URL for an '
                        'external route pattern (pattern was "%s") ' %
                        (pattern,)
                        )
                        'external route pattern (pattern was "%s") '
                        % (pattern,)
                    )
                if '_scheme' in kw:
                    scheme = kw['_scheme']
                elif parsed.scheme:
@@ -365,8 +370,7 @@
                kw['_app_url'] = '{0}://{1}'.format(scheme, parsed.netloc)
                if original_pregenerator:
                    elements, kw = original_pregenerator(
                        request, elements, kw)
                    elements, kw = original_pregenerator(request, elements, kw)
                return elements, kw
            pregenerator = external_url_pregenerator
@@ -379,10 +383,9 @@
        introspectables = []
        intr = self.introspectable('routes',
                                   name,
                                   '%s (pattern: %r)' % (name, pattern),
                                   'route')
        intr = self.introspectable(
            'routes', name, '%s (pattern: %r)' % (name, pattern), 'route'
        )
        intr['name'] = name
        intr['pattern'] = pattern
        intr['factory'] = factory
@@ -404,17 +407,21 @@
        introspectables.append(intr)
        if factory:
            factory_intr = self.introspectable('root factories',
                                               name,
                                               self.object_description(factory),
                                               'root factory')
            factory_intr = self.introspectable(
                'root factories',
                name,
                self.object_description(factory),
                'root factory',
            )
            factory_intr['factory'] = factory
            factory_intr['route_name'] = name
            factory_intr.relate('routes', name)
            introspectables.append(factory_intr)
        def register_route_request_iface():
            request_iface = self.registry.queryUtility(IRouteRequest, name=name)
            request_iface = self.registry.queryUtility(
                IRouteRequest, name=name
            )
            if request_iface is None:
                if use_global_views:
                    bases = (IRequest,)
@@ -422,7 +429,8 @@
                    bases = ()
                request_iface = route_request_iface(name, bases)
                self.registry.registerUtility(
                    request_iface, IRouteRequest, name=name)
                    request_iface, IRouteRequest, name=name
                )
        def register_connect():
            pvals = predicates.copy()
@@ -436,15 +444,19 @@
                    accept=accept,
                    traverse=traverse,
                    custom=predvalseq(custom_predicates),
                    )
                )
            )
            predlist = self.get_predlist('route')
            _, preds, _ = predlist.make(self, **pvals)
            route = mapper.connect(
                name, pattern, factory, predicates=preds,
                pregenerator=pregenerator, static=static
                )
                name,
                pattern,
                factory,
                predicates=preds,
                pregenerator=pregenerator,
                static=static,
            )
            intr['object'] = route
            return route
@@ -455,12 +467,17 @@
        # But IRouteRequest interfaces must be registered before we begin to
        # process view registrations (in phase 3)
        self.action(('route', name), register_route_request_iface,
                    order=PHASE2_CONFIG, introspectables=introspectables)
        self.action(
            ('route', name),
            register_route_request_iface,
            order=PHASE2_CONFIG,
            introspectables=introspectables,
        )
    @action_method
    def add_route_predicate(self, name, factory, weighs_more_than=None,
                            weighs_less_than=None):
    def add_route_predicate(
        self, name, factory, weighs_more_than=None, weighs_less_than=None
    ):
        """ Adds a route predicate factory.  The view predicate can later be
        named as a keyword argument to
        :meth:`pyramid.config.Configurator.add_route`.
@@ -481,8 +498,8 @@
            name,
            factory,
            weighs_more_than=weighs_more_than,
            weighs_less_than=weighs_less_than
            )
            weighs_less_than=weighs_less_than,
        )
    def add_default_route_predicates(self):
        p = pyramid.predicates
@@ -496,7 +513,7 @@
            ('effective_principals', p.EffectivePrincipalsPredicate),
            ('custom', p.CustomPredicate),
            ('traverse', p.TraversePredicate),
            ):
        ):
            self.add_route_predicate(name, factory)
    def get_routes_mapper(self):
@@ -541,8 +558,7 @@
            old_route_prefix = ''
        route_prefix = '{}/{}'.format(
            old_route_prefix.rstrip('/'),
            route_prefix.lstrip('/'),
            old_route_prefix.rstrip('/'), route_prefix.lstrip('/')
        )
        route_prefix = route_prefix.strip('/')
src/pyramid/config/security.py
@@ -8,7 +8,7 @@
    IDefaultPermission,
    PHASE1_CONFIG,
    PHASE2_CONFIG,
    )
)
from pyramid.csrf import LegacySessionCSRFStoragePolicy
from pyramid.exceptions import ConfigurationError
@@ -16,8 +16,8 @@
from pyramid.config.util import action_method
class SecurityConfiguratorMixin(object):
class SecurityConfiguratorMixin(object):
    def add_default_security(self):
        self.set_csrf_storage_policy(LegacySessionCSRFStoragePolicy())
@@ -35,20 +35,30 @@
           achieve the same purpose.
        """
        def register():
            self._set_authentication_policy(policy)
            if self.registry.queryUtility(IAuthorizationPolicy) is None:
                raise ConfigurationError(
                    'Cannot configure an authentication policy without '
                    'also configuring an authorization policy '
                    '(use the set_authorization_policy method)')
        intr = self.introspectable('authentication policy', None,
                                   self.object_description(policy),
                                   'authentication policy')
                    '(use the set_authorization_policy method)'
                )
        intr = self.introspectable(
            'authentication policy',
            None,
            self.object_description(policy),
            'authentication policy',
        )
        intr['policy'] = policy
        # authentication policy used by view config (phase 3)
        self.action(IAuthenticationPolicy, register, order=PHASE2_CONFIG,
                    introspectables=(intr,))
        self.action(
            IAuthenticationPolicy,
            register,
            order=PHASE2_CONFIG,
            introspectables=(intr,),
        )
    def _set_authentication_policy(self, policy):
        policy = self.maybe_dotted(policy)
@@ -67,8 +77,10 @@
           :class:`pyramid.config.Configurator` constructor can be used to
           achieve the same purpose.
        """
        def register():
            self._set_authorization_policy(policy)
        def ensure():
            if self.autocommit:
                return
@@ -76,16 +88,24 @@
                raise ConfigurationError(
                    'Cannot configure an authorization policy without '
                    'also configuring an authentication policy '
                    '(use the set_authorization_policy method)')
                    '(use the set_authorization_policy method)'
                )
        intr = self.introspectable('authorization policy', None,
                                   self.object_description(policy),
                                   'authorization policy')
        intr = self.introspectable(
            'authorization policy',
            None,
            self.object_description(policy),
            'authorization policy',
        )
        intr['policy'] = policy
        # authorization policy used by view config (phase 3) and
        # authentication policy (phase 2)
        self.action(IAuthorizationPolicy, register, order=PHASE1_CONFIG,
                    introspectables=(intr,))
        self.action(
            IAuthorizationPolicy,
            register,
            order=PHASE1_CONFIG,
            introspectables=(intr,),
        )
        self.action(None, ensure)
    def _set_authorization_policy(self, policy):
@@ -133,21 +153,25 @@
           :class:`pyramid.config.Configurator` constructor can be used to
           achieve the same purpose.
        """
        def register():
            self.registry.registerUtility(permission, IDefaultPermission)
        intr = self.introspectable('default permission',
                                   None,
                                   permission,
                                   'default permission')
        intr = self.introspectable(
            'default permission', None, permission, 'default permission'
        )
        intr['value'] = permission
        perm_intr = self.introspectable('permissions',
                                        permission,
                                        permission,
                                        'permission')
        perm_intr = self.introspectable(
            'permissions', permission, permission, 'permission'
        )
        perm_intr['value'] = permission
        # default permission used during view registration (phase 3)
        self.action(IDefaultPermission, register, order=PHASE1_CONFIG,
                    introspectables=(intr, perm_intr,))
        self.action(
            IDefaultPermission,
            register,
            order=PHASE1_CONFIG,
            introspectables=(intr, perm_intr),
        )
    def add_permission(self, permission_name):
        """
@@ -161,11 +185,8 @@
          config.add_permission('view')
        """
        intr = self.introspectable(
            'permissions',
            permission_name,
            permission_name,
            'permission'
            )
            'permissions', permission_name, permission_name, 'permission'
        )
        intr['value'] = permission_name
        self.action(None, introspectables=(intr,))
@@ -217,22 +238,30 @@
        """
        options = DefaultCSRFOptions(
            require_csrf, token, header, safe_methods, callback,
            require_csrf, token, header, safe_methods, callback
        )
        def register():
            self.registry.registerUtility(options, IDefaultCSRFOptions)
        intr = self.introspectable('default csrf view options',
                                   None,
                                   options,
                                   'default csrf view options')
        intr = self.introspectable(
            'default csrf view options',
            None,
            options,
            'default csrf view options',
        )
        intr['require_csrf'] = require_csrf
        intr['token'] = token
        intr['header'] = header
        intr['safe_methods'] = as_sorted_tuple(safe_methods)
        intr['callback'] = callback
        self.action(IDefaultCSRFOptions, register, order=PHASE1_CONFIG,
                    introspectables=(intr,))
        self.action(
            IDefaultCSRFOptions,
            register,
            order=PHASE1_CONFIG,
            introspectables=(intr,),
        )
    @action_method
    def set_csrf_storage_policy(self, policy):
@@ -245,12 +274,13 @@
        how to generate and persist CSRF tokens.
        """
        def register():
            self.registry.registerUtility(policy, ICSRFStoragePolicy)
        intr = self.introspectable('csrf storage policy',
                                   None,
                                   policy,
                                   'csrf storage policy')
        intr = self.introspectable(
            'csrf storage policy', None, policy, 'csrf storage policy'
        )
        intr['policy'] = policy
        self.action(ICSRFStoragePolicy, register, introspectables=(intr,))
src/pyramid/config/settings.py
@@ -2,6 +2,7 @@
from pyramid.settings import asbool, aslist
class SettingsConfiguratorMixin(object):
    def _set_settings(self, mapping):
        if mapping is None:
@@ -60,11 +61,13 @@
    d.update(**kw)
    eget = _environ_.get
    def expand_key(key):
        keys = [key]
        if not key.startswith('pyramid.'):
            keys.append('pyramid.' + key)
        return keys
    def S(settings_key, env_key=None, type_=str, default=False):
        value = default
        keys = expand_key(settings_key)
@@ -74,6 +77,7 @@
            value = eget(env_key, value)
        value = type_(value)
        d.update({k: value for k in keys})
    def O(settings_key, override_key):  # noqa: E743
        for key in expand_key(settings_key):
            d[key] = d[key] or d[override_key]
src/pyramid/config/testing.py
@@ -5,22 +5,25 @@
    IAuthorizationPolicy,
    IAuthenticationPolicy,
    IRendererFactory,
    )
)
from pyramid.renderers import RendererHelper
from pyramid.traversal import (
    decode_path_info,
    split_path_info,
    )
from pyramid.traversal import decode_path_info, split_path_info
from pyramid.config.util import action_method
class TestingConfiguratorMixin(object):
    # testing API
    def testing_securitypolicy(self, userid=None, groupids=(),
                               permissive=True, remember_result=None,
                               forget_result=None):
    def testing_securitypolicy(
        self,
        userid=None,
        groupids=(),
        permissive=True,
        remember_result=None,
        forget_result=None,
    ):
        """Unit/integration testing helper: Registers a pair of faux
        :app:`Pyramid` security policies: a :term:`authentication
        policy` and a :term:`authorization policy`.
@@ -64,9 +67,10 @@
           The ``forget_result`` argument.
        """
        from pyramid.testing import DummySecurityPolicy
        policy = DummySecurityPolicy(
            userid, groupids, permissive, remember_result, forget_result
            )
        )
        self.registry.registerUtility(policy, IAuthorizationPolicy)
        self.registry.registerUtility(policy, IAuthenticationPolicy)
        return policy
@@ -85,6 +89,7 @@
        :func:`pyramid.traversal.find_resource` is called with an
        equivalent path string or tuple.
        """
        class DummyTraverserFactory:
            def __init__(self, context):
                self.context = context
@@ -93,14 +98,22 @@
                path = decode_path_info(request.environ['PATH_INFO'])
                ob = resources[path]
                traversed = split_path_info(path)
                return {'context':ob, 'view_name':'','subpath':(),
                        'traversed':traversed, 'virtual_root':ob,
                        'virtual_root_path':(), 'root':ob}
        self.registry.registerAdapter(DummyTraverserFactory, (Interface,),
                                      ITraverser)
                return {
                    'context': ob,
                    'view_name': '',
                    'subpath': (),
                    'traversed': traversed,
                    'virtual_root': ob,
                    'virtual_root_path': (),
                    'root': ob,
                }
        self.registry.registerAdapter(
            DummyTraverserFactory, (Interface,), ITraverser
        )
        return resources
    testing_models = testing_resources # b/w compat
    testing_models = testing_resources  # b/w compat
    @action_method
    def testing_add_subscriber(self, event_iface=None):
@@ -122,8 +135,10 @@
        """
        event_iface = self.maybe_dotted(event_iface)
        L = []
        def subscriber(*event):
            L.extend(event)
        self.add_subscriber(subscriber, event_iface)
        return L
@@ -149,19 +164,22 @@
        """
        from pyramid.testing import DummyRendererFactory
        helper = RendererHelper(name=path, registry=self.registry)
        factory = self.registry.queryUtility(IRendererFactory, name=helper.type)
        factory = self.registry.queryUtility(
            IRendererFactory, name=helper.type
        )
        if not isinstance(factory, DummyRendererFactory):
            factory = DummyRendererFactory(helper.type, factory)
            self.registry.registerUtility(factory, IRendererFactory,
                                          name=helper.type)
            self.registry.registerUtility(
                factory, IRendererFactory, name=helper.type
            )
        from pyramid.testing import DummyTemplateRenderer
        if renderer is None:
            renderer = DummyTemplateRenderer()
        factory.add(path, renderer)
        return renderer
    testing_add_template = testing_add_renderer
src/pyramid/config/tweens.py
@@ -2,25 +2,16 @@
from pyramid.interfaces import ITweens
from pyramid.compat import (
    string_types,
    is_nonstr_iter,
    )
from pyramid.compat import string_types, is_nonstr_iter
from pyramid.exceptions import ConfigurationError
from pyramid.tweens import (
    MAIN,
    INGRESS,
    EXCVIEW,
    )
from pyramid.tweens import MAIN, INGRESS, EXCVIEW
from pyramid.util import (
    is_string_or_iterable,
    TopologicalSorter,
    )
from pyramid.util import is_string_or_iterable, TopologicalSorter
from pyramid.config.util import action_method
class TweensConfiguratorMixin(object):
    def add_tween(self, tween_factory, under=None, over=None):
@@ -65,7 +56,7 @@
        - An iterable of any combination of the above. This allows the user
          to specify fallbacks if the desired tween is not included, as well
          as compatibility with multiple other tweens.
        ``under`` means 'closer to the main Pyramid application than',
        ``over`` means 'closer to the request ingress than'.
@@ -104,8 +95,9 @@
        For more information, see :ref:`registering_tweens`.
        """
        return self._add_tween(tween_factory, under=under, over=over,
                               explicit=False)
        return self._add_tween(
            tween_factory, under=under, over=over, explicit=False
        )
    def add_default_tweens(self):
        self.add_tween(EXCVIEW)
@@ -116,8 +108,9 @@
        if not isinstance(tween_factory, string_types):
            raise ConfigurationError(
                'The "tween_factory" argument to add_tween must be a '
                'dotted name to a globally importable object, not %r' %
                tween_factory)
                'dotted name to a globally importable object, not %r'
                % tween_factory
            )
        name = tween_factory
@@ -130,7 +123,8 @@
            if p is not None:
                if not is_string_or_iterable(p):
                    raise ConfigurationError(
                        '"%s" must be a string or iterable, not %s' % (t, p))
                        '"%s" must be a string or iterable, not %s' % (t, p)
                    )
        if over is INGRESS or is_nonstr_iter(over) and INGRESS in over:
            raise ConfigurationError('%s cannot be over INGRESS' % name)
@@ -150,15 +144,16 @@
            if explicit:
                tweens.add_explicit(name, tween_factory)
            else:
                tweens.add_implicit(name, tween_factory, under=under, over=over)
                tweens.add_implicit(
                    name, tween_factory, under=under, over=over
                )
        discriminator = ('tween', name, explicit)
        tween_type = explicit and 'explicit' or 'implicit'
        intr = self.introspectable('tweens',
                                   discriminator,
                                   name,
                                   '%s tween' % tween_type)
        intr = self.introspectable(
            'tweens', discriminator, name, '%s tween' % tween_type
        )
        intr['name'] = name
        intr['factory'] = tween_factory
        intr['type'] = tween_type
@@ -167,6 +162,7 @@
        introspectables.append(intr)
        self.action(discriminator, register, introspectables=introspectables)
@implementer(ITweens)
class Tweens(object):
    def __init__(self):
@@ -174,7 +170,8 @@
            default_before=None,
            default_after=INGRESS,
            first=INGRESS,
            last=MAIN)
            last=MAIN,
        )
        self.explicit = []
    def add_explicit(self, name, factory):
src/pyramid/config/util.py
@@ -4,22 +4,17 @@
from webob.acceptparse import Accept
from zope.interface import implementer
from pyramid.compat import (
    bytes_,
    is_nonstr_iter
)
from pyramid.compat import bytes_, is_nonstr_iter
from pyramid.interfaces import IActionInfo
from pyramid.exceptions import ConfigurationError
from pyramid.predicates import Notted
from pyramid.registry import predvalseq
from pyramid.util import (
    TopologicalSorter,
    takes_one_arg,
)
from pyramid.util import TopologicalSorter, takes_one_arg
TopologicalSorter = TopologicalSorter  # support bw-compat imports
takes_one_arg = takes_one_arg  # support bw-compat imports
@implementer(IActionInfo)
class ActionInfo(object):
@@ -34,10 +29,12 @@
        src = '\n'.join('    %s' % x for x in srclines)
        return 'Line %s of file %s:\n%s' % (self.line, self.file, src)
def action_method(wrapped):
    """ Wrapper to provide the right conflict info report data when a method
    that calls Configurator.action calls another that does the same.  Not a
    documented API but used by some external systems."""
    def wrapper(self, *arg, **kw):
        if self._ainfo is None:
            self._ainfo = []
@@ -55,10 +52,10 @@
                # extra stack frame. This should no longer be necessary in
                # Python 3.5.1
                last_frame = ActionInfo(*f[-1])
                if last_frame.function == 'extract_stack': # pragma: no cover
                if last_frame.function == 'extract_stack':  # pragma: no cover
                    f.pop()
                info = ActionInfo(*f[-backframes])
            except Exception: # pragma: no cover
            except Exception:  # pragma: no cover
                info = ActionInfo(None, 0, '', '')
        self._ainfo.append(info)
        try:
@@ -112,6 +109,7 @@
    .. versionadded:: 1.5
    """
    def __init__(self, value):
        self.value = value
@@ -119,8 +117,8 @@
# under = after
# over = before
class PredicateList(object):
class PredicateList(object):
    def __init__(self):
        self.sorter = TopologicalSorter()
        self.last_added = None
@@ -128,16 +126,13 @@
    def add(self, name, factory, weighs_more_than=None, weighs_less_than=None):
        # Predicates should be added to a predicate list in (presumed)
        # computation expense order.
        ## if weighs_more_than is None and weighs_less_than is None:
        ##     weighs_more_than = self.last_added or FIRST
        ##     weighs_less_than = LAST
        # if weighs_more_than is None and weighs_less_than is None:
        #     weighs_more_than = self.last_added or FIRST
        #     weighs_less_than = LAST
        self.last_added = name
        self.sorter.add(
            name,
            factory,
            after=weighs_more_than,
            before=weighs_less_than,
            )
            name, factory, after=weighs_more_than, before=weighs_less_than
        )
    def names(self):
        # Return the list of valid predicate names.
@@ -161,7 +156,7 @@
        preds = []
        for n, (name, predicate_factory) in enumerate(ordered):
            vals = kw.pop(name, None)
            if vals is None: # XXX should this be a sentinel other than None?
            if vals is None:  # XXX should this be a sentinel other than None?
                continue
            if not isinstance(vals, predvalseq):
                vals = (vals,)
@@ -183,8 +178,9 @@
                preds.append(pred)
        if kw:
            from difflib import get_close_matches
            closest = []
            names = [ name for name, _ in ordered ]
            names = [name for name, _ in ordered]
            for name in kw:
                closest.extend(get_close_matches(name, names, 3))
@@ -266,8 +262,7 @@
        parsed = Accept.parse_offer(value)
        type_w = find_order_index(
            parsed.type + '/' + parsed.subtype,
            max_weight,
            parsed.type + '/' + parsed.subtype, max_weight
        )
        if parsed.params:
src/pyramid/config/views.py
@@ -6,11 +6,7 @@
import warnings
from webob.acceptparse import Accept
from zope.interface import (
    Interface,
    implementedBy,
    implementer,
    )
from zope.interface import Interface, implementedBy, implementer
from zope.interface.interfaces import IInterface
from pyramid.interfaces import (
@@ -31,7 +27,7 @@
    IViewDeriverInfo,
    IViewMapperFactory,
    PHASE1_CONFIG,
    )
)
from pyramid import renderers
@@ -42,20 +38,17 @@
    url_quote,
    WIN,
    is_nonstr_iter,
    )
)
from pyramid.decorator import reify
from pyramid.exceptions import (
    ConfigurationError,
    PredicateMismatch,
    )
from pyramid.exceptions import ConfigurationError, PredicateMismatch
from pyramid.httpexceptions import (
    HTTPForbidden,
    HTTPNotFound,
    default_exceptionresponse_view,
    )
)
from pyramid.registry import Deferred
@@ -66,10 +59,7 @@
from pyramid.view import AppendSlashNotFoundViewFactory
from pyramid.util import (
    as_sorted_tuple,
    TopologicalSorter,
    )
from pyramid.util import as_sorted_tuple, TopologicalSorter
import pyramid.predicates
import pyramid.viewderivers
@@ -91,19 +81,19 @@
    normalize_accept_offer,
    predvalseq,
    sort_accept_offers,
    )
)
urljoin = urlparse.urljoin
url_parse = urlparse.urlparse
DefaultViewMapper = DefaultViewMapper # bw-compat
preserve_view_attrs = preserve_view_attrs # bw-compat
requestonly = requestonly # bw-compat
view_description = view_description # bw-compat
DefaultViewMapper = DefaultViewMapper  # bw-compat
preserve_view_attrs = preserve_view_attrs  # bw-compat
requestonly = requestonly  # bw-compat
view_description = view_description  # bw-compat
@implementer(IMultiView)
class MultiView(object):
    def __init__(self, name):
        self.name = name
        self.media_views = {}
@@ -181,21 +171,22 @@
                continue
        raise PredicateMismatch(self.name)
def attr_wrapped_view(view, info):
    accept, order, phash = (info.options.get('accept', None),
                            getattr(info, 'order', MAX_ORDER),
                            getattr(info, 'phash', DEFAULT_PHASH))
    accept, order, phash = (
        info.options.get('accept', None),
        getattr(info, 'order', MAX_ORDER),
        getattr(info, 'phash', DEFAULT_PHASH),
    )
    # this is a little silly but we don't want to decorate the original
    # function with attributes that indicate accept, order, and phash,
    # so we use a wrapper
    if (
        (accept is None) and
        (order == MAX_ORDER) and
        (phash == DEFAULT_PHASH)
    ):
        return view # defaults
    if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH):
        return view  # defaults
    def attr_view(context, request):
        return view(context, request)
    attr_view.__accept__ = accept
    attr_view.__order__ = order
    attr_view.__phash__ = phash
@@ -203,31 +194,38 @@
    attr_view.__permission__ = info.options.get('permission')
    return attr_view
attr_wrapped_view.options = ('accept', 'attr', 'permission')
def predicated_view(view, info):
    preds = info.predicates
    if not preds:
        return view
    def predicate_wrapper(context, request):
        for predicate in preds:
            if not predicate(context, request):
                view_name = getattr(view, '__name__', view)
                raise PredicateMismatch(
                    'predicate mismatch for view %s (%s)' % (
                        view_name, predicate.text()))
                    'predicate mismatch for view %s (%s)'
                    % (view_name, predicate.text())
                )
        return view(context, request)
    def checker(context, request):
        return all((predicate(context, request) for predicate in
                    preds))
        return all((predicate(context, request) for predicate in preds))
    predicate_wrapper.__predicated__ = checker
    predicate_wrapper.__predicates__ = preds
    return predicate_wrapper
def viewdefaults(wrapped):
    """ Decorator for add_view-like methods which takes into account
    __view_defaults__ attached to view it is passed.  Not a documented API but
    used by some external systems."""
    def wrapper(self, *arg, **kw):
        defaults = {}
        if arg:
@@ -238,10 +236,12 @@
        if inspect.isclass(view):
            defaults = getattr(view, '__view_defaults__', {}).copy()
        if '_backframes' not in kw:
            kw['_backframes'] = 1 # for action_method
            kw['_backframes'] = 1  # for action_method
        defaults.update(kw)
        return wrapped(self, *arg, **defaults)
    return functools.wraps(wrapped)(wrapper)
def combine_decorators(*decorators):
    def decorated(view_callable):
@@ -249,7 +249,9 @@
        for decorator in reversed(decorators):
            view_callable = decorator(view_callable)
        return view_callable
    return decorated
class ViewsConfiguratorMixin(object):
    @viewdefaults
@@ -281,7 +283,8 @@
        check_csrf=None,
        require_csrf=None,
        exception_only=False,
        **view_options):
        **view_options
    ):
        """ Add a :term:`view configuration` to the current
        configuration state.  Arguments to ``add_view`` are broken
        down below into *predicate* arguments and *non-predicate*
@@ -725,8 +728,8 @@
          If CSRF checking is performed, the checked value will be the value of
          ``request.params[check_name]``. This value will be compared against
          the value of ``policy.get_csrf_token()`` (where ``policy`` is an
          implementation of :meth:`pyramid.interfaces.ICSRFStoragePolicy`), and the
          check will pass if these two values are the same. If the check
          implementation of :meth:`pyramid.interfaces.ICSRFStoragePolicy`), and
          the check will pass if these two values are the same. If the check
          passes, the associated view will be permitted to execute. If the
          check fails, the associated view will not be permitted to execute.
@@ -804,42 +807,49 @@
        """
        if custom_predicates:
            warnings.warn(
                ('The "custom_predicates" argument to Configurator.add_view '
                 'is deprecated as of Pyramid 1.5.  Use '
                 '"config.add_view_predicate" and use the registered '
                 'view predicate as a predicate argument to add_view instead. '
                 'See "Adding A Third Party View, Route, or Subscriber '
                 'Predicate" in the "Hooks" chapter of the documentation '
                 'for more information.'),
                (
                    'The "custom_predicates" argument to '
                    'Configurator.add_view is deprecated as of Pyramid 1.5. '
                    'Use "config.add_view_predicate" and use the registered '
                    'view predicate as a predicate argument to add_view '
                    'instead. See "Adding A Third Party View, Route, or '
                    'Subscriber Predicate" in the "Hooks" chapter of the '
                    'documentation for more information.'
                ),
                DeprecationWarning,
                stacklevel=4,
                )
            )
        if check_csrf is not None:
            warnings.warn(
                ('The "check_csrf" argument to Configurator.add_view is '
                 'deprecated as of Pyramid 1.7. Use the "require_csrf" option '
                 'instead or see "Checking CSRF Tokens Automatically" in the '
                 '"Sessions" chapter of the documentation for more '
                 'information.'),
                (
                    'The "check_csrf" argument to Configurator.add_view is '
                    'deprecated as of Pyramid 1.7. Use the "require_csrf" '
                    'option instead or see "Checking CSRF Tokens '
                    'Automatically" in the "Sessions" chapter of the '
                    'documentation for more information.'
                ),
                DeprecationWarning,
                stacklevel=4,
                )
            )
        if accept is not None:
            if is_nonstr_iter(accept):
                raise ConfigurationError(
                    'A list is not supported in the "accept" view predicate.',
                    'A list is not supported in the "accept" view predicate.'
                )
            if '*' in accept:
                warnings.warn(
                    ('Passing a media range to the "accept" argument of '
                     'Configurator.add_view is deprecated as of Pyramid 1.10. '
                     'Use explicit media types to avoid ambiguities in '
                     'content negotiation that may impact your users.'),
                    (
                        'Passing a media range to the "accept" argument of '
                        'Configurator.add_view is deprecated as of '
                        'Pyramid 1.10. Use explicit media types to avoid '
                        'ambiguities in content negotiation that may impact '
                        'your users.'
                    ),
                    DeprecationWarning,
                    stacklevel=4,
                    )
                )
            # XXX when media ranges are gone, switch allow_range=False
            accept = normalize_accept_offer(accept, allow_range=True)
@@ -856,17 +866,21 @@
        if not view:
            if renderer:
                def view(context, request):
                    return {}
            else:
                raise ConfigurationError('"view" was not specified and '
                                         'no "renderer" specified')
                raise ConfigurationError(
                    '"view" was not specified and ' 'no "renderer" specified'
                )
        if request_type is not None:
            request_type = self.maybe_dotted(request_type)
            if not IInterface.providedBy(request_type):
                raise ConfigurationError(
                    'request_type must be an interface, not %s' % request_type)
                    'request_type must be an interface, not %s' % request_type
                )
        if context is None:
            context = for_
@@ -875,7 +889,8 @@
        if exception_only and not isexc:
            raise ConfigurationError(
                'view "context" must be an exception type when '
                '"exception_only" is True')
                '"exception_only" is True'
            )
        r_context = context
        if r_context is None:
@@ -885,24 +900,26 @@
        if isinstance(renderer, string_types):
            renderer = renderers.RendererHelper(
                name=renderer, package=self.package,
                registry=self.registry)
                name=renderer, package=self.package, registry=self.registry
            )
        introspectables = []
        ovals = view_options.copy()
        ovals.update(dict(
            xhr=xhr,
            request_method=request_method,
            path_info=path_info,
            request_param=request_param,
            header=header,
            accept=accept,
            containment=containment,
            request_type=request_type,
            match_param=match_param,
            check_csrf=check_csrf,
            custom=predvalseq(custom_predicates),
        ))
        ovals.update(
            dict(
                xhr=xhr,
                request_method=request_method,
                path_info=path_info,
                request_param=request_param,
                header=header,
                accept=accept,
                containment=containment,
                request_type=request_type,
                match_param=match_param,
                check_csrf=check_csrf,
                custom=predvalseq(custom_predicates),
            )
        )
        def discrim_func():
            # We need to defer the discriminator until we know what the phash
@@ -924,80 +941,82 @@
            order, preds, phash = predlist.make(self, **pvals)
            view_intr.update({
                'phash': phash,
                'order': order,
                'predicates': preds,
                })
            view_intr.update(
                {'phash': phash, 'order': order, 'predicates': preds}
            )
            return ('view', context, name, route_name, phash)
        discriminator = Deferred(discrim_func)
        if inspect.isclass(view) and attr:
            view_desc = 'method %r of %s' % (
                attr, self.object_description(view))
                attr,
                self.object_description(view),
            )
        else:
            view_desc = self.object_description(view)
        tmpl_intr = None
        view_intr = self.introspectable('views',
                                        discriminator,
                                        view_desc,
                                        'view')
        view_intr.update(dict(
            name=name,
            context=context,
            exception_only=exception_only,
            containment=containment,
            request_param=request_param,
            request_methods=request_method,
            route_name=route_name,
            attr=attr,
            xhr=xhr,
            accept=accept,
            header=header,
            path_info=path_info,
            match_param=match_param,
            check_csrf=check_csrf,
            http_cache=http_cache,
            require_csrf=require_csrf,
            callable=view,
            mapper=mapper,
            decorator=decorator,
        ))
        view_intr = self.introspectable(
            'views', discriminator, view_desc, 'view'
        )
        view_intr.update(
            dict(
                name=name,
                context=context,
                exception_only=exception_only,
                containment=containment,
                request_param=request_param,
                request_methods=request_method,
                route_name=route_name,
                attr=attr,
                xhr=xhr,
                accept=accept,
                header=header,
                path_info=path_info,
                match_param=match_param,
                check_csrf=check_csrf,
                http_cache=http_cache,
                require_csrf=require_csrf,
                callable=view,
                mapper=mapper,
                decorator=decorator,
            )
        )
        view_intr.update(view_options)
        introspectables.append(view_intr)
        def register(permission=permission, renderer=renderer):
            request_iface = IRequest
            if route_name is not None:
                request_iface = self.registry.queryUtility(IRouteRequest,
                                                           name=route_name)
                request_iface = self.registry.queryUtility(
                    IRouteRequest, name=route_name
                )
                if request_iface is None:
                    # route configuration should have already happened in
                    # phase 2
                    raise ConfigurationError(
                        'No route named %s found for view registration' %
                        route_name)
                        'No route named %s found for view registration'
                        % route_name
                    )
            if renderer is None:
                # use default renderer if one exists (reg'd in phase 1)
                if self.registry.queryUtility(IRendererFactory) is not None:
                    renderer = renderers.RendererHelper(
                        name=None,
                        package=self.package,
                        registry=self.registry
                        )
                        name=None, package=self.package, registry=self.registry
                    )
            renderer_type = getattr(renderer, 'type', None)
            intrspc = self.introspector
            if (
                renderer_type is not None and
                tmpl_intr is not None and
                intrspc is not None and
                intrspc.get('renderer factories', renderer_type) is not None
                ):
                renderer_type is not None
                and tmpl_intr is not None
                and intrspc is not None
                and intrspc.get('renderer factories', renderer_type)
                is not None
            ):
                # allow failure of registered template factories to be deferred
                # until view execution, like other bad renderer factories; if
                # we tried to relate this to an existing renderer factory
@@ -1013,8 +1032,9 @@
                register_view(IViewClassifier, request_iface, derived_view)
            if isexc:
                derived_exc_view = derive_view(True, renderer)
                register_view(IExceptionViewClassifier, request_iface,
                              derived_exc_view)
                register_view(
                    IExceptionViewClassifier, request_iface, derived_exc_view
                )
                if exception_only:
                    derived_view = derived_exc_view
@@ -1085,8 +1105,8 @@
            for view_type in (IView, ISecuredView, IMultiView):
                old_view = registered(
                    (classifier, request_iface, r_context),
                    view_type, name)
                    (classifier, request_iface, r_context), view_type, name
                )
                if old_view is not None:
                    break
@@ -1109,8 +1129,8 @@
                    derived_view,
                    (classifier, request_iface, context),
                    view_iface,
                    name
                    )
                    name,
                )
            else:
                # - A view or multiview was already registered for this
@@ -1136,32 +1156,33 @@
                    # unregister any existing views
                    self.registry.adapters.unregister(
                        (classifier, request_iface, r_context),
                        view_type, name=name)
                        view_type,
                        name=name,
                    )
                self.registry.registerAdapter(
                    multiview,
                    (classifier, request_iface, context),
                    IMultiView, name=name)
                    IMultiView,
                    name=name,
                )
        if mapper:
            mapper_intr = self.introspectable(
                'view mappers',
                discriminator,
                'view mapper for %s' % view_desc,
                'view mapper'
                )
                'view mapper',
            )
            mapper_intr['mapper'] = mapper
            mapper_intr.relate('views', discriminator)
            introspectables.append(mapper_intr)
        if route_name:
            view_intr.relate('routes', route_name) # see add_route
            view_intr.relate('routes', route_name)  # see add_route
        if renderer is not None and renderer.name and '.' in renderer.name:
            # the renderer is a template
            tmpl_intr = self.introspectable(
                'templates',
                discriminator,
                renderer.name,
                'template'
                )
                'templates', discriminator, renderer.name, 'template'
            )
            tmpl_intr.relate('views', discriminator)
            tmpl_intr['name'] = renderer.name
            tmpl_intr['type'] = renderer.type
@@ -1170,11 +1191,8 @@
        if permission is not None:
            # if a permission exists, register a permission introspectable
            perm_intr = self.introspectable(
                'permissions',
                permission,
                permission,
                'permission'
                )
                'permissions', permission, permission, 'permission'
            )
            perm_intr['value'] = permission
            perm_intr.relate('views', discriminator)
            introspectables.append(perm_intr)
@@ -1192,8 +1210,10 @@
    def _apply_view_derivers(self, info):
        # These derivers are not really derivers and so have fixed order
        outer_derivers = [('attr_wrapped_view', attr_wrapped_view),
                          ('predicated_view', predicated_view)]
        outer_derivers = [
            ('attr_wrapped_view', attr_wrapped_view),
            ('predicated_view', predicated_view),
        ]
        view = info.original_view
        derivers = self.registry.getUtility(IViewDerivers)
@@ -1202,8 +1222,9 @@
        return view
    @action_method
    def add_view_predicate(self, name, factory, weighs_more_than=None,
                           weighs_less_than=None):
    def add_view_predicate(
        self, name, factory, weighs_more_than=None, weighs_less_than=None
    ):
        """
        .. versionadded:: 1.4
@@ -1226,8 +1247,8 @@
            name,
            factory,
            weighs_more_than=weighs_more_than,
            weighs_less_than=weighs_less_than
            )
            weighs_less_than=weighs_less_than,
        )
    def add_default_view_predicates(self):
        p = pyramid.predicates
@@ -1245,7 +1266,7 @@
            ('physical_path', p.PhysicalPathPredicate),
            ('effective_principals', p.EffectivePrincipalsPredicate),
            ('custom', p.CustomPredicate),
            ):
        ):
            self.add_view_predicate(name, factory)
    def add_default_accept_view_order(self):
@@ -1261,10 +1282,7 @@
    @action_method
    def add_accept_view_order(
        self,
        value,
        weighs_more_than=None,
        weighs_less_than=None,
        self, value, weighs_more_than=None, weighs_less_than=None
    ):
        """
        Specify an ordering preference for the ``accept`` view option used
@@ -1293,19 +1311,22 @@
        .. versionadded:: 1.10
        """
        def check_type(than):
            than_type, than_subtype, than_params = Accept.parse_offer(than)
            # text/plain vs text/html;charset=utf8
            if bool(offer_params) ^ bool(than_params):
                raise ConfigurationError(
                    'cannot compare a media type with params to one without '
                    'params')
                    'params'
                )
            # text/plain;charset=utf8 vs text/html;charset=utf8
            if offer_params and (
                offer_subtype != than_subtype or offer_type != than_type
            ):
                raise ConfigurationError(
                    'cannot compare params across different media types')
                    'cannot compare params across different media types'
                )
        def normalize_types(thans):
            thans = [normalize_accept_offer(than) for than in thans]
@@ -1328,25 +1349,27 @@
        discriminator = ('accept view order', value)
        intr = self.introspectable(
            'accept view order',
            value,
            value,
            'accept view order')
            'accept view order', value, value, 'accept view order'
        )
        intr['value'] = value
        intr['weighs_more_than'] = weighs_more_than
        intr['weighs_less_than'] = weighs_less_than
        def register():
            sorter = self.registry.queryUtility(IAcceptOrder)
            if sorter is None:
                sorter = TopologicalSorter()
                self.registry.registerUtility(sorter, IAcceptOrder)
            sorter.add(
                value, value,
                before=weighs_more_than,
                after=weighs_less_than,
                value, value, before=weighs_more_than, after=weighs_less_than
            )
        self.action(discriminator, register, introspectables=(intr,),
                    order=PHASE1_CONFIG) # must be registered before add_view
        self.action(
            discriminator,
            register,
            introspectables=(intr,),
            order=PHASE1_CONFIG,
        )  # must be registered before add_view
    @action_method
    def add_view_deriver(self, deriver, name=None, under=None, over=None):
@@ -1390,8 +1413,9 @@
            name = deriver.__name__
        if name in (INGRESS, VIEW):
            raise ConfigurationError('%s is a reserved view deriver name'
                                     % name)
            raise ConfigurationError(
                '%s is a reserved view deriver name' % name
            )
        if under is None:
            under = 'decorated_view'
@@ -1415,15 +1439,12 @@
            raise ConfigurationError('%s cannot be under "mapped_view"' % name)
        discriminator = ('view deriver', name)
        intr = self.introspectable(
            'view derivers',
            name,
            name,
            'view deriver')
        intr = self.introspectable('view derivers', name, name, 'view deriver')
        intr['name'] = name
        intr['deriver'] = deriver
        intr['under'] = under
        intr['over'] = over
        def register():
            derivers = self.registry.queryUtility(IViewDerivers)
            if derivers is None:
@@ -1435,8 +1456,13 @@
                )
                self.registry.registerUtility(derivers, IViewDerivers)
            derivers.add(name, deriver, before=over, after=under)
        self.action(discriminator, register, introspectables=(intr,),
                    order=PHASE1_CONFIG) # must be registered before add_view
        self.action(
            discriminator,
            register,
            introspectables=(intr,),
            order=PHASE1_CONFIG,
        )  # must be registered before add_view
    def add_default_view_derivers(self):
        d = pyramid.viewderivers
@@ -1450,12 +1476,7 @@
        ]
        last = INGRESS
        for name, deriver in derivers:
            self.add_view_deriver(
                deriver,
                name=name,
                under=last,
                over=VIEW,
            )
            self.add_view_deriver(deriver, name=name, under=last, over=VIEW)
            last = name
        # leave the csrf_view loosely coupled to the rest of the pipeline
@@ -1547,26 +1568,39 @@
        return self._derive_view(view, attr=attr, renderer=renderer)
    # b/w compat
    def _derive_view(self, view, permission=None, predicates=(),
                     attr=None, renderer=None, wrapper_viewname=None,
                     viewname=None, accept=None, order=MAX_ORDER,
                     phash=DEFAULT_PHASH, decorator=None, route_name=None,
                     mapper=None, http_cache=None, context=None,
                     require_csrf=None, exception_only=False,
                     extra_options=None):
    def _derive_view(
        self,
        view,
        permission=None,
        predicates=(),
        attr=None,
        renderer=None,
        wrapper_viewname=None,
        viewname=None,
        accept=None,
        order=MAX_ORDER,
        phash=DEFAULT_PHASH,
        decorator=None,
        route_name=None,
        mapper=None,
        http_cache=None,
        context=None,
        require_csrf=None,
        exception_only=False,
        extra_options=None,
    ):
        view = self.maybe_dotted(view)
        mapper = self.maybe_dotted(mapper)
        if isinstance(renderer, string_types):
            renderer = renderers.RendererHelper(
                name=renderer, package=self.package,
                registry=self.registry)
                name=renderer, package=self.package, registry=self.registry
            )
        if renderer is None:
            # use default renderer if one exists
            if self.registry.queryUtility(IRendererFactory) is not None:
                renderer = renderers.RendererHelper(
                    name=None,
                    package=self.package,
                    registry=self.registry)
                    name=None, package=self.package, registry=self.registry
                )
        options = dict(
            view=view,
@@ -1581,7 +1615,7 @@
            decorator=decorator,
            http_cache=http_cache,
            require_csrf=require_csrf,
            route_name=route_name
            route_name=route_name,
        )
        if extra_options:
            options.update(extra_options)
@@ -1624,7 +1658,7 @@
        mapper=None,
        match_param=None,
        **view_options
        ):
    ):
        """ Add a forbidden view to the current configuration state.  The
        view will be called when Pyramid or application code raises a
        :exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of
@@ -1658,13 +1692,18 @@
           The view is created using ``exception_only=True``.
        """
        for arg in (
            'name', 'permission', 'context', 'for_', 'require_csrf',
            'name',
            'permission',
            'context',
            'for_',
            'require_csrf',
            'exception_only',
        ):
            if arg in view_options:
                raise ConfigurationError(
                    '%s may not be used as an argument to add_forbidden_view'
                    % (arg,))
                    % (arg,)
                )
        if view is None:
            view = default_exceptionresponse_view
@@ -1691,11 +1730,11 @@
            require_csrf=False,
            attr=attr,
            renderer=renderer,
            )
        )
        settings.update(view_options)
        return self.add_view(**settings)
    set_forbidden_view = add_forbidden_view # deprecated sorta-bw-compat alias
    set_forbidden_view = add_forbidden_view  # deprecated sorta-bw-compat alias
    @viewdefaults
    @action_method
@@ -1720,7 +1759,7 @@
        match_param=None,
        append_slash=False,
        **view_options
        ):
    ):
        """ Add a default :term:`Not Found View` to the current configuration
        state. The view will be called when Pyramid or application code raises
        an :exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g., when a
@@ -1766,7 +1805,8 @@
            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.HTTPTemporaryRedirect`
        attempted, but instead of
        :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`
        being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will
        be used` for the redirect response if a slash-appended route is found.
@@ -1789,18 +1829,24 @@
        .. versionchanged: 1.10
           Default response was changed from :class:`~pyramid.httpexceptions.HTTPFound`
           Default response was changed from
           :class:`~pyramid.httpexceptions.HTTPFound`
           to :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`.
        """
        for arg in (
            'name', 'permission', 'context', 'for_', 'require_csrf',
            'name',
            'permission',
            'context',
            'for_',
            'require_csrf',
            'exception_only',
        ):
            if arg in view_options:
                raise ConfigurationError(
                    '%s may not be used as an argument to add_notfound_view'
                    % (arg,))
                    % (arg,)
                )
        if view is None:
            view = default_exceptionresponse_view
@@ -1825,13 +1871,13 @@
            route_name=route_name,
            permission=NO_PERMISSION_REQUIRED,
            require_csrf=False,
            )
        )
        settings.update(view_options)
        if append_slash:
            view = self._derive_view(view, attr=attr, renderer=renderer)
            if IResponse.implementedBy(append_slash):
                view = AppendSlashNotFoundViewFactory(
                    view, redirect_class=append_slash,
                    view, redirect_class=append_slash
                )
            else:
                view = AppendSlashNotFoundViewFactory(view)
@@ -1841,7 +1887,7 @@
            settings['renderer'] = renderer
        return self.add_view(**settings)
    set_notfound_view = add_notfound_view # deprecated sorta-bw-compat alias
    set_notfound_view = add_notfound_view  # deprecated sorta-bw-compat alias
    @viewdefaults
    @action_method
@@ -1851,7 +1897,7 @@
        context=None,
        # force all other arguments to be specified as key=value
        **view_options
        ):
    ):
        """ Add an :term:`exception view` for the specified ``exception`` to
        the current configuration state. The view will be called when Pyramid
        or application code raises the given exception.
@@ -1867,21 +1913,28 @@
        .. versionadded:: 1.8
        """
        for arg in (
            'name', 'for_', 'exception_only', 'require_csrf', 'permission',
            'name',
            'for_',
            'exception_only',
            'require_csrf',
            'permission',
        ):
            if arg in view_options:
                raise ConfigurationError(
                    '%s may not be used as an argument to add_exception_view'
                    % (arg,))
                    % (arg,)
                )
        if context is None:
            context = Exception
        view_options.update(dict(
            view=view,
            context=context,
            exception_only=True,
            permission=NO_PERMISSION_REQUIRED,
            require_csrf=False,
        ))
        view_options.update(
            dict(
                view=view,
                context=context,
                exception_only=True,
                permission=NO_PERMISSION_REQUIRED,
                require_csrf=False,
            )
        )
        return self.add_view(**view_options)
    @action_method
@@ -1909,17 +1962,25 @@
           can be used to achieve the same purpose.
        """
        mapper = self.maybe_dotted(mapper)
        def register():
            self.registry.registerUtility(mapper, IViewMapperFactory)
        # IViewMapperFactory is looked up as the result of view config
        # in phase 3
        intr = self.introspectable('view mappers',
                                   IViewMapperFactory,
                                   self.object_description(mapper),
                                   'default view mapper')
        intr = self.introspectable(
            'view mappers',
            IViewMapperFactory,
            self.object_description(mapper),
            'default view mapper',
        )
        intr['mapper'] = mapper
        self.action(IViewMapperFactory, register, order=PHASE1_CONFIG,
                    introspectables=(intr,))
        self.action(
            IViewMapperFactory,
            register,
            order=PHASE1_CONFIG,
            introspectables=(intr,),
        )
    @action_method
    def add_static_view(self, name, path, **kw):
@@ -2054,14 +2115,15 @@
            self.registry.registerUtility(info, IStaticURLInfo)
        return info
def isexception(o):
    if IInterface.providedBy(o):
        if IException.isEqualOrExtendedBy(o):
            return True
    return (
        isinstance(o, Exception) or
        (inspect.isclass(o) and (issubclass(o, Exception)))
        )
    return isinstance(o, Exception) or (
        inspect.isclass(o) and (issubclass(o, Exception))
    )
def runtime_exc_view(view, excview):
    # create a view callable which can pretend to be both a normal view
@@ -2094,6 +2156,7 @@
            fn = getattr(selected_view, attr, None)
            if fn is not None:
                return fn(context, request)
        return wrapper
    # these methods are dynamic per-request and should dispatch to their
@@ -2104,16 +2167,12 @@
    wrapper_view.__predicates__ = wrap_fn('__predicates__')
    return wrapper_view
@implementer(IViewDeriverInfo)
class ViewDeriverInfo(object):
    def __init__(self,
                 view,
                 registry,
                 package,
                 predicates,
                 exception_only,
                 options,
                 ):
    def __init__(
        self, view, registry, package, predicates, exception_only, options
    ):
        self.original_view = view
        self.registry = registry
        self.package = package
@@ -2125,6 +2184,7 @@
    def settings(self):
        return self.registry.settings
@implementer(IStaticURLInfo)
class StaticURLInfo(object):
    def __init__(self):
@@ -2134,12 +2194,13 @@
    def generate(self, path, request, **kw):
        for (url, spec, route_name) in self.registrations:
            if path.startswith(spec):
                subpath = path[len(spec):]
                if WIN: # pragma: no cover
                    subpath = subpath.replace('\\', '/') # windows
                subpath = path[len(spec) :]
                if WIN:  # pragma: no cover
                    subpath = subpath.replace('\\', '/')  # windows
                if self.cache_busters:
                    subpath, kw = self._bust_asset_path(
                        request, spec, subpath, kw)
                        request, spec, subpath, kw
                    )
                if url is None:
                    kw['subpath'] = subpath
                    return request.route_url(route_name, **kw)
@@ -2147,8 +2208,11 @@
                    app_url, qs, anchor = parse_url_overrides(request, kw)
                    parsed = url_parse(url)
                    if not parsed.scheme:
                        url = urlparse.urlunparse(parsed._replace(
                            scheme=request.environ['wsgi.url_scheme']))
                        url = urlparse.urlunparse(
                            parsed._replace(
                                scheme=request.environ['wsgi.url_scheme']
                            )
                        )
                    subpath = url_quote(subpath)
                    result = urljoin(url, subpath)
                    return result + qs + anchor
@@ -2161,7 +2225,7 @@
        # appending a slash here if the spec doesn't have one is
        # required for proper prefix matching done in ``generate``
        # (``subpath = path[len(spec):]``).
        if os.path.isabs(spec): # FBO windows
        if os.path.isabs(spec):  # FBO windows
            sep = os.sep
        else:
            sep = '/'
@@ -2189,8 +2253,9 @@
            cache_max_age = extra.pop('cache_max_age', None)
            # create a view
            view = static_view(spec, cache_max_age=cache_max_age,
                               use_subpath=True)
            view = static_view(
                spec, cache_max_age=cache_max_age, use_subpath=True
            )
            # Mutate extra to allow factory, etc to be passed through here.
            # Treat permission specially because we'd like to default to
@@ -2207,7 +2272,7 @@
            # register a route using the computed view, permission, and
            # pattern, plus any extras passed to us via add_static_view
            pattern = "%s*subpath" % name # name already ends with slash
            pattern = "%s*subpath" % name  # name already ends with slash
            if config.route_prefix:
                route_name = '__%s/%s' % (config.route_prefix, name)
            else:
@@ -2233,10 +2298,9 @@
            # url, spec, route_name
            registrations.append((url, spec, route_name))
        intr = config.introspectable('static views',
                                     name,
                                     'static view for %r' % name,
                                     'static view')
        intr = config.introspectable(
            'static views', name, 'static view for %r' % name, 'static view'
        )
        intr['name'] = name
        intr['spec'] = spec
@@ -2245,7 +2309,7 @@
    def add_cache_buster(self, config, spec, cachebust, explicit=False):
        # ensure the spec always has a trailing slash as we only support
        # adding cache busters to folders, not files
        if os.path.isabs(spec): # FBO windows
        if os.path.isabs(spec):  # FBO windows
            sep = os.sep
        else:
            sep = '/'
@@ -2282,10 +2346,9 @@
            cache_busters.insert(new_idx, (spec, cachebust, explicit))
        intr = config.introspectable('cache busters',
                                     spec,
                                     'cache buster for %r' % spec,
                                     'cache buster')
        intr = config.introspectable(
            'cache busters', spec, 'cache buster for %r' % spec, 'cache buster'
        )
        intr['cachebust'] = cachebust
        intr['path'] = spec
        intr['explicit'] = explicit
@@ -2318,9 +2381,8 @@
        kw['pathspec'] = pathspec
        kw['rawspec'] = rawspec
        for spec_, cachebust, explicit in reversed(self.cache_busters):
            if (
                (explicit and rawspec.startswith(spec_)) or
                (not explicit and pathspec.startswith(spec_))
            if (explicit and rawspec.startswith(spec_)) or (
                not explicit and pathspec.startswith(spec_)
            ):
                subpath, kw = cachebust(request, subpath, kw)
                break
src/pyramid/config/zca.py
@@ -1,5 +1,6 @@
from pyramid.threadlocal import get_current_registry
class ZCAConfiguratorMixin(object):
    def hook_zca(self):
        """ Call :func:`zope.component.getSiteManager.sethook` with the
@@ -10,11 +11,12 @@
        :app:`Pyramid` :term:`application registry` rather than the Zope
        'global' registry."""
        from zope.component import getSiteManager
        getSiteManager.sethook(get_current_registry)
    def unhook_zca(self):
        """ Call :func:`zope.component.getSiteManager.reset` to undo the
        action of :meth:`pyramid.config.Configurator.hook_zca`."""
        from zope.component import getSiteManager
        getSiteManager.reset()
        getSiteManager.reset()
src/pyramid/csrf.py
@@ -4,22 +4,11 @@
from zope.interface import implementer
from pyramid.compat import (
    bytes_,
    urlparse,
    text_,
)
from pyramid.exceptions import (
    BadCSRFOrigin,
    BadCSRFToken,
)
from pyramid.compat import bytes_, urlparse, text_
from pyramid.exceptions import BadCSRFOrigin, BadCSRFToken
from pyramid.interfaces import ICSRFStoragePolicy
from pyramid.settings import aslist
from pyramid.util import (
    SimpleSerializer,
    is_same_domain,
    strings_differ
)
from pyramid.util import SimpleSerializer, is_same_domain, strings_differ
@implementer(ICSRFStoragePolicy)
@@ -37,6 +26,7 @@
    .. versionadded:: 1.9
    """
    def new_csrf_token(self, request):
        """ Sets a new CSRF token into the session and returns it. """
        return request.session.new_csrf_token()
@@ -50,7 +40,8 @@
        """ Returns ``True`` if the ``supplied_token`` is valid."""
        expected_token = self.get_csrf_token(request)
        return not strings_differ(
            bytes_(expected_token), bytes_(supplied_token))
            bytes_(expected_token), bytes_(supplied_token)
        )
@implementer(ICSRFStoragePolicy)
@@ -68,6 +59,7 @@
    .. versionadded:: 1.9
    """
    _token_factory = staticmethod(lambda: text_(uuid.uuid4().hex))
    def __init__(self, key='_csrft_'):
@@ -91,7 +83,8 @@
        """ Returns ``True`` if the ``supplied_token`` is valid."""
        expected_token = self.get_csrf_token(request)
        return not strings_differ(
            bytes_(expected_token), bytes_(supplied_token))
            bytes_(expected_token), bytes_(supplied_token)
        )
@implementer(ICSRFStoragePolicy)
@@ -111,10 +104,19 @@
       Added the ``samesite`` option and made the default ``'Lax'``.
    """
    _token_factory = staticmethod(lambda: text_(uuid.uuid4().hex))
    def __init__(self, cookie_name='csrf_token', secure=False, httponly=False,
                 domain=None, max_age=None, path='/', samesite='Lax'):
    def __init__(
        self,
        cookie_name='csrf_token',
        secure=False,
        httponly=False,
        domain=None,
        max_age=None,
        path='/',
        samesite='Lax',
    ):
        serializer = SimpleSerializer()
        self.cookie_profile = CookieProfile(
            cookie_name=cookie_name,
@@ -132,11 +134,10 @@
        """ Sets a new CSRF token into the request and returns it. """
        token = self._token_factory()
        request.cookies[self.cookie_name] = token
        def set_cookie(request, response):
            self.cookie_profile.set_cookies(
                response,
                token,
            )
            self.cookie_profile.set_cookies(response, token)
        request.add_response_callback(set_cookie)
        return token
@@ -153,7 +154,8 @@
        """ Returns ``True`` if the ``supplied_token`` is valid."""
        expected_token = self.get_csrf_token(request)
        return not strings_differ(
            bytes_(expected_token), bytes_(supplied_token))
            bytes_(expected_token), bytes_(supplied_token)
        )
def get_csrf_token(request):
@@ -182,10 +184,9 @@
    return csrf.new_csrf_token(request)
def check_csrf_token(request,
                     token='csrf_token',
                     header='X-CSRF-Token',
                     raises=True):
def check_csrf_token(
    request, token='csrf_token', header='X-CSRF-Token', raises=True
):
    """ Check the CSRF token returned by the
    :class:`pyramid.interfaces.ICSRFStoragePolicy` implementation against the
    value in ``request.POST.get(token)`` (if a POST request) or
@@ -246,8 +247,8 @@
    Check the ``Origin`` of the request to see if it is a cross site request or
    not.
    If the value supplied by the ``Origin`` or ``Referer`` header isn't one of the
    trusted origins and ``raises`` is ``True``, this function will raise a
    If the value supplied by the ``Origin`` or ``Referer`` header isn't one of
    the trusted origins and ``raises`` is ``True``, this function will raise a
    :exc:`pyramid.exceptions.BadCSRFOrigin` exception, but if ``raises`` is
    ``False``, this function will return ``False`` instead. If the CSRF origin
    checks are successful this function will return ``True`` unconditionally.
@@ -267,6 +268,7 @@
       Moved from :mod:`pyramid.session` to :mod:`pyramid.csrf`
    """
    def _fail(reason):
        if raises:
            raise BadCSRFOrigin(reason)
@@ -315,7 +317,8 @@
        if trusted_origins is None:
            trusted_origins = aslist(
                request.registry.settings.get(
                    "pyramid.csrf_trusted_origins", [])
                    "pyramid.csrf_trusted_origins", []
                )
            )
        if request.host_port not in set(["80", "443"]):
@@ -325,8 +328,9 @@
        # Actually check to see if the request's origin matches any of our
        # trusted origins.
        if not any(is_same_domain(originp.netloc, host)
                   for host in trusted_origins):
        if not any(
            is_same_domain(originp.netloc, host) for host in trusted_origins
        ):
            reason = (
                "Referer checking failed - {0} does not match any trusted "
                "origins."
src/pyramid/decorator.py
@@ -32,6 +32,7 @@
        >>> f.jammy
        2
    """
    def __init__(self, wrapped):
        self.wrapped = wrapped
        update_wrapper(self, wrapped)
@@ -42,4 +43,3 @@
        val = self.wrapped(inst)
        setattr(inst, self.wrapped.__name__, val)
        return val
src/pyramid/encode.py
@@ -4,15 +4,17 @@
    is_nonstr_iter,
    url_quote as _url_quote,
    url_quote_plus as _quote_plus,
    )
)
def url_quote(val, safe=''): # bw compat api
def url_quote(val, safe=''):  # bw compat api
    cls = val.__class__
    if cls is text_type:
        val = val.encode('utf-8')
    elif cls is not binary_type:
        val = str(val).encode('utf-8')
    return _url_quote(val, safe=safe)
# bw compat api (dnr)
def quote_plus(val, safe=''):
@@ -23,6 +25,7 @@
        val = str(val).encode('utf-8')
    return _quote_plus(val, safe=safe)
def urlencode(query, doseq=True, quote_via=quote_plus):
    """
    An alternate implementation of Python's stdlib
src/pyramid/events.py
@@ -1,9 +1,6 @@
import venusian
from zope.interface import (
    implementer,
    Interface
    )
from zope.interface import implementer, Interface
from pyramid.interfaces import (
    IContextFound,
@@ -12,7 +9,8 @@
    IApplicationCreated,
    IBeforeRender,
    IBeforeTraversal,
    )
)
class subscriber(object):
    """ Decorator activated via a :term:`scan` which treats the function
@@ -89,7 +87,8 @@
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian # for unit testing
    venusian = venusian  # for unit testing
    def __init__(self, *ifaces, **predicates):
        self.ifaces = ifaces
@@ -103,9 +102,14 @@
            config.add_subscriber(wrapped, iface, **self.predicates)
    def __call__(self, wrapped):
        self.venusian.attach(wrapped, self.register, category=self.category,
                             depth=self.depth + 1)
        self.venusian.attach(
            wrapped,
            self.register,
            category=self.category,
            depth=self.depth + 1,
        )
        return wrapped
@implementer(INewRequest)
class NewRequest(object):
@@ -114,8 +118,10 @@
    event instance has an attribute, ``request``, which is a
    :term:`request` object.  This event class implements the
    :class:`pyramid.interfaces.INewRequest` interface."""
    def __init__(self, request):
        self.request = request
@implementer(INewResponse)
class NewResponse(object):
@@ -149,9 +155,11 @@
       almost purely for symmetry with the
       :class:`pyramid.interfaces.INewRequest` event.
    """
    def __init__(self, request, response):
        self.request = request
        self.response = response
@implementer(IBeforeTraversal)
class BeforeTraversal(object):
@@ -172,6 +180,7 @@
    def __init__(self, request):
        self.request = request
@implementer(IContextFound)
class ContextFound(object):
@@ -194,10 +203,13 @@
       As of :app:`Pyramid` 1.0, for backwards compatibility purposes, this
       event may also be imported as :class:`pyramid.events.AfterTraversal`.
    """
    def __init__(self, request):
        self.request = request
AfterTraversal = ContextFound # b/c as of 1.0
AfterTraversal = ContextFound  # b/c as of 1.0
@implementer(IApplicationCreated)
class ApplicationCreated(object):
@@ -214,11 +226,14 @@
       :class:`pyramid.events.WSGIApplicationCreatedEvent`.  This was the name
       of the event class before :app:`Pyramid` 1.0.
    """
    def __init__(self, app):
        self.app = app
        self.object = app
WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.0)
WSGIApplicationCreatedEvent = ApplicationCreated  # b/c (as of 1.0)
@implementer(IBeforeRender)
class BeforeRender(dict):
@@ -283,7 +298,7 @@
        See also :class:`pyramid.interfaces.IBeforeRender`.
    """
    def __init__(self, system, rendering_val=None):
        dict.__init__(self, system)
        self.rendering_val = rendering_val
src/pyramid/exceptions.py
@@ -1,11 +1,7 @@
from pyramid.httpexceptions import (
    HTTPBadRequest,
    HTTPNotFound,
    HTTPForbidden,
    )
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPForbidden
NotFound = HTTPNotFound # bw compat
Forbidden = HTTPForbidden # bw compat
NotFound = HTTPNotFound  # bw compat
Forbidden = HTTPForbidden  # bw compat
CR = '\n'
@@ -15,6 +11,7 @@
    This exception indicates the request has failed cross-site request forgery
    origin validation.
    """
    title = "Bad CSRF Origin"
    explanation = (
        "Access is denied. This server can not verify that the origin or "
@@ -29,6 +26,7 @@
    This exception indicates the request has failed cross-site request
    forgery token validation.
    """
    title = 'Bad CSRF Token'
    explanation = (
        'Access is denied.  This server can not verify that your cross-site '
@@ -36,7 +34,9 @@
        'supplied the wrong cross-site request forgery token or your session '
        'no longer exists.  This may be due to session timeout or because '
        'browser is not supplying the credentials required, as can happen '
        'when the browser has cookies turned off.')
        'when the browser has cookies turned off.'
    )
class PredicateMismatch(HTTPNotFound):
    """
@@ -65,6 +65,7 @@
    exception view.
    """
class URLDecodeError(UnicodeDecodeError):
    """
    This exception is raised when :app:`Pyramid` cannot
@@ -76,9 +77,11 @@
    decoded.
    """
class ConfigurationError(Exception):
    """ Raised when inappropriate input values are supplied to an API
    method of a :term:`Configurator`"""
class ConfigurationConflictError(ConfigurationError):
    """ Raised when a configuration conflict is detected during action
@@ -91,7 +94,7 @@
        r = ["Conflicting configuration actions"]
        items = sorted(self._conflicts.items())
        for discriminator, infos in items:
            r.append("  For: %s" % (discriminator, ))
            r.append("  For: %s" % (discriminator,))
            for info in infos:
                for line in str(info).rstrip().split(CR):
                    r.append("    " + line)
@@ -113,6 +116,7 @@
class CyclicDependencyError(Exception):
    """ The exception raised when the Pyramid topological sorter detects a
    cyclic dependency."""
    def __init__(self, cycles):
        self.cycles = cycles
src/pyramid/httpexceptions.py
@@ -137,15 +137,11 @@
from webob import html_escape as _html_escape
from webob.acceptparse import create_accept_header
from pyramid.compat import (
    class_types,
    text_type,
    binary_type,
    text_,
    )
from pyramid.compat import class_types, text_type, binary_type, text_
from pyramid.interfaces import IExceptionResponse
from pyramid.response import Response
def _no_escape(value):
    if value is None:
@@ -159,10 +155,10 @@
            value = text_type(value)
    return value
@implementer(IExceptionResponse)
class HTTPException(Response, Exception):
    ## You should set in subclasses:
    # You should set in subclasses:
    # code = 200
    # title = 'OK'
    # explanation = 'why this happens'
@@ -190,23 +186,29 @@
    #   implies that this class' ``exception`` property always returns
    #   ``self`` (it exists only for bw compat at this point).
    #
    # - documentation improvements (Pyramid-specific docstrings where necessary)
    # - documentation improvements (Pyramid-specific docstrings where
    #   necessary)
    #
    code = 520
    title = 'Unknown Error'
    explanation = ''
    body_template_obj = Template('''\
    body_template_obj = Template(
        '''\
${explanation}${br}${br}
${detail}
${html_comment}
''')
'''
    )
    plain_template_obj = Template('''\
    plain_template_obj = Template(
        '''\
${status}
${body}''')
${body}'''
    )
    html_template_obj = Template('''\
    html_template_obj = Template(
        '''\
<html>
 <head>
  <title>${status}</title>
@@ -215,13 +217,21 @@
  <h1>${status}</h1>
  ${body}
 </body>
</html>''')
</html>'''
    )
    ## Set this to True for responses that should have no request body
    # Set this to True for responses that should have no request body
    empty_body = False
    def __init__(self, detail=None, headers=None, comment=None,
                 body_template=None, json_formatter=None, **kw):
    def __init__(
        self,
        detail=None,
        headers=None,
        comment=None,
        body_template=None,
        json_formatter=None,
        **kw
    ):
        status = '%s %s' % (self.code, self.title)
        Response.__init__(self, status=status, **kw)
        Exception.__init__(self, detail)
@@ -243,9 +253,7 @@
        return str(self.detail) if self.detail else self.explanation
    def _json_formatter(self, status, body, title, environ):
        return {'message': body,
                'code': status,
                'title': self.title}
        return {'message': body, 'code': status, 'title': self.title}
    def prepare(self, environ):
        if not self.has_body and not self.empty_body:
@@ -255,7 +263,9 @@
            accept = create_accept_header(accept_value)
            # Attempt to match text/html or application/json, if those don't
            # match, we will fall through to defaulting to text/plain
            acceptable = accept.acceptable_offers(['text/html', 'application/json'])
            acceptable = accept.acceptable_offers(
                ['text/html', 'application/json']
            )
            acceptable = [offer[0] for offer in acceptable] + ['text/plain']
            match = acceptable[0]
@@ -281,8 +291,10 @@
                    def substitute(self, status, body):
                        jsonbody = self.excobj._json_formatter(
                            status=status,
                            body=body, title=self.excobj.title,
                            environ=environ)
                            body=body,
                            title=self.excobj.title,
                            environ=environ,
                        )
                        return json.dumps(jsonbody)
                page_template = JsonPageTemplate(self)
@@ -299,7 +311,7 @@
                'detail': escape(self.detail or ''),
                'comment': escape(comment),
                'html_comment': html_comment,
                }
            }
            body_tmpl = self.body_template_obj
            if HTTPException.body_template_obj is not body_tmpl:
                # Custom template; add headers to args
@@ -324,7 +336,7 @@
        # bw compat only
        return self
    exception = wsgi_response # bw compat only
    exception = wsgi_response  # bw compat only
    def __call__(self, environ, start_response):
        # differences from webob.exc.WSGIHTTPException
@@ -337,7 +349,9 @@
        self.prepare(environ)
        return Response.__call__(self, environ, start_response)
WSGIHTTPException = HTTPException # b/c post 1.5
WSGIHTTPException = HTTPException  # b/c post 1.5
class HTTPError(HTTPException):
    """
@@ -346,6 +360,7 @@
    This is an exception which indicates that an error has occurred,
    and that any work in progress should not be committed.
    """
class HTTPRedirection(HTTPException):
    """
@@ -357,15 +372,18 @@
    condition.
    """
class HTTPSuccessful(HTTPException):
    """
    Base class for exceptions with status codes in the 200s (successful
    responses)
    """
############################################################
## 2xx success
# 2xx success
############################################################
class HTTPOk(HTTPSuccessful):
    """
@@ -375,8 +393,10 @@
    code: 200, title: OK
    """
    code = 200
    title = 'OK'
class HTTPCreated(HTTPSuccessful):
    """
@@ -387,8 +407,10 @@
    code: 201, title: Created
    """
    code = 201
    title = 'Created'
class HTTPAccepted(HTTPSuccessful):
    """
@@ -399,9 +421,11 @@
    code: 202, title: Accepted
    """
    code = 202
    title = 'Accepted'
    explanation = 'The request is accepted for processing.'
class HTTPNonAuthoritativeInformation(HTTPSuccessful):
    """
@@ -413,8 +437,10 @@
    code: 203, title: Non-Authoritative Information
    """
    code = 203
    title = 'Non-Authoritative Information'
class HTTPNoContent(HTTPSuccessful):
    """
@@ -426,9 +452,11 @@
    code: 204, title: No Content
    """
    code = 204
    title = 'No Content'
    empty_body = True
class HTTPResetContent(HTTPSuccessful):
    """
@@ -440,9 +468,11 @@
    code: 205, title: Reset Content
    """
    code = 205
    title = 'Reset Content'
    empty_body = True
class HTTPPartialContent(HTTPSuccessful):
    """
@@ -453,14 +483,17 @@
    code: 206, title: Partial Content
    """
    code = 206
    title = 'Partial Content'
## FIXME: add 207 Multi-Status (but it's complicated)
# FIXME: add 207 Multi-Status (but it's complicated)
############################################################
## 3xx redirection
# 3xx redirection
############################################################
class _HTTPMove(HTTPRedirection):
    """
@@ -472,6 +505,7 @@
    You must provide a ``location`` keyword argument.
    """
    # differences from webob.exc._HTTPMove:
    #
    # - ${location} isn't wrapped in an <a> tag in body
@@ -486,18 +520,33 @@
    # - ``add_slash`` argument is no longer accepted:  code that passes
    #   add_slash argument to the constructor will receive an exception.
    explanation = 'The resource has been moved to'
    body_template_obj = Template('''\
    body_template_obj = Template(
        '''\
${explanation} ${location}; you should be redirected automatically.
${detail}
${html_comment}''')
${html_comment}'''
    )
    def __init__(self, location='', detail=None, headers=None, comment=None,
                 body_template=None, **kw):
    def __init__(
        self,
        location='',
        detail=None,
        headers=None,
        comment=None,
        body_template=None,
        **kw
    ):
        if location is None:
            raise ValueError("HTTP redirects need a location to redirect to.")
        super(_HTTPMove, self).__init__(
            detail=detail, headers=headers, comment=comment,
            body_template=body_template, location=location, **kw)
            detail=detail,
            headers=headers,
            comment=comment,
            body_template=body_template,
            location=location,
            **kw
        )
class HTTPMultipleChoices(_HTTPMove):
    """
@@ -511,8 +560,10 @@
    code: 300, title: Multiple Choices
    """
    code = 300
    title = 'Multiple Choices'
class HTTPMovedPermanently(_HTTPMove):
    """
@@ -524,8 +575,10 @@
    code: 301, title: Moved Permanently
    """
    code = 301
    title = 'Moved Permanently'
class HTTPFound(_HTTPMove):
    """
@@ -536,9 +589,11 @@
    code: 302, title: Found
    """
    code = 302
    title = 'Found'
    explanation = 'The resource was found at'
# This one is safe after a POST (the redirected location will be
# retrieved with GET):
@@ -552,8 +607,10 @@
    code: 303, title: See Other
    """
    code = 303
    title = 'See Other'
class HTTPNotModified(HTTPRedirection):
    """
@@ -565,10 +622,12 @@
    code: 304, title: Not Modified
    """
    # FIXME: this should include a date or etag header
    code = 304
    title = 'Not Modified'
    empty_body = True
class HTTPUseProxy(_HTTPMove):
    """
@@ -579,11 +638,12 @@
    code: 305, title: Use Proxy
    """
    # Not a move, but looks a little like one
    code = 305
    title = 'Use Proxy'
    explanation = (
        'The resource must be accessed through a proxy located at')
    explanation = 'The resource must be accessed through a proxy located at'
class HTTPTemporaryRedirect(_HTTPMove):
    """
@@ -594,8 +654,10 @@
    code: 307, title: Temporary Redirect
    """
    code = 307
    title = 'Temporary Redirect'
class HTTPPermanentRedirect(_HTTPMove):
    """
@@ -607,12 +669,15 @@
    code: 308, title: Permanent Redirect
    """
    code = 308
    title = 'Permanent Redirect'
############################################################
## 4xx client error
# 4xx client error
############################################################
class HTTPClientError(HTTPError):
    """
@@ -623,8 +688,10 @@
    a bug.  A server-side traceback is not warranted.  Unless specialized,
    this is a '400 Bad Request'
    """
    code = 400
    title = 'Bad Request'
class HTTPBadRequest(HTTPClientError):
    """
@@ -635,8 +702,12 @@
    code: 400, title: Bad Request
    """
    explanation = ('The server could not comply with the request since '
                   'it is either malformed or otherwise incorrect.')
    explanation = (
        'The server could not comply with the request since '
        'it is either malformed or otherwise incorrect.'
    )
class HTTPUnauthorized(HTTPClientError):
    """
@@ -646,13 +717,16 @@
    code: 401, title: Unauthorized
    """
    code = 401
    title = 'Unauthorized'
    explanation = (
        'This server could not verify that you are authorized to '
        'access the document you requested.  Either you supplied the '
        'wrong credentials (e.g., bad password), or your browser '
        'does not understand how to supply the credentials required.')
        'does not understand how to supply the credentials required.'
    )
class HTTPPaymentRequired(HTTPClientError):
    """
@@ -660,9 +734,11 @@
    code: 402, title: Payment Required
    """
    code = 402
    title = 'Payment Required'
    explanation = ('Access was denied for financial reasons.')
    explanation = 'Access was denied for financial reasons.'
class HTTPForbidden(HTTPClientError):
    """
@@ -693,6 +769,7 @@
    exception as necessary to provide extended information in an error
    report shown to a user.
    """
    # differences from webob.exc.HTTPForbidden:
    #
    # - accepts a ``result`` keyword argument
@@ -705,13 +782,27 @@
    #
    code = 403
    title = 'Forbidden'
    explanation = ('Access was denied to this resource.')
    def __init__(self, detail=None, headers=None, comment=None,
                 body_template=None, result=None, **kw):
        HTTPClientError.__init__(self, detail=detail, headers=headers,
                                 comment=comment, body_template=body_template,
                                 **kw)
    explanation = 'Access was denied to this resource.'
    def __init__(
        self,
        detail=None,
        headers=None,
        comment=None,
        body_template=None,
        result=None,
        **kw
    ):
        HTTPClientError.__init__(
            self,
            detail=detail,
            headers=headers,
            comment=comment,
            body_template=body_template,
            **kw
        )
        self.result = result
class HTTPNotFound(HTTPClientError):
    """
@@ -732,9 +823,11 @@
    string will be available as the ``message`` attribute of this exception,
    for availability to the :term:`Not Found View`.
    """
    code = 404
    title = 'Not Found'
    explanation = ('The resource could not be found.')
    explanation = 'The resource could not be found.'
class HTTPMethodNotAllowed(HTTPClientError):
    """
@@ -745,14 +838,18 @@
    code: 405, title: Method Not Allowed
    """
    # differences from webob.exc.HTTPMethodNotAllowed:
    #
    # - body_template_obj uses ${br} instead of <br />
    code = 405
    title = 'Method Not Allowed'
    body_template_obj = Template('''\
    body_template_obj = Template(
        '''\
The method ${REQUEST_METHOD} is not allowed for this resource. ${br}${br}
${detail}''')
${detail}'''
    )
class HTTPNotAcceptable(HTTPClientError):
    """
@@ -765,11 +862,13 @@
    code: 406, title: Not Acceptable
    """
    # differences from webob.exc.HTTPNotAcceptable:
    #
    # - "template" attribute left off (useless, bug in webob?)
    code = 406
    title = 'Not Acceptable'
class HTTPProxyAuthenticationRequired(HTTPClientError):
    """
@@ -780,9 +879,11 @@
    code: 407, title: Proxy Authentication Required
    """
    code = 407
    title = 'Proxy Authentication Required'
    explanation = ('Authentication with a local proxy is needed.')
    explanation = 'Authentication with a local proxy is needed.'
class HTTPRequestTimeout(HTTPClientError):
    """
@@ -793,10 +894,14 @@
    code: 408, title: Request Timeout
    """
    code = 408
    title = 'Request Timeout'
    explanation = ('The server has waited too long for the request to '
                   'be sent by the client.')
    explanation = (
        'The server has waited too long for the request to '
        'be sent by the client.'
    )
class HTTPConflict(HTTPClientError):
    """
@@ -807,10 +912,13 @@
    code: 409, title: Conflict
    """
    code = 409
    title = 'Conflict'
    explanation = ('There was a conflict when trying to complete '
                   'your request.')
    explanation = (
        'There was a conflict when trying to complete ' 'your request.'
    )
class HTTPGone(HTTPClientError):
    """
@@ -821,10 +929,14 @@
    code: 410, title: Gone
    """
    code = 410
    title = 'Gone'
    explanation = ('This resource is no longer available.  No forwarding '
                   'address is given.')
    explanation = (
        'This resource is no longer available.  No forwarding '
        'address is given.'
    )
class HTTPLengthRequired(HTTPClientError):
    """
@@ -835,9 +947,11 @@
    code: 411, title: Length Required
    """
    code = 411
    title = 'Length Required'
    explanation = ('Content-Length header required.')
    explanation = 'Content-Length header required.'
class HTTPPreconditionFailed(HTTPClientError):
    """
@@ -849,9 +963,11 @@
    code: 412, title: Precondition Failed
    """
    code = 412
    title = 'Precondition Failed'
    explanation = ('Request precondition failed.')
    explanation = 'Request precondition failed.'
class HTTPRequestEntityTooLarge(HTTPClientError):
    """
@@ -863,9 +979,11 @@
    code: 413, title: Request Entity Too Large
    """
    code = 413
    title = 'Request Entity Too Large'
    explanation = ('The body of your request was too large for this server.')
    explanation = 'The body of your request was too large for this server.'
class HTTPRequestURITooLong(HTTPClientError):
    """
@@ -877,9 +995,11 @@
    code: 414, title: Request-URI Too Long
    """
    code = 414
    title = 'Request-URI Too Long'
    explanation = ('The request URI was too long for this server.')
    explanation = 'The request URI was too long for this server.'
class HTTPUnsupportedMediaType(HTTPClientError):
    """
@@ -891,11 +1011,13 @@
    code: 415, title: Unsupported Media Type
    """
    # differences from webob.exc.HTTPUnsupportedMediaType:
    #
    # - "template_obj" attribute left off (useless, bug in webob?)
    code = 415
    title = 'Unsupported Media Type'
class HTTPRequestRangeNotSatisfiable(HTTPClientError):
    """
@@ -909,9 +1031,11 @@
    code: 416, title: Request Range Not Satisfiable
    """
    code = 416
    title = 'Request Range Not Satisfiable'
    explanation = ('The Range requested is not available.')
    explanation = 'The Range requested is not available.'
class HTTPExpectationFailed(HTTPClientError):
    """
@@ -922,9 +1046,11 @@
    code: 417, title: Expectation Failed
    """
    code = 417
    title = 'Expectation Failed'
    explanation = ('Expectation failed.')
    explanation = 'Expectation failed.'
class HTTPUnprocessableEntity(HTTPClientError):
    """
@@ -940,10 +1066,12 @@
    code: 422, title: Unprocessable Entity
    """
    ## Note: from WebDAV
    # Note: from WebDAV
    code = 422
    title = 'Unprocessable Entity'
    explanation = 'Unable to process the contained instructions'
class HTTPLocked(HTTPClientError):
    """
@@ -953,10 +1081,12 @@
    code: 423, title: Locked
    """
    ## Note: from WebDAV
    # Note: from WebDAV
    code = 423
    title = 'Locked'
    explanation = ('The resource is locked')
    explanation = 'The resource is locked'
class HTTPFailedDependency(HTTPClientError):
    """
@@ -967,12 +1097,15 @@
    code: 424, title: Failed Dependency
    """
    ## Note: from WebDAV
    # Note: from WebDAV
    code = 424
    title = 'Failed Dependency'
    explanation = (
        'The method could not be performed because the requested '
        'action dependended on another action and that action failed')
        'action dependended on another action and that action failed'
    )
class HTTPPreconditionRequired(HTTPClientError):
    """
@@ -991,10 +1124,11 @@
    code: 428, title: Precondition Required
    """
    code = 428
    title = 'Precondition Required'
    explanation = (
        'The origin server requires the request to be conditional.')
    explanation = 'The origin server requires the request to be conditional.'
class HTTPTooManyRequests(HTTPClientError):
    """
@@ -1007,11 +1141,14 @@
    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.')
        'many requests by the client.'
    )
class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
    """
@@ -1025,13 +1162,14 @@
    code: 431, title: Request Header Fields Too Large
    """
    code = 431
    title = 'Request Header Fields Too Large'
    explanation = (
        'The requests header fields were too large.')
    explanation = 'The requests header fields were too large.'
############################################################
## 5xx Server Error
# 5xx Server Error
############################################################
#  Response status codes beginning with the digit "5" indicate cases in
#  which the server is aware that it has erred or is incapable of
@@ -1041,6 +1179,7 @@
#  agents SHOULD display any included entity to the user. These response
#  codes are applicable to any request method.
class HTTPServerError(HTTPError):
    """
    base class for the 500s, where the server is in-error
@@ -1048,8 +1187,10 @@
    This is an error condition in which the server is presumed to be
    in-error.  Unless specialized, this is a '500 Internal Server Error'.
    """
    code = 500
    title = 'Internal Server Error'
class HTTPInternalServerError(HTTPServerError):
    """
@@ -1060,9 +1201,12 @@
    code: 500, title: Internal Server Error
    """
    explanation = (
        'The server has either erred or is incapable of performing '
        'the requested operation.')
        'the requested operation.'
    )
class HTTPNotImplemented(HTTPServerError):
    """
@@ -1073,11 +1217,13 @@
    code: 501, title: Not Implemented
    """
    # differences from webob.exc.HTTPNotAcceptable:
    #
    # - "template" attr left off (useless, bug in webob?)
    code = 501
    title = 'Not Implemented'
class HTTPBadGateway(HTTPServerError):
    """
@@ -1089,9 +1235,11 @@
    code: 502, title: Bad Gateway
    """
    code = 502
    title = 'Bad Gateway'
    explanation = ('Bad gateway.')
    explanation = 'Bad gateway.'
class HTTPServiceUnavailable(HTTPServerError):
    """
@@ -1102,10 +1250,14 @@
    code: 503, title: Service Unavailable
    """
    code = 503
    title = 'Service Unavailable'
    explanation = ('The server is currently unavailable. '
                   'Please try again at a later time.')
    explanation = (
        'The server is currently unavailable. '
        'Please try again at a later time.'
    )
class HTTPGatewayTimeout(HTTPServerError):
    """
@@ -1118,9 +1270,11 @@
    code: 504, title: Gateway Timeout
    """
    code = 504
    title = 'Gateway Timeout'
    explanation = ('The gateway has timed out.')
    explanation = 'The gateway has timed out.'
class HTTPVersionNotSupported(HTTPServerError):
    """
@@ -1132,9 +1286,11 @@
    code: 505, title: HTTP Version Not Supported
    """
    code = 505
    title = 'HTTP Version Not Supported'
    explanation = ('The HTTP version is not supported.')
    explanation = 'The HTTP version is not supported.'
class HTTPInsufficientStorage(HTTPServerError):
    """
@@ -1145,9 +1301,11 @@
    code: 507, title: Insufficient Storage
    """
    code = 507
    title = 'Insufficient Storage'
    explanation = ('There was not enough space to save the resource')
    explanation = 'There was not enough space to save the resource'
def exception_response(status_code, **kw):
    """Creates an HTTP exception based on a status code. Example::
@@ -1159,22 +1317,24 @@
    exc = status_map[status_code](**kw)
    return exc
def default_exceptionresponse_view(context, request):
    if not isinstance(context, Exception):
        # backwards compat for an exception response view registered via
        # config.set_notfound_view or config.set_forbidden_view
        # instead of as a proper exception view
        context = request.exception or context
    return context # assumed to be an IResponse
    return context  # assumed to be an IResponse
status_map = {}
code = None
for name, value in list(globals().items()):
    if (
            isinstance(value, class_types) and
            issubclass(value, HTTPException) and
            value not in {HTTPClientError, HTTPServerError} and
            not name.startswith('_')
        isinstance(value, class_types)
        and issubclass(value, HTTPException)
        and value not in {HTTPClientError, HTTPServerError}
        and not name.startswith('_')
    ):
        code = getattr(value, 'code', None)
        if code:
src/pyramid/i18n.py
@@ -4,9 +4,9 @@
from translationstring import (
    Translator,
    Pluralizer,
    TranslationString, # API
    TranslationStringFactory, # API
    )
    TranslationString,  # API
    TranslationStringFactory,  # API
)
from pyramid.compat import PY2
from pyramid.decorator import reify
@@ -15,7 +15,7 @@
    ILocalizer,
    ITranslationDirectories,
    ILocaleNegotiator,
    )
)
from pyramid.threadlocal import get_current_registry
@@ -24,6 +24,7 @@
DEFAULT_PLURAL = lambda n: int(n != 1)
class Localizer(object):
    """
    An object providing translation and pluralizations related to
@@ -31,6 +32,7 @@
    :class:`pyramid.i18n.Localizer` object is created using the
    :func:`pyramid.i18n.get_localizer` function.
    """
    def __init__(self, locale_name, translations):
        self.locale_name = locale_name
        self.translations = translations
@@ -80,13 +82,13 @@
        and ``plural`` objects should be unicode strings. There is no
        reason to use translation string objects as arguments as all
        metadata is ignored.
        ``n`` represents the number of elements. ``domain`` is the
        translation domain to use to do the pluralization, and ``mapping``
        is the interpolation mapping that should be used on the result. If
        the ``domain`` is not supplied, a default domain is used (usually
        ``messages``).
        Example::
           num = 1
@@ -108,12 +110,13 @@
                                            num,
                                            mapping={'num':num})
        """
        if self.pluralizer is None:
            self.pluralizer = Pluralizer(self.translations)
        return self.pluralizer(singular, plural, n, domain=domain,
                               mapping=mapping)
        return self.pluralizer(
            singular, plural, n, domain=domain, mapping=mapping
        )
def default_locale_negotiator(request):
@@ -124,7 +127,7 @@
      the request object (possibly set by a view or a listener for an
      :term:`event`). If the attribute exists and it is not ``None``,
      its value will be used.
    - Then it looks for the ``request.params['_LOCALE_']`` value.
    - Then it looks for the ``request.cookies['_LOCALE_']`` value.
@@ -142,6 +145,7 @@
            locale_name = request.cookies.get(name)
    return locale_name
def negotiate_locale_name(request):
    """ Negotiate and return the :term:`locale name` associated with
    the current request."""
@@ -149,8 +153,9 @@
        registry = request.registry
    except AttributeError:
        registry = get_current_registry()
    negotiator = registry.queryUtility(ILocaleNegotiator,
                                       default=default_locale_negotiator)
    negotiator = registry.queryUtility(
        ILocaleNegotiator, default=default_locale_negotiator
    )
    locale_name = negotiator(request)
    if locale_name is None:
@@ -158,6 +163,7 @@
        locale_name = settings.get('default_locale_name', 'en')
    return locale_name
def get_locale_name(request):
    """
@@ -167,9 +173,10 @@
    """
    return request.locale_name
def make_localizer(current_locale_name, translation_directories):
    """ Create a :class:`pyramid.i18n.Localizer` object
    corresponding to the provided locale name from the
    corresponding to the provided locale name from the
    translations found in the list of translation directories."""
    translations = Translations()
    translations._catalog = {}
@@ -199,16 +206,17 @@
            if not os.path.isdir(os.path.realpath(messages_dir)):
                continue
            for mofile in os.listdir(messages_dir):
                mopath = os.path.realpath(os.path.join(messages_dir,
                                                       mofile))
                mopath = os.path.realpath(os.path.join(messages_dir, mofile))
                if mofile.endswith('.mo') and os.path.isfile(mopath):
                    with open(mopath, 'rb') as mofp:
                        domain = mofile[:-3]
                        dtrans = Translations(mofp, domain)
                        translations.add(dtrans)
    return Localizer(locale_name=current_locale_name,
                          translations=translations)
    return Localizer(
        locale_name=current_locale_name, translations=translations
    )
def get_localizer(request):
    """
@@ -218,6 +226,7 @@
        corresponding to the current request's locale name.
    """
    return request.localizer
class Translations(gettext.GNUTranslations, object):
    """An extended translation catalog class (ripped off from Babel) """
@@ -272,8 +281,10 @@
            return cls(fileobj=fp, domain=domain)
    def __repr__(self):
        return '<%s: "%s">' % (type(self).__name__,
                               self._info.get('project-id-version'))
        return '<%s: "%s">' % (
            type(self).__name__,
            self._info.get('project-id-version'),
        )
    def add(self, translations, merge=True):
        """Add the given translations to the catalog.
@@ -331,13 +342,13 @@
        domain.
        """
        return self._domains.get(domain, self).gettext(message)
    def ldgettext(self, domain, message):
        """Like ``lgettext()``, but look the message up in the specified
        """Like ``lgettext()``, but look the message up in the specified
        domain.
        """
        """
        return self._domains.get(domain, self).lgettext(message)
    def dugettext(self, domain, message):
        """Like ``ugettext()``, but look the message up in the specified
        domain.
@@ -346,29 +357,32 @@
            return self._domains.get(domain, self).ugettext(message)
        else:
            return self._domains.get(domain, self).gettext(message)
    def dngettext(self, domain, singular, plural, num):
        """Like ``ngettext()``, but look the message up in the specified
        domain.
        """
        return self._domains.get(domain, self).ngettext(singular, plural, num)
    def ldngettext(self, domain, singular, plural, num):
        """Like ``lngettext()``, but look the message up in the specified
        domain.
        """
        return self._domains.get(domain, self).lngettext(singular, plural, num)
    def dungettext(self, domain, singular, plural, num):
        """Like ``ungettext()`` but look the message up in the specified
        domain.
        """
        if PY2:
            return self._domains.get(domain, self).ungettext(
                singular, plural, num)
                singular, plural, num
            )
        else:
            return self._domains.get(domain, self).ngettext(
                singular, plural, num)
                singular, plural, num
            )
class LocalizerRequestMixin(object):
    @reify
@@ -384,8 +398,9 @@
            tdirs = registry.queryUtility(ITranslationDirectories, default=[])
            localizer = make_localizer(current_locale_name, tdirs)
            registry.registerUtility(localizer, ILocalizer,
                                     name=current_locale_name)
            registry.registerUtility(
                localizer, ILocalizer, name=current_locale_name
            )
        return localizer
@@ -393,5 +408,3 @@
    def locale_name(self):
        locale_name = negotiate_locale_name(self)
        return locale_name
src/pyramid/interfaces.py
@@ -1,13 +1,11 @@
from zope.deprecation import deprecated
from zope.interface import (
    Attribute,
    Interface,
    )
from zope.interface import Attribute, Interface
from pyramid.compat import PY2
# public API interfaces
class IContextFound(Interface):
    """ An event type that is emitted after :app:`Pyramid` finds a
@@ -21,9 +19,12 @@
       :app:`Pyramid` before 1.0, this event interface can also be
       imported as :class:`pyramid.interfaces.IAfterTraversal`.
    """
    request = Attribute('The request object')
IAfterTraversal = IContextFound
class IBeforeTraversal(Interface):
    """
@@ -31,21 +32,27 @@
    route but before it calls any traversal or view code. See the documentation
    attached to :class:`pyramid.events.Routefound` for more information.
    """
    request = Attribute('The request object')
class INewRequest(Interface):
    """ An event type that is emitted whenever :app:`Pyramid`
    begins to process a new request.  See the documentation attached
    to :class:`pyramid.events.NewRequest` for more information."""
    request = Attribute('The request object')
class INewResponse(Interface):
    """ An event type that is emitted whenever any :app:`Pyramid`
    view returns a response. See the
    documentation attached to :class:`pyramid.events.NewResponse`
    for more information."""
    request = Attribute('The request object')
    response = Attribute('The response object')
class IApplicationCreated(Interface):
    """ Event issued when the
@@ -60,9 +67,12 @@
       versions before 1.0, this interface can also be imported as
       :class:`pyramid.interfaces.IWSGIApplicationCreatedEvent`.
    """
    app = Attribute("Created application")
IWSGIApplicationCreatedEvent = IApplicationCreated # b /c
IWSGIApplicationCreatedEvent = IApplicationCreated  # b /c
class IResponse(Interface):
    """ Represents a WSGI response using the WebOb response interface.
@@ -74,7 +84,8 @@
    :mod:`pyramid.httpexceptions`."""
    RequestClass = Attribute(
        """ Alias for :class:`pyramid.request.Request` """)
        """ Alias for :class:`pyramid.request.Request` """
    )
    def __call__(environ, start_response):
        """ :term:`WSGI` call interface, should call the start_response
@@ -82,21 +93,25 @@
    accept_ranges = Attribute(
        """Gets and sets and deletes the Accept-Ranges header. For more
        information on Accept-Ranges see RFC 2616, section 14.5""")
        information on Accept-Ranges see RFC 2616, section 14.5"""
    )
    age = Attribute(
        """Gets and sets and deletes the Age header. Converts using int.
        For more information on Age see RFC 2616, section 14.6.""")
        For more information on Age see RFC 2616, section 14.6."""
    )
    allow = Attribute(
        """Gets and sets and deletes the Allow header. Converts using
        list. For more information on Allow see RFC 2616, Section 14.7.""")
        list. For more information on Allow see RFC 2616, Section 14.7."""
    )
    app_iter = Attribute(
        """Returns the app_iter of the response.
        If body was set, this will create an app_iter from that body
        (a single-item list)""")
        (a single-item list)"""
    )
    def app_iter_range(start, stop):
        """ Return a new app_iter built from the response app_iter that
@@ -104,21 +119,24 @@
    body = Attribute(
        """The body of the response, as a str. This will read in the entire
        app_iter if necessary.""")
        app_iter if necessary."""
    )
    body_file = Attribute(
        """A file-like object that can be used to write to the body. If you
        passed in a list app_iter, that app_iter will be modified by writes.""")
        passed in a list app_iter, that app_iter will be modified by writes."""
    )
    cache_control = Attribute(
        """Get/set/modify the Cache-Control header (RFC 2616 section 14.9)""")
        """Get/set/modify the Cache-Control header (RFC 2616 section 14.9)"""
    )
    cache_expires = Attribute(
        """ Get/set the Cache-Control and Expires headers. This sets the
            response to expire in the number of seconds passed when set. """)
            response to expire in the number of seconds passed when set. """
    )
    charset = Attribute(
        """Get/set the charset (in the Content-Type)""")
    charset = Attribute("""Get/set the charset (in the Content-Type)""")
    def conditional_response_app(environ, start_response):
        """ Like the normal __call__ interface, but checks conditional
@@ -133,52 +151,62 @@
    content_disposition = Attribute(
        """Gets and sets and deletes the Content-Disposition header.
        For more information on Content-Disposition see RFC 2616 section
        19.5.1.""")
        19.5.1."""
    )
    content_encoding = Attribute(
        """Gets and sets and deletes the Content-Encoding header.  For more
        information about Content-Encoding see RFC 2616 section 14.11.""")
        information about Content-Encoding see RFC 2616 section 14.11."""
    )
    content_language = Attribute(
        """Gets and sets and deletes the Content-Language header. Converts
        using list.  For more information about Content-Language see RFC 2616
        section 14.12.""")
        section 14.12."""
    )
    content_length = Attribute(
        """Gets and sets and deletes the Content-Length header. For more
        information on Content-Length see RFC 2616 section 14.17.
        Converts using int. """)
        Converts using int. """
    )
    content_location = Attribute(
        """Gets and sets and deletes the Content-Location header. For more
        information on Content-Location see RFC 2616 section 14.14.""")
        information on Content-Location see RFC 2616 section 14.14."""
    )
    content_md5 = Attribute(
        """Gets and sets and deletes the Content-MD5 header. For more
        information on Content-MD5 see RFC 2616 section 14.14.""")
        information on Content-MD5 see RFC 2616 section 14.14."""
    )
    content_range = Attribute(
        """Gets and sets and deletes the Content-Range header. For more
        information on Content-Range see section 14.16. Converts using
        ContentRange object.""")
        ContentRange object."""
    )
    content_type = Attribute(
        """Get/set the Content-Type header (or None), without the charset
        or any parameters. If you include parameters (or ; at all) when
        setting the content_type, any existing parameters will be deleted;
        otherwise they will be preserved.""")
        otherwise they will be preserved."""
    )
    content_type_params = Attribute(
        """A dictionary of all the parameters in the content type.  This is
        not a view, set to change, modifications of the dict would not
        be applied otherwise.""")
        be applied otherwise."""
    )
    def copy():
        """ Makes a copy of the response and returns the copy. """
    date = Attribute(
        """Gets and sets and deletes the Date header. For more information on
        Date see RFC 2616 section 14.18. Converts using HTTP date.""")
        Date see RFC 2616 section 14.18. Converts using HTTP date."""
    )
    def delete_cookie(name, path='/', domain=None):
        """ Delete a cookie from the client. Note that path and domain must
@@ -191,31 +219,34 @@
    environ = Attribute(
        """Get/set the request environ associated with this response,
        if any.""")
        if any."""
    )
    etag = Attribute(
        """ Gets and sets and deletes the ETag header. For more information
        on ETag see RFC 2616 section 14.19. Converts using Entity tag.""")
        on ETag see RFC 2616 section 14.19. Converts using Entity tag."""
    )
    expires = Attribute(
        """ Gets and sets and deletes the Expires header. For more
        information on Expires see RFC 2616 section 14.21. Converts using
        HTTP date.""")
        HTTP date."""
    )
    headerlist = Attribute(
        """ The list of response headers. """)
    headerlist = Attribute(""" The list of response headers. """)
    headers = Attribute(
        """ The headers in a dictionary-like object """)
    headers = Attribute(""" The headers in a dictionary-like object """)
    last_modified = Attribute(
        """ Gets and sets and deletes the Last-Modified header. For more
        information on Last-Modified see RFC 2616 section 14.29. Converts
        using HTTP date.""")
        using HTTP date."""
    )
    location = Attribute(
        """ Gets and sets and deletes the Location header. For more
        information on Location see RFC 2616 section 14.30.""")
        information on Location see RFC 2616 section 14.30."""
    )
    def md5_etag(body=None, set_content_md5=False):
        """ Generate an etag for the response object using an MD5 hash of the
@@ -230,34 +261,46 @@
    pragma = Attribute(
        """ Gets and sets and deletes the Pragma header. For more information
        on Pragma see RFC 2616 section 14.32. """)
        on Pragma see RFC 2616 section 14.32. """
    )
    request = Attribute(
        """ Return the request associated with this response if any. """)
        """ Return the request associated with this response if any. """
    )
    retry_after = Attribute(
        """ Gets and sets and deletes the Retry-After header. For more
        information on Retry-After see RFC 2616 section 14.37. Converts
        using HTTP date or delta seconds.""")
        using HTTP date or delta seconds."""
    )
    server = Attribute(
        """ Gets and sets and deletes the Server header. For more information
        on Server see RFC216 section 14.38. """)
        on Server see RFC216 section 14.38. """
    )
    def set_cookie(name, value='', max_age=None, path='/', domain=None,
                   secure=False, httponly=False, comment=None, expires=None,
                   overwrite=False):
    def set_cookie(
        name,
        value='',
        max_age=None,
        path='/',
        domain=None,
        secure=False,
        httponly=False,
        comment=None,
        expires=None,
        overwrite=False,
    ):
        """ Set (add) a cookie for the response """
    status = Attribute(
        """ The status string. """)
    status = Attribute(""" The status string. """)
    status_int = Attribute(
        """ The status as an integer """)
    status_int = Attribute(""" The status as an integer """)
    unicode_body = Attribute(
        """ Get/set the unicode value of the body (using the charset of
        the Content-Type)""")
        the Content-Type)"""
    )
    def unset_cookie(name, strict=True):
        """ Unset a cookie with the given name (remove it from the
@@ -265,15 +308,19 @@
    vary = Attribute(
        """Gets and sets and deletes the Vary header. For more information
        on Vary see section 14.44. Converts using list.""")
        on Vary see section 14.44. Converts using list."""
    )
    www_authenticate = Attribute(
        """ Gets and sets and deletes the WWW-Authenticate header. For more
        information on WWW-Authenticate see RFC 2616 section 14.47. Converts
        using 'parse_auth' and 'serialize_auth'. """)
        using 'parse_auth' and 'serialize_auth'. """
    )
class IException(Interface): # not an API
class IException(Interface):  # not an API
    """ An interface representing a generic exception """
class IExceptionResponse(IException, IResponse):
    """ An interface representing a WSGI response which is also an exception
@@ -283,8 +330,10 @@
    :class:`pyramid.response.Response`, including
    :class:`pyramid.httpexceptions.HTTPNotFound` and
    :class:`pyramid.httpexceptions.HTTPForbidden`)."""
    def prepare(environ):
        """ Prepares the response for being called as a WSGI application """
class IDict(Interface):
    # Documentation-only interface
@@ -354,6 +403,7 @@
    def clear():
        """ Clear all values from the dictionary """
class IBeforeRender(IDict):
    """
    Subscribers to this event may introspect and modify the set of
@@ -373,25 +423,36 @@
        See also :ref:`beforerender_event`.
    """
    rendering_val = Attribute('The value returned by a view or passed to a '
                              '``render`` method for this rendering. '
                              'This feature is new in Pyramid 1.2.')
    rendering_val = Attribute(
        'The value returned by a view or passed to a '
        '``render`` method for this rendering. '
        'This feature is new in Pyramid 1.2.'
    )
class IRendererInfo(Interface):
    """ An object implementing this interface is passed to every
    :term:`renderer factory` constructor as its only argument (conventionally
    named ``info``)"""
    name = Attribute('The value passed by the user as the renderer name')
    package = Attribute('The "current package" when the renderer '
                        'configuration statement was found')
    package = Attribute(
        'The "current package" when the renderer '
        'configuration statement was found'
    )
    type = Attribute('The renderer type name')
    registry = Attribute('The "current" application registry when the '
                         'renderer was created')
    settings = Attribute('The deployment settings dictionary related '
                         'to the current application')
    registry = Attribute(
        'The "current" application registry when the ' 'renderer was created'
    )
    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):
@@ -399,6 +460,7 @@
        :class:`pyramid.interfaces.IRenderer`. ``info`` is an
        object that implements :class:`pyramid.interfaces.IRendererInfo`.
        """
class IRenderer(Interface):
    def __call__(value, system):
@@ -413,6 +475,7 @@
        view), and ``request`` (the request object passed to the
        view)."""
class ITemplateRenderer(IRenderer):
    def implementation():
        """ Return the object that the underlying templating system
@@ -420,12 +483,14 @@
        accepts arbitrary keyword arguments and returns a string or
        unicode object """
deprecated(
    'ITemplateRenderer',
    'As of Pyramid 1.5 the, "pyramid.interfaces.ITemplateRenderer" interface '
    'is scheduled to be removed. It was used by the Mako and Chameleon '
    'renderers which have been split into their own packages.'
    )
    'renderers which have been split into their own packages.',
)
class IViewMapper(Interface):
    def __call__(self, object):
@@ -434,6 +499,7 @@
        request)``.  The callable returned should itself return a Response
        object.  An IViewMapper is returned by
        :class:`pyramid.interfaces.IViewMapperFactory`."""
class IViewMapperFactory(Interface):
    def __call__(self, **kw):
@@ -447,6 +513,7 @@
        to extension developers who want to modify potential view callable
        invocation signatures and response values.
        """
class IAuthenticationPolicy(Interface):
    """ An object representing a Pyramid authentication policy. """
@@ -500,8 +567,10 @@
        """
class IAuthorizationPolicy(Interface):
    """ An object representing a Pyramid authorization policy. """
    def permits(context, principals, permission):
        """ Return an instance of :class:`pyramid.security.Allowed` if any
        of the ``principals`` is allowed the ``permission`` in the current
@@ -518,7 +587,8 @@
        ``pyramid.security.principals_allowed_by_permission`` API is
        used."""
class IMultiDict(IDict): # docs-only interface
class IMultiDict(IDict):  # docs-only interface
    """
    An ordered dictionary that can have multiple values for each key. A
    multidict adds the methods ``getall``, ``getone``, ``mixed``, ``extend``,
@@ -556,35 +626,45 @@
        dictionary. This is similar to the kind of dictionary often used to
        represent the variables in a web request. """
# internal interfaces
class IRequest(Interface):
    """ Request type interface attached to all request objects """
class ITweens(Interface):
    """ Marker interface for utility registration representing the ordered
    set of a configuration's tween factories"""
class IRequestHandler(Interface):
    """ """
    def __call__(self, request):
        """ Must return a tuple of IReqest, IResponse or raise an exception.
        The ``request`` argument will be an instance of an object that
        provides IRequest."""
IRequest.combined = IRequest # for exception view lookups
IRequest.combined = IRequest  # for exception view lookups
class IRequestExtensions(Interface):
    """ Marker interface for storing request extensions (properties and
    methods) which will be added to the request object."""
    descriptors = Attribute(
        """A list of descriptors that will be added to each request.""")
    methods = Attribute(
        """A list of methods to be added to each request.""")
        """A list of descriptors that will be added to each request."""
    )
    methods = Attribute("""A list of methods to be added to each request.""")
class IRouteRequest(Interface):
    """ *internal only* interface used as in a utility lookup to find
    route-specific interfaces.  Not an API."""
class IAcceptOrder(Interface):
    """
@@ -593,8 +673,10 @@
    """
class IStaticURLInfo(Interface):
    """ A policy for generating URLs to static assets """
    def add(config, name, spec, **extra):
        """ Add a new static info registration """
@@ -604,15 +686,19 @@
    def add_cache_buster(config, spec, cache_buster):
        """ Add a new cache buster to a particular set of assets """
class IResponseFactory(Interface):
    """ 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 instance of ``pyramid.request.Request``"""
@@ -620,18 +706,23 @@
        """ Return an empty request object (see
        :meth:`pyramid.request.Request.blank`)"""
class IViewClassifier(Interface):
    """ *Internal only* marker interface for views."""
class IExceptionViewClassifier(Interface):
    """ *Internal only* marker interface for exception views."""
class IView(Interface):
    def __call__(context, request):
        """ Must return an object that implements IResponse. """
class ISecuredView(IView):
    """ *Internal only* interface.  Not an API. """
    def __call_permissive__(context, request):
        """ Guaranteed-permissive version of __call__ """
@@ -639,20 +730,25 @@
        """ Return True if view execution will be permitted using the
        context and request, False otherwise"""
class IMultiView(ISecuredView):
    """ *internal only*.  A multiview is a secured view that is a
    collection of other views.  Each of the views is associated with
    zero or more predicates.  Not an API."""
    def add(view, predicates, order, accept=None, phash=None):
        """ Add a view to the multiview. """
class IRootFactory(Interface):
    def __call__(request):
        """ Return a root object based on the request """
class IDefaultRootFactory(Interface):
    def __call__(request):
        """ Return the *default* root object for an application """
class ITraverser(Interface):
    def __call__(request):
@@ -678,12 +774,15 @@
        as attributes of the ``request`` object by the :term:`router`.
        """
ITraverserFactory = ITraverser # b / c for 1.0 code
ITraverserFactory = ITraverser  # b / c for 1.0 code
class IViewPermission(Interface):
    def __call__(context, request):
        """ Return True if the permission allows, return False if it denies.
        """
class IRouter(Interface):
    """
@@ -691,8 +790,10 @@
    a view registry.
    """
    registry = Attribute(
        """Component architecture registry local to this application.""")
        """Component architecture registry local to this application."""
    )
    def request_context(environ):
        """
@@ -734,6 +835,7 @@
        """
class IExecutionPolicy(Interface):
    def __call__(environ, router):
        """
@@ -762,22 +864,28 @@
                        return request.invoke_exception_view(reraise=True)
        """
class ISettings(IDict):
    """ Runtime settings utility for pyramid; represents the
    deployment settings for the application.  Implements a mapping
    interface."""
# this interface, even if it becomes unused within Pyramid, is
# imported by other packages (such as traversalwrapper)
class ILocation(Interface):
    """Objects that have a structural location"""
    __parent__ = Attribute("The parent in the location hierarchy")
    __name__ = Attribute("The name within the parent")
class IDebugLogger(Interface):
    """ Interface representing a PEP 282 logger """
ILogger = IDebugLogger # b/c
ILogger = IDebugLogger  # b/c
class IRoutePregenerator(Interface):
    def __call__(request, elements, kw):
@@ -804,21 +912,27 @@
        """
class IRoute(Interface):
    """ Interface representing the type of object returned from
    ``IRoutesMapper.get_route``"""
    name = Attribute('The route name')
    pattern = Attribute('The route pattern')
    factory = Attribute(
        'The :term:`root factory` used by the :app:`Pyramid` router '
        'when this route matches (or ``None``)')
        'when this route matches (or ``None``)'
    )
    predicates = Attribute(
        'A sequence of :term:`route predicate` objects used to '
        'determine if a request matches this route or not after '
        'basic pattern matching has been completed.')
    pregenerator = Attribute('This attribute should either be ``None`` or '
                             'a callable object implementing the '
                             '``IRoutePregenerator`` interface')
        'basic pattern matching has been completed.'
    )
    pregenerator = Attribute(
        'This attribute should either be ``None`` or '
        'a callable object implementing the '
        '``IRoutePregenerator`` interface'
    )
    def match(path):
        """
@@ -831,14 +945,17 @@
        If the ``path`` passed to this function cannot be matched by
        the ``pattern`` of this route, return ``None``.
        """
    def generate(kw):
        """
        Generate a URL based on filling in the dynamic segment markers
        in the pattern using the ``kw`` dictionary provided.
        """
class IRoutesMapper(Interface):
    """ Interface representing a Routes ``Mapper`` object """
    def get_routes():
        """ Return a sequence of Route objects registered in the mapper.
        Static routes will not be returned in this sequence."""
@@ -850,8 +967,14 @@
        """ Returns an ``IRoute`` object if a route with the name ``name``
        was registered, otherwise return ``None``."""
    def connect(name, pattern, factory=None, predicates=(), pregenerator=None,
                static=True):
    def connect(
        name,
        pattern,
        factory=None,
        predicates=(),
        pregenerator=None,
        static=True,
    ):
        """ Add a new route. """
    def generate(name, kw):
@@ -865,23 +988,26 @@
        ``match`` key will be the matchdict or ``None`` if no route
        matched.  Static routes will not be considered for matching.  """
class IResourceURL(Interface):
    virtual_path = Attribute(
        'The virtual url path of the resource as a string.'
        )
    )
    physical_path = Attribute(
        'The physical url path of the resource as a string.'
        )
    )
    virtual_path_tuple = Attribute(
        'The virtual url path of the resource as a tuple.  (New in 1.5)'
        )
    )
    physical_path_tuple = Attribute(
        'The physical url path of the resource as a tuple. (New in 1.5)'
        )
    )
class IPEP302Loader(Interface):
    """ See http://www.python.org/dev/peps/pep-0302/#id30.
    """
    def get_data(path):
        """ Retrieve data for and arbitrary "files" from storage backend.
@@ -924,42 +1050,53 @@
class IPackageOverrides(IPEP302Loader):
    """ Utility for pkg_resources overrides """
# VH_ROOT_KEY is an interface; its imported from other packages (e.g.
# traversalwrapper)
VH_ROOT_KEY = 'HTTP_X_VHM_ROOT'
class ILocalizer(Interface):
    """ Localizer for a specific language """
class ILocaleNegotiator(Interface):
    def __call__(request):
        """ Return a locale name """
class ITranslationDirectories(Interface):
    """ A list object representing all known translation directories
    for an application"""
class IDefaultPermission(Interface):
    """ A string object representing the default permission to be used
    for all view configurations which do not explicitly declare their
    own."""
class IDefaultCSRFOptions(Interface):
    """ An object representing the default CSRF settings to be used for
    all view configurations which do not explicitly declare their own."""
    require_csrf = Attribute(
        'Boolean attribute. If ``True``, then CSRF checks will be enabled by '
        'default for the view unless overridden.')
        'default for the view unless overridden.'
    )
    token = Attribute('The key to be matched in the body of the request.')
    header = Attribute('The header to be matched with the CSRF token.')
    safe_methods = Attribute('A set of safe methods that skip CSRF checks.')
    callback = Attribute('A callback to disable CSRF checks per-request.')
class ISessionFactory(Interface):
    """ An interface representing a factory which accepts a request object and
    returns an ISession object """
    def __call__(request):
        """ Return an ISession object """
class ISession(IDict):
    """ An interface representing a session (a web session object,
@@ -1158,16 +1295,21 @@
    title = Attribute('Text title describing this introspectable')
    type_name = Attribute('Text type name describing this introspectable')
    order = Attribute('integer order in which registered with introspector '
                      '(managed by introspector, usually)')
    order = Attribute(
        'integer order in which registered with introspector '
        '(managed by introspector, usually)'
    )
    category_name = Attribute('introspection category name')
    discriminator = Attribute('introspectable discriminator (within category) '
                              '(must be hashable)')
    discriminator = Attribute(
        'introspectable discriminator (within category) ' '(must be hashable)'
    )
    discriminator_hash = Attribute('an integer hash of the discriminator')
    action_info = Attribute('An IActionInfo object representing the caller '
                            'that invoked the creation of this introspectable '
                            '(usually a sentinel until updated during '
                            'self.register)')
    action_info = Attribute(
        'An IActionInfo object representing the caller '
        'that invoked the creation of this introspectable '
        '(usually a sentinel until updated during '
        'self.register)'
    )
    def relate(category_name, discriminator):
        """ Indicate an intent to relate this IIntrospectable with another
@@ -1199,7 +1341,7 @@
                    method = getattr(introspector, methodname)
                    method((i.category_name, i.discriminator),
                           (category_name, discriminator))
        """
        """  # noqa: E501
    def __hash__():
@@ -1209,18 +1351,22 @@
          return hash((self.category_name,) + (self.discriminator,))
        """
class IActionInfo(Interface):
    """ Class which provides code introspection capability associated with an
    action.  The ParserInfo class used by ZCML implements the same interface."""
    file = Attribute(
        'Filename of action-invoking code as a string')
    action.  The ParserInfo class used by ZCML implements the same interface.
    """
    file = Attribute('Filename of action-invoking code as a string')
    line = Attribute(
        'Starting line number in file (as an integer) of action-invoking code.'
        'This will be ``None`` if the value could not be determined.')
        'This will be ``None`` if the value could not be determined.'
    )
    def __str__():
        """ Return a representation of the action information (including
        source code from file, if possible) """
class IAssetDescriptor(Interface):
    """
@@ -1260,19 +1406,24 @@
        Returns True if asset exists, otherwise returns False.
        """
class IJSONAdapter(Interface):
    """
    Marker interface for objects that can convert an arbitrary object
    into a JSON-serializable primitive.
    """
class IPredicateList(Interface):
    """ Interface representing a predicate list """
class IViewDeriver(Interface):
    options = Attribute('A list of supported options to be passed to '
                        ':meth:`pyramid.config.Configurator.add_view`. '
                        'This attribute is optional.')
    options = Attribute(
        'A list of supported options to be passed to '
        ':meth:`pyramid.config.Configurator.add_view`. '
        'This attribute is optional.'
    )
    def __call__(view, info):
        """
@@ -1285,23 +1436,34 @@
        """
class IViewDeriverInfo(Interface):
    """ An object implementing this interface is passed to every
    :term:`view deriver` during configuration."""
    registry = Attribute('The "current" application registry where the '
                         'view was created')
    package = Attribute('The "current package" where the view '
                        'configuration statement was found')
    settings = Attribute('The deployment settings dictionary related '
                         'to the current application')
    options = Attribute('The view options passed to the view, including any '
                        'default values that were not overriden')
    registry = Attribute(
        'The "current" application registry where the ' 'view was created'
    )
    package = Attribute(
        'The "current package" where the view '
        'configuration statement was found'
    )
    settings = Attribute(
        'The deployment settings dictionary related '
        'to the current application'
    )
    options = Attribute(
        'The view options passed to the view, including any '
        'default values that were not overriden'
    )
    predicates = Attribute('The list of predicates active on the view')
    original_view = Attribute('The original view object being wrapped')
    exception_only = Attribute('The view will only be invoked for exceptions')
class IViewDerivers(Interface):
    """ Interface for view derivers list """
class ICacheBuster(Interface):
    """
@@ -1310,6 +1472,7 @@
    .. versionadded:: 1.6
    """
    def __call__(request, subpath, kw):
        """
        Modifies a subpath and/or keyword arguments from which a static asset
@@ -1344,6 +1507,7 @@
        ``config.override_asset('myapp:static/foo.png', 'themepkg:bar.png')``.
        """
# 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.
src/pyramid/location.py
@@ -12,6 +12,7 @@
#
##############################################################################
def inside(resource1, resource2):
    """Is ``resource1`` 'inside' ``resource2``?  Return ``True`` if so, else
    ``False``.
@@ -27,6 +28,7 @@
        resource1 = resource1.__parent__
    return False
def lineage(resource):
    """
@@ -46,7 +48,7 @@
    Calling ``lineage(thing2)`` will return a generator.  When we turn
    it into a list, we will get::
      list(lineage(thing2))
      [ <Thing object at thing2>, <Thing object at thing1> ]
    """
@@ -63,4 +65,3 @@
            resource = resource.__parent__
        except AttributeError:
            resource = None
src/pyramid/paster.py
@@ -1,6 +1,7 @@
from pyramid.scripting import prepare
from pyramid.scripts.common import get_config_loader
def setup_logging(config_uri, global_conf=None):
    """
    Set up Python logging with the filename specified via ``config_uri``
@@ -10,6 +11,7 @@
    """
    loader = get_config_loader(config_uri)
    loader.setup_logging(global_conf)
def get_app(config_uri, name=None, options=None):
    """ Return the WSGI application named ``name`` in the PasteDeploy
@@ -27,6 +29,7 @@
    loader = get_config_loader(config_uri)
    return loader.get_wsgi_app(name, options)
def get_appsettings(config_uri, name=None, options=None):
    """ Return a dictionary representing the key/value pairs in an ``app``
    section within the file represented by ``config_uri``.
@@ -42,6 +45,7 @@
    """
    loader = get_config_loader(config_uri)
    return loader.get_wsgi_app_settings(name, options)
def bootstrap(config_uri, request=None, options=None):
    """ Load a WSGI application from the PasteDeploy config file specified
@@ -83,7 +87,7 @@
    for you if none is provided. You can mutate the request's ``environ``
    later to setup a specific host/port/scheme/etc.
    ``options`` Is passed to get_app for use as variable assignments like
    ``options`` Is passed to get_app for use as variable assignments like
    {'http_port': 8080} and then use %(http_port)s in the
    config file.
@@ -108,4 +112,3 @@
    env = prepare(request)
    env['app'] = app
    return env
src/pyramid/path.py
@@ -9,9 +9,13 @@
from pyramid.compat import string_types
ignore_types = [ imp.C_EXTENSION, imp.C_BUILTIN ]
init_names = [ '__init__%s' % x[0] for x in imp.get_suffixes() if
               x[0] and x[2] not in ignore_types ]
ignore_types = [imp.C_EXTENSION, imp.C_BUILTIN]
init_names = [
    '__init__%s' % x[0]
    for x in imp.get_suffixes()
    if x[0] and x[2] not in ignore_types
]
def caller_path(path, level=2):
    if not os.path.isabs(path):
@@ -20,11 +24,13 @@
        path = os.path.join(prefix, path)
    return path
def caller_module(level=2, sys=sys):
    module_globals = sys._getframe(level).f_globals
    module_name = module_globals.get('__name__') or '__main__'
    module = sys.modules[module_name]
    return module
def package_name(pkg_or_module):
    """ If this function is passed a module, return the dotted Python
@@ -45,22 +51,25 @@
        return pkg_name
    return pkg_name.rsplit('.', 1)[0]
def package_of(pkg_or_module):
    """ Return the package of a module or return the package itself """
    pkg_name = package_name(pkg_or_module)
    __import__(pkg_name)
    return sys.modules[pkg_name]
def caller_package(level=2, caller_module=caller_module):
    # caller_module in arglist for tests
    module = caller_module(level + 1)
    f = getattr(module, '__file__', '')
    if (('__init__.py' in f) or ('__init__$py' in f)): # empty at >>>
    if ('__init__.py' in f) or ('__init__$py' in f):  # empty at >>>
        # Module is a package
        return module
    # Go up one level to get package
    package_name = module.__name__.rsplit('.', 1)[0]
    return sys.modules[package_name]
def package_path(package):
    # computing the abspath is actually kinda expensive so we memoize
@@ -78,11 +87,14 @@
            pass
    return prefix
class _CALLER_PACKAGE(object):
    def __repr__(self): # pragma: no cover (for docs)
    def __repr__(self):  # pragma: no cover (for docs)
        return 'pyramid.path.CALLER_PACKAGE'
CALLER_PACKAGE = _CALLER_PACKAGE()
class Resolver(object):
    def __init__(self, package=CALLER_PACKAGE):
@@ -95,7 +107,7 @@
                except ImportError:
                    raise ValueError(
                        'The dotted name %r cannot be imported' % (package,)
                        )
                    )
                package = sys.modules[package]
            self.package = package_of(package)
@@ -164,6 +176,7 @@
    to the :meth:`~pyramid.path.AssetResolver.resolve` method, the resulting
    absolute asset spec would be ``xml.minidom:template.pt``.
    """
    def resolve(self, spec):
        """
        Resolve the asset spec named as ``spec`` to an object that has the
@@ -207,6 +220,7 @@
                    'relative spec %r irresolveable without package' % (spec,)
                )
        return PkgResourcesAssetDescriptor(package_name, path)
class DottedNameResolver(Resolver):
    """ A class used to resolve a :term:`dotted Python name` to a package or
@@ -258,6 +272,7 @@
    :meth:`~pyramid.path.DottedNameResolver.resolve` method, the resulting
    import would be for ``xml.minidom``.
    """
    def resolve(self, dotted):
        """
        This method resolves a dotted name reference to a global Python
@@ -332,7 +347,7 @@
            if not package:
                raise ValueError(
                    'relative name %r irresolveable without package' % (value,)
                    )
                )
            if value in ['.', ':']:
                value = package.__name__
            else:
@@ -348,7 +363,7 @@
    def _zope_dottedname_style(self, value, package):
        """ package.module.attr style """
        module = getattr(package, '__name__', None) # package may be None
        module = getattr(package, '__name__', None)  # package may be None
        if not module:
            module = None
        if value == '.':
@@ -364,7 +379,7 @@
                    raise ValueError(
                        'relative name %r irresolveable without '
                        'package' % (value,)
                        )
                    )
                module = module.split('.')
                name.pop(0)
                while not name[0]:
@@ -380,9 +395,10 @@
                found = getattr(found, n)
            except AttributeError:
                __import__(used)
                found = getattr(found, n) # pragma: no cover
                found = getattr(found, n)  # pragma: no cover
        return found
@implementer(IAssetDescriptor)
class PkgResourcesAssetDescriptor(object):
@@ -397,7 +413,8 @@
    def abspath(self):
        return os.path.abspath(
            self.pkg_resources.resource_filename(self.pkg_name, self.path))
            self.pkg_resources.resource_filename(self.pkg_name, self.path)
        )
    def stream(self):
        return self.pkg_resources.resource_stream(self.pkg_name, self.path)
@@ -411,9 +428,9 @@
    def exists(self):
        return self.pkg_resources.resource_exists(self.pkg_name, self.path)
@implementer(IAssetDescriptor)
class FSAssetDescriptor(object):
    def __init__(self, path):
        self.path = os.path.abspath(path)
src/pyramid/predicates.py
@@ -8,16 +8,14 @@
from pyramid.traversal import (
    find_interface,
    traversal_path,
    resource_path_tuple
    )
from pyramid.urldispatch import _compile_route
from pyramid.util import (
    as_sorted_tuple,
    object_description,
    resource_path_tuple,
)
from pyramid.urldispatch import _compile_route
from pyramid.util import as_sorted_tuple, object_description
_marker = object()
class XHRPredicate(object):
    def __init__(self, val, config):
@@ -30,6 +28,7 @@
    def __call__(self, context, request):
        return bool(request.is_xhr) is self.val
class RequestMethodPredicate(object):
    def __init__(self, val, config):
@@ -47,6 +46,7 @@
    def __call__(self, context, request):
        return request.method in self.val
class PathInfoPredicate(object):
    def __init__(self, val, config):
        self.orig = val
@@ -63,7 +63,8 @@
    def __call__(self, context, request):
        return self.val.match(request.upath_info) is not None
class RequestParamPredicate(object):
    def __init__(self, val, config):
        val = as_sorted_tuple(val)
@@ -85,7 +86,7 @@
    def text(self):
        return 'request_param %s' % ','.join(
            ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
            ['%s=%s' % (x, y) if y else x for x, y in self.reqs]
        )
    phash = text
@@ -98,6 +99,7 @@
            if v is not None and actual != v:
                return False
        return True
class HeaderPredicate(object):
    def __init__(self, val, config):
@@ -129,6 +131,7 @@
            return False
        return self.val.match(val) is not None
class AcceptPredicate(object):
    _is_using_deprecated_ranges = False
@@ -151,6 +154,7 @@
            return self.values[0] in request.accept
        return bool(request.accept.acceptable_offers(self.values))
class ContainmentPredicate(object):
    def __init__(self, val, config):
        self.val = config.maybe_dotted(val)
@@ -163,7 +167,8 @@
    def __call__(self, context, request):
        ctx = getattr(request, 'context', context)
        return find_interface(ctx, self.val) is not None
class RequestTypePredicate(object):
    def __init__(self, val, config):
        self.val = val
@@ -175,18 +180,19 @@
    def __call__(self, context, request):
        return self.val.providedBy(request)
class MatchParamPredicate(object):
    def __init__(self, val, config):
        val = as_sorted_tuple(val)
        self.val = val
        reqs = [ p.split('=', 1) for p in val ]
        self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
        reqs = [p.split('=', 1) for p in val]
        self.reqs = [(x.strip(), y.strip()) for x, y in reqs]
    def text(self):
        return 'match_param %s' % ','.join(
            ['%s=%s' % (x,y) for x, y in self.reqs]
            )
            ['%s=%s' % (x, y) for x, y in self.reqs]
        )
    phash = text
@@ -198,7 +204,8 @@
            if request.matchdict.get(k) != v:
                return False
        return True
class CustomPredicate(object):
    def __init__(self, func, config):
        self.func = func
@@ -207,8 +214,8 @@
        return getattr(
            self.func,
            '__text__',
            'custom predicate: %s' % object_description(self.func)
            )
            'custom predicate: %s' % object_description(self.func),
        )
    def phash(self):
        # using hash() here rather than id() is intentional: we
@@ -221,8 +228,8 @@
    def __call__(self, context, request):
        return self.func(context, request)
class TraversePredicate(object):
    # Can only be used as a *route* "predicate"; it adds 'traverse' to the
    # matchdict if it's specified in the routing args.  This causes the
@@ -231,7 +238,7 @@
    def __init__(self, val, config):
        _, self.tgenerate = _compile_route(val)
        self.val = val
    def text(self):
        return 'traverse matchdict pseudo-predicate'
@@ -252,10 +259,11 @@
        # return True.
        return True
class CheckCSRFTokenPredicate(object):
    check_csrf_token = staticmethod(check_csrf_token) # testing
    check_csrf_token = staticmethod(check_csrf_token)  # testing
    def __init__(self, val, config):
        self.val = val
@@ -271,6 +279,7 @@
                val = 'csrf_token'
            return self.check_csrf_token(request, val, raises=False)
        return True
class PhysicalPathPredicate(object):
    def __init__(self, val, config):
@@ -289,6 +298,7 @@
        if getattr(context, '__name__', _marker) is not _marker:
            return resource_path_tuple(context) == self.val
        return False
class EffectivePrincipalsPredicate(object):
    def __init__(self, val, config):
@@ -310,6 +320,7 @@
                return True
        return False
class Notted(object):
    def __init__(self, predicate):
        self.predicate = predicate
src/pyramid/registry.py
@@ -7,18 +7,12 @@
from pyramid.compat import text_
from pyramid.decorator import reify
from pyramid.interfaces import (
    IIntrospector,
    IIntrospectable,
    ISettings,
)
from pyramid.interfaces import IIntrospector, IIntrospectable, ISettings
from pyramid.path import (
    CALLER_PACKAGE,
    caller_package,
)
from pyramid.path import CALLER_PACKAGE, caller_package
empty = text_('')
class Registry(Components, dict):
    """ A registry object is an :term:`application registry`.
@@ -82,13 +76,19 @@
        self.has_listeners = True
        return result
    def registerSelfAdapter(self, required=None, provided=None, name=empty,
                            info=empty, event=True):
    def registerSelfAdapter(
        self, required=None, provided=None, name=empty, info=empty, event=True
    ):
        # registerAdapter analogue which always returns the object itself
        # when required is matched
        return self.registerAdapter(lambda x: x, required=required,
                                    provided=provided, name=name,
                                    info=info, event=event)
        return self.registerAdapter(
            lambda x: x,
            required=required,
            provided=provided,
            name=name,
            info=info,
            event=event,
        )
    def queryAdapterOrSelf(self, object, interface, default=None):
        # queryAdapter analogue which returns the object if it implements
@@ -106,7 +106,7 @@
    def notify(self, *events):
        if self.has_listeners:
            # iterating over subscribers assures they get executed
            [ _ for _ in self.subscribers(events, None) ]
            [_ for _ in self.subscribers(events, None)]
    # backwards compatibility for code that wants to look up a settings
    # object via ``registry.getUtility(ISettings)``
@@ -118,6 +118,7 @@
        self._settings = settings
    settings = property(_get_settings, _set_settings)
@implementer(IIntrospector)
class Introspector(object):
@@ -147,16 +148,19 @@
        values = category.values()
        values = sorted(set(values), key=sort_key)
        return [
            {'introspectable': intr,
             'related': self.related(intr)}
            {'introspectable': intr, 'related': self.related(intr)}
            for intr in values
        ]
    def categorized(self, sort_key=None):
        L = []
        for category_name in self.categories():
            L.append((category_name, self.get_category(category_name,
                                                       sort_key=sort_key)))
            L.append(
                (
                    category_name,
                    self.get_category(category_name, sort_key=sort_key),
                )
            )
        return L
    def categories(self):
@@ -186,7 +190,7 @@
    def relate(self, *pairs):
        introspectables = self._get_intrs_by_pairs(pairs)
        relatable = ((x,y) for x in introspectables for y in introspectables)
        relatable = ((x, y) for x in introspectables for y in introspectables)
        for x, y in relatable:
            L = self._refs.setdefault(x, [])
            if x is not y and y not in L:
@@ -194,7 +198,7 @@
    def unrelate(self, *pairs):
        introspectables = self._get_intrs_by_pairs(pairs)
        relatable = ((x,y) for x in introspectables for y in introspectables)
        relatable = ((x, y) for x in introspectables for y in introspectables)
        for x, y in relatable:
            L = self._refs.get(x, [])
            if y in L:
@@ -207,11 +211,12 @@
            raise KeyError((category_name, discriminator))
        return self._refs.get(intr, [])
@implementer(IIntrospectable)
class Introspectable(dict):
    order = 0 # mutated by introspector.add
    action_info = None # mutated by self.register
    order = 0  # mutated by introspector.add
    action_info = None  # mutated by self.register
    def __init__(self, category_name, discriminator, title, type_name):
        self.category_name = category_name
@@ -240,14 +245,16 @@
    def __repr__(self):
        self._assert_resolved()
        return '<%s category %r, discriminator %r>' % (self.__class__.__name__,
                                                       self.category_name,
                                                       self.discriminator)
        return '<%s category %r, discriminator %r>' % (
            self.__class__.__name__,
            self.category_name,
            self.discriminator,
        )
    def __nonzero__(self):
        return True
    __bool__ = __nonzero__ # py3
    __bool__ = __nonzero__  # py3
    def register(self, introspector, action_info):
        self.discriminator = undefer(self.discriminator)
@@ -261,8 +268,9 @@
                method = introspector.unrelate
            method(
                (self.category_name, self.discriminator),
                (category_name, discriminator)
                )
                (category_name, discriminator),
            )
class Deferred(object):
    """ Can be used by a third-party configuration extender to wrap a
@@ -270,6 +278,7 @@
    discriminator cannot be computed because it relies on unresolved values.
    The function should accept no arguments and should return a hashable
    discriminator."""
    def __init__(self, func):
        self.func = func
@@ -282,6 +291,7 @@
    def resolve(self):
        return self.value
def undefer(v):
    """ Function which accepts an object and returns it unless it is a
    :class:`pyramid.registry.Deferred` instance.  If it is an instance of
@@ -291,7 +301,9 @@
        v = v.resolve()
    return v
class predvalseq(tuple):
    """ A subtype of tuple used to represent a sequence of predicate values """
global_registry = Registry('global')
src/pyramid/renderers.py
@@ -3,22 +3,12 @@
import os
import re
from zope.interface import (
    implementer,
    providedBy,
    )
from zope.interface import implementer, providedBy
from zope.interface.registry import Components
from pyramid.interfaces import (
    IJSONAdapter,
    IRendererFactory,
    IRendererInfo,
    )
from pyramid.interfaces import IJSONAdapter, IRendererFactory, IRendererInfo
from pyramid.compat import (
    string_types,
    text_type,
    )
from pyramid.compat import string_types, text_type
from pyramid.csrf import get_csrf_token
from pyramid.decorator import reify
@@ -34,6 +24,7 @@
from pyramid.util import hide_attrs
# API
def render(renderer_name, value, request=None, package=None):
    """ Using the renderer ``renderer_name`` (a template
@@ -76,19 +67,19 @@
        registry = None
    if package is None:
        package = caller_package()
    helper = RendererHelper(name=renderer_name, package=package,
                            registry=registry)
    helper = RendererHelper(
        name=renderer_name, package=package, registry=registry
    )
    with hide_attrs(request, 'response'):
        result = helper.render(value, None, request=request)
    return result
def render_to_response(renderer_name,
                       value,
                       request=None,
                       package=None,
                       response=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
@@ -137,8 +128,9 @@
        registry = None
    if package is None:
        package = caller_package()
    helper = RendererHelper(name=renderer_name, package=package,
                            registry=registry)
    helper = RendererHelper(
        name=renderer_name, package=package, registry=registry
    )
    with hide_attrs(request, 'response'):
        if response is not None:
@@ -146,6 +138,7 @@
        result = helper.render_to_response(value, None, request=request)
    return result
def get_renderer(renderer_name, package=None, registry=None):
    """ Return the renderer object for the renderer ``renderer_name``.
@@ -165,11 +158,14 @@
    """
    if package is None:
        package = caller_package()
    helper = RendererHelper(name=renderer_name, package=package,
                            registry=registry)
    helper = RendererHelper(
        name=renderer_name, package=package, registry=registry
    )
    return helper.renderer
# concrete renderer factory implementations (also API)
def string_renderer_factory(info):
    def _render(value, system):
@@ -182,9 +178,12 @@
            if ct == response.default_content_type:
                response.content_type = 'text/plain'
        return value
    return _render
_marker = object()
class JSON(object):
    """ Renderer that returns a JSON-encoded string.
@@ -265,13 +264,15 @@
        instances of the ``Foo`` class when they're encountered in your view
        results."""
        self.components.registerAdapter(adapter, (type_or_iface,),
                                        IJSONAdapter)
        self.components.registerAdapter(
            adapter, (type_or_iface,), IJSONAdapter
        )
    def __call__(self, info):
        """ Returns a plain JSON-encoded string with content-type
        ``application/json``. The content-type may be overridden by
        setting ``request.response.content_type``."""
        def _render(value, system):
            request = system.get('request')
            if request is not None:
@@ -290,16 +291,20 @@
                return obj.__json__(request)
            obj_iface = providedBy(obj)
            adapters = self.components.adapters
            result = adapters.lookup((obj_iface,), IJSONAdapter,
                                     default=_marker)
            result = adapters.lookup(
                (obj_iface,), IJSONAdapter, default=_marker
            )
            if result is _marker:
                raise TypeError('%r is not JSON serializable' % (obj,))
            return result(obj, request)
        return default
json_renderer_factory = JSON() # bw compat
json_renderer_factory = JSON()  # bw compat
JSONP_VALID_CALLBACK = re.compile(r"^[$a-z_][$0-9a-z_\.\[\]]+[^.]$", re.I)
class JSONP(JSON):
    """ `JSONP <https://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper
@@ -373,6 +378,7 @@
        ``application/javascript`` if query parameter matching
        ``self.param_name`` is present in request.GET; otherwise returns
        plain-JSON encoded string with content-type ``application/json``"""
        def _render(value, system):
            request = system.get('request')
            default = self._make_default(request)
@@ -384,7 +390,9 @@
                if callback is not None:
                    if not JSONP_VALID_CALLBACK.match(callback):
                        raise HTTPBadRequest('Invalid JSONP callback function name.')
                        raise HTTPBadRequest(
                            'Invalid JSONP callback function name.'
                        )
                    ct = 'application/javascript'
                    body = '/**/{0}({1});'.format(callback, val)
@@ -392,7 +400,9 @@
                if response.content_type == response.default_content_type:
                    response.content_type = ct
            return body
        return _render
@implementer(IRendererInfo)
class RendererHelper(object):
@@ -422,36 +432,36 @@
    def renderer(self):
        factory = self.registry.queryUtility(IRendererFactory, name=self.type)
        if factory is None:
            raise ValueError(
                'No such renderer factory %s' % str(self.type))
            raise ValueError('No such renderer factory %s' % str(self.type))
        return factory(self)
    def get_renderer(self):
        return self.renderer
    def render_view(self, request, response, view, context):
        system = {'view':view,
                  'renderer_name':self.name, # b/c
                  'renderer_info':self,
                  'context':context,
                  'request':request,
                  'req':request,
                  'get_csrf_token':partial(get_csrf_token, request),
                  }
        system = {
            'view': view,
            'renderer_name': self.name,  # b/c
            'renderer_info': self,
            'context': context,
            'request': request,
            'req': request,
            'get_csrf_token': partial(get_csrf_token, request),
        }
        return self.render_to_response(response, system, request=request)
    def render(self, value, system_values, request=None):
        renderer = self.renderer
        if system_values is None:
            system_values = {
                'view':None,
                'renderer_name':self.name, # b/c
                'renderer_info':self,
                'context':getattr(request, 'context', None),
                'request':request,
                'req':request,
                'get_csrf_token':partial(get_csrf_token, request),
                }
                'view': None,
                'renderer_name': self.name,  # b/c
                'renderer_info': self,
                'context': getattr(request, 'context', None),
                'request': request,
                'req': request,
                'get_csrf_token': partial(get_csrf_token, request),
            }
        system_values = BeforeRender(system_values, value)
@@ -495,12 +505,14 @@
            registry = self.registry
        return self.__class__(name=name, package=package, registry=registry)
class NullRendererHelper(RendererHelper):
    """ Special renderer helper that has render_* methods which simply return
    the value they are fed rather than converting them to response objects;
    useful for testing purposes and special case view configuration
    registrations that want to use the view configuration machinery but do
    not want actual rendering to happen ."""
    def __init__(self, name=None, package=None, registry=None):
        # we override the initializer to avoid calling get_current_registry
        # (it will return a reference to the global registry when this
@@ -526,4 +538,5 @@
    def clone(self, name=None, package=None, registry=None):
        return self
null_renderer = NullRendererHelper()
src/pyramid/request.py
@@ -11,31 +11,22 @@
    IRequestExtensions,
    IResponse,
    ISessionFactory,
    )
)
from pyramid.compat import (
    text_,
    bytes_,
    native_,
    iteritems_,
    )
from pyramid.compat import text_, bytes_, native_, iteritems_
from pyramid.decorator import reify
from pyramid.i18n import LocalizerRequestMixin
from pyramid.response import Response, _get_response_factory
from pyramid.security import (
    AuthenticationAPIMixin,
    AuthorizationAPIMixin,
    )
from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin
from pyramid.url import URLMethodsMixin
from pyramid.util import (
    InstancePropertyHelper,
    InstancePropertyMixin,
)
from pyramid.util import InstancePropertyHelper, InstancePropertyMixin
from pyramid.view import ViewMethodsMixin
class TemplateContext(object):
    pass
class CallbackMethodsMixin(object):
    @reify
@@ -146,6 +137,7 @@
            callback = callbacks.popleft()
            callback(self)
@implementer(IRequest)
class Request(
    BaseRequest,
@@ -156,7 +148,7 @@
    AuthenticationAPIMixin,
    AuthorizationAPIMixin,
    ViewMethodsMixin,
    ):
):
    """
    A subclass of the :term:`WebOb` Request class.  An instance of
    this class is created by the :term:`router` and is provided to a
@@ -177,6 +169,7 @@
    release of this :app:`Pyramid` version.  See
    https://webob.org/ for further information.
    """
    exception = None
    exc_info = None
    matchdict = None
@@ -201,7 +194,8 @@
        if factory is None:
            raise AttributeError(
                'No session factory registered '
                '(see the Sessions chapter of the Pyramid documentation)')
                '(see the Sessions chapter of the Pyramid documentation)'
            )
        return factory(self)
    @reify
@@ -244,13 +238,17 @@
    # zope.interface.interface.Element.__init__ and
    # https://github.com/Pylons/pyramid/issues/232; as a result, always pass
    # __doc__ to the InterfaceClass constructor.
    iface = InterfaceClass('%s_IRequest' % name, bases=bases,
                           __doc__="route_request_iface-generated interface")
    iface = InterfaceClass(
        '%s_IRequest' % name,
        bases=bases,
        __doc__="route_request_iface-generated interface",
    )
    # for exception view lookups
    iface.combined = InterfaceClass(
        '%s_combined_IRequest' % name,
        bases=(iface, IRequest),
        __doc__='route_request_iface-generated combined interface')
        __doc__='route_request_iface-generated combined interface',
    )
    return iface
@@ -258,7 +256,9 @@
    def add_headers(request, response):
        for k, v in headerlist:
            response.headerlist.append((k, v))
    request.add_response_callback(add_headers)
def call_app_with_subpath_as_path_info(request, app):
    # Copy the request.  Use the source request's subpath (if it exists) as
@@ -280,11 +280,12 @@
    new_script_name = ''
    # compute new_path_info
    new_path_info = '/' + '/'.join([native_(x.encode('utf-8'), 'latin-1')
                                    for x in subpath])
    new_path_info = '/' + '/'.join(
        [native_(x.encode('utf-8'), 'latin-1') for x in subpath]
    )
    if new_path_info != '/': # don't want a sole double-slash
        if path_info != '/': # if orig path_info is '/', we're already done
    if new_path_info != '/':  # don't want a sole double-slash
        if path_info != '/':  # if orig path_info is '/', we're already done
            if path_info.endswith('/'):
                # readd trailing slash stripped by subpath (traversal)
                # conversion
@@ -314,6 +315,7 @@
    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
@@ -331,4 +333,5 @@
            setattr(request, name, method)
        InstancePropertyHelper.apply_properties(
            request, extensions.descriptors)
            request, extensions.descriptors
        )
src/pyramid/resource.py
@@ -1,5 +1,6 @@
""" Backwards compatibility shim module (forever). """
from pyramid.asset import * # b/w compat
resolve_resource_spec = resolve_asset_spec
resource_spec_from_abspath = asset_spec_from_abspath
abspath_from_resource_spec = abspath_from_asset_spec
from pyramid.asset import *  # noqa b/w compat
resolve_resource_spec = resolve_asset_spec  # noqa
resource_spec_from_abspath = asset_spec_from_abspath  # noqa
abspath_from_resource_spec = abspath_from_asset_spec  # noqa
src/pyramid/response.py
@@ -1,8 +1,5 @@
import mimetypes
from os.path import (
    getmtime,
    getsize,
    )
from os.path import getmtime, getsize
import venusian
@@ -18,16 +15,19 @@
        return True
    return False
# See http://bugs.python.org/issue5853 which is a recursion bug
# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix
# has been applied on the Python 2 trunk).
init_mimetypes(mimetypes)
_BLOCK_SIZE = 4096 * 64 # 256K
_BLOCK_SIZE = 4096 * 64  # 256K
@implementer(IResponse)
class Response(_Response):
    pass
class FileResponse(Response):
    """
@@ -51,14 +51,21 @@
    binary file.  This argument will be ignored if you also leave
    ``content-type`` as ``None``.
    """
    def __init__(self, path, request=None, cache_max_age=None,
                 content_type=None, content_encoding=None):
    def __init__(
        self,
        path,
        request=None,
        cache_max_age=None,
        content_type=None,
        content_encoding=None,
    ):
        if content_type is None:
            content_type, content_encoding = _guess_type(path)
        super(FileResponse, self).__init__(
            conditional_response=True,
            content_type=content_type,
            content_encoding=content_encoding
            content_encoding=content_encoding,
        )
        self.last_modified = getmtime(path)
        content_length = getsize(path)
@@ -76,6 +83,7 @@
        if cache_max_age is not None:
            self.cache_expires = cache_max_age
class FileIter(object):
    """ A fixed-block-size iterator for use as a WSGI app_iter.
@@ -84,6 +92,7 @@
    ``block_size`` is an optional block size for iteration.
    """
    def __init__(self, file, block_size=_BLOCK_SIZE):
        self.file = file
        self.block_size = block_size
@@ -97,7 +106,7 @@
            raise StopIteration
        return val
    __next__ = next # py3
    __next__ = next  # py3
    def close(self):
        self.file.close()
@@ -166,7 +175,8 @@
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian # for unit testing
    venusian = venusian  # for unit testing
    def __init__(self, *types_or_ifaces, **kwargs):
        self.types_or_ifaces = types_or_ifaces
@@ -180,8 +190,12 @@
            config.add_response_adapter(wrapped, type_or_iface, **self.kwargs)
    def __call__(self, wrapped):
        self.venusian.attach(wrapped, self.register, category=self.category,
                             depth=self.depth + 1)
        self.venusian.attach(
            wrapped,
            self.register,
            category=self.category,
            depth=self.depth + 1,
        )
        return wrapped
@@ -190,18 +204,14 @@
    `pyramid.interfaces.IResponseFactory`.
    """
    response_factory = registry.queryUtility(
        IResponseFactory,
        default=lambda r: Response()
        IResponseFactory, default=lambda r: Response()
    )
    return response_factory
def _guess_type(path):
    content_type, content_encoding = mimetypes.guess_type(
        path,
        strict=False
        )
    content_type, content_encoding = mimetypes.guess_type(path, strict=False)
    if content_type is None:
        content_type = 'application/octet-stream'
    # str-ifying content_type is a workaround for a bug in Python 2.7.7
src/pyramid/router.py
@@ -1,7 +1,4 @@
from zope.interface import (
    implementer,
    providedBy,
    )
from zope.interface import implementer, providedBy
from pyramid.interfaces import (
    IDebugLogger,
@@ -15,14 +12,14 @@
    IRoutesMapper,
    ITraverser,
    ITweens,
    )
)
from pyramid.events import (
    ContextFound,
    NewRequest,
    NewResponse,
    BeforeTraversal,
    )
)
from pyramid.httpexceptions import HTTPNotFound
from pyramid.request import Request
@@ -30,10 +27,8 @@
from pyramid.request import apply_request_extensions
from pyramid.threadlocal import RequestContext
from pyramid.traversal import (
    DefaultRootFactory,
    ResourceTreeTraverser,
    )
from pyramid.traversal import DefaultRootFactory, ResourceTreeTraverser
@implementer(IRouter)
class Router(object):
@@ -49,12 +44,13 @@
        self.request_factory = q(IRequestFactory, default=Request)
        self.request_extensions = q(IRequestExtensions)
        self.execution_policy = q(
            IExecutionPolicy, default=default_execution_policy)
            IExecutionPolicy, default=default_execution_policy
        )
        self.orig_handle_request = self.handle_request
        tweens = q(ITweens)
        if tweens is not None:
            self.handle_request = tweens(self.handle_request, registry)
        self.root_policy = self.root_factory # b/w compat
        self.root_policy = self.root_factory  # b/w compat
        self.registry = registry
        settings = registry.settings
        if settings is not None:
@@ -82,8 +78,7 @@
            match, route = info['match'], info['route']
            if route is None:
                if debug_routematch:
                    msg = ('no route matched for url %s' %
                           request.url)
                    msg = 'no route matched for url %s' % request.url
                    logger and logger.debug(msg)
            else:
                attrs['matchdict'] = match
@@ -96,20 +91,21 @@
                        'path_info: %r, '
                        'pattern: %r, '
                        'matchdict: %r, '
                        'predicates: %r' % (
                        'predicates: %r'
                        % (
                            request.url,
                            route.name,
                            request.path_info,
                            route.pattern,
                            match,
                            ', '.join([p.text() for p in route.predicates]))
                            ', '.join([p.text() for p in route.predicates]),
                        )
                    )
                    logger and logger.debug(msg)
                request.request_iface = registry.queryUtility(
                    IRouteRequest,
                    name=route.name,
                    default=IRequest)
                    IRouteRequest, name=route.name, default=IRequest
                )
                root_factory = route.factory or self.root_factory
@@ -137,8 +133,8 @@
            tdict['subpath'],
            tdict['traversed'],
            tdict['virtual_root'],
            tdict['virtual_root_path']
            )
            tdict['virtual_root_path'],
        )
        attrs.update(tdict)
@@ -149,12 +145,8 @@
        # find a view callable
        context_iface = providedBy(context)
        response = _call_view(
            registry,
            request,
            context,
            context_iface,
            view_name
            )
            registry, request, context, context_iface, view_name
        )
        if response is None:
            if self.debug_notfound:
@@ -162,11 +154,19 @@
                    'debug_notfound of url %s; path_info: %r, '
                    'context: %r, view_name: %r, subpath: %r, '
                    'traversed: %r, root: %r, vroot: %r, '
                    'vroot_path: %r' % (
                        request.url, request.path_info, context,
                        view_name, subpath, traversed, root, vroot,
                        vroot_path)
                    'vroot_path: %r'
                    % (
                        request.url,
                        request.path_info,
                        context,
                        view_name,
                        subpath,
                        traversed,
                        root,
                        vroot,
                        vroot_path,
                    )
                )
                logger and logger.debug(msg)
            else:
                msg = request.path_info
@@ -270,6 +270,7 @@
        response = self.execution_policy(environ, self)
        return response(environ, start_response)
def default_execution_policy(environ, router):
    with router.request_context(environ) as request:
        try:
src/pyramid/scaffolds/__init__.py
@@ -6,11 +6,13 @@
from pyramid.scaffolds.template import Template  # API
class PyramidTemplate(Template):
    """
     A class that can be used as a base class for Pyramid scaffolding
     templates.
    """
    def pre(self, command, output_dir, vars):
        """ Overrides :meth:`pyramid.scaffolds.template.Template.pre`, adding
        several variables to the default variables list (including
@@ -26,7 +28,7 @@
        vars['package_logger'] = package_logger
        return Template.pre(self, command, output_dir, vars)
    def post(self, command, output_dir, vars): # pragma: no cover
    def post(self, command, output_dir, vars):  # pragma: no cover
        """ Overrides :meth:`pyramid.scaffolds.template.Template.post`, to
        print "Welcome to Pyramid.  Sorry for the convenience." after a
        successful scaffolding rendering."""
@@ -42,24 +44,29 @@
            Welcome to Pyramid.  Sorry for the convenience.
            %(separator)s
        """ % {'separator': separator})
        """  # noqa: E501
            % {'separator': separator}
        )
        self.out(msg)
        return Template.post(self, command, output_dir, vars)
    def out(self, msg): # pragma: no cover (replaceable testing hook)
    def out(self, msg):  # pragma: no cover (replaceable testing hook)
        print(msg)
class StarterProjectTemplate(PyramidTemplate):
    _template_dir = 'starter'
    summary = 'Pyramid starter project using URL dispatch and Jinja2'
class ZODBProjectTemplate(PyramidTemplate):
    _template_dir = 'zodb'
    summary = 'Pyramid project using ZODB, traversal, and Chameleon'
class AlchemyProjectTemplate(PyramidTemplate):
    _template_dir = 'alchemy'
    summary = (
        'Pyramid project using SQLAlchemy, SQLite, URL dispatch, and '
        'Jinja2')
        'Pyramid project using SQLAlchemy, SQLite, URL dispatch, and ' 'Jinja2'
    )
src/pyramid/scaffolds/copydir.py
@@ -11,7 +11,7 @@
    native_,
    url_quote as compat_url_quote,
    escape,
    )
)
fsenc = sys.getfilesystemencoding()
@@ -22,9 +22,20 @@
    Raise this exception during the substitution of your template
    """
def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
             sub_vars=True, interactive=False, overwrite=True,
             template_renderer=None, out_=sys.stdout):
def copy_dir(
    source,
    dest,
    vars,
    verbosity,
    simulate,
    indent=0,
    sub_vars=True,
    interactive=False,
    overwrite=True,
    template_renderer=None,
    out_=sys.stdout,
):
    """
    Copies the ``source`` directory to the ``dest`` directory.
@@ -49,10 +60,12 @@
    ``template_renderer(content_as_string, vars_as_dict,
    filename=filename)``.
    """
    def out(msg):
        out_.write(msg)
        out_.write('\n')
        out_.flush()
    # This allows you to use a leading +dot+ in filenames which would
    # otherwise be skipped because leading dots make the file hidden:
    vars.setdefault('dot', '.')
@@ -80,7 +93,7 @@
            if verbosity >= 2:
                reason = pad + reason % {'filename': full}
                out(reason)
            continue # pragma: no cover
            continue  # pragma: no cover
        if sub_vars:
            dest_full = os.path.join(dest, substitute_filename(name, vars))
        sub_file = False
@@ -90,18 +103,36 @@
        if use_pkg_resources and pkg_resources.resource_isdir(source[0], full):
            if verbosity:
                out('%sRecursing into %s' % (pad, os.path.basename(full)))
            copy_dir((source[0], full), dest_full, vars, verbosity, simulate,
                     indent=indent + 1, sub_vars=sub_vars,
                     interactive=interactive, overwrite=overwrite,
                     template_renderer=template_renderer, out_=out_)
            copy_dir(
                (source[0], full),
                dest_full,
                vars,
                verbosity,
                simulate,
                indent=indent + 1,
                sub_vars=sub_vars,
                interactive=interactive,
                overwrite=overwrite,
                template_renderer=template_renderer,
                out_=out_,
            )
            continue
        elif not use_pkg_resources and os.path.isdir(full):
            if verbosity:
                out('%sRecursing into %s' % (pad, os.path.basename(full)))
            copy_dir(full, dest_full, vars, verbosity, simulate,
                     indent=indent + 1, sub_vars=sub_vars,
                     interactive=interactive, overwrite=overwrite,
                     template_renderer=template_renderer, out_=out_)
            copy_dir(
                full,
                dest_full,
                vars,
                verbosity,
                simulate,
                indent=indent + 1,
                sub_vars=sub_vars,
                interactive=interactive,
                overwrite=overwrite,
                template_renderer=template_renderer,
                out_=out_,
            )
            continue
        elif use_pkg_resources:
            content = pkg_resources.resource_string(source[0], full)
@@ -111,12 +142,14 @@
        if sub_file:
            try:
                content = substitute_content(
                    content, vars, filename=full,
                    template_renderer=template_renderer
                    )
            except SkipTemplate:
                continue # pragma: no cover
            if content is None:
                    content,
                    vars,
                    filename=full,
                    template_renderer=template_renderer,
                )
            except SkipTemplate:
                continue  # pragma: no cover
            if content is None:
                continue  # pragma: no cover
        already_exists = os.path.exists(dest_full)
        if already_exists:
@@ -124,26 +157,32 @@
                old_content = f.read()
            if old_content == content:
                if verbosity:
                    out('%s%s already exists (same content)' %
                          (pad, dest_full))
                continue # pragma: no cover
                    out(
                        '%s%s already exists (same content)' % (pad, dest_full)
                    )
                continue  # pragma: no cover
            if interactive:
                if not query_interactive(
                    native_(full, fsenc), native_(dest_full, fsenc),
                    native_(content, fsenc), native_(old_content, fsenc),
                    simulate=simulate, out_=out_):
                    native_(full, fsenc),
                    native_(dest_full, fsenc),
                    native_(content, fsenc),
                    native_(old_content, fsenc),
                    simulate=simulate,
                    out_=out_,
                ):
                    continue
            elif not overwrite:
                continue # pragma: no cover
                continue  # pragma: no cover
        if verbosity and use_pkg_resources:
            out('%sCopying %s to %s' % (pad, full, dest_full))
        elif verbosity:
            out(
                '%sCopying %s to %s' % (pad, os.path.basename(full),
                                        dest_full))
                '%sCopying %s to %s' % (pad, os.path.basename(full), dest_full)
            )
        if not simulate:
            with open(dest_full, 'wb') as f:
                f.write(content)
def should_skip_file(name):
    """
@@ -164,38 +203,60 @@
        return 'Skipping version control directory %(filename)s'
    return None
# Overridden on user's request:
all_answer = None
def query_interactive(src_fn, dest_fn, src_content, dest_content,
                      simulate, out_=sys.stdout):
def query_interactive(
    src_fn, dest_fn, src_content, dest_content, simulate, out_=sys.stdout
):
    def out(msg):
        out_.write(msg)
        out_.write('\n')
        out_.flush()
    global all_answer
    from difflib import unified_diff, context_diff
    u_diff = list(unified_diff(
        dest_content.splitlines(),
        src_content.splitlines(),
        dest_fn, src_fn))
    c_diff = list(context_diff(
        dest_content.splitlines(),
        src_content.splitlines(),
        dest_fn, src_fn))
    added = len([l for l in u_diff if l.startswith('+') and
                 not l.startswith('+++')])
    removed = len([l for l in u_diff if l.startswith('-') and
                   not l.startswith('---')])
    u_diff = list(
        unified_diff(
            dest_content.splitlines(),
            src_content.splitlines(),
            dest_fn,
            src_fn,
        )
    )
    c_diff = list(
        context_diff(
            dest_content.splitlines(),
            src_content.splitlines(),
            dest_fn,
            src_fn,
        )
    )
    added = len(
        [l for l in u_diff if l.startswith('+') and not l.startswith('+++')]
    )
    removed = len(
        [l for l in u_diff if l.startswith('-') and not l.startswith('---')]
    )
    if added > removed:
        msg = '; %i lines added' % (added - removed)
    elif removed > added:
        msg = '; %i lines removed' % (removed - added)
    else:
        msg = ''
    out('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % (
        len(dest_content), len(src_content),
        removed, len(dest_content.splitlines()), msg))
    out(
        'Replace %i bytes with %i bytes (%i/%i lines changed%s)'
        % (
            len(dest_content),
            len(src_content),
            removed,
            len(dest_content.splitlines()),
            msg,
        )
    )
    prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn
    while 1:
        if all_answer is None:
@@ -204,6 +265,7 @@
            response = all_answer
        if not response or response[0] == 'b':
            import shutil
            new_dest_fn = dest_fn + '.bak'
            n = 0
            while os.path.exists(new_dest_fn):
@@ -230,6 +292,7 @@
        else:
            out(query_usage)
query_usage = """\
Responses:
  Y(es):    Overwrite the file with the new content.
@@ -240,38 +303,46 @@
  Type "all Y/N/B" to use Y/N/B for answer to all future questions
"""
def makedirs(dir, verbosity, pad):
    parent = os.path.dirname(os.path.abspath(dir))
    if not os.path.exists(parent):
        makedirs(parent, verbosity, pad)  # pragma: no cover
    os.mkdir(dir)
def substitute_filename(fn, vars):
    for var, value in vars.items():
        fn = fn.replace('+%s+' % var, str(value))
    return fn
def substitute_content(content, vars, filename='<string>',
                       template_renderer=None):
def substitute_content(
    content, vars, filename='<string>', template_renderer=None
):
    v = standard_vars.copy()
    v.update(vars)
    return template_renderer(content, v, filename=filename)
def html_quote(s):
    if s is None:
        return ''
    return escape(str(s), 1)
def url_quote(s):
    if s is None:
        return ''
    return compat_url_quote(str(s))
def test(conf, true_cond, false_cond=None):
    if conf:
        return true_cond
    else:
        return false_cond
def skip_template(condition=True, *args):
    """
@@ -286,6 +357,7 @@
    if condition:
        raise SkipTemplate(*args)
standard_vars = {
    'nothing': None,
    'html_quote': html_quote,
@@ -297,5 +369,4 @@
    'bool': bool,
    'SkipTemplate': SkipTemplate,
    'skip_template': skip_template,
    }
}
src/pyramid/scaffolds/template.py
@@ -6,19 +6,18 @@
import sys
import os
from pyramid.compat import (
    native_,
    bytes_,
    )
from pyramid.compat import native_, bytes_
from pyramid.scaffolds import copydir
fsenc = sys.getfilesystemencoding()
class Template(object):
    """ Inherit from this base class and override methods to use the Pyramid
    scaffolding system."""
    copydir = copydir # for testing
    copydir = copydir  # for testing
    _template_dir = None
    def __init__(self, name):
@@ -36,7 +35,10 @@
        try:
            return bytes_(
                substitute_escaped_double_braces(
                    substitute_double_braces(content, TypeMapper(vars))), fsenc)
                    substitute_double_braces(content, TypeMapper(vars))
                ),
                fsenc,
            )
        except Exception as e:
            _add_except(e, ' in file %s' % filename)
            raise
@@ -54,7 +56,8 @@
        construct a path.  If _template_dir is a tuple, it should be a
        2-element tuple: ``(package_name, package_relative_path)``."""
        assert self._template_dir is not None, (
            "Template %r didn't set _template_dir" % self)
            "Template %r didn't set _template_dir" % self
        )
        if isinstance(self._template_dir, tuple):
            return self._template_dir
        else:
@@ -65,13 +68,13 @@
        self.write_files(command, output_dir, vars)
        self.post(command, output_dir, vars)
    def pre(self, command, output_dir, vars): # pragma: no cover
    def pre(self, command, output_dir, vars):  # pragma: no cover
        """
        Called before template is applied.
        """
        pass
    def post(self, command, output_dir, vars): # pragma: no cover
    def post(self, command, output_dir, vars):  # pragma: no cover
        """
        Called after template is applied.
        """
@@ -95,15 +98,15 @@
            overwrite=command.args.overwrite,
            indent=1,
            template_renderer=self.render_template,
            )
        )
    def makedirs(self, dir): # pragma: no cover
    def makedirs(self, dir):  # pragma: no cover
        return os.makedirs(dir)
    def exists(self, path): # pragma: no cover
    def exists(self, path):  # pragma: no cover
        return os.path.exists(path)
    def out(self, msg): # pragma: no cover
    def out(self, msg):  # pragma: no cover
        print(msg)
    # hair for exit with usage when paster create is used under 1.3 instead
@@ -113,13 +116,15 @@
    # required_templates tuple is required to allow it to get as far as
    # calling check_vars.
    required_templates = ()
    def check_vars(self, vars, other):
        raise RuntimeError(
            'Under Pyramid 1.3, you should use the "pcreate" command rather '
            'than "paster create"')
            'than "paster create"'
        )
class TypeMapper(dict):
    def __getitem__(self, item):
        options = item.split('|')
        for op in options[:-1]:
@@ -135,6 +140,7 @@
        else:
            return str(value)
def eval_with_catch(expr, vars):
    try:
        return eval(expr, vars)
@@ -142,23 +148,32 @@
        _add_except(e, 'in expression %r' % expr)
        raise
double_brace_pattern = re.compile(r'{{(?P<braced>.*?)}}')
def substitute_double_braces(content, values):
    def double_bracerepl(match):
        value = match.group('braced').strip()
        return values[value]
    return double_brace_pattern.sub(double_bracerepl, content)
escaped_double_brace_pattern = re.compile(r'\\{\\{(?P<escape_braced>[^\\]*?)\\}\\}')
escaped_double_brace_pattern = re.compile(
    r'\\{\\{(?P<escape_braced>[^\\]*?)\\}\\}'
)
def substitute_escaped_double_braces(content):
    def escaped_double_bracerepl(match):
        value = match.group('escape_braced').strip()
        return "{{%(value)s}}" % locals()
    return escaped_double_brace_pattern.sub(escaped_double_bracerepl, content)
def _add_except(exc, info): # pragma: no cover
def _add_except(exc, info):  # pragma: no cover
    if not hasattr(exc, 'args') or exc.args is None:
        return
    args = list(exc.args)
@@ -168,5 +183,3 @@
        args = [info]
    exc.args = tuple(args)
    return
src/pyramid/scaffolds/tests.py
@@ -15,12 +15,12 @@
    def make_venv(self, directory):  # pragma: no cover
        import virtualenv
        from virtualenv import Logger
        logger = Logger([(Logger.level_for_integer(2), sys.stdout)])
        virtualenv.logger = logger
        virtualenv.create_environment(directory,
                                      site_packages=False,
                                      clear=False,
                                      unzip_setuptools=True)
        virtualenv.create_environment(
            directory, site_packages=False, clear=False, unzip_setuptools=True
        )
    def install(self, tmpl_name):  # pragma: no cover
        try:
@@ -36,14 +36,18 @@
            os.chdir('Dingle')
            subprocess.check_call([pip, 'install', '.[testing]'])
            if tmpl_name == 'alchemy':
                populate = os.path.join(self.directory, 'bin',
                                        'initialize_Dingle_db')
                populate = os.path.join(
                    self.directory, 'bin', 'initialize_Dingle_db'
                )
                subprocess.check_call([populate, 'development.ini'])
            subprocess.check_call([
                os.path.join(self.directory, 'bin', 'py.test')])
            subprocess.check_call(
                [os.path.join(self.directory, 'bin', 'py.test')]
            )
            pserve = os.path.join(self.directory, 'bin', 'pserve')
            for ininame, hastoolbar in (('development.ini', True),
                                        ('production.ini', False)):
            for ininame, hastoolbar in (
                ('development.ini', True),
                ('production.ini', False),
            ):
                proc = subprocess.Popen([pserve, ininame])
                try:
                    time.sleep(5)
@@ -66,10 +70,10 @@
            shutil.rmtree(self.directory)
            os.chdir(self.old_cwd)
if __name__ == '__main__':     # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    templates = ['starter', 'alchemy', 'zodb']
    for name in templates:
        test = TemplateTest()
        test.install(name)
src/pyramid/scripting.py
@@ -1,15 +1,13 @@
from pyramid.config import global_registries
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import (
    IRequestFactory,
    IRootFactory,
    )
from pyramid.interfaces import IRequestFactory, IRootFactory
from pyramid.request import Request
from pyramid.request import apply_request_extensions
from pyramid.threadlocal import RequestContext
from pyramid.traversal import DefaultRootFactory
def get_root(app, request=None):
    """ Return a tuple composed of ``(root, closer)`` when provided a
@@ -29,10 +27,13 @@
    request.registry = registry
    ctx = RequestContext(request)
    ctx.begin()
    def closer():
        ctx.end()
    root = app.root_factory(request)
    return root, closer
def prepare(request=None, registry=None):
    """ This function pushes data onto the Pyramid threadlocal stack
@@ -80,9 +81,11 @@
    if registry is None:
        registry = getattr(request, 'registry', global_registries.last)
    if registry is None:
        raise ConfigurationError('No valid Pyramid applications could be '
                                 'found, make sure one has been created '
                                 'before trying to activate it.')
        raise ConfigurationError(
            'No valid Pyramid applications could be '
            'found, make sure one has been created '
            'before trying to activate it.'
        )
    if request is None:
        request = _make_request('/', registry)
    # NB: even though _make_request might have already set registry on
@@ -92,10 +95,13 @@
    ctx = RequestContext(request)
    ctx.begin()
    apply_request_extensions(request)
    def closer():
        ctx.end()
    root_factory = registry.queryUtility(IRootFactory,
                                         default=DefaultRootFactory)
    root_factory = registry.queryUtility(
        IRootFactory, default=DefaultRootFactory
    )
    root = root_factory(request)
    if getattr(request, 'context', None) is None:
        request.context = root
@@ -107,6 +113,7 @@
        root_factory=root_factory,
    )
class AppEnvironment(dict):
    def __enter__(self):
        return self
@@ -114,6 +121,7 @@
    def __exit__(self, type, value, traceback):
        self['closer']()
def _make_request(path, registry=None):
    """ Return a :meth:`pyramid.request.Request` object anchored at a
    given path. The object returned will be generated from the supplied
src/pyramid/scripts/common.py
@@ -1,5 +1,6 @@
import plaster
def parse_vars(args):
    """
    Given variables like ``['a=b', 'c=d']`` turns it into ``{'a':
@@ -8,13 +9,12 @@
    result = {}
    for arg in args:
        if '=' not in arg:
            raise ValueError(
                'Variable assignment %r invalid (no "=")'
                % arg)
            raise ValueError('Variable assignment %r invalid (no "=")' % arg)
        name, value = arg.split('=', 1)
        result[name] = value
    return result
def get_config_loader(config_uri):
    """
    Find a ``plaster.ILoader`` object supporting the "wsgi" protocol.
src/pyramid/scripts/pcreate.py
@@ -33,60 +33,88 @@
""",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument('-s', '--scaffold',
                        dest='scaffold_name',
                        action='append',
                        help=("Add a scaffold to the create process "
                              "(multiple -s args accepted)"))
    parser.add_argument('-t', '--template',
                        dest='scaffold_name',
                        action='append',
                        help=('A backwards compatibility alias for '
                              '-s/--scaffold.  Add a scaffold to the '
                              'create process (multiple -t args accepted)'))
    parser.add_argument('-l', '--list',
                        dest='list',
                        action='store_true',
                        help="List all available scaffold names")
    parser.add_argument('--list-templates',
                        dest='list',
                        action='store_true',
                        help=("A backwards compatibility alias for -l/--list. "
                              "List all available scaffold names."))
    parser.add_argument('--package-name',
                        dest='package_name',
                        action='store',
                        help='Package name to use. The name provided is '
                             'assumed to be a valid Python package name, and '
                             'will not be validated. By default the package '
                             'name is derived from the value of '
                             'output_directory.')
    parser.add_argument('--simulate',
                        dest='simulate',
                        action='store_true',
                        help='Simulate but do no work')
    parser.add_argument('--overwrite',
                        dest='overwrite',
                        action='store_true',
                        help='Always overwrite')
    parser.add_argument('--interactive',
                        dest='interactive',
                        action='store_true',
                        help='When a file would be overwritten, interrogate '
                             '(this is the default, but you may specify it to '
                             'override --overwrite)')
    parser.add_argument('--ignore-conflicting-name',
                        dest='force_bad_name',
                        action='store_true',
                        default=False,
                        help='Do create a project even if the chosen name '
                             'is the name of an already existing / importable '
                             'package.')
    parser.add_argument('output_directory',
                        nargs='?',
                        default=None,
                        help='The directory where the project will be '
                             'created.')
    parser.add_argument(
        '-s',
        '--scaffold',
        dest='scaffold_name',
        action='append',
        help=(
            "Add a scaffold to the create process "
            "(multiple -s args accepted)"
        ),
    )
    parser.add_argument(
        '-t',
        '--template',
        dest='scaffold_name',
        action='append',
        help=(
            'A backwards compatibility alias for '
            '-s/--scaffold.  Add a scaffold to the '
            'create process (multiple -t args accepted)'
        ),
    )
    parser.add_argument(
        '-l',
        '--list',
        dest='list',
        action='store_true',
        help="List all available scaffold names",
    )
    parser.add_argument(
        '--list-templates',
        dest='list',
        action='store_true',
        help=(
            "A backwards compatibility alias for -l/--list. "
            "List all available scaffold names."
        ),
    )
    parser.add_argument(
        '--package-name',
        dest='package_name',
        action='store',
        help='Package name to use. The name provided is '
        'assumed to be a valid Python package name, and '
        'will not be validated. By default the package '
        'name is derived from the value of '
        'output_directory.',
    )
    parser.add_argument(
        '--simulate',
        dest='simulate',
        action='store_true',
        help='Simulate but do no work',
    )
    parser.add_argument(
        '--overwrite',
        dest='overwrite',
        action='store_true',
        help='Always overwrite',
    )
    parser.add_argument(
        '--interactive',
        dest='interactive',
        action='store_true',
        help='When a file would be overwritten, interrogate '
        '(this is the default, but you may specify it to '
        'override --overwrite)',
    )
    parser.add_argument(
        '--ignore-conflicting-name',
        dest='force_bad_name',
        action='store_true',
        default=False,
        help='Do create a project even if the chosen name '
        'is the name of an already existing / importable '
        'package.',
    )
    parser.add_argument(
        'output_directory',
        nargs='?',
        default=None,
        help='The directory where the project will be ' 'created.',
    )
    pyramid_dist = pkg_resources.get_distribution("pyramid")
@@ -123,7 +151,8 @@
        project_name = os.path.basename(os.path.split(output_dir)[1])
        if self.args.package_name is None:
            pkg_name = _bad_chars_re.sub(
                '', project_name.lower().replace('-', '_'))
                '', project_name.lower().replace('-', '_')
            )
            safe_name = pkg_resources.safe_name(project_name)
        else:
            pkg_name = self.args.package_name
@@ -170,9 +199,14 @@
            max_name = max([len(t.name) for t in scaffolds])
            self.out('Available scaffolds:')
            for scaffold in scaffolds:
                self.out('  %s:%s  %s' % (
                    scaffold.name,
                    ' ' * (max_name - len(scaffold.name)), scaffold.summary))
                self.out(
                    '  %s:%s  %s'
                    % (
                        scaffold.name,
                        ' ' * (max_name - len(scaffold.name)),
                        scaffold.summary,
                    )
                )
        else:
            self.out('No scaffolds available')
        return 0
@@ -186,8 +220,10 @@
                scaffold = scaffold_class(entry.name)
                scaffolds.append(scaffold)
            except Exception as e:  # pragma: no cover
                self.out('Warning: could not load entry point %s (%s: %s)' % (
                    entry.name, e.__class__.__name__, e))
                self.out(
                    'Warning: could not load entry point %s (%s: %s)'
                    % (entry.name, e.__class__.__name__, e)
                )
        return scaffolds
    def out(self, msg):  # pragma: no cover
@@ -196,8 +232,10 @@
    def validate_input(self):
        if not self.args.scaffold_name:
            self.out('You must provide at least one scaffold name: '
                     '-s <scaffold name>')
            self.out(
                'You must provide at least one scaffold name: '
                '-s <scaffold name>'
            )
            self.out('')
            self.show_scaffolds()
            return False
@@ -213,11 +251,14 @@
        pkg_name = self.project_vars['package']
        if pkg_name == 'site' and not self.args.force_bad_name:
            self.out('The package name "site" has a special meaning in '
                     'Python. Are you sure you want to use it as your '
                     'project\'s name?')
            return self.confirm_bad_name('Really use "{0}"?: '.format(
                pkg_name))
            self.out(
                'The package name "site" has a special meaning in '
                'Python. Are you sure you want to use it as your '
                'project\'s name?'
            )
            return self.confirm_bad_name(
                'Really use "{0}"?: '.format(pkg_name)
            )
        # check if pkg_name can be imported (i.e. already exists in current
        # $PYTHON_PATH, if so - let the user confirm
@@ -232,8 +273,10 @@
        if self.args.force_bad_name:
            return True
        self.out('A package named "{0}" already exists, are you sure you want '
                 'to use it as your project\'s name?'.format(pkg_name))
        self.out(
            'A package named "{0}" already exists, are you sure you want '
            'to use it as your project\'s name?'.format(pkg_name)
        )
        return self.confirm_bad_name('Really use "{0}"?: '.format(pkg_name))
    def confirm_bad_name(self, prompt):  # pragma: no cover
@@ -241,11 +284,14 @@
        return answer.strip().lower() == 'y'
    def _warn_pcreate_deprecated(self):
        self.out('''\
        self.out(
            '''\
Note: As of Pyramid 1.8, this command is deprecated. Use a specific
cookiecutter instead:
https://github.com/pylons/?query=cookiecutter
''')
'''
        )
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/scripts/pdistreport.py
@@ -4,19 +4,27 @@
import argparse
from operator import itemgetter
def out(*args): # pragma: no cover
def out(*args):  # pragma: no cover
    for arg in args:
        sys.stdout.write(arg)
        sys.stdout.write(' ')
    sys.stdout.write('\n')
def get_parser():
    parser = argparse.ArgumentParser(
        description="Show Python distribution versions and locations in use")
        description="Show Python distribution versions and locations in use"
    )
    return parser
def main(argv=sys.argv, pkg_resources=pkg_resources, platform=platform.platform,
         out=out):
def main(
    argv=sys.argv,
    pkg_resources=pkg_resources,
    platform=platform.platform,
    out=out,
):
    # all args except argv are for unit testing purposes only
    parser = get_parser()
    parser.parse_args(argv[1:])
@@ -24,11 +32,13 @@
    for distribution in pkg_resources.working_set:
        name = distribution.project_name
        packages.append(
            {'version': distribution.version,
             'lowername': name.lower(),
             'name': name,
             'location':distribution.location}
            )
            {
                'version': distribution.version,
                'lowername': name.lower(),
                'name': name,
                'location': distribution.location,
            }
        )
    packages = sorted(packages, key=itemgetter('lowername'))
    pyramid_version = pkg_resources.get_distribution('pyramid').version
    plat = platform()
@@ -39,5 +49,6 @@
        out(' ', package['name'], package['version'])
        out('   ', package['location'])
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/scripts/prequest.py
@@ -8,9 +8,11 @@
from pyramid.scripts.common import get_config_loader
from pyramid.scripts.common import parse_vars
def main(argv=sys.argv, quiet=False):
    command = PRequestCommand(argv, quiet)
    return command.run()
class PRequestCommand(object):
    description = """\
@@ -48,15 +50,16 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    )
    parser.add_argument(
        '-n', '--app-name',
        '-n',
        '--app-name',
        dest='app_name',
        metavar='NAME',
        help=(
            "Load the named application from the config file (default 'main')"
        ),
        )
    )
    parser.add_argument(
        '--header',
        dest='headers',
@@ -67,47 +70,55 @@
        ),
    )
    parser.add_argument(
        '-d', '--display-headers',
        '-d',
        '--display-headers',
        dest='display_headers',
        action='store_true',
        help='Display status and headers before the response body'
        )
        help='Display status and headers before the response body',
    )
    parser.add_argument(
        '-m', '--method',
        '-m',
        '--method',
        dest='method',
        choices=['GET', 'HEAD', 'POST', 'PUT', 'PATCH','DELETE',
                 'PROPFIND', 'OPTIONS'],
        choices=[
            'GET',
            'HEAD',
            'POST',
            'PUT',
            'PATCH',
            'DELETE',
            'PROPFIND',
            'OPTIONS',
        ],
        help='Request method type (GET, POST, PUT, PATCH, DELETE, '
             'PROPFIND, OPTIONS)',
        )
        'PROPFIND, OPTIONS)',
    )
    parser.add_argument(
        '-l', '--login',
        '-l',
        '--login',
        dest='login',
        help='HTTP basic auth username:password pair',
        )
    )
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    )
    parser.add_argument(
        'path_info',
        nargs='?',
        default=None,
        help='The path of the request.',
        )
        'path_info', nargs='?', default=None, help='The path of the request.'
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    _get_config_loader = staticmethod(get_config_loader)
    stdin = sys.stdin
@@ -116,7 +127,7 @@
        self.quiet = quiet
        self.args = self.parser.parse_args(argv[1:])
    def out(self, msg): # pragma: no cover
    def out(self, msg):  # pragma: no cover
        if not self.quiet:
            print(msg)
@@ -153,7 +164,8 @@
                if ':' not in item:
                    self.out(
                        "Bad --header=%s option, value must be in the form "
                        "'name:value'" % item)
                        "'name:value'" % item
                    )
                    return 2
                name, value = item.split(':', 1)
                headers[name] = value.strip()
@@ -162,13 +174,13 @@
        environ = {
            'REQUEST_METHOD': request_method,
            'SCRIPT_NAME': '',           # may be empty if app is at the root
            'SCRIPT_NAME': '',  # may be empty if app is at the root
            'PATH_INFO': path,
            'SERVER_NAME': 'localhost',  # always mandatory
            'SERVER_PORT': '80',         # always mandatory
            'SERVER_PORT': '80',  # always mandatory
            'SERVER_PROTOCOL': 'HTTP/1.0',
            'CONTENT_TYPE': 'text/plain',
            'REMOTE_ADDR':'127.0.0.1',
            'REMOTE_ADDR': '127.0.0.1',
            'wsgi.run_once': True,
            'wsgi.multithread': False,
            'wsgi.multiprocess': False,
@@ -178,7 +190,7 @@
            'QUERY_STRING': qs,
            'HTTP_ACCEPT': 'text/plain;q=1.0, */*;q=0.1',
            'paste.command_request': True,
            }
        }
        if request_method in ('POST', 'PUT', 'PATCH'):
            environ['wsgi.input'] = self.stdin
@@ -203,5 +215,6 @@
            self.out(response.body)
        return 0
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/scripts/proutes.py
@@ -110,7 +110,7 @@
            if original_view.package_name is not None:
                return '%s:%s' % (
                    original_view.package_name,
                    original_view.docroot
                    original_view.docroot,
                )
            else:
                return original_view.docroot
@@ -122,10 +122,7 @@
        # for them and remove this logic
        view_name = str(view_callable)
    view_module = '%s.%s' % (
        view_callable.__module__,
        view_name,
    )
    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
@@ -139,24 +136,17 @@
def get_route_data(route, registry):
    pattern = _get_pattern(route)
    request_iface = registry.queryUtility(
        IRouteRequest,
        name=route.name
    )
    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
    )
    route_intr = registry.introspector.get('routes', route.name)
    if request_iface is None:
        return [
            (route.name, _get_pattern(route), UNKNOWN_KEY, ANY_KEY)
        ]
        return [(route.name, _get_pattern(route), UNKNOWN_KEY, ANY_KEY)]
    view_callables = _find_views(registry, request_iface, Interface, '')
    if view_callables:
@@ -188,7 +178,7 @@
                        view_callable = getattr(view['callable'], view['attr'])
                        view_module = '%s.%s' % (
                            _get_view_module(view['callable']),
                            view['attr']
                            view['attr'],
                        )
                    else:
                        view_callable = view['callable']
@@ -217,17 +207,11 @@
    for view_module in view_request_methods_order:
        methods = view_request_methods[view_module]
        request_methods = _get_request_methods(
            route_request_methods,
            methods
        )
        request_methods = _get_request_methods(route_request_methods, methods)
        final_routes.append((
            route.name,
            pattern,
            view_module,
            request_methods,
        ))
        final_routes.append(
            (route.name, pattern, view_module, request_methods)
        )
    return final_routes
@@ -251,43 +235,49 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    parser.add_argument('-g', '--glob',
                        action='store',
                        dest='glob',
                        default='',
                        help='Display routes matching glob pattern')
    )
    parser.add_argument(
        '-g',
        '--glob',
        action='store',
        dest='glob',
        default='',
        help='Display routes matching glob pattern',
    )
    parser.add_argument('-f', '--format',
                        action='store',
                        dest='format',
                        default='',
                        help=('Choose which columns to display, this will '
                              'override the format key in the [proutes] ini '
                              'section'))
    parser.add_argument(
        '-f',
        '--format',
        action='store',
        dest='format',
        default='',
        help=(
            'Choose which columns to display, this will '
            'override the format key in the [proutes] ini '
            'section'
        ),
    )
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    def __init__(self, argv, quiet=False):
        self.args = self.parser.parse_args(argv[1:])
        self.quiet = quiet
        self.available_formats = [
            'name', 'pattern', 'view', 'method'
        ]
        self.available_formats = ['name', 'pattern', 'view', 'method']
        self.column_format = self.available_formats
    def validate_formats(self, formats):
@@ -296,10 +286,7 @@
            if fmt not in self.available_formats:
                invalid_formats.append(fmt)
        msg = (
            'You provided invalid formats %s, '
            'Available formats are %s'
        )
        msg = 'You provided invalid formats %s, ' 'Available formats are %s'
        if invalid_formats:
            msg = msg % (invalid_formats, self.available_formats)
@@ -321,6 +308,7 @@
    def _get_mapper(self, registry):
        from pyramid.config import Configurator
        config = Configurator(registry=registry)
        return config.get_routes_mapper()
@@ -361,25 +349,29 @@
        if len(routes) == 0:
            return 0
        mapped_routes = [{
            'name': 'Name',
            'pattern': 'Pattern',
            'view': 'View',
            'method': 'Method'
        },{
            'name': '----',
            'pattern': '-------',
            'view': '----',
            'method': '------'
        }]
        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.args.glob:
                    match = (fnmatch.fnmatch(name, self.args.glob) or
                             fnmatch.fnmatch(pattern, self.args.glob))
                    match = fnmatch.fnmatch(
                        name, self.args.glob
                    ) or fnmatch.fnmatch(pattern, self.args.glob)
                    if not match:
                        continue
@@ -395,12 +387,14 @@
                if len(method) > max_method:
                    max_method = len(method)
                mapped_routes.append({
                    'name': name,
                    'pattern': pattern,
                    'view': view,
                    'method': 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
src/pyramid/scripts/pserve.py
@@ -46,67 +46,86 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    )
    parser.add_argument(
        '-n', '--app-name',
        '-n',
        '--app-name',
        dest='app_name',
        metavar='NAME',
        help="Load the named application (default main)")
        help="Load the named application (default main)",
    )
    parser.add_argument(
        '-s', '--server',
        '-s',
        '--server',
        dest='server',
        metavar='SERVER_TYPE',
        help="Use the named server.")
        help="Use the named server.",
    )
    parser.add_argument(
        '--server-name',
        dest='server_name',
        metavar='SECTION_NAME',
        help=("Use the named server as defined in the configuration file "
              "(default: main)"))
        help=(
            "Use the named server as defined in the configuration file "
            "(default: main)"
        ),
    )
    parser.add_argument(
        '--reload',
        dest='reload',
        action='store_true',
        help="Use auto-restart file monitor")
        help="Use auto-restart file monitor",
    )
    parser.add_argument(
        '--reload-interval',
        dest='reload_interval',
        default=1,
        help=("Seconds between checking files (low number can cause "
              "significant CPU usage)"))
        help=(
            "Seconds between checking files (low number can cause "
            "significant CPU usage)"
        ),
    )
    parser.add_argument(
        '-b', '--browser',
        '-b',
        '--browser',
        dest='browser',
        action='store_true',
        help=("Open a web browser to the server url. The server url is "
              "determined from the 'open_url' setting in the 'pserve' "
              "section of the configuration file."))
        help=(
            "Open a web browser to the server url. The server url is "
            "determined from the 'open_url' setting in the 'pserve' "
            "section of the configuration file."
        ),
    )
    parser.add_argument(
        '-v', '--verbose',
        '-v',
        '--verbose',
        default=default_verbosity,
        dest='verbose',
        action='count',
        help="Set verbose level (default " + str(default_verbosity) + ")")
        help="Set verbose level (default " + str(default_verbosity) + ")",
    )
    parser.add_argument(
        '-q', '--quiet',
        '-q',
        '--quiet',
        action='store_const',
        const=0,
        dest='verbose',
        help="Suppress verbose output")
        help="Suppress verbose output",
    )
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    _get_config_loader = staticmethod(get_config_loader)  # for testing
@@ -187,18 +206,23 @@
            if not url:
                url = self.guess_server_url(
                    server_loader, server_name, config_vars)
                    server_loader, server_name, config_vars
                )
            if not url:
                self.out('WARNING: could not determine the server\'s url to '
                         'open the browser. To fix this set the "open_url" '
                         'setting in the [pserve] section of the '
                         'configuration file.')
                self.out(
                    'WARNING: could not determine the server\'s url to '
                    'open the browser. To fix this set the "open_url" '
                    'setting in the [pserve] section of the '
                    'configuration file.'
                )
            else:
                def open_browser():
                    time.sleep(1)
                    webbrowser.open(url)
                t = threading.Thread(target=open_browser)
                t.setDaemon(True)
                t.start()
@@ -210,7 +234,7 @@
                'pyramid.scripts.pserve.main',
                reload_interval=int(self.args.reload_interval),
                verbose=self.args.verbose,
                worker_kwargs=self.worker_kwargs
                worker_kwargs=self.worker_kwargs,
            )
            return 0
@@ -250,6 +274,7 @@
# For paste.deploy server instantiation (egg:pyramid#wsgiref)
def wsgiref_server_runner(wsgi_app, global_conf, **kw):  # pragma: no cover
    from wsgiref.simple_server import make_server
    host = kw.get('host', '0.0.0.0')
    port = int(kw.get('port', 8080))
    server = make_server(host, port, wsgi_app)
@@ -259,11 +284,18 @@
# For paste.deploy server instantiation (egg:pyramid#cherrypy)
def cherrypy_server_runner(
        app, global_conf=None, host='127.0.0.1', port=None,
        ssl_pem=None, protocol_version=None, numthreads=None,
        server_name=None, max=None, request_queue_size=None,
        timeout=None
        ):  # pragma: no cover
    app,
    global_conf=None,
    host='127.0.0.1',
    port=None,
    ssl_pem=None,
    protocol_version=None,
    numthreads=None,
    server_name=None,
    max=None,
    request_queue_size=None,
    timeout=None,
):  # pragma: no cover
    """
    Entry point for CherryPy's WSGI server
@@ -346,8 +378,7 @@
    except ImportError:
        from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer
    server = WSGIServer(bind_addr, app,
                        server_name=server_name, **kwargs)
    server = WSGIServer(bind_addr, app, server_name=server_name, **kwargs)
    if ssl_pem is not None:
        if PY2:
            server.ssl_certificate = server.ssl_private_key = ssl_pem
@@ -368,8 +399,10 @@
    try:
        protocol = is_ssl and 'https' or 'http'
        if host == '0.0.0.0':
            print('serving on 0.0.0.0:%s view at %s://127.0.0.1:%s' %
                  (port, protocol, port))
            print(
                'serving on 0.0.0.0:%s view at %s://127.0.0.1:%s'
                % (port, protocol, port)
            )
        else:
            print('serving on %s://%s:%s' % (protocol, host, port))
        server.start()
src/pyramid/scripts/pshell.py
@@ -16,6 +16,7 @@
from pyramid.scripts.common import get_config_loader
from pyramid.scripts.common import parse_vars
def main(argv=sys.argv, quiet=False):
    command = PShellCommand(argv, quiet)
    return command.run()
@@ -49,38 +50,52 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    parser.add_argument('-p', '--python-shell',
                        action='store',
                        dest='python_shell',
                        default='',
                        help=('Select the shell to use. A list of possible '
                              'shells is available using the --list-shells '
                              'option.'))
    parser.add_argument('-l', '--list-shells',
                        dest='list',
                        action='store_true',
                        help='List all available shells.')
    parser.add_argument('--setup',
                        dest='setup',
                        help=("A callable that will be passed the environment "
                              "before it is made available to the shell. This "
                              "option will override the 'setup' key in the "
                              "[pshell] ini section."))
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    )
    parser.add_argument(
        '-p',
        '--python-shell',
        action='store',
        dest='python_shell',
        default='',
        help=(
            'Select the shell to use. A list of possible '
            'shells is available using the --list-shells '
            'option.'
        ),
    )
    parser.add_argument(
        '-l',
        '--list-shells',
        dest='list',
        action='store_true',
        help='List all available shells.',
    )
    parser.add_argument(
        '--setup',
        dest='setup',
        help=(
            "A callable that will be passed the environment "
            "before it is made available to the shell. This "
            "option will override the 'setup' key in the "
            "[pshell] ini section."
        ),
    )
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    default_runner = python_shell_runner # testing
    default_runner = python_shell_runner  # testing
    loaded_objects = {}
    object_help = {}
@@ -107,7 +122,7 @@
                self.loaded_objects[k] = self.resolver.maybe_resolve(v)
                self.object_help[k] = v
    def out(self, msg): # pragma: no cover
    def out(self, msg):  # pragma: no cover
        if not self.quiet:
            print(msg)
@@ -152,8 +167,9 @@
        env_help['root'] = 'Root of the default resource tree.'
        env_help['registry'] = 'Active Pyramid registry.'
        env_help['request'] = 'Active request object.'
        env_help['root_factory'] = (
            'Default root factory used to create `root`.')
        env_help[
            'root_factory'
        ] = 'Default root factory used to create `root`.'
        # load the pshell section of the ini file
        env.update(self.loaded_objects)
@@ -236,6 +252,7 @@
                # by default prioritize all shells above python
                preferred_shells = [k for k in shells.keys() if k != 'python']
            max_weight = len(preferred_shells)
            def order(x):
                # invert weight to reverse sort the list
                # (closer to the front is higher priority)
@@ -243,6 +260,7 @@
                    return preferred_shells.index(x[0].lower()) - max_weight
                except ValueError:
                    return 1
            sorted_shells = sorted(shells.items(), key=order)
            if len(sorted_shells) > 0:
@@ -266,5 +284,5 @@
        return shell
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/scripts/ptweens.py
@@ -10,9 +10,11 @@
from pyramid.paster import setup_logging
from pyramid.scripts.common import parse_vars
def main(argv=sys.argv, quiet=False):
    command = PTweensCommand(argv, quiet)
    return command.run()
class PTweensCommand(object):
    description = """\
@@ -31,25 +33,27 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    )
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    stdout = sys.stdout
    bootstrap = staticmethod(bootstrap) # testing
    setup_logging = staticmethod(setup_logging) # testing
    bootstrap = staticmethod(bootstrap)  # testing
    setup_logging = staticmethod(setup_logging)  # testing
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
@@ -57,10 +61,11 @@
    def _get_tweens(self, registry):
        from pyramid.config import Configurator
        config = Configurator(registry=registry)
        return config.registry.queryUtility(ITweens)
    def out(self, msg): # pragma: no cover
    def out(self, msg):  # pragma: no cover
        if not self.quiet:
            print(msg)
@@ -86,8 +91,10 @@
        if tweens is not None:
            explicit = tweens.explicit
            if explicit:
                self.out('"pyramid.tweens" config value set '
                         '(explicitly ordered tweens used)')
                self.out(
                    '"pyramid.tweens" config value set '
                    '(explicitly ordered tweens used)'
                )
                self.out('')
                self.out('Explicit Tween Chain (used)')
                self.out('')
@@ -97,13 +104,16 @@
                self.out('')
                self.show_chain(tweens.implicit())
            else:
                self.out('"pyramid.tweens" config value NOT set '
                         '(implicitly ordered tweens used)')
                self.out(
                    '"pyramid.tweens" config value NOT set '
                    '(implicitly ordered tweens used)'
                )
                self.out('')
                self.out('Implicit Tween Chain')
                self.out('')
                self.show_chain(tweens.implicit())
        return 0
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/scripts/pviews.py
@@ -9,9 +9,11 @@
from pyramid.scripts.common import parse_vars
from pyramid.view import _find_views
def main(argv=sys.argv, quiet=False):
    command = PViewsCommand(argv, quiet)
    return command.run()
class PViewsCommand(object):
    description = """\
@@ -31,38 +33,41 @@
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
    )
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
    )
    parser.add_argument('url',
                        nargs='?',
                        default=None,
                        help='The path info portion of the URL.')
    parser.add_argument(
        'url',
        nargs='?',
        default=None,
        help='The path info portion of the URL.',
    )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
        "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
        "passed here.",
    )
    bootstrap = staticmethod(bootstrap) # testing
    setup_logging = staticmethod(setup_logging) # testing
    bootstrap = staticmethod(bootstrap)  # testing
    setup_logging = staticmethod(setup_logging)  # testing
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.args = self.parser.parse_args(argv[1:])
    def out(self, msg): # pragma: no cover
    def out(self, msg):  # pragma: no cover
        if not self.quiet:
            print(msg)
    def _find_multi_routes(self, mapper, request):
        infos = []
        path = request.environ['PATH_INFO']
@@ -70,7 +75,7 @@
        for route in mapper.get_routes():
            match = route.match(path)
            if match is not None:
                info = {'match':match, 'route':route}
                info = {'match': match, 'route': route}
                infos.append(info)
        return infos
@@ -99,22 +104,17 @@
        @implementer(IMultiView)
        class RoutesMultiView(object):
            def __init__(self, infos, context_iface, root_factory, request):
                self.views = []
                for info in infos:
                    match, route = info['match'], info['route']
                    if route is not None:
                        request_iface = registry.queryUtility(
                            IRouteRequest,
                            name=route.name,
                            default=IRequest)
                            IRouteRequest, name=route.name, default=IRequest
                        )
                        views = _find_views(
                            request.registry,
                            request_iface,
                            context_iface,
                            ''
                            )
                            request.registry, request_iface, context_iface, ''
                        )
                        if not views:
                            continue
                        view = views[0]
@@ -148,9 +148,8 @@
                    attrs['matched_route'] = route
                    request.environ['bfg.routes.matchdict'] = match
                    request_iface = registry.queryUtility(
                        IRouteRequest,
                        name=route.name,
                        default=IRequest)
                        IRouteRequest, name=route.name, default=IRequest
                    )
                    root_factory = route.factory or root_factory
            if len(infos) > 1:
                routes_multiview = infos
@@ -171,11 +170,8 @@
        context_iface = providedBy(context)
        if routes_multiview is None:
            views = _find_views(
                request.registry,
                request_iface,
                context_iface,
                view_name,
                )
                request.registry, request_iface, context_iface, view_name
            )
            if views:
                view = views[0]
            else:
@@ -186,11 +182,8 @@
        # routes are not registered with a view name
        if view is None:
            views = _find_views(
                request.registry,
                request_iface,
                context_iface,
                '',
                )
                request.registry, request_iface, context_iface, ''
            )
            if views:
                view = views[0]
            else:
@@ -285,5 +278,6 @@
        env['closer']()
        return 0
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
src/pyramid/security.py
@@ -7,7 +7,7 @@
    ISecuredView,
    IView,
    IViewClassifier,
    )
)
from pyramid.compat import map_
from pyramid.threadlocal import get_current_registry
@@ -16,6 +16,7 @@
Authenticated = 'system.Authenticated'
Allow = 'Allow'
Deny = 'Deny'
class AllPermissionsList(object):
    """ Stand in 'permission list' to represent all permissions """
@@ -29,94 +30,105 @@
    def __eq__(self, other):
        return isinstance(other, self.__class__)
ALL_PERMISSIONS = AllPermissionsList()
DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS)
NO_PERMISSION_REQUIRED = '__no_permission_required__'
def _get_registry(request):
    try:
        reg = request.registry
    except AttributeError:
        reg = get_current_registry() # b/c
        reg = get_current_registry()  # b/c
    return reg
def _get_authentication_policy(request):
    registry = _get_registry(request)
    return registry.queryUtility(IAuthenticationPolicy)
def has_permission(permission, context, request):
    """
    A function that calls :meth:`pyramid.request.Request.has_permission`
    and returns its result.
    .. deprecated:: 1.5
        Use :meth:`pyramid.request.Request.has_permission` instead.
    .. versionchanged:: 1.5a3
        If context is None, then attempt to use the context attribute of self;
        if not set, then the AttributeError is propagated.
    """
    """
    return request.has_permission(permission, context)
deprecated(
    'has_permission',
    'As of Pyramid 1.5 the "pyramid.security.has_permission" API is now '
    'deprecated.  It will be removed in Pyramid 1.8.  Use the '
    '"has_permission" method of the Pyramid request instead.'
    )
    '"has_permission" method of the Pyramid request instead.',
)
def authenticated_userid(request):
    """
    A function that returns the value of the property
    :attr:`pyramid.request.Request.authenticated_userid`.
    .. deprecated:: 1.5
       Use :attr:`pyramid.request.Request.authenticated_userid` instead.
    """
    """
    return request.authenticated_userid
deprecated(
    'authenticated_userid',
    'As of Pyramid 1.5 the "pyramid.security.authenticated_userid" API is now '
    'deprecated.  It will be removed in Pyramid 1.8.  Use the '
    '"authenticated_userid" attribute of the Pyramid request instead.'
    )
    '"authenticated_userid" attribute of the Pyramid request instead.',
)
def unauthenticated_userid(request):
    """
    """
    A function that returns the value of the property
    :attr:`pyramid.request.Request.unauthenticated_userid`.
    .. deprecated:: 1.5
        Use :attr:`pyramid.request.Request.unauthenticated_userid` instead.
    """
    """
    return request.unauthenticated_userid
deprecated(
    'unauthenticated_userid',
    'As of Pyramid 1.5 the "pyramid.security.unauthenticated_userid" API is '
    'now deprecated.  It will be removed in Pyramid 1.8.  Use the '
    '"unauthenticated_userid" attribute of the Pyramid request instead.'
    )
    '"unauthenticated_userid" attribute of the Pyramid request instead.',
)
def effective_principals(request):
    """
    A function that returns the value of the property
    :attr:`pyramid.request.Request.effective_principals`.
    .. deprecated:: 1.5
        Use :attr:`pyramid.request.Request.effective_principals` instead.
    """
    """
    return request.effective_principals
deprecated(
    'effective_principals',
    'As of Pyramid 1.5 the "pyramid.security.effective_principals" API is '
    'now deprecated.  It will be removed in Pyramid 1.8.  Use the '
    '"effective_principals" attribute of the Pyramid request instead.'
    )
    '"effective_principals" attribute of the Pyramid request instead.',
)
def remember(request, userid, **kw):
    """
@@ -154,6 +166,7 @@
        return []
    return policy.remember(request, userid, **kw)
def forget(request):
    """
    Return a sequence of header tuples (e.g. ``[('Set-Cookie',
@@ -172,11 +185,12 @@
    If no :term:`authentication policy` is in use, this function will
    always return an empty sequence.
    """
    """
    policy = _get_authentication_policy(request)
    if policy is None:
        return []
    return policy.forget(request)
def principals_allowed_by_permission(context, permission):
    """ Provided a ``context`` (a resource object), and a ``permission``
@@ -201,6 +215,7 @@
        return [Everyone]
    return policy.principals_allowed_by_permission(context, permission)
def view_execution_permitted(context, request, name=''):
    """ If the view specified by ``context`` and ``name`` is protected
    by a :term:`permission`, check the permission associated with the
@@ -222,12 +237,15 @@
    if view is None:
        view = reg.adapters.lookup(provides, IView, name=name)
        if view is None:
            raise TypeError('No registered view satisfies the constraints. '
                            'It would not make sense to claim that this view '
                            '"is" or "is not" permitted.')
            raise TypeError(
                'No registered view satisfies the constraints. '
                'It would not make sense to claim that this view '
                '"is" or "is not" permitted.'
            )
        return Allowed(
            'Allowed: view name %r in context %r (no permission defined)' %
            (name, context))
            'Allowed: view name %r in context %r (no permission defined)'
            % (name, context)
        )
    return view.__permitted__(context, request)
@@ -255,9 +273,12 @@
        return self.msg
    def __repr__(self):
        return '<%s instance at %s with msg %r>' % (self.__class__.__name__,
                                                    id(self),
                                                    self.msg)
        return '<%s instance at %s with msg %r>' % (
            self.__class__.__name__,
            id(self),
            self.msg,
        )
class Denied(PermitsResult):
    """
@@ -268,7 +289,9 @@
    the deny.
    """
    boolval = 0
class Allowed(PermitsResult):
    """
@@ -279,7 +302,9 @@
    the allow.
    """
    boolval = 1
class ACLPermitsResult(PermitsResult):
    def __new__(cls, ace, acl, permission, principals, context):
@@ -294,17 +319,12 @@
                        searched.
        """
        fmt = ('%s permission %r via ACE %r in ACL %r on context %r for '
               'principals %r')
        fmt = (
            '%s permission %r via ACE %r in ACL %r on context %r for '
            'principals %r'
        )
        inst = PermitsResult.__new__(
            cls,
            fmt,
            cls.__name__,
            permission,
            ace,
            acl,
            context,
            principals,
            cls, fmt, cls.__name__, permission, ace, acl, context, principals
        )
        inst.permission = permission
        inst.ace = ace
@@ -312,6 +332,7 @@
        inst.principals = principals
        inst.context = context
        return inst
class ACLDenied(ACLPermitsResult, Denied):
    """
@@ -326,6 +347,7 @@
    """
class ACLAllowed(ACLPermitsResult, Allowed):
    """
    An instance of ``ACLAllowed`` is a specialization of
@@ -339,8 +361,8 @@
    """
class AuthenticationAPIMixin(object):
class AuthenticationAPIMixin(object):
    def _get_authentication_policy(self):
        reg = _get_registry(self)
        return reg.queryUtility(IAuthenticationPolicy)
@@ -389,8 +411,8 @@
            return [Everyone]
        return policy.effective_principals(self)
class AuthorizationAPIMixin(object):
class AuthorizationAPIMixin(object):
    def has_permission(self, permission, context=None):
        """ Given a permission and an optional context, returns an instance of
        :data:`pyramid.security.Allowed` if the permission is granted to this
@@ -421,7 +443,9 @@
            return Allowed('No authentication policy in use.')
        authz_policy = reg.queryUtility(IAuthorizationPolicy)
        if authz_policy is None:
            raise ValueError('Authentication policy registered without '
                             'authorization policy') # should never happen
            raise ValueError(
                'Authentication policy registered without '
                'authorization policy'
            )  # should never happen
        principals = authn_policy.effective_principals(self)
        return authz_policy.permits(context, principals, permission)
src/pyramid/session.py
@@ -9,22 +9,10 @@
from zope.deprecation import deprecated
from zope.interface import implementer
from webob.cookies import (
    JSONSerializer,
    SignedSerializer,
)
from webob.cookies import JSONSerializer, SignedSerializer
from pyramid.compat import (
    pickle,
    PY2,
    text_,
    bytes_,
    native_,
    )
from pyramid.csrf import (
    check_csrf_origin,
    check_csrf_token,
)
from pyramid.compat import pickle, PY2, text_, bytes_, native_
from pyramid.csrf import check_csrf_origin, check_csrf_token
from pyramid.interfaces import ISession
from pyramid.util import strings_differ
@@ -33,24 +21,30 @@
def manage_accessed(wrapped):
    """ Decorator which causes a cookie to be renewed when an accessor
    method is called."""
    def accessed(session, *arg, **kw):
        session.accessed = now = int(time.time())
        if session._reissue_time is not None:
            if now - session.renewed > session._reissue_time:
                session.changed()
        return wrapped(session, *arg, **kw)
    accessed.__doc__ = wrapped.__doc__
    return accessed
def manage_changed(wrapped):
    """ Decorator which causes a cookie to be set when a setter method
    is called."""
    def changed(session, *arg, **kw):
        session.accessed = int(time.time())
        session.changed()
        return wrapped(session, *arg, **kw)
    changed.__doc__ = wrapped.__doc__
    return changed
def signed_serialize(data, secret):
    """ Serialize any pickleable structure (``data``) and sign it
@@ -82,12 +76,14 @@
    sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest()
    return sig + native_(base64.b64encode(pickled))
deprecated(
    'signed_serialize',
    'This function will be removed in Pyramid 2.0. It is using pickle-based '
    'serialization, which is considered vulnerable to remote code execution '
    'attacks.',
)
def signed_deserialize(serialized, secret, hmac=hmac):
    """ Deserialize the value returned from ``signed_serialize``.  If
@@ -111,8 +107,10 @@
    """
    # hmac parameterized only for unit tests
    try:
        input_sig, pickled = (bytes_(serialized[:40]),
                              base64.b64decode(bytes_(serialized[40:])))
        input_sig, pickled = (
            bytes_(serialized[:40]),
            base64.b64decode(bytes_(serialized[40:])),
        )
    except (binascii.Error, TypeError) as e:
        # Badly formed data can make base64 die
        raise ValueError('Badly formed base64 data: %s' % e)
@@ -130,6 +128,7 @@
        raise ValueError('Invalid signature')
    return pickle.loads(pickled)
deprecated(
    'signed_deserialize',
@@ -149,6 +148,7 @@
    Defaults to :attr:`pickle.HIGHEST_PROTOCOL`.
    """
    def __init__(self, protocol=pickle.HIGHEST_PROTOCOL):
        self.protocol = protocol
@@ -180,7 +180,7 @@
    timeout=1200,
    reissue_time=0,
    set_on_exception=True,
    ):
):
    """
    Configure a :term:`session factory` which will provide cookie-based
    sessions.  The return value of this function is a :term:`session factory`,
@@ -280,7 +280,9 @@
        _cookie_samesite = samesite
        _cookie_on_exception = set_on_exception
        _timeout = timeout if timeout is None else int(timeout)
        _reissue_time = reissue_time if reissue_time is None else int(reissue_time)
        _reissue_time = (
            reissue_time if reissue_time is None else int(reissue_time)
        )
        # dirty flag
        _dirty = False
@@ -330,13 +332,15 @@
        def changed(self):
            if not self._dirty:
                self._dirty = True
                def set_cookie_callback(request, response):
                    self._set_cookie(response)
                    self.request = None # explicitly break cycle for gc
                    self.request = None  # explicitly break cycle for gc
                self.request.add_response_callback(set_cookie_callback)
        def invalidate(self):
            self.clear() # XXX probably needs to unset cookie
            self.clear()  # XXX probably needs to unset cookie
        # non-modifying dictionary methods
        get = manage_accessed(dict.get)
@@ -398,16 +402,18 @@
        def _set_cookie(self, response):
            if not self._cookie_on_exception:
                exception = getattr(self.request, 'exception', None)
                if exception is not None: # dont set a cookie during exceptions
                if (
                    exception is not None
                ):  # dont set a cookie during exceptions
                    return False
            cookieval = native_(serializer.dumps(
                (self.accessed, self.created, dict(self))
                ))
            cookieval = native_(
                serializer.dumps((self.accessed, self.created, dict(self)))
            )
            if len(cookieval) > 4064:
                raise ValueError(
                    'Cookie value is too long to store (%s bytes)' %
                    len(cookieval)
                    )
                    'Cookie value is too long to store (%s bytes)'
                    % len(cookieval)
                )
            response.set_cookie(
                self._cookie_name,
                value=cookieval,
@@ -417,7 +423,7 @@
                secure=self._cookie_secure,
                httponly=self._cookie_httponly,
                samesite=self._cookie_samesite,
                )
            )
            return True
    return CookieSession
@@ -436,7 +442,7 @@
    cookie_on_exception=True,
    signed_serialize=signed_serialize,
    signed_deserialize=signed_deserialize,
    ):
):
    """
    .. deprecated:: 1.5
        Use :func:`pyramid.session.SignedCookieSessionFactory` instead.
@@ -530,9 +536,10 @@
        httponly=cookie_httponly,
        samesite=cookie_samesite,
        timeout=timeout,
        reissue_time=0, # to keep session.accessed == session.renewed
        reissue_time=0,  # to keep session.accessed == session.renewed
        set_on_exception=cookie_on_exception,
    )
deprecated(
    'UnencryptedCookieSessionFactoryConfig',
@@ -540,8 +547,8 @@
    'Pyramid 1.5.  Use ``pyramid.session.SignedCookieSessionFactory`` instead.'
    ' Caveat: Cookies generated using SignedCookieSessionFactory are not '
    'compatible with cookies generated using UnencryptedCookieSessionFactory, '
    'so existing user session data will be destroyed if you switch to it.'
    )
    'so existing user session data will be destroyed if you switch to it.',
)
def SignedCookieSessionFactory(
@@ -559,7 +566,7 @@
    hashalg='sha512',
    salt='pyramid.session.',
    serializer=None,
    ):
):
    """
    .. versionadded:: 1.5
@@ -681,11 +688,8 @@
        )
    signed_serializer = SignedSerializer(
        secret,
        salt,
        hashalg,
        serializer=serializer,
        )
        secret, salt, hashalg, serializer=serializer
    )
    return BaseCookieSessionFactory(
        signed_serializer,
@@ -701,12 +705,17 @@
        set_on_exception=set_on_exception,
    )
check_csrf_origin = check_csrf_origin  # api
deprecated('check_csrf_origin',
           'pyramid.session.check_csrf_origin is deprecated as of Pyramid '
           '1.9. Use pyramid.csrf.check_csrf_origin instead.')
deprecated(
    'check_csrf_origin',
    'pyramid.session.check_csrf_origin is deprecated as of Pyramid '
    '1.9. Use pyramid.csrf.check_csrf_origin instead.',
)
check_csrf_token = check_csrf_token  # api
deprecated('check_csrf_token',
           'pyramid.session.check_csrf_token is deprecated as of Pyramid '
           '1.9. Use pyramid.csrf.check_csrf_token instead.')
deprecated(
    'check_csrf_token',
    'pyramid.session.check_csrf_token is deprecated as of Pyramid '
    '1.9. Use pyramid.csrf.check_csrf_token instead.',
)
src/pyramid/settings.py
@@ -3,6 +3,7 @@
truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
falsey = frozenset(('f', 'false', 'n', 'no', 'off', '0'))
def asbool(s):
    """ Return the boolean value ``True`` if the case-lowered value of string
    input ``s`` is a :term:`truthy string`. If ``s`` is already one of the
@@ -14,11 +15,13 @@
    s = str(s).strip()
    return s.lower() in truthy
def aslist_cronly(value):
    if isinstance(value, string_types):
        value = filter(None, [x.strip() for x in value.splitlines()])
    return list(value)
def aslist(value, flatten=True):
    """ Return a list of strings, separating the input based on newlines
    and, if flatten=True (the default), also split on spaces within
src/pyramid/static.py
@@ -2,46 +2,24 @@
import json
import os
from os.path import (
    getmtime,
    normcase,
    normpath,
    join,
    isdir,
    exists,
    )
from os.path import getmtime, normcase, normpath, join, isdir, exists
from pkg_resources import (
    resource_exists,
    resource_filename,
    resource_isdir,
    )
from pkg_resources import resource_exists, resource_filename, resource_isdir
from pyramid.asset import (
    abspath_from_asset_spec,
    resolve_asset_spec,
)
from pyramid.asset import abspath_from_asset_spec, resolve_asset_spec
from pyramid.compat import (
    lru_cache,
    text_,
)
from pyramid.compat import lru_cache, text_
from pyramid.httpexceptions import (
    HTTPNotFound,
    HTTPMovedPermanently,
    )
from pyramid.httpexceptions import HTTPNotFound, HTTPMovedPermanently
from pyramid.path import caller_package
from pyramid.response import (
    _guess_type,
    FileResponse,
)
from pyramid.response import _guess_type, FileResponse
from pyramid.traversal import traversal_path_info
slash = text_('/')
class static_view(object):
    """ An instance of this class is a callable which can act as a
@@ -88,8 +66,14 @@
       to override the assets it contains.
    """
    def __init__(self, root_dir, cache_max_age=3600, package_name=None,
                 use_subpath=False, index='index.html'):
    def __init__(
        self,
        root_dir,
        cache_max_age=3600,
        package_name=None,
        use_subpath=False,
        index='index.html',
    ):
        # package_name is for bw compat; it is preferred to pass in a
        # package-relative path as root_dir
        # (e.g. ``anotherpackage:foo/static``).
@@ -113,20 +97,21 @@
        if path is None:
            raise HTTPNotFound('Out of bounds: %s' % request.url)
        if self.package_name: # package resource
        if self.package_name:  # package resource
            resource_path = '%s/%s' % (self.docroot.rstrip('/'), path)
            if resource_isdir(self.package_name, resource_path):
                if not request.path_url.endswith('/'):
                    self.add_slash_redirect(request)
                resource_path = '%s/%s' % (
                    resource_path.rstrip('/'), self.index
                    resource_path.rstrip('/'),
                    self.index,
                )
            if not resource_exists(self.package_name, resource_path):
                raise HTTPNotFound(request.url)
            filepath = resource_filename(self.package_name, resource_path)
        else: # filesystem file
        else:  # filesystem file
            # os.path.normpath converts / to \ on windows
            filepath = normcase(normpath(join(self.norm_docroot, path)))
@@ -139,8 +124,12 @@
        content_type, content_encoding = _guess_type(filepath)
        return FileResponse(
            filepath, request, self.cache_max_age,
            content_type, content_encoding=None)
            filepath,
            request,
            self.cache_max_age,
            content_type,
            content_encoding=None,
        )
    def add_slash_redirect(self, request):
        url = request.path_url + '/'
@@ -149,13 +138,18 @@
            url = url + '?' + qs
        raise HTTPMovedPermanently(url)
_seps = set(['/', os.sep])
def _contains_slash(item):
    for sep in _seps:
        if sep in item:
            return True
_has_insecure_pathelement = set(['..', '.', '']).intersection
@lru_cache(1000)
def _secure_path(path_tuple):
@@ -166,8 +160,9 @@
        return None
    if any([_contains_slash(item) for item in path_tuple]):
        return None
    encoded = slash.join(path_tuple) # will be unicode
    encoded = slash.join(path_tuple)  # will be unicode
    return encoded
class QueryStringCacheBuster(object):
    """
@@ -182,6 +177,7 @@
    .. versionadded:: 1.6
    """
    def __init__(self, param='x'):
        self.param = param
@@ -193,6 +189,7 @@
        else:
            kw['_query'] = tuple(query) + ((self.param, token),)
        return subpath, kw
class QueryStringConstantCacheBuster(QueryStringCacheBuster):
    """
@@ -207,12 +204,14 @@
    .. versionadded:: 1.6
    """
    def __init__(self, token, param='x'):
        super(QueryStringConstantCacheBuster, self).__init__(param=param)
        self._token = token
    def tokenize(self, request, subpath, kw):
        return self._token
class ManifestCacheBuster(object):
    """
@@ -255,13 +254,15 @@
    .. versionadded:: 1.6
    """
    exists = staticmethod(exists) # testing
    getmtime = staticmethod(getmtime) # testing
    exists = staticmethod(exists)  # testing
    getmtime = staticmethod(getmtime)  # testing
    def __init__(self, manifest_spec, reload=False):
        package_name = caller_package().__name__
        self.manifest_path = abspath_from_asset_spec(
            manifest_spec, package_name)
            manifest_spec, package_name
        )
        self.reload = reload
        self._mtime = None
src/pyramid/testing.py
@@ -2,22 +2,11 @@
import os
from contextlib import contextmanager
from zope.interface import (
    implementer,
    alsoProvides,
    )
from zope.interface import implementer, alsoProvides
from pyramid.interfaces import (
    IRequest,
    ISession,
    )
from pyramid.interfaces import IRequest, ISession
from pyramid.compat import (
    PY3,
    PYPY,
    class_types,
    text_,
    )
from pyramid.compat import PY3, PYPY, class_types, text_
from pyramid.config import Configurator
from pyramid.decorator import reify
@@ -30,12 +19,9 @@
    Everyone,
    AuthenticationAPIMixin,
    AuthorizationAPIMixin,
    )
)
from pyramid.threadlocal import (
    get_current_registry,
    manager,
    )
from pyramid.threadlocal import get_current_registry, manager
from pyramid.i18n import LocalizerRequestMixin
from pyramid.request import CallbackMethodsMixin
@@ -46,17 +32,27 @@
_marker = object()
class DummyRootFactory(object):
    __parent__ = None
    __name__ = None
    def __init__(self, request):
        if 'bfg.routes.matchdict' in request:
            self.__dict__.update(request['bfg.routes.matchdict'])
class DummySecurityPolicy(object):
    """ A standin for both an IAuthentication and IAuthorization policy """
    def __init__(self, userid=None, groupids=(), permissive=True,
                 remember_result=None, forget_result=None):
    def __init__(
        self,
        userid=None,
        groupids=(),
        permissive=True,
        remember_result=None,
        forget_result=None,
    ):
        self.userid = userid
        self.groupids = groupids
        self.permissive = permissive
@@ -95,6 +91,7 @@
    def principals_allowed_by_permission(self, context, permission):
        return self.effective_principals(None)
class DummyTemplateRenderer(object):
    """
    An instance of this class is returned from
@@ -103,6 +100,7 @@
    assertion which compares data passed to the renderer by the view
    function against expected key/value pairs.
    """
    def __init__(self, string_response=''):
        self._received = {}
        self._string_response = string_response
@@ -113,9 +111,11 @@
    # source code, *everything* is an API!
    def _get_string_response(self):
        return self._string_response
    def _set_string_response(self, response):
        self._string_response = response
        self._implementation.response = response
    string_response = property(_get_string_response, _set_string_response)
    def implementation(self):
@@ -151,19 +151,23 @@
                if myval is _marker:
                    raise AssertionError(
                        'A value for key "%s" was not passed to the renderer'
                        % k)
                        % k
                    )
            if myval != v:
                raise AssertionError(
                    '\nasserted value for %s: %r\nactual value: %r' % (
                        k, v, myval))
                    '\nasserted value for %s: %r\nactual value: %r'
                    % (k, v, myval)
                )
        return True
class DummyResource:
    """ A dummy :app:`Pyramid` :term:`resource` object."""
    def __init__(self, __name__=None, __parent__=None, __provides__=None,
                 **kw):
    def __init__(
        self, __name__=None, __parent__=None, __provides__=None, **kw
    ):
        """ The resource's ``__name__`` attribute will be set to the
        value of the ``__name__`` argument, and the resource's
        ``__parent__`` attribute will be set to the value of the
@@ -250,12 +254,15 @@
            inst.__parent__ = __parent__
        return inst
DummyModel = DummyResource # b/w compat (forever)
DummyModel = DummyResource  # b/w compat (forever)
@implementer(ISession)
class DummySession(dict):
    created = None
    new = True
    def changed(self):
        pass
@@ -286,6 +293,7 @@
            token = self.new_csrf_token()
        return token
@implementer(IRequest)
class DummyRequest(
    URLMethodsMixin,
@@ -295,7 +303,7 @@
    AuthenticationAPIMixin,
    AuthorizationAPIMixin,
    ViewMethodsMixin,
    ):
):
    """ A DummyRequest object (incompletely) imitates a :term:`request` object.
    The ``params``, ``environ``, ``headers``, ``path``, and
@@ -322,6 +330,7 @@
    a Request, use the :class:`pyramid.request.Request` class itself rather
    than this class while writing tests.
    """
    method = 'GET'
    application_url = 'http://example.com'
    host = 'example.com:80'
@@ -333,8 +342,16 @@
    _registry = None
    request_iface = IRequest
    def __init__(self, params=None, environ=None, headers=None, path='/',
                 cookies=None, post=None, **kw):
    def __init__(
        self,
        params=None,
        environ=None,
        headers=None,
        path='/',
        cookies=None,
        post=None,
        **kw
    ):
        if environ is None:
            environ = {}
        if params is None:
@@ -369,7 +386,7 @@
        self.context = None
        self.root = None
        self.virtual_root = None
        self.marshalled = params # repoze.monty
        self.marshalled = params  # repoze.monty
        self.session = DummySession()
        self.__dict__.update(kw)
@@ -391,11 +408,18 @@
        f = _get_response_factory(self.registry)
        return f(self)
have_zca = True
def setUp(registry=None, request=None, hook_zca=True, autocommit=True,
          settings=None, package=None):
def setUp(
    registry=None,
    request=None,
    hook_zca=True,
    autocommit=True,
    settings=None,
    package=None,
):
    """
    Set :app:`Pyramid` registry and request thread locals for the
    duration of a single unit test.
@@ -462,8 +486,9 @@
        registry = Registry('testing')
    if package is None:
        package = caller_package()
    config = Configurator(registry=registry, autocommit=autocommit,
                          package=package)
    config = Configurator(
        registry=registry, autocommit=autocommit, package=package
    )
    if settings is None:
        settings = {}
    if getattr(registry, 'settings', None) is None:
@@ -486,11 +511,12 @@
    global have_zca
    try:
        have_zca and hook_zca and config.hook_zca()
    except ImportError: # pragma: no cover
    except ImportError:  # pragma: no cover
        # (dont choke on not being able to import z.component)
        have_zca = False
    config.begin(request=request)
    return config
def tearDown(unhook_zca=True):
    """Undo the effects of :func:`pyramid.testing.setUp`.  Use this
@@ -507,8 +533,9 @@
    if unhook_zca and have_zca:
        try:
            from zope.component import getSiteManager
            getSiteManager.reset()
        except ImportError: # pragma: no cover
        except ImportError:  # pragma: no cover
            have_zca = False
    info = manager.pop()
    manager.clear()
@@ -524,6 +551,7 @@
                # understand, let's not blow up
                pass
def cleanUp(*arg, **kw):
    """ An alias for :func:`pyramid.testing.setUp`. """
    package = kw.get('package', None)
@@ -531,6 +559,7 @@
        package = caller_package()
        kw['package'] = package
    return setUp(*arg, **kw)
class DummyRendererFactory(object):
    """ Registered by
@@ -540,9 +569,10 @@
    wild believing they can register either.  The ``factory`` argument
    passed to this constructor is usually the *real* template renderer
    factory, found when ``testing_add_renderer`` is called."""
    def __init__(self, name, factory):
        self.name = name
        self.factory = factory # the "real" renderer factory reg'd previously
        self.factory = factory  # the "real" renderer factory reg'd previously
        self.renderers = {}
    def add(self, spec, renderer):
@@ -562,8 +592,9 @@
                if self.factory:
                    renderer = self.factory(info)
                else:
                    raise KeyError('No testing renderer registered for %r' %
                                   spec)
                    raise KeyError(
                        'No testing renderer registered for %r' % spec
                    )
        return renderer
@@ -571,15 +602,19 @@
    def __init__(self, response):
        self._received = {}
        self.response = response
    def __getattr__(self, attrname):
        return self
    def __getitem__(self, attrname):
        return self
    def __call__(self, *arg, **kw):
        self._received.update(kw)
        return self.response
def skip_on(*platforms): # pragma: no  cover
def skip_on(*platforms):  # pragma: no  cover
    skip = False
    for platform in platforms:
        if skip_on.os_name.startswith(platform):
@@ -596,22 +631,26 @@
            else:
                return func
        else:
            def wrapper(*args, **kw):
                if skip:
                    return
                return func(*args, **kw)
            wrapper.__name__ = func.__name__
            wrapper.__doc__ = func.__doc__
            return wrapper
    return decorator
skip_on.os_name = os.name # for testing
skip_on.os_name = os.name  # for testing
@contextmanager
def testConfig(registry=None,
        request=None,
        hook_zca=True,
        autocommit=True,
        settings=None):
def testConfig(
    registry=None, request=None, hook_zca=True, autocommit=True, settings=None
):
    """Returns a context manager for test set up.
    This context manager calls :func:`pyramid.testing.setUp` when
@@ -630,11 +669,13 @@
            req = DummyRequest()
            resp = myview(req)
    """
    config = setUp(registry=registry,
            request=request,
            hook_zca=hook_zca,
            autocommit=autocommit,
            settings=settings)
    config = setUp(
        registry=registry,
        request=request,
        hook_zca=hook_zca,
        autocommit=autocommit,
        settings=settings,
    )
    try:
        yield config
    finally:
src/pyramid/threadlocal.py
@@ -2,6 +2,7 @@
from pyramid.registry import global_registry
class ThreadLocalManager(threading.local):
    def __init__(self, default=None):
        # http://code.google.com/p/google-app-engine-django/issues/detail?id=119
@@ -15,7 +16,7 @@
    def push(self, info):
        self.stack.append(info)
    set = push # b/c
    set = push  # b/c
    def pop(self):
        if self.stack:
@@ -30,10 +31,13 @@
    def clear(self):
        self.stack[:] = []
def defaults():
    return {'request': None, 'registry': global_registry}
manager = ThreadLocalManager(default=defaults)
def get_current_request():
    """
@@ -49,7 +53,10 @@
    """
    return manager.get()['request']
def get_current_registry(context=None): # context required by getSiteManager API
def get_current_registry(
    context=None
):  # context required by getSiteManager API
    """
    Return the currently active :term:`application registry` or the
    global application registry if no request is currently active.
@@ -63,6 +70,7 @@
    """
    return manager.get()['registry']
class RequestContext(object):
    def __init__(self, request):
        self.request = request
src/pyramid/traversal.py
@@ -6,7 +6,7 @@
    IRequestFactory,
    ITraverser,
    VH_ROOT_KEY,
    )
)
from pyramid.compat import (
    PY2,
@@ -19,17 +19,18 @@
    decode_path_info,
    unquote_bytes_to_wsgi,
    lru_cache,
    )
)
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
PATH_SEGMENT_SAFE = "~!$&'()*+,;=:@" # from webob
PATH_SEGMENT_SAFE = "~!$&'()*+,;=:@"  # from webob
PATH_SAFE = PATH_SEGMENT_SAFE + "/"
empty = text_('')
def find_root(resource):
    """ Find the root node in the resource tree to which ``resource``
@@ -42,6 +43,7 @@
            resource = location
            break
    return resource
def find_resource(resource, path):
    """ Given a resource object and a string or tuple representing a path
@@ -101,7 +103,9 @@
        raise KeyError('%r has no subelement %s' % (context, view_name))
    return context
find_model = find_resource # b/w compat (forever)
find_model = find_resource  # b/w compat (forever)
def find_interface(resource, class_or_interface):
    """
@@ -120,6 +124,7 @@
    for location in lineage(resource):
        if test(location):
            return location
def resource_path(resource, *elements):
    """ Return a string object representing the absolute physical path of the
@@ -166,7 +171,9 @@
    # which caches the joined result for us
    return _join_path_tuple(resource_path_tuple(resource, *elements))
model_path = resource_path # b/w compat (forever)
model_path = resource_path  # b/w compat (forever)
def traverse(resource, path):
    """Given a resource object as ``resource`` and a string or tuple
@@ -314,7 +321,8 @@
    request_factory = reg.queryUtility(IRequestFactory)
    if request_factory is None:
        from pyramid.request import Request # avoid circdep
        from pyramid.request import Request  # avoid circdep
        request_factory = Request
    request = request_factory.blank(path)
@@ -324,6 +332,7 @@
        traverser = ResourceTreeTraverser(resource)
    return traverser(request)
def resource_path_tuple(resource, *elements):
    """
@@ -360,21 +369,26 @@
       The :term:`root` resource *must* have a ``__name__`` attribute with a
       value of either ``None`` or the empty string for path tuples to be
       generated properly.  If the root resource has a non-null ``__name__``
       attribute, its name will be the first element in the generated path tuple
       rather than the empty string.
       attribute, its name will be the first element in the generated path
       tuple rather than the empty string.
    """
    return tuple(_resource_path_list(resource, *elements))
model_path_tuple = resource_path_tuple  # b/w compat (forever)
def _resource_path_list(resource, *elements):
    """ Implementation detail shared by resource_path and resource_path_tuple"""
    """ Implementation detail shared by resource_path and
    resource_path_tuple"""
    path = [loc.__name__ or '' for loc in lineage(resource)]
    path.reverse()
    path.extend(elements)
    return path
_model_path_list = _resource_path_list # b/w compat, not an API
_model_path_list = _resource_path_list  # b/w compat, not an API
def virtual_root(resource, request):
    """
@@ -412,13 +426,14 @@
    vpath, rpath = url_adapter.virtual_path, url_adapter.physical_path
    if rpath != vpath and rpath.endswith(vpath):
        vroot_path = rpath[:-len(vpath)]
        vroot_path = rpath[: -len(vpath)]
        return find_resource(resource, vroot_path)
    try:
        return request.root
    except AttributeError:
        return find_root(resource)
def traversal_path(path):
    """ Variant of :func:`pyramid.traversal.traversal_path_info` suitable for
@@ -435,8 +450,9 @@
        # must not possess characters outside ascii
        path = path.encode('ascii')
    # we unquote this path exactly like a PEP 3333 server would
    path = unquote_bytes_to_wsgi(path) # result will be a native string
    return traversal_path_info(path) # result will be a tuple of unicode
    path = unquote_bytes_to_wsgi(path)  # result will be a native string
    return traversal_path_info(path)  # result will be a tuple of unicode
@lru_cache(1000)
def traversal_path_info(path):
@@ -501,19 +517,20 @@
      This function does not generate the same type of tuples that
      :func:`pyramid.traversal.resource_path_tuple` does.  In particular, the
      leading empty string is not present in the tuple it returns, unlike tuples
      returned by :func:`pyramid.traversal.resource_path_tuple`.  As a result,
      tuples generated by ``traversal_path`` are not resolveable by the
      :func:`pyramid.traversal.find_resource` API.  ``traversal_path`` is a
      function mostly used by the internals of :app:`Pyramid` and by people
      leading empty string is not present in the tuple it returns, unlike
      tuples returned by :func:`pyramid.traversal.resource_path_tuple`.  As a
      result, tuples generated by ``traversal_path`` are not resolveable by
      the :func:`pyramid.traversal.find_resource` API.  ``traversal_path`` is
      a function mostly used by the internals of :app:`Pyramid` and by people
      writing their own traversal machinery, as opposed to users writing
      applications in :app:`Pyramid`.
    """
    try:
        path = decode_path_info(path) # result will be Unicode
        path = decode_path_info(path)  # result will be Unicode
    except UnicodeDecodeError as e:
        raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
    return split_path_info(path) # result will be tuple of Unicode
    return split_path_info(path)  # result will be tuple of Unicode
@lru_cache(1000)
def split_path_info(path):
@@ -530,6 +547,7 @@
        else:
            clean.append(segment)
    return tuple(clean)
_segment_cache = {}
@@ -574,7 +592,9 @@
        try:
            return _segment_cache[(segment, safe)]
        except KeyError:
            if segment.__class__ is text_type: #isinstance slighly slower (~15%)
            if (
                segment.__class__ is text_type
            ):  # isinstance slighly slower (~15%)
                result = url_quote(segment.encode('utf-8'), safe)
            else:
                result = url_quote(str(segment), safe)
@@ -582,7 +602,10 @@
            # will generate exactly one Python bytecode (STORE_SUBSCR)
            _segment_cache[(segment, safe)] = result
            return result
else:
    def quote_path_segment(segment, safe=PATH_SEGMENT_SAFE):
        """ %s """ % quote_path_segment_doc
        # The bit of this code that deals with ``_segment_cache`` is an
@@ -601,7 +624,9 @@
            _segment_cache[(segment, safe)] = result
            return result
slash = text_('/')
@implementer(ITraverser)
class ResourceTreeTraverser(object):
@@ -609,7 +634,6 @@
    every resource in the tree supplies a ``__name__`` and
    ``__parent__`` attribute (ie. every resource in the tree is
    :term:`location` aware) ."""
    VH_ROOT_KEY = VH_ROOT_KEY
    VIEW_SELECTOR = '@@'
@@ -647,14 +671,17 @@
                # if environ['PATH_INFO'] is just not there
                path = slash
            except UnicodeDecodeError as e:
                raise URLDecodeError(e.encoding, e.object, e.start, e.end,
                                     e.reason)
                raise URLDecodeError(
                    e.encoding, e.object, e.start, e.end, e.reason
                )
        if self.VH_ROOT_KEY in environ:
            # HTTP_X_VHM_ROOT
            vroot_path = decode_path_info(environ[self.VH_ROOT_KEY])
            vroot_tuple = split_path_info(vroot_path)
            vpath = vroot_path + path # both will (must) be unicode or asciistr
            vpath = (
                vroot_path + path
            )  # both will (must) be unicode or asciistr
            vroot_idx = len(vroot_tuple) - 1
        else:
            vroot_tuple = ()
@@ -664,7 +691,7 @@
        root = self.root
        ob = vroot = root
        if vpath == slash: # invariant: vpath must not be empty
        if vpath == slash:  # invariant: vpath must not be empty
            # prevent a call to traversal_path if we know it's going
            # to return the empty tuple
            vpath_tuple = ()
@@ -677,44 +704,60 @@
            vpath_tuple = split_path_info(vpath)
            for segment in vpath_tuple:
                if segment[:2] == view_selector:
                    return {'context': ob,
                            'view_name': segment[2:],
                            'subpath': vpath_tuple[i + 1:],
                            'traversed': vpath_tuple[:vroot_idx + i + 1],
                            'virtual_root': vroot,
                            'virtual_root_path': vroot_tuple,
                            'root': root}
                    return {
                        'context': ob,
                        'view_name': segment[2:],
                        'subpath': vpath_tuple[i + 1 :],
                        'traversed': vpath_tuple[: vroot_idx + i + 1],
                        'virtual_root': vroot,
                        'virtual_root_path': vroot_tuple,
                        'root': root,
                    }
                try:
                    getitem = ob.__getitem__
                except AttributeError:
                    return {'context': ob,
                            'view_name': segment,
                            'subpath': vpath_tuple[i + 1:],
                            'traversed': vpath_tuple[:vroot_idx + i + 1],
                            'virtual_root': vroot,
                            'virtual_root_path': vroot_tuple,
                            'root': root}
                    return {
                        'context': ob,
                        'view_name': segment,
                        'subpath': vpath_tuple[i + 1 :],
                        'traversed': vpath_tuple[: vroot_idx + i + 1],
                        'virtual_root': vroot,
                        'virtual_root_path': vroot_tuple,
                        'root': root,
                    }
                try:
                    next = getitem(segment)
                except KeyError:
                    return {'context': ob,
                            'view_name': segment,
                            'subpath': vpath_tuple[i + 1:],
                            'traversed': vpath_tuple[:vroot_idx + i + 1],
                            'virtual_root': vroot,
                            'virtual_root_path': vroot_tuple,
                            'root': root}
                    return {
                        'context': ob,
                        'view_name': segment,
                        'subpath': vpath_tuple[i + 1 :],
                        'traversed': vpath_tuple[: vroot_idx + i + 1],
                        'virtual_root': vroot,
                        'virtual_root_path': vroot_tuple,
                        'root': root,
                    }
                if i == vroot_idx:
                    vroot = next
                ob = next
                i += 1
        return {'context':ob, 'view_name':empty, 'subpath':subpath,
                'traversed':vpath_tuple, 'virtual_root':vroot,
                'virtual_root_path':vroot_tuple, 'root':root}
        return {
            'context': ob,
            'view_name': empty,
            'subpath': subpath,
            'traversed': vpath_tuple,
            'virtual_root': vroot,
            'virtual_root_path': vroot_tuple,
            'root': root,
        }
ModelGraphTraverser = ResourceTreeTraverser # b/w compat, not API, used in wild
ModelGraphTraverser = (
    ResourceTreeTraverser
)  # b/w compat, not API, used in wild
@implementer(IResourceURL)
class ResourceURL(object):
@@ -742,19 +785,24 @@
                vroot_path_tuple = tuple(vroot_path.split('/'))
                numels = len(vroot_path_tuple)
                virtual_path_tuple = ('',) + physical_path_tuple[numels:]
                virtual_path = physical_path[len(vroot_path):]
                virtual_path = physical_path[len(vroot_path) :]
        self.virtual_path = virtual_path    # IResourceURL attr
        self.virtual_path = virtual_path  # IResourceURL attr
        self.physical_path = physical_path  # IResourceURL attr
        self.virtual_path_tuple = virtual_path_tuple # IResourceURL attr (1.5)
        self.physical_path_tuple = physical_path_tuple # IResourceURL attr (1.5)
        self.virtual_path_tuple = virtual_path_tuple  # IResourceURL attr (1.5)
        self.physical_path_tuple = (
            physical_path_tuple
        )  # IResourceURL attr (1.5)
@lru_cache(1000)
def _join_path_tuple(tuple):
    return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/'
class DefaultRootFactory:
    __parent__ = None
    __name__ = None
    def __init__(self, request):
        pass
src/pyramid/tweens.py
@@ -3,6 +3,7 @@
from pyramid.compat import reraise
from pyramid.httpexceptions import HTTPNotFound
def _error_handler(request, exc):
    # NOTE: we do not need to delete exc_info because this function
    # should never be in the call stack of the exception
@@ -16,6 +17,7 @@
        reraise(*exc_info)
    return response
def excview_tween_factory(handler, registry):
    """ A :term:`tween` factory which produces a tween that catches an
@@ -43,6 +45,7 @@
    return excview_tween
MAIN = 'MAIN'
INGRESS = 'INGRESS'
EXCVIEW = 'pyramid.tweens.excview_tween_factory'
src/pyramid/url.py
@@ -2,21 +2,10 @@
import os
from pyramid.interfaces import (
    IResourceURL,
    IRoutesMapper,
    IStaticURLInfo,
    )
from pyramid.interfaces import IResourceURL, IRoutesMapper, IStaticURLInfo
from pyramid.compat import (
    bytes_,
    lru_cache,
    string_types,
    )
from pyramid.encode import (
    url_quote,
    urlencode,
)
from pyramid.compat import bytes_, lru_cache, string_types
from pyramid.encode import url_quote, urlencode
from pyramid.path import caller_package
from pyramid.threadlocal import get_current_registry
@@ -25,10 +14,11 @@
    quote_path_segment,
    PATH_SAFE,
    PATH_SEGMENT_SAFE,
    )
)
QUERY_SAFE = "/?:@!$&'()*+,;=" # RFC 3986
QUERY_SAFE = "/?:@!$&'()*+,;="  # RFC 3986
ANCHOR_SAFE = QUERY_SAFE
def parse_url_overrides(request, kw):
    """
@@ -48,7 +38,7 @@
    anchor = kw.pop('_anchor', '')
    if app_url is None:
        if (scheme is not None or host is not None or port is not None):
        if scheme is not None or host is not None or port is not None:
            app_url = request._partial_application_url(scheme, host, port)
        else:
            app_url = request.application_url
@@ -65,6 +55,7 @@
        frag = '#' + url_quote(anchor, ANCHOR_SAFE)
    return app_url, qs, frag
class URLMethodsMixin(object):
    """ Request methods mixin for BaseRequest having to do with URL
@@ -115,7 +106,7 @@
        if port:
            url += ':%s' % port
        url_encoding = getattr(self, 'url_encoding', 'utf-8') # webob 1.2b3+
        url_encoding = getattr(self, 'url_encoding', 'utf-8')  # webob 1.2b3+
        bscript_name = bytes_(self.script_name, url_encoding)
        return url + url_quote(bscript_name, PATH_SAFE)
@@ -181,10 +172,10 @@
           Python data structures that are passed as ``_query`` which are
           sequences or dictionaries are turned into a string under the same
           rules as when run through :func:`urllib.urlencode` with the ``doseq``
           argument equal to ``True``.  This means that sequences can be passed
           as values, and a k=v pair will be placed into the query string for
           each value.
           rules as when run through :func:`urllib.urlencode` with the
           ``doseq`` argument equal to ``True``.  This means that sequences can
           be passed as values, and a k=v pair will be placed into the query
           string for each value.
        If a keyword argument ``_anchor`` is present, its string
        representation will be quoted per :rfc:`3986#section-3.5` and used as
@@ -209,7 +200,7 @@
        ``_host='foo.com'``, and the URL that would have been generated
        without the host replacement is ``http://example.com/a``, the result
        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
        ``443``.  Likewise, if ``_scheme`` is passed as ``http`` and
@@ -255,7 +246,7 @@
        try:
            reg = self.registry
        except AttributeError:
            reg = get_current_registry() # b/c
            reg = get_current_registry()  # b/c
        mapper = reg.getUtility(IRoutesMapper)
        route = mapper.get_route(route_name)
@@ -267,7 +258,7 @@
        app_url, qs, anchor = parse_url_overrides(self, kw)
        path = route.generate(kw) # raises KeyError if generate fails
        path = route.generate(kw)  # raises KeyError if generate fails
        if elements:
            suffix = _join_elements(elements)
@@ -372,10 +363,10 @@
           Python data structures that are passed as ``query`` which are
           sequences or dictionaries are turned into a string under the same
           rules as when run through :func:`urllib.urlencode` with the ``doseq``
           argument equal to ``True``.  This means that sequences can be passed
           as values, and a k=v pair will be placed into the query string for
           each value.
           rules as when run through :func:`urllib.urlencode` with the
           ``doseq`` argument equal to ``True``.  This means that sequences can
           be passed as values, and a k=v pair will be placed into the query
           string for each value.
        If a keyword argument ``anchor`` is present, its string
        representation will be used as a named anchor in the generated URL
@@ -399,7 +390,7 @@
        ``host='foo.com'``, and the URL that would have been generated
        without the host replacement is ``http://example.com/a``, the result
        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``.
        Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not
@@ -424,11 +415,11 @@
        If the ``resource`` passed in has a ``__resource_url__`` method, it
        will be used to generate the URL (scheme, host, port, path) for the
        base resource which is operated upon by this function.
        .. seealso::
            See also :ref:`overriding_resource_url_generation`.
        If ``route_name`` is passed, this function will delegate its URL
        production to the ``route_url`` function.  Calling
        ``resource_url(someresource, 'element1', 'element2', query={'a':1},
@@ -481,21 +472,21 @@
        is passed, the ``__resource_url__`` method of the resource passed is
        ignored unconditionally.  This feature is incompatible with
        resources which generate their own URLs.
        .. note::
           If the :term:`resource` used is the result of a :term:`traversal`, it
           must be :term:`location`-aware.  The resource can also be the context
           of a :term:`URL dispatch`; contexts found this way do not need to be
           location-aware.
           If the :term:`resource` used is the result of a :term:`traversal`,
           it must be :term:`location`-aware.  The resource can also be the
           context of a :term:`URL dispatch`; contexts found this way do not
           need to be location-aware.
        .. note::
           If a 'virtual root path' is present in the request environment (the
           value of the WSGI environ key ``HTTP_X_VHM_ROOT``), and the resource
           was obtained via :term:`traversal`, the URL path will not include the
           virtual root prefix (it will be stripped off the left hand side of
           the generated URL).
           was obtained via :term:`traversal`, the URL path will not include
           the virtual root prefix (it will be stripped off the left hand side
           of the generated URL).
        .. note::
@@ -522,7 +513,7 @@
        try:
            reg = self.registry
        except AttributeError:
            reg = get_current_registry() # b/c
            reg = get_current_registry()  # b/c
        url_adapter = reg.queryMultiAdapter((resource, self), IResourceURL)
        if url_adapter is None:
@@ -531,9 +522,7 @@
        virtual_path = getattr(url_adapter, 'virtual_path', None)
        urlkw = {}
        for name in (
            'app_url', 'scheme', 'host', 'port', 'query', 'anchor'
        ):
        for name in ('app_url', 'scheme', 'host', 'port', 'query', 'anchor'):
            val = kw.get(name, None)
            if val is not None:
                urlkw['_' + name] = val
@@ -583,7 +572,7 @@
        return resource_url + suffix + qs + anchor
    model_url = resource_url # b/w compat forever
    model_url = resource_url  # b/w compat forever
    def resource_path(self, resource, *elements, **kw):
        """
@@ -651,7 +640,7 @@
        try:
            reg = self.registry
        except AttributeError:
            reg = get_current_registry() # b/c
            reg = get_current_registry()  # b/c
        info = reg.queryUtility(IStaticURLInfo)
        if info is None:
@@ -803,6 +792,7 @@
    """
    return request.route_url(route_name, *elements, **kw)
def route_path(route_name, request, *elements, **kw):
    """
    This is a backwards compatibility function.  Its result is the same as
@@ -813,6 +803,7 @@
    See :meth:`pyramid.request.Request.route_path` for more information.
    """
    return request.route_path(route_name, *elements, **kw)
def resource_url(resource, request, *elements, **kw):
    """
@@ -825,7 +816,8 @@
    """
    return request.resource_url(resource, *elements, **kw)
model_url = resource_url # b/w compat (forever)
model_url = resource_url  # b/w compat (forever)
def static_url(path, request, **kw):
@@ -865,6 +857,7 @@
            path = '%s:%s' % (package.__name__, path)
    return request.static_path(path, **kw)
def current_route_url(request, *elements, **kw):
    """
    This is a backwards compatibility function.  Its result is the same as
@@ -876,6 +869,7 @@
    information.
    """
    return request.current_route_url(*elements, **kw)
def current_route_path(request, *elements, **kw):
    """
@@ -889,6 +883,9 @@
    """
    return request.current_route_path(*elements, **kw)
@lru_cache(1000)
def _join_elements(elements):
    return '/'.join([quote_path_segment(s, safe=PATH_SEGMENT_SAFE) for s in elements])
    return '/'.join(
        [quote_path_segment(s, safe=PATH_SEGMENT_SAFE) for s in elements]
    )
src/pyramid/urldispatch.py
@@ -1,10 +1,7 @@
import re
from zope.interface import implementer
from pyramid.interfaces import (
    IRoutesMapper,
    IRoute,
    )
from pyramid.interfaces import IRoutesMapper, IRoute
from pyramid.compat import (
    PY2,
@@ -15,29 +12,28 @@
    binary_type,
    is_nonstr_iter,
    decode_path_info,
    )
)
from pyramid.exceptions import URLDecodeError
from pyramid.traversal import (
    quote_path_segment,
    split_path_info,
    PATH_SAFE,
    )
from pyramid.traversal import quote_path_segment, split_path_info, PATH_SAFE
_marker = object()
@implementer(IRoute)
class Route(object):
    def __init__(self, name, pattern, factory=None, predicates=(),
                 pregenerator=None):
    def __init__(
        self, name, pattern, factory=None, predicates=(), pregenerator=None
    ):
        self.pattern = pattern
        self.path = pattern # indefinite b/w compat, not in interface
        self.path = pattern  # indefinite b/w compat, not in interface
        self.match, self.generate = _compile_route(pattern)
        self.name = name
        self.factory = factory
        self.predicates = predicates
        self.pregenerator = pregenerator
@implementer(IRoutesMapper)
class RoutesMapper(object):
@@ -59,8 +55,15 @@
    def get_route(self, name):
        return self.routes.get(name)
    def connect(self, name, pattern, factory=None, predicates=(),
                pregenerator=None, static=False):
    def connect(
        self,
        name,
        pattern,
        factory=None,
        predicates=(),
        pregenerator=None,
        static=False,
    ):
        if name in self.routes:
            oldroute = self.routes[name]
            if oldroute in self.routelist:
@@ -86,18 +89,21 @@
        except KeyError:
            path = '/'
        except UnicodeDecodeError as e:
            raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
            raise URLDecodeError(
                e.encoding, e.object, e.start, e.end, e.reason
            )
        for route in self.routelist:
            match = route.match(path)
            if match is not None:
                preds = route.predicates
                info = {'match':match, 'route':route}
                info = {'match': match, 'route': route}
                if preds and not all((p(info, request) for p in preds)):
                    continue
                return info
        return {'route':None, 'match':None}
        return {'route': None, 'match': None}
# stolen from bobo and modified
old_route_re = re.compile(r'(\:[_a-zA-Z]\w*)')
@@ -109,9 +115,11 @@
# (\{[a-zA-Z][^\}]*\}) but that choked when supplied with e.g. {foo:\d{4}}.
route_re = re.compile(r'(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})')
def update_pattern(matchobj):
    name = matchobj.group(0)
    return '{%s}' % name[1:]
def _compile_route(route):
    # This function really wants to consume Unicode patterns natively, but if
@@ -126,7 +134,8 @@
            raise ValueError(
                'The pattern value passed to add_route must be '
                'either a Unicode string or a plain string without '
                'any non-ASCII characters (you provided %r).' % route)
                'any non-ASCII characters (you provided %r).' % route
            )
    if old_route_re.search(route) and not route_re.search(route):
        route = old_route_re.sub(update_pattern, route)
@@ -145,17 +154,19 @@
    pat.reverse()
    rpat = []
    gen = []
    prefix = pat.pop() # invar: always at least one element (route='/'+route)
    prefix = pat.pop()  # invar: always at least one element (route='/'+route)
    # We want to generate URL-encoded URLs, so we url-quote the prefix, being
    # careful not to quote any embedded slashes.  We have to replace '%' with
    # '%%' afterwards, as the strings that go into "gen" are used as string
    # replacement targets.
    gen.append(quote_path_segment(prefix, safe='/').replace('%', '%%')) # native
    rpat.append(re.escape(prefix)) # unicode
    gen.append(
        quote_path_segment(prefix, safe='/').replace('%', '%%')
    )  # native
    rpat.append(re.escape(prefix))  # unicode
    while pat:
        name = pat.pop() # unicode
        name = pat.pop()  # unicode
        name = name[1:-1]
        if ':' in name:
            # reg may contain colons as well,
@@ -163,12 +174,12 @@
            name, reg = name.split(':', 1)
        else:
            reg = '[^/]+'
        gen.append('%%(%s)s' % native_(name)) # native
        name = '(?P<%s>%s)' % (name, reg) # unicode
        gen.append('%%(%s)s' % native_(name))  # native
        name = '(?P<%s>%s)' % (name, reg)  # unicode
        rpat.append(name)
        s = pat.pop() # unicode
        s = pat.pop()  # unicode
        if s:
            rpat.append(re.escape(s)) # unicode
            rpat.append(re.escape(s))  # unicode
            # We want to generate URL-encoded URLs, so we url-quote this
            # literal in the pattern, being careful not to quote the embedded
            # slashes.  We have to replace '%' with '%%' afterwards, as the
@@ -177,12 +188,13 @@
            gen.append(quote_path_segment(s, safe='/').replace('%', '%%'))
    if remainder:
        rpat.append('(?P<%s>.*?)' % remainder) # unicode
        gen.append('%%(%s)s' % native_(remainder)) # native
        rpat.append('(?P<%s>.*?)' % remainder)  # unicode
        gen.append('%%(%s)s' % native_(remainder))  # native
    pattern = ''.join(rpat) + '$' # unicode
    pattern = ''.join(rpat) + '$'  # unicode
    match = re.compile(pattern).match
    def matcher(path):
        # This function really wants to consume Unicode patterns natively,
        # but if someone passes us a bytestring, we allow it by converting it
@@ -227,9 +239,7 @@
            if k == remainder:
                # a stararg argument
                if is_nonstr_iter(v):
                    v = '/'.join(
                        [q(x) for x in v]
                        ) # native
                    v = '/'.join([q(x) for x in v])  # native
                else:
                    if v.__class__ not in string_types:
                        v = str(v)
@@ -243,7 +253,7 @@
            # at this point, the value will be a native string
            newdict[k] = v
        result = gen % newdict # native string result
        result = gen % newdict  # native string result
        return result
    return matcher, generator
src/pyramid/util.py
@@ -1,5 +1,6 @@
from contextlib import contextmanager
import functools
try:
    # py2.7.7+ and py3.3+ have native comparison support
    from hmac import compare_digest
@@ -8,10 +9,7 @@
import inspect
import weakref
from pyramid.exceptions import (
    ConfigurationError,
    CyclicDependencyError,
    )
from pyramid.exceptions import ConfigurationError, CyclicDependencyError
from pyramid.compat import (
    getargspec,
@@ -22,8 +20,8 @@
    bytes_,
    text_,
    PY2,
    native_
    )
    native_,
)
from pyramid.path import DottedNameResolver as _DottedNameResolver
@@ -31,8 +29,11 @@
class DottedNameResolver(_DottedNameResolver):
    def __init__(self, package=None): # default to package = None for bw compat
    def __init__(
        self, package=None
    ):  # default to package = None for bw compat
        _DottedNameResolver.__init__(self, package)
def is_string_or_iterable(v):
    if isinstance(v, string_types):
@@ -40,11 +41,13 @@
    if hasattr(v, '__iter__'):
        return True
def as_sorted_tuple(val):
    if not is_nonstr_iter(val):
        val = (val,)
    val = tuple(sorted(val))
    return val
class InstancePropertyHelper(object):
    """A helper object for assigning properties and descriptors to instances.
@@ -56,6 +59,7 @@
    per-property and then invoking :meth:`.apply` on target objects.
    """
    def __init__(self):
        self.properties = {}
@@ -81,7 +85,8 @@
            name = callable.__name__
            fn = callable
        if reify:
            import pyramid.decorator # avoid circular import
            import pyramid.decorator  # avoid circular import
            fn = pyramid.decorator.reify(fn)
        elif not is_property:
            fn = property(fn)
@@ -140,6 +145,7 @@
        """ 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
@@ -200,7 +206,9 @@
           1
        """
        InstancePropertyHelper.set_property(
            self, callable, name=name, reify=reify)
            self, callable, name=name, reify=reify
        )
class WeakOrderedSet(object):
    """ Maintain a set of items.
@@ -268,6 +276,7 @@
            oid = self._order[-1]
            return self._items[oid]()
def strings_differ(string1, string2, compare_digest=compare_digest):
    """Check whether two strings differ while avoiding timing attacks.
@@ -298,6 +307,7 @@
        for a, b in zip(left, right):
            invalid_bits += a != b
    return invalid_bits != 0
def object_description(object):
    """ Produce a human-consumable text description of ``object``,
@@ -345,11 +355,12 @@
        return text_('module %s' % modulename)
    if inspect.ismethod(object):
        oself = getattr(object, '__self__', None)
        if oself is None: # pragma: no cover
        if oself is None:  # pragma: no cover
            oself = getattr(object, 'im_self', None)
        return text_('method %s of class %s.%s' %
                     (object.__name__, modulename,
                      oself.__class__.__name__))
        return text_(
            'method %s of class %s.%s'
            % (object.__name__, modulename, oself.__class__.__name__)
        )
    if inspect.isclass(object):
        dottedname = '%s.%s' % (modulename, object.__name__)
@@ -359,11 +370,13 @@
        return text_('function %s' % dottedname)
    return text_('object %s' % str(object))
def shortrepr(object, closer):
    r = str(object)
    if len(r) > 100:
        r = r[:100] + ' ... %s' % closer
    return r
class Sentinel(object):
    def __init__(self, repr):
@@ -372,19 +385,18 @@
    def __repr__(self):
        return self.repr
FIRST = Sentinel('FIRST')
LAST = Sentinel('LAST')
class TopologicalSorter(object):
    """ A utility class which can be used to perform topological sorts against
    tuple-like data."""
    def __init__(
        self,
        default_before=LAST,
        default_after=None,
        first=FIRST,
        last=LAST,
        ):
        self, default_before=LAST, default_after=None, first=FIRST, last=LAST
    ):
        self.names = []
        self.req_before = set()
        self.req_after = set()
@@ -414,7 +426,7 @@
            self.req_before.remove(name)
            for u in before:
                self.order.remove((name, u))
    def add(self, name, val, after=None, before=None):
        """ Add a node to the sort input.  The ``name`` should be a string or
        any other hashable object, the ``val`` should be the sortable (doesn't
@@ -454,7 +466,6 @@
            self.order += [(name, o) for o in before]
            self.req_before.add(name)
    def sorted(self):
        """ Returns the sort input values in topologically sorted order"""
        order = [(self.first, self.last)]
@@ -469,7 +480,7 @@
        def add_node(node):
            if node not in graph:
                roots.append(node)
                graph[node] = [0] # 0 = number of arcs coming into this node
                graph[node] = [0]  # 0 = number of arcs coming into this node
        def add_arc(fromnode, tonode):
            graph[fromnode].append(tonode)
@@ -482,7 +493,7 @@
        has_before, has_after = set(), set()
        for a, b in order:
            if a in names and b in names: # deal with missing dependencies
            if a in names and b in names:  # deal with missing dependencies
                add_arc(a, b)
                has_before.add(a)
                has_after.add(b)
@@ -507,7 +518,7 @@
            for child in children:
                arcs = graph[child][0]
                arcs -= 1
                graph[child][0] = arcs
                graph[child][0] = arcs
                if arcs == 0:
                    roots.insert(0, child)
            del graph[root]
@@ -542,6 +553,7 @@
        )
        raise ConfigurationError(msg % name)
@contextmanager
def hide_attrs(obj, *attrs):
    """
@@ -574,9 +586,11 @@
        return False
    pattern = pattern.lower()
    return (pattern[0] == "." and
            (host.endswith(pattern) or host == pattern[1:]) or
            pattern == host)
    return (
        pattern[0] == "."
        and (host.endswith(pattern) or host == pattern[1:])
        or pattern == host
    )
def make_contextmanager(fn):
@@ -590,6 +604,7 @@
    @functools.wraps(fn)
    def wrapper(*a, **kw):
        yield fn(*a, **kw)
    return wrapper
src/pyramid/view.py
@@ -13,30 +13,25 @@
    IViewClassifier,
    IRequest,
    IExceptionViewClassifier,
    )
)
from pyramid.compat import decode_path_info
from pyramid.compat import reraise as reraise_
from pyramid.exceptions import (
    ConfigurationError,
    PredicateMismatch,
)
from pyramid.exceptions import ConfigurationError, PredicateMismatch
from pyramid.httpexceptions import (
    HTTPNotFound,
    HTTPTemporaryRedirect,
    default_exceptionresponse_view,
    )
)
from pyramid.threadlocal import (
    get_current_registry,
    manager,
    )
from pyramid.threadlocal import get_current_registry, manager
from pyramid.util import hide_attrs
_marker = object()
def render_view_to_response(context, request, name='', secure=True):
    """ Call the :term:`view callable` configured with a :term:`view
@@ -84,9 +79,9 @@
        name,
        secure=secure,
        request_iface=request_iface,
        )
    )
    return response # NB: might be None
    return response  # NB: might be None
def render_view_to_iterable(context, request, name='', secure=True):
@@ -119,6 +114,7 @@
        return None
    return response.app_iter
def render_view(context, request, name='', secure=True):
    """ Call the :term:`view callable` configured with a :term:`view
    configuration` that matches the :term:`view name` ``name``
@@ -145,6 +141,7 @@
    if iterable is None:
        return None
    return b''.join(iterable)
class view_config(object):
    """ A function, class or method :term:`decorator` which allows a
@@ -201,19 +198,21 @@
    See the :py:func:`venusian.attach` function in Venusian for more
    information about the ``_depth`` and ``_category`` arguments.
    .. seealso::
        See also :ref:`mapping_views_using_a_decorator_section` for
        details about using :class:`pyramid.view.view_config`.
    .. warning::
        ``view_config`` will work ONLY on module top level members
        because of the limitation of ``venusian.Scanner.scan``.
    """
    venusian = venusian # for testing injection
    venusian = venusian  # for testing injection
    def __init__(self, **settings):
        if 'for_' in settings:
            if settings.get('context') is None:
@@ -229,8 +228,9 @@
            config = context.config.with_package(info.module)
            config.add_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        info = self.venusian.attach(
            wrapped, callback, category=category, depth=depth + 1
        )
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -239,10 +239,12 @@
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
        settings['_info'] = info.codeinfo # fbo "action_method"
        settings['_info'] = info.codeinfo  # fbo "action_method"
        return wrapped
bfg_view = view_config # bw compat (forever)
bfg_view = view_config  # bw compat (forever)
class view_defaults(view_config):
    """ A class :term:`decorator` which, when applied to a class, will
@@ -256,6 +258,7 @@
    def __call__(self, wrapped):
        wrapped.__view_defaults__ = self.__dict__.copy()
        return wrapped
class AppendSlashNotFoundViewFactory(object):
    """ There can only be one :term:`Not Found view` in any
@@ -292,7 +295,10 @@
    .. deprecated:: 1.3
    """
    def __init__(self, notfound_view=None, redirect_class=HTTPTemporaryRedirect):
    def __init__(
        self, notfound_view=None, redirect_class=HTTPTemporaryRedirect
    ):
        if notfound_view is None:
            notfound_view = default_exceptionresponse_view
        self.notfound_view = notfound_view
@@ -309,8 +315,11 @@
                    qs = request.query_string
                    if qs:
                        qs = '?' + qs
                    return self.redirect_class(location=request.path + '/' + qs)
                    return self.redirect_class(
                        location=request.path + '/' + qs
                    )
        return self.notfound_view(context, request)
append_slash_notfound_view = AppendSlashNotFoundViewFactory()
append_slash_notfound_view.__doc__ = """\
@@ -336,6 +345,7 @@
.. deprecated:: 1.3
"""
class notfound_view_config(object):
    """
@@ -392,7 +402,8 @@
            return HTTPNotFound('not found')
    The above means that a redirect to a slash-appended route will be
    attempted, but instead of :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`
    attempted, but instead of
    :class:`~pyramid.httpexceptions.HTTPTemporaryRedirect`
    being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will
    be used` for the redirect response if a slash-appended route is found.
@@ -417,8 +428,9 @@
            config = context.config.with_package(info.module)
            config.add_notfound_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        info = self.venusian.attach(
            wrapped, callback, category=category, depth=depth + 1
        )
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -427,8 +439,9 @@
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
        settings['_info'] = info.codeinfo # fbo "action_method"
        settings['_info'] = info.codeinfo  # fbo "action_method"
        return wrapped
class forbidden_view_config(object):
    """
@@ -479,8 +492,9 @@
            config = context.config.with_package(info.module)
            config.add_forbidden_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        info = self.venusian.attach(
            wrapped, callback, category=category, depth=depth + 1
        )
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -489,8 +503,9 @@
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
        settings['_info'] = info.codeinfo # fbo "action_method"
        settings['_info'] = info.codeinfo  # fbo "action_method"
        return wrapped
class exception_view_config(object):
    """
@@ -526,6 +541,7 @@
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian
    def __init__(self, *args, **settings):
@@ -545,8 +561,9 @@
            config = context.config.with_package(info.module)
            config.add_exception_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        info = self.venusian.attach(
            wrapped, callback, category=category, depth=depth + 1
        )
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -555,8 +572,9 @@
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
        settings['_info'] = info.codeinfo # fbo "action_method"
        settings['_info'] = info.codeinfo  # fbo "action_method"
        return wrapped
def _find_views(
    registry,
@@ -565,7 +583,7 @@
    view_name,
    view_types=None,
    view_classifier=None,
    ):
):
    if view_types is None:
        view_types = (IView, ISecuredView, IMultiView)
    if view_classifier is None:
@@ -581,9 +599,7 @@
            source_ifaces = (view_classifier, req_type, ctx_type)
            for view_type in view_types:
                view_callable = registered(
                    source_ifaces,
                    view_type,
                    name=view_name,
                    source_ifaces, view_type, name=view_name
                )
                if view_callable is not None:
                    views.append(view_callable)
@@ -599,6 +615,7 @@
    return views
def _call_view(
    registry,
    request,
@@ -609,7 +626,7 @@
    view_classifier=None,
    secure=True,
    request_iface=None,
    ):
):
    if request_iface is None:
        request_iface = getattr(request, 'request_iface', IRequest)
    view_callables = _find_views(
@@ -619,7 +636,7 @@
        view_name,
        view_types=view_types,
        view_classifier=view_classifier,
        )
    )
    pme = None
    response = None
@@ -631,10 +648,8 @@
                # the view will have a __call_permissive__ attribute if it's
                # secured; otherwise it won't.
                view_callable = getattr(
                    view_callable,
                    '__call_permissive__',
                    view_callable
                    )
                    view_callable, '__call_permissive__', view_callable
                )
            # if this view is secured, it will raise a Forbidden
            # appropriately if the executing user does not have the proper
@@ -649,15 +664,13 @@
    return response
class ViewMethodsMixin(object):
    """ Request methods mixin for BaseRequest having to do with executing
    views """
    def invoke_exception_view(
        self,
        exc_info=None,
        request=None,
        secure=True,
        reraise=False,
        self, exc_info=None, request=None, secure=True, reraise=False
    ):
        """ Executes an exception view related to the request it's called upon.
        The arguments it takes are these:
@@ -742,7 +755,7 @@
                    view_classifier=IExceptionViewClassifier,
                    secure=secure,
                    request_iface=request_iface.combined,
                    )
                )
            except Exception:
                if reraise:
                    reraise_(*exc_info)
src/pyramid/viewderivers.py
@@ -1,15 +1,9 @@
import inspect
from zope.interface import (
    implementer,
    provider,
    )
from zope.interface import implementer, provider
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.csrf import (
    check_csrf_origin,
    check_csrf_token,
)
from pyramid.csrf import check_csrf_origin, check_csrf_token
from pyramid.response import Response
from pyramid.interfaces import (
@@ -21,21 +15,13 @@
    IResponse,
    IViewMapper,
    IViewMapperFactory,
    )
from pyramid.compat import (
    is_bound_method,
    is_unbound_method,
    )
from pyramid.exceptions import (
    ConfigurationError,
    )
from pyramid.httpexceptions import HTTPForbidden
from pyramid.util import (
    object_description,
    takes_one_arg,
)
from pyramid.compat import is_bound_method, is_unbound_method
from pyramid.exceptions import ConfigurationError
from pyramid.httpexceptions import HTTPForbidden
from pyramid.util import object_description, takes_one_arg
from pyramid.view import render_view_to_response
from pyramid import renderers
@@ -47,8 +33,10 @@
        # custom view mappers might not add __text__
        return object_description(view)
def requestonly(view, attr=None):
    return takes_one_arg(view, attr=attr, argname='request')
@implementer(IViewMapper)
@provider(IViewMapperFactory)
@@ -58,10 +46,12 @@
    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`'
            ))
            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)
@@ -76,7 +66,9 @@
        else:
            mapped_view = self.map_class_native(view)
        mapped_view.__text__ = 'method %s of %s' % (
            self.attr or '__call__', object_description(view))
            self.attr or '__call__',
            object_description(view),
        )
        return mapped_view
    def map_nonclass(self, view):
@@ -98,11 +90,15 @@
            # bound method.
            if is_bound_method(view):
                _mapped_view = mapped_view
                def mapped_view(context, request):
                    return _mapped_view(context, request)
            if self.attr is not None:
                mapped_view.__text__ = 'attr %s of %s' % (
                    self.attr, object_description(view))
                    self.attr,
                    object_description(view),
                )
            else:
                mapped_view.__text__ = object_description(view)
        return mapped_view
@@ -110,6 +106,7 @@
    def map_class_requestonly(self, view):
        # its a class that has an __init__ which only accepts request
        attr = self.attr
        def _class_requestonly_view(context, request):
            inst = view(request)
            request.__view__ = inst
@@ -118,12 +115,14 @@
            else:
                response = getattr(inst, attr)()
            return response
        return _class_requestonly_view
    def map_class_native(self, view):
        # its a class that has an __init__ which accepts both context and
        # request
        attr = self.attr
        def _class_view(context, request):
            inst = view(context, request)
            request.__view__ = inst
@@ -132,18 +131,21 @@
            else:
                response = getattr(inst, attr)()
            return response
        return _class_view
    def map_nonclass_requestonly(self, view):
        # its a function that has a __call__ which accepts only a single
        # request argument
        attr = self.attr
        def _requestonly_view(context, request):
            if attr is None:
                response = view(request)
            else:
                response = getattr(view, attr)(request)
            return response
        return _requestonly_view
    def map_nonclass_attr(self, view):
@@ -152,6 +154,7 @@
        def _attr_view(context, request):
            response = getattr(view, self.attr)(context, request)
            return response
        return _attr_view
@@ -159,7 +162,9 @@
    def inner(view, info):
        wrapper_view = wrapper(view, info)
        return preserve_view_attrs(view, wrapper_view)
    return inner
def preserve_view_attrs(view, wrapper):
    if view is None:
@@ -185,15 +190,23 @@
    # attrs that may not exist on "view", but, if so, must be attached to
    # "wrapped view"
    for attr in ('__permitted__', '__call_permissive__', '__permission__',
                 '__predicated__', '__predicates__', '__accept__',
                 '__order__', '__text__'):
    for attr in (
        '__permitted__',
        '__call_permissive__',
        '__permission__',
        '__predicated__',
        '__predicates__',
        '__accept__',
        '__order__',
        '__text__',
    ):
        try:
            setattr(wrapper, attr, getattr(view, attr))
        except AttributeError:
            pass
    return wrapper
def mapped_view(view, info):
    mapper = info.options.get('mapper')
@@ -207,28 +220,36 @@
    mapped_view = mapper(**info.options)(view)
    return mapped_view
mapped_view.options = ('mapper', 'attr')
def owrapped_view(view, info):
    wrapper_viewname = info.options.get('wrapper')
    viewname = info.options.get('name')
    if not wrapper_viewname:
        return view
    def _owrapped_view(context, request):
        response = view(context, request)
        request.wrapped_response = response
        request.wrapped_body = response.body
        request.wrapped_view = view
        wrapped_response = render_view_to_response(context, request,
                                                   wrapper_viewname)
        wrapped_response = render_view_to_response(
            context, request, wrapper_viewname
        )
        if wrapped_response is None:
            raise ValueError(
                'No wrapper view named %r found when executing view '
                'named %r' % (wrapper_viewname, viewname))
                'named %r' % (wrapper_viewname, viewname)
            )
        return wrapped_response
    return _owrapped_view
owrapped_view.options = ('name', 'wrapper')
def http_cached_view(view, info):
    if info.settings.get('prevent_http_cache', False):
@@ -247,26 +268,32 @@
        except ValueError:
            raise ConfigurationError(
                'If http_cache parameter is a tuple or list, it must be '
                'in the form (seconds, options); not %s' % (seconds,))
                'in the form (seconds, options); not %s' % (seconds,)
            )
    def wrapper(context, request):
        response = view(context, request)
        prevent_caching = getattr(response.cache_control, 'prevent_auto',
                                  False)
        prevent_caching = getattr(
            response.cache_control, 'prevent_auto', False
        )
        if not prevent_caching:
            response.cache_expires(seconds, **options)
        return response
    return wrapper
http_cached_view.options = ('http_cache',)
def secured_view(view, info):
    for wrapper in (_secured_view, _authdebug_view):
        view = wraps_view(wrapper)(view, info)
    return view
secured_view.options = ('permission',)
def _secured_view(view, info):
    permission = explicit_val = info.options.get('permission')
@@ -287,24 +314,30 @@
        return view
    if authn_policy and authz_policy and (permission is not None):
        def permitted(context, request):
            principals = authn_policy.effective_principals(request)
            return authz_policy.permits(context, principals, permission)
        def secured_view(context, request):
            result = permitted(context, request)
            if result:
                return view(context, request)
            view_name = getattr(view, '__name__', view)
            msg = getattr(
                request, 'authdebug_message',
                'Unauthorized: %s failed permission check' % view_name)
                request,
                'authdebug_message',
                'Unauthorized: %s failed permission check' % view_name,
            )
            raise HTTPForbidden(msg, result=result)
        wrapped_view = secured_view
        wrapped_view.__call_permissive__ = view
        wrapped_view.__permitted__ = permitted
        wrapped_view.__permission__ = permission
    return wrapped_view
def _authdebug_view(view, info):
    wrapped_view = view
@@ -321,6 +354,7 @@
        return view
    if settings and settings.get('debug_authorization', False):
        def authdebug_view(context, request):
            view_name = getattr(request, 'view_name', None)
@@ -331,23 +365,28 @@
                    msg = 'Allowed (no permission registered)'
                else:
                    principals = authn_policy.effective_principals(request)
                    msg = str(authz_policy.permits(
                        context, principals, permission))
                    msg = str(
                        authz_policy.permits(context, principals, permission)
                    )
            else:
                msg = 'Allowed (no authorization policy in use)'
            view_name = getattr(request, 'view_name', None)
            url = getattr(request, 'url', None)
            msg = ('debug_authorization of url %s (view name %r against '
                   'context %r): %s' % (url, view_name, context, msg))
            msg = (
                'debug_authorization of url %s (view name %r against '
                'context %r): %s' % (url, view_name, context, msg)
            )
            if logger:
                logger.debug(msg)
            if request is not None:
                request.authdebug_message = msg
            return view(context, request)
        wrapped_view = authdebug_view
    return wrapped_view
def rendered_view(view, info):
    # one way or another this wrapper must produce a Response (unless
@@ -360,23 +399,29 @@
        # a view registration.
        def viewresult_to_response(context, request):
            result = view(context, request)
            if result.__class__ is Response: # common case
            if result.__class__ is Response:  # common case
                response = result
            else:
                response = info.registry.queryAdapterOrSelf(result, IResponse)
                if response is None:
                    if result is None:
                        append = (' You may have forgotten to return a value '
                                  'from the view callable.')
                        append = (
                            ' You may have forgotten to return a value '
                            'from the view callable.'
                        )
                    elif isinstance(result, dict):
                        append = (' You may have forgotten to define a '
                                  'renderer in the view configuration.')
                        append = (
                            ' You may have forgotten to define a '
                            'renderer in the view configuration.'
                        )
                    else:
                        append = ''
                    msg = ('Could not convert return value of the view '
                           'callable %s into a response object. '
                           'The value returned was %r.' + append)
                    msg = (
                        'Could not convert return value of the view '
                        'callable %s into a response object. '
                        'The value returned was %r.' + append
                    )
                    raise ValueError(msg % (view_description(view), result))
@@ -389,7 +434,7 @@
    def rendered_view(context, request):
        result = view(context, request)
        if result.__class__ is Response: # potential common case
        if result.__class__ is Response:  # potential common case
            response = result
        else:
            # this must adapt, it can't do a simple interface check
@@ -403,7 +448,8 @@
                    view_renderer = renderers.RendererHelper(
                        name=renderer_name,
                        package=info.package,
                        registry=info.registry)
                        registry=info.registry,
                    )
                else:
                    view_renderer = renderer.clone()
                if '__view__' in attrs:
@@ -411,12 +457,15 @@
                else:
                    view_inst = getattr(view, '__original_view__', view)
                response = view_renderer.render_view(
                    request, result, view_inst, context)
                    request, result, view_inst, context
                )
        return response
    return rendered_view
rendered_view.options = ('renderer',)
def decorated_view(view, info):
    decorator = info.options.get('decorator')
@@ -424,7 +473,9 @@
        return view
    return decorator(view)
decorated_view.options = ('decorator',)
def csrf_view(view, info):
    explicit_val = info.options.get('require_csrf')
@@ -443,29 +494,29 @@
        callback = defaults.callback
    enabled = (
        explicit_val is True or
        explicit_val is True
        or
        # fallback to the default val if not explicitly enabled
        # but only if the view is not an exception view
        (
            explicit_val is not False and default_val and
            not info.exception_only
        )
        (explicit_val is not False and default_val and not info.exception_only)
    )
    # disable if both header and token are disabled
    enabled = enabled and (token or header)
    wrapped_view = view
    if enabled:
        def csrf_view(context, request):
            if (
                request.method not in safe_methods and
                (callback is None or callback(request))
            if request.method not in safe_methods and (
                callback is None or callback(request)
            ):
                check_csrf_origin(request, raises=True)
                check_csrf_token(request, token, header, raises=True)
            return view(context, request)
        wrapped_view = csrf_view
    return wrapped_view
csrf_view.options = ('require_csrf',)
VIEW = 'VIEW'
src/pyramid/wsgi.py
@@ -1,6 +1,7 @@
from functools import wraps
from pyramid.request import call_app_with_subpath_as_path_info
def wsgiapp(wrapped):
    """ Decorator to turn a WSGI application into a :app:`Pyramid`
    :term:`view callable`.  This decorator differs from the
@@ -41,6 +42,7 @@
        return wraps(wrapped)(decorator)
    return wraps(wrapped, ('__module__', '__doc__'))(decorator)
def wsgiapp2(wrapped):
    """ Decorator to turn a WSGI application into a :app:`Pyramid`
    view callable.  This decorator differs from the
tests/__init__.py
@@ -1,3 +1,2 @@
def dummy_extend(*args):
    """used to test Configurator.extend"""
tests/pkgs/ccbugapp/__init__.py
@@ -1,16 +1,20 @@
from webob import Response
def rdf_view(request):
    """ """
    return Response('rdf')
def juri_view(request):
    """ """
    return Response('juri')
def includeme(config):
    config.add_route('rdf', 'licenses/:license_code/:license_version/rdf')
    config.add_route('juri',
                     'licenses/:license_code/:license_version/:jurisdiction')
    config.add_route(
        'juri', 'licenses/:license_code/:license_version/:jurisdiction'
    )
    config.add_view(rdf_view, route_name='rdf')
    config.add_view(juri_view, route_name='juri')
tests/pkgs/conflictapp/__init__.py
@@ -2,14 +2,18 @@
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
def aview(request):
    return Response('a view')
def routeview(request):
    return Response('route view')
def protectedview(request):
    return Response('protected view')
def includeme(config):
    # purposely sorta-randomly ordered (route comes after view naming it,
@@ -18,7 +22,8 @@
    config.add_view(protectedview, name='protected', permission='view')
    config.add_view(routeview, route_name='aroute')
    config.add_route('aroute', '/route')
    config.set_authentication_policy(AuthTktAuthenticationPolicy(
        'seekri1t', hashalg='sha512'))
    config.set_authentication_policy(
        AuthTktAuthenticationPolicy('seekri1t', hashalg='sha512')
    )
    config.set_authorization_policy(ACLAuthorizationPolicy())
    config.include('tests.pkgs.conflictapp.included')
tests/pkgs/conflictapp/included.py
@@ -1,6 +1,9 @@
from webob import Response
def bview(request): return Response('b view')
def bview(request):  # pragma: no cover
    return Response('b view')
def includeme(config):
    config.add_view(bview)
tests/pkgs/defpermbugapp/__init__.py
@@ -2,25 +2,29 @@
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.view import view_config
@view_config(name='x')
def x_view(request): # pragma: no cover
     return Response('this is private!')
def x_view(request):  # pragma: no cover
    return Response('this is private!')
@view_config(name='y', permission='private2')
def y_view(request): # pragma: no cover
     return Response('this is private too!')
def y_view(request):  # pragma: no cover
    return Response('this is private too!')
@view_config(name='z', permission=NO_PERMISSION_REQUIRED)
def z_view(request):
     return Response('this is public')
    return Response('this is public')
def includeme(config):
     from pyramid.authorization import ACLAuthorizationPolicy
     from pyramid.authentication import AuthTktAuthenticationPolicy
     authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512')
     authz_policy = ACLAuthorizationPolicy()
     config.scan('tests.pkgs.defpermbugapp')
     config._set_authentication_policy(authn_policy)
     config._set_authorization_policy(authz_policy)
     config.set_default_permission('private')
    from pyramid.authorization import ACLAuthorizationPolicy
    from pyramid.authentication import AuthTktAuthenticationPolicy
    authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512')
    authz_policy = ACLAuthorizationPolicy()
    config.scan('tests.pkgs.defpermbugapp')
    config._set_authentication_policy(authn_policy)
    config._set_authorization_policy(authz_policy)
    config.set_default_permission('private')
tests/pkgs/eventonly/__init__.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.events import subscriber
class Yup(object):
    def __init__(self, val, config):
        self.val = val
@@ -13,36 +14,45 @@
    def __call__(self, event):
        return getattr(event.response, 'yup', False)
class Foo(object):
    def __init__(self, response):
        self.response = response
class Bar(object):
    pass
@subscriber(Foo)
def foo(event):
    event.response.text += 'foo '
@subscriber(Foo, yup=True)
def fooyup(event):
    event.response.text += 'fooyup '
@subscriber([Foo, Bar])
def foobar(event):
    event.response.text += 'foobar '
@subscriber([Foo, Bar])
def foobar2(event, context):
    event.response.text += 'foobar2 '
@subscriber([Foo, Bar], yup=True)
def foobaryup(event):
    event.response.text += 'foobaryup '
@subscriber([Foo, Bar], yup=True)
def foobaryup2(event, context):
    event.response.text += 'foobaryup2 '
@view_config(name='sendfoo')
def sendfoo(request):
@@ -51,6 +61,7 @@
    request.registry.notify(Foo(response))
    return response
@view_config(name='sendfoobar')
def sendfoobar(request):
    response = request.response
@@ -58,7 +69,7 @@
    request.registry.notify(Foo(response), Bar())
    return response
def includeme(config):
    config.add_subscriber_predicate('yup', Yup)
    config.scan('tests.pkgs.eventonly')
tests/pkgs/exceptionviewapp/__init__.py
@@ -1,31 +1,47 @@
from pyramid.httpexceptions import HTTPException
def includeme(config):
    config.add_route('route_raise_exception', 'route_raise_exception')
    config.add_route('route_raise_httpexception', 'route_raise_httpexception')
    config.add_route('route_raise_exception2', 'route_raise_exception2',
                     factory='.models.route_factory')
    config.add_route('route_raise_exception3', 'route_raise_exception3',
                    factory='.models.route_factory2')
    config.add_route(
        'route_raise_exception2',
        'route_raise_exception2',
        factory='.models.route_factory',
    )
    config.add_route(
        'route_raise_exception3',
        'route_raise_exception3',
        factory='.models.route_factory2',
    )
    config.add_route('route_raise_exception4', 'route_raise_exception4')
    config.add_view('.views.maybe')
    config.add_view('.views.no', context='.models.NotAnException')
    config.add_view('.views.yes', context=".models.AnException")
    config.add_view('.views.raise_exception', name='raise_exception')
    config.add_view('.views.raise_exception',
                    route_name='route_raise_exception')
    config.add_view('.views.raise_exception',
                    route_name='route_raise_exception2')
    config.add_view('.views.raise_exception',
                    route_name='route_raise_exception3')
    config.add_view('.views.whoa', context='.models.AnException',
                    route_name='route_raise_exception3')
    config.add_view('.views.raise_exception',
                    route_name='route_raise_exception4')
    config.add_view('.views.whoa', context='.models.AnException',
                    route_name='route_raise_exception4')
    config.add_view('.views.raise_httpexception',
                    route_name='route_raise_httpexception')
    config.add_view(
        '.views.raise_exception', route_name='route_raise_exception'
    )
    config.add_view(
        '.views.raise_exception', route_name='route_raise_exception2'
    )
    config.add_view(
        '.views.raise_exception', route_name='route_raise_exception3'
    )
    config.add_view(
        '.views.whoa',
        context='.models.AnException',
        route_name='route_raise_exception3',
    )
    config.add_view(
        '.views.raise_exception', route_name='route_raise_exception4'
    )
    config.add_view(
        '.views.whoa',
        context='.models.AnException',
        route_name='route_raise_exception4',
    )
    config.add_view(
        '.views.raise_httpexception', route_name='route_raise_httpexception'
    )
    config.add_view('.views.catch_httpexception', context=HTTPException)
tests/pkgs/exceptionviewapp/models.py
@@ -1,18 +1,22 @@
class NotAnException(object):
    pass
class AnException(Exception):
    pass
class RouteContext(object):
    pass
class RouteContext2(object):
    pass
def route_factory(*arg):
    return RouteContext()
def route_factory2(*arg):
    return RouteContext2()
tests/pkgs/exceptionviewapp/views.py
@@ -2,23 +2,30 @@
from .models import AnException
from pyramid.httpexceptions import HTTPBadRequest
def no(request):
    return Response('no')
def yes(request):
    return Response('yes')
def maybe(request):
    return Response('maybe')
def whoa(request):
    return Response('whoa')
def raise_exception(request):
    raise AnException()
def raise_httpexception(request):
    raise HTTPBadRequest
def catch_httpexception(request):
    return Response('caught')
tests/pkgs/fixtureapp/__init__.py
@@ -3,10 +3,12 @@
    config.add_view('.views.exception_view', context=RuntimeError)
    config.add_view('.views.protected_view', name='protected.html')
    config.add_view('.views.erroneous_view', name='error.html')
    config.add_view('.views.fixture_view', name='dummyskin.html',
                    request_type='.views.IDummy')
    config.add_view(
        '.views.fixture_view',
        name='dummyskin.html',
        request_type='.views.IDummy',
    )
    from .models import fixture, IFixture
    config.registry.registerUtility(fixture, IFixture)
    config.add_view('.views.fixture_view', name='another.html')
tests/pkgs/fixtureapp/models.py
@@ -1,8 +1,9 @@
from zope.interface import Interface
class IFixture(Interface):
    pass
def fixture():
    """ """
tests/pkgs/fixtureapp/subpackage/__init__.py
@@ -1 +1 @@
#package
# package
tests/pkgs/fixtureapp/views.py
@@ -2,21 +2,26 @@
from webob import Response
from pyramid.httpexceptions import HTTPForbidden
def fixture_view(context, request):
    """ """
    return Response('fixture')
def erroneous_view(context, request):
    """ """
    raise RuntimeError()
def exception_view(context, request):
    """ """
    return Response('supressed')
def protected_view(context, request):
    """ """
    raise HTTPForbidden()
class IDummy(Interface):
    pass
tests/pkgs/forbiddenapp/__init__.py
@@ -2,23 +2,27 @@
from pyramid.httpexceptions import HTTPForbidden
from pyramid.compat import bytes_
def x_view(request): # pragma: no cover
     return Response('this is private!')
def x_view(request):  # pragma: no cover
    return Response('this is private!')
def forbidden_view(context, request):
     msg = context.message
     result = context.result
     message = msg + '\n' + str(result)
     resp = HTTPForbidden()
     resp.body = bytes_(message)
     return resp
    msg = context.message
    result = context.result
    message = msg + '\n' + str(result)
    resp = HTTPForbidden()
    resp.body = bytes_(message)
    return resp
def includeme(config):
     from pyramid.authentication import AuthTktAuthenticationPolicy
     from pyramid.authorization import ACLAuthorizationPolicy
     authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512')
     authz_policy = ACLAuthorizationPolicy()
     config._set_authentication_policy(authn_policy)
     config._set_authorization_policy(authz_policy)
     config.add_view(x_view, name='x', permission='private')
     config.add_view(forbidden_view, context=HTTPForbidden)
    from pyramid.authentication import AuthTktAuthenticationPolicy
    from pyramid.authorization import ACLAuthorizationPolicy
    authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512')
    authz_policy = ACLAuthorizationPolicy()
    config._set_authentication_policy(authn_policy)
    config._set_authorization_policy(authz_policy)
    config.add_view(x_view, name='x', permission='private')
    config.add_view(forbidden_view, context=HTTPForbidden)
tests/pkgs/forbiddenview/__init__.py
@@ -3,21 +3,26 @@
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
@forbidden_view_config(route_name='foo')
def foo_forbidden(request): # pragma: no cover
def foo_forbidden(request):  # pragma: no cover
    return Response('foo_forbidden')
@forbidden_view_config()
def forbidden(request):
    return Response('generic_forbidden')
@view_config(route_name='foo')
def foo(request): # pragma: no cover
def foo(request):  # pragma: no cover
    return Response('OK foo')
@view_config(route_name='bar')
def bar(request): # pragma: no cover
def bar(request):  # pragma: no cover
    return Response('OK bar')
def includeme(config):
    authn_policy = AuthTktAuthenticationPolicy('seekri1', hashalg='sha512')
@@ -28,4 +33,3 @@
    config.add_route('foo', '/foo')
    config.add_route('bar', '/bar')
    config.scan('tests.pkgs.forbiddenview')
tests/pkgs/hybridapp/__init__.py
@@ -1,39 +1,46 @@
def includeme(config):
  # <!-- we want this view to "win" -->
  config.add_route('route', 'abc')
  config.add_view('.views.route_view', route_name='route')
  # <!-- .. even though this one has a more specific context -->
  config.add_view('.views.global_view',
                  context='pyramid.traversal.DefaultRootFactory')
  config.add_view('.views.global2_view',
                  context='pyramid.traversal.DefaultRootFactory',
                  name='global2')
  config.add_route('route2', 'def')
  # <!-- we want this view to win for route2 even though global view with
  #     context is more specific -->
  config.add_view('.views.route2_view', route_name='route2')
    # <!-- we want this view to "win" -->
    config.add_route('route', 'abc')
    config.add_view('.views.route_view', route_name='route')
    # <!-- .. even though this one has a more specific context -->
    config.add_view(
        '.views.global_view', context='pyramid.traversal.DefaultRootFactory'
    )
    config.add_view(
        '.views.global2_view',
        context='pyramid.traversal.DefaultRootFactory',
        name='global2',
    )
    config.add_route('route2', 'def')
    # <!-- we want this view to win for route2 even though global view with
    #     context is more specific -->
    config.add_view('.views.route2_view', route_name='route2')
  # <!-- the global view should be found for this route -->
  config.add_route('route3', 'ghi', use_global_views=True)
  # <!-- the global view should not be found for this route -->
  config.add_route('route4', 'jkl')
  # <!-- the global view should not be found for this route (/global2) -->
  config.add_route('route5', 'mno/*traverse')
  # <!-- the global view should be found for this route (/global2) -->
  config.add_route('route6', 'pqr/*traverse', use_global_views=True)
  config.add_route('route7', 'error')
  config.add_view('.views.erroneous_view', route_name='route7')
  config.add_route('route8', 'error2')
  config.add_view('.views.erroneous_view', route_name='route8')
  # <!-- we want this view to "win" for route7 as exception view -->
  config.add_view('.views.exception_view', context=RuntimeError)
  # <!-- we want this view to "win" for route8 as exception view-->
  config.add_view('.views.exception2_view', context=RuntimeError,
                  route_name='route8')
  config.add_route('route9', 'error_sub')
  config.add_view('.views.erroneous_sub_view', route_name='route9')
  # <!-- we want this view to "win" for route9 as exception view... -->
  config.add_view('.views.exception2_view', context='.views.SuperException',
                  route_name='route9')
  # <!-- ...even if we have more context-specialized view for exception -->
  config.add_view('.views.exception_view', context='.views.SubException')
    # <!-- the global view should be found for this route -->
    config.add_route('route3', 'ghi', use_global_views=True)
    # <!-- the global view should not be found for this route -->
    config.add_route('route4', 'jkl')
    # <!-- the global view should not be found for this route (/global2) -->
    config.add_route('route5', 'mno/*traverse')
    # <!-- the global view should be found for this route (/global2) -->
    config.add_route('route6', 'pqr/*traverse', use_global_views=True)
    config.add_route('route7', 'error')
    config.add_view('.views.erroneous_view', route_name='route7')
    config.add_route('route8', 'error2')
    config.add_view('.views.erroneous_view', route_name='route8')
    # <!-- we want this view to "win" for route7 as exception view -->
    config.add_view('.views.exception_view', context=RuntimeError)
    # <!-- we want this view to "win" for route8 as exception view-->
    config.add_view(
        '.views.exception2_view', context=RuntimeError, route_name='route8'
    )
    config.add_route('route9', 'error_sub')
    config.add_view('.views.erroneous_sub_view', route_name='route9')
    # <!-- we want this view to "win" for route9 as exception view... -->
    config.add_view(
        '.views.exception2_view',
        context='.views.SuperException',
        route_name='route9',
    )
    # <!-- ...even if we have more context-specialized view for exception -->
    config.add_view('.views.exception_view', context='.views.SubException')
tests/pkgs/hybridapp/views.py
@@ -1,39 +1,49 @@
from webob import Response
def route_view(request):
    """ """
    return Response('route')
def global_view(request):
    """ """
    return Response('global')
def global2_view(request):
    """ """
    return Response('global2')
def route2_view(request):
    """ """
    return Response('route2')
def exception_view(request):
    """ """
    return Response('supressed')
def exception2_view(request):
    """ """
    return Response('supressed2')
def erroneous_view(request):
    """ """
    raise RuntimeError()
def erroneous_sub_view(request):
    """ """
    raise SubException()
class SuperException(Exception):
    """ """
class SubException(SuperException):
    """ """
tests/pkgs/includeapp1/root.py
@@ -1,10 +1,11 @@
from pyramid.response import Response
def aview(request):
    return Response('root')
def configure(config):
    config.add_view(aview)
    config.include('tests.pkgs.includeapp1.two.configure')
    config.commit()
tests/pkgs/includeapp1/three.py
@@ -1,10 +1,11 @@
from pyramid.response import Response
def aview(request):
    return Response('three')
def configure(config):
    config.add_view(aview, name='three')
    config.include('tests.pkgs.includeapp1.two.configure') # should not cycle
    config.add_view(aview) # will be overridden by root when resolved
    config.include('tests.pkgs.includeapp1.two.configure')  # should not cycle
    config.add_view(aview)  # will be overridden by root when resolved
tests/pkgs/includeapp1/two.py
@@ -1,9 +1,11 @@
from pyramid.response import Response
def aview(request):
    return Response('two')
def configure(config):
    config.add_view(aview, name='two')
    config.include('tests.pkgs.includeapp1.three.configure')
    config.add_view(aview) # will be overridden by root when resolved
    config.add_view(aview)  # will be overridden by root when resolved
tests/pkgs/notfoundview/__init__.py
@@ -1,25 +1,31 @@
from pyramid.view import notfound_view_config, view_config
from pyramid.response import Response
@notfound_view_config(route_name='foo', append_slash=True)
def foo_notfound(request): # pragma: no cover
def foo_notfound(request):  # pragma: no cover
    return Response('foo_notfound')
@notfound_view_config(route_name='baz')
def baz_notfound(request):
    return Response('baz_notfound')
@notfound_view_config(append_slash=True)
def notfound(request):
    return Response('generic_notfound')
@view_config(route_name='bar')
def bar(request):
    return Response('OK bar')
@view_config(route_name='foo2')
def foo2(request):
    return Response('OK foo2')
def includeme(config):
    config.add_route('foo', '/foo')
@@ -27,4 +33,3 @@
    config.add_route('bar', '/bar/')
    config.add_route('baz', '/baz')
    config.scan('tests.pkgs.notfoundview')
tests/pkgs/permbugapp/__init__.py
@@ -2,21 +2,26 @@
from pyramid.security import view_execution_permitted
from pyramid.response import Response
def x_view(request): # pragma: no cover
     return Response('this is private!')
def x_view(request):  # pragma: no cover
    return Response('this is private!')
def test(context, request):
    # should return false
     msg = 'Allow ./x? %s' % repr(view_execution_permitted(
         context, request, 'x'))
     return Response(escape(msg))
    msg = 'Allow ./x? %s' % repr(
        view_execution_permitted(context, request, 'x')
    )
    return Response(escape(msg))
def includeme(config):
     from pyramid.authentication import AuthTktAuthenticationPolicy
     from pyramid.authorization import ACLAuthorizationPolicy
     authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512')
     authz_policy = ACLAuthorizationPolicy()
     config.set_authentication_policy(authn_policy)
     config.set_authorization_policy(authz_policy)
     config.add_view(test, name='test')
     config.add_view(x_view, name='x', permission='private')
    from pyramid.authentication import AuthTktAuthenticationPolicy
    from pyramid.authorization import ACLAuthorizationPolicy
    authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512')
    authz_policy = ACLAuthorizationPolicy()
    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(authz_policy)
    config.add_view(test, name='test')
    config.add_view(x_view, name='x', permission='private')
tests/pkgs/rendererscanapp/__init__.py
@@ -1,9 +1,10 @@
from pyramid.view import view_config
@view_config(name='one', renderer='json')
def one(request):
    return {'name':'One!'}
    return {'name': 'One!'}
def includeme(config):
    config.scan()
tests/pkgs/rendererscanapp/two/__init__.py
@@ -1,6 +1,6 @@
from pyramid.view import view_config
@view_config(name='two', renderer='json')
def two(request):
    return {'nameagain':'Two!'}
    return {'nameagain': 'Two!'}
tests/pkgs/restbugapp/__init__.py
@@ -1,15 +1,19 @@
def includeme(config):
    config.add_route('gameactions_pet_get_pets', '/pet',
                     request_method='GET')
    config.add_route('gameactions_pet_care_for_pet', '/pet',
                     request_method='POST')
    config.add_view('.views.PetRESTView',
                    route_name='gameactions_pet_get_pets',
                    attr='GET',
                    permission='view',
                    renderer='json')
    config.add_view('.views.PetRESTView',
                    route_name='gameactions_pet_care_for_pet',
                    attr='POST',
                    permission='view',
                    renderer='json')
    config.add_route('gameactions_pet_get_pets', '/pet', request_method='GET')
    config.add_route(
        'gameactions_pet_care_for_pet', '/pet', request_method='POST'
    )
    config.add_view(
        '.views.PetRESTView',
        route_name='gameactions_pet_get_pets',
        attr='GET',
        permission='view',
        renderer='json',
    )
    config.add_view(
        '.views.PetRESTView',
        route_name='gameactions_pet_care_for_pet',
        attr='POST',
        permission='view',
        renderer='json',
    )
tests/pkgs/restbugapp/views.py
@@ -1,15 +1,17 @@
from pyramid.response import Response
class BaseRESTView(object):
    def __init__(self, context, request):
        self.context = context
        self.request = request
class PetRESTView(BaseRESTView):
    """ REST Controller to control action of an avatar """
    def __init__(self, context, request):
        super(PetRESTView, self).__init__(context, request)
    def GET(self):
        return Response('gotten')
tests/pkgs/static_abspath/__init__.py
@@ -1,7 +1,7 @@
import os
def includeme(config):
    here =  here = os.path.dirname(__file__)
    here = here = os.path.dirname(__file__)
    fixtures = os.path.normpath(os.path.join(here, '..', '..', 'fixtures'))
    config.add_static_view('/', fixtures)
tests/pkgs/static_assetspec/__init__.py
@@ -1,3 +1,2 @@
def includeme(config):
    config.add_static_view('/', 'tests:fixtures')
tests/pkgs/static_routeprefix/__init__.py
@@ -2,6 +2,6 @@
    config.add_static_view('/static', 'tests:fixtures')
    config.include(includeme2, route_prefix='/prefix')
def includeme2(config):
    config.add_static_view('/static', 'tests:fixtures/static')
tests/pkgs/staticpermapp/__init__.py
@@ -1,25 +1,32 @@
class RootFactory(object):
    __acl__ = [('Allow', 'fred', 'view')]
    def __init__(self, request):
        pass
class LocalRootFactory(object):
    __acl__ = [('Allow', 'bob', 'view')]
    def __init__(self, request):
        pass
def includeme(config):
     from pyramid.authentication import RemoteUserAuthenticationPolicy
     from pyramid.authorization import ACLAuthorizationPolicy
     authn_policy = RemoteUserAuthenticationPolicy()
     authz_policy = ACLAuthorizationPolicy()
     config._set_authentication_policy(authn_policy)
     config._set_authorization_policy(authz_policy)
     config.add_static_view('allowed', 'tests:fixtures/static/')
     config.add_static_view('protected', 'tests:fixtures/static/',
                            permission='view')
     config.add_static_view('factory_protected',
                            'tests:fixtures/static/',
                            permission='view',
                            factory=LocalRootFactory)
    from pyramid.authentication import RemoteUserAuthenticationPolicy
    from pyramid.authorization import ACLAuthorizationPolicy
    authn_policy = RemoteUserAuthenticationPolicy()
    authz_policy = ACLAuthorizationPolicy()
    config._set_authentication_policy(authn_policy)
    config._set_authorization_policy(authz_policy)
    config.add_static_view('allowed', 'tests:fixtures/static/')
    config.add_static_view(
        'protected', 'tests:fixtures/static/', permission='view'
    )
    config.add_static_view(
        'factory_protected',
        'tests:fixtures/static/',
        permission='view',
        factory=LocalRootFactory,
    )
tests/pkgs/subrequestapp/__init__.py
@@ -1,25 +1,30 @@
from pyramid.config import Configurator
from pyramid.request import Request
def view_one(request):
    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq, use_tweens=False)
    return response
def view_two(request):
    # check that request.foo is valid for a subrequest
    return 'This came from view_two, foo=%s' % (request.foo,)
def view_three(request):
    subreq = Request.blank('/view_four')
    try:
        return request.invoke_subrequest(subreq, use_tweens=True)
    except: # pragma: no cover
    except Exception:  # pragma: no cover
        request.response.body = b'Value error raised'
        return request.response
def view_four(request):
    raise ValueError('foo')
def view_five(request):
    subreq = Request.blank('/view_four')
@@ -29,10 +34,12 @@
        request.response.body = b'Value error raised'
        return request.response
def excview(request):
    request.response.status_int = 500
    request.response.body = b'Bad stuff happened'
    return request.response
def main():
    config = Configurator()
@@ -49,4 +56,3 @@
    config.add_view(view_five, route_name='five')
    config.add_request_method(lambda r: 'bar', 'foo', property=True)
    return config
tests/pkgs/viewdecoratorapp/__init__.py
@@ -1,3 +1,2 @@
def includeme(config):
    config.scan('tests.pkgs.viewdecoratorapp')
tests/pkgs/viewdecoratorapp/views/views.py
@@ -1,12 +1,11 @@
from pyramid.view import view_config
@view_config(renderer='json', name='first')
def first(request):
    return {'result':'OK1'}
    return {'result': 'OK1'}
@view_config(
    renderer='json',
             name='second')
@view_config(renderer='json', name='second')
def second(request):
    return {'result':'OK2'}
    return {'result': 'OK2'}
tests/pkgs/wsgiapp2app/__init__.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.wsgi import wsgiapp2
@view_config(name='hello', renderer='string')
@wsgiapp2
def hello(environ, start_response):
@@ -10,8 +11,10 @@
    start_response('200 OK', response_headers)
    return [b'Hello!']
def main():
    from pyramid.config import Configurator
    c = Configurator()
    c.scan()
    return c
tests/test_asset.py
@@ -3,9 +3,11 @@
here = os.path.abspath(os.path.dirname(__file__))
class Test_resolve_asset_spec(unittest.TestCase):
    def _callFUT(self, spec, package_name='__main__'):
        from pyramid.resource import resolve_asset_spec
        return resolve_asset_spec(spec, package_name)
    def test_abspath(self):
@@ -19,7 +21,7 @@
        package_name, filename = self._callFUT(path, pkg)
        self.assertEqual(package_name, 'tests')
        self.assertEqual(filename, 'test_asset.py')
    def test_abs_spec(self):
        pkg = 'tests'
        path = 'pyramid.nottests:test_asset.py'
@@ -36,6 +38,7 @@
    def test_package_name_is_package_object(self):
        import tests
        pkg = tests
        path = 'test_asset.py'
        package_name, filename = self._callFUT(path, pkg)
@@ -46,6 +49,7 @@
class Test_abspath_from_asset_spec(unittest.TestCase):
    def _callFUT(self, spec, pname='__main__'):
        from pyramid.resource import abspath_from_asset_spec
        return abspath_from_asset_spec(spec, pname)
    def test_pname_is_None_before_resolve_asset_spec(self):
@@ -60,9 +64,11 @@
        result = self._callFUT('abc', 'tests')
        self.assertEqual(result, os.path.join(here, 'abc'))
class Test_asset_spec_from_abspath(unittest.TestCase):
    def _callFUT(self, abspath, package):
        from pyramid.asset import asset_spec_from_abspath
        return asset_spec_from_abspath(abspath, package)
    def test_package_name_is_main(self):
@@ -82,7 +88,7 @@
        result = self._callFUT(here, pkg)
        self.assertEqual(result, here)
class DummyPackage:
    def __init__(self, name):
        self.__name__ = name
tests/test_authentication.py
@@ -1,14 +1,13 @@
import unittest
import warnings
from pyramid import testing
from pyramid.compat import (
    text_,
    bytes_,
    )
from pyramid.compat import text_, bytes_
class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
    def setUp(self):
        from pyramid.interfaces import IDebugLogger
        self.config = testing.setUp()
        self.config.registry.registerUtility(self, IDebugLogger)
        self.messages = []
@@ -21,9 +20,11 @@
    def _makeOne(self, userid=None, callback=None):
        from pyramid.authentication import CallbackAuthenticationPolicy
        class MyAuthenticationPolicy(CallbackAuthenticationPolicy):
            def unauthenticated_userid(self, request):
                return userid
        policy = MyAuthenticationPolicy()
        policy.debug = True
        policy.callback = callback
@@ -38,7 +39,8 @@
            self.messages[0],
            'tests.test_authentication.MyAuthenticationPolicy.'
            'authenticated_userid: call to unauthenticated_userid returned '
            'None; returning None')
            'None; returning None',
        )
    def test_authenticated_userid_no_callback(self):
        request = DummyRequest(registry=self.config.registry)
@@ -47,14 +49,17 @@
        self.assertEqual(len(self.messages), 1)
        self.assertEqual(
            self.messages[0],
             "tests.test_authentication.MyAuthenticationPolicy."
            "tests.test_authentication.MyAuthenticationPolicy."
            "authenticated_userid: there was no groupfinder callback; "
            "returning 'fred'")
            "returning 'fred'",
        )
    def test_authenticated_userid_with_callback_fail(self):
        request = DummyRequest(registry=self.config.registry)
        def callback(userid, request):
            return None
        policy = self._makeOne(userid='fred', callback=callback)
        self.assertEqual(policy.authenticated_userid(request), None)
        self.assertEqual(len(self.messages), 1)
@@ -62,12 +67,15 @@
            self.messages[0],
            'tests.test_authentication.MyAuthenticationPolicy.'
            'authenticated_userid: groupfinder callback returned None; '
            'returning None')
            'returning None',
        )
    def test_authenticated_userid_with_callback_success(self):
        request = DummyRequest(registry=self.config.registry)
        def callback(userid, request):
            return []
        policy = self._makeOne(userid='fred', callback=callback)
        self.assertEqual(policy.authenticated_userid(request), 'fred')
        self.assertEqual(len(self.messages), 1)
@@ -75,7 +83,8 @@
            self.messages[0],
            "tests.test_authentication.MyAuthenticationPolicy."
            "authenticated_userid: groupfinder callback returned []; "
            "returning 'fred'")
            "returning 'fred'",
        )
    def test_authenticated_userid_fails_cleaning_as_Authenticated(self):
        request = DummyRequest(registry=self.config.registry)
@@ -87,7 +96,8 @@
            "tests.test_authentication.MyAuthenticationPolicy."
            "authenticated_userid: use of userid 'system.Authenticated' is "
            "disallowed by any built-in Pyramid security policy, returning "
            "None")
            "None",
        )
    def test_authenticated_userid_fails_cleaning_as_Everyone(self):
        request = DummyRequest(registry=self.config.registry)
@@ -99,107 +109,127 @@
            "tests.test_authentication.MyAuthenticationPolicy."
            "authenticated_userid: use of userid 'system.Everyone' is "
            "disallowed by any built-in Pyramid security policy, returning "
            "None")
            "None",
        )
    def test_effective_principals_no_unauthenticated_userid(self):
        request = DummyRequest(registry=self.config.registry)
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request),
                         ['system.Everyone'])
        self.assertEqual(
            policy.effective_principals(request), ['system.Everyone']
        )
        self.assertEqual(len(self.messages), 1)
        self.assertEqual(
            self.messages[0],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: unauthenticated_userid returned None; "
            "returning ['system.Everyone']")
            "returning ['system.Everyone']",
        )
    def test_effective_principals_no_callback(self):
        request = DummyRequest(registry=self.config.registry)
        policy = self._makeOne(userid='fred')
        self.assertEqual(
            policy.effective_principals(request),
            ['system.Everyone', 'system.Authenticated', 'fred'])
            ['system.Everyone', 'system.Authenticated', 'fred'],
        )
        self.assertEqual(len(self.messages), 2)
        self.assertEqual(
            self.messages[0],
            'tests.test_authentication.MyAuthenticationPolicy.'
            'effective_principals: groupfinder callback is None, so groups '
            'is []')
            'is []',
        )
        self.assertEqual(
            self.messages[1],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: returning effective principals: "
            "['system.Everyone', 'system.Authenticated', 'fred']")
            "['system.Everyone', 'system.Authenticated', 'fred']",
        )
    def test_effective_principals_with_callback_fail(self):
        request = DummyRequest(registry=self.config.registry)
        def callback(userid, request):
            return None
        policy = self._makeOne(userid='fred', callback=callback)
        self.assertEqual(
            policy.effective_principals(request), ['system.Everyone'])
            policy.effective_principals(request), ['system.Everyone']
        )
        self.assertEqual(len(self.messages), 2)
        self.assertEqual(
            self.messages[0],
            'tests.test_authentication.MyAuthenticationPolicy.'
            'effective_principals: groupfinder callback returned None as '
            'groups')
            'groups',
        )
        self.assertEqual(
            self.messages[1],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: returning effective principals: "
            "['system.Everyone']")
            "['system.Everyone']",
        )
    def test_effective_principals_with_callback_success(self):
        request = DummyRequest(registry=self.config.registry)
        def callback(userid, request):
            return []
        policy = self._makeOne(userid='fred', callback=callback)
        self.assertEqual(
            policy.effective_principals(request),
            ['system.Everyone', 'system.Authenticated', 'fred'])
            ['system.Everyone', 'system.Authenticated', 'fred'],
        )
        self.assertEqual(len(self.messages), 2)
        self.assertEqual(
            self.messages[0],
            'tests.test_authentication.MyAuthenticationPolicy.'
            'effective_principals: groupfinder callback returned [] as groups')
            'effective_principals: groupfinder callback returned [] as groups',
        )
        self.assertEqual(
            self.messages[1],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: returning effective principals: "
            "['system.Everyone', 'system.Authenticated', 'fred']")
            "['system.Everyone', 'system.Authenticated', 'fred']",
        )
    def test_effective_principals_with_unclean_principal_Authenticated(self):
        request = DummyRequest(registry=self.config.registry)
        policy = self._makeOne(userid='system.Authenticated')
        self.assertEqual(
            policy.effective_principals(request),
            ['system.Everyone'])
            policy.effective_principals(request), ['system.Everyone']
        )
        self.assertEqual(len(self.messages), 1)
        self.assertEqual(
            self.messages[0],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: unauthenticated_userid returned disallowed "
            "'system.Authenticated'; returning ['system.Everyone'] as if it "
            "was None")
            "was None",
        )
    def test_effective_principals_with_unclean_principal_Everyone(self):
        request = DummyRequest(registry=self.config.registry)
        policy = self._makeOne(userid='system.Everyone')
        self.assertEqual(
            policy.effective_principals(request),
            ['system.Everyone'])
            policy.effective_principals(request), ['system.Everyone']
        )
        self.assertEqual(len(self.messages), 1)
        self.assertEqual(
            self.messages[0],
            "tests.test_authentication.MyAuthenticationPolicy."
            "effective_principals: unauthenticated_userid returned disallowed "
            "'system.Everyone'; returning ['system.Everyone'] as if it "
            "was None")
            "was None",
        )
class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import RepozeWho1AuthenticationPolicy
        return RepozeWho1AuthenticationPolicy
    def _makeOne(self, identifier_name='auth_tkt', callback=None):
@@ -208,11 +238,13 @@
    def test_class_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthenticationPolicy
        verifyClass(IAuthenticationPolicy, self._getTargetClass())
    def test_instance_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IAuthenticationPolicy
        verifyObject(IAuthenticationPolicy, self._makeOne())
    def test_unauthenticated_userid_returns_None(self):
@@ -222,7 +254,8 @@
    def test_unauthenticated_userid(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred'}})
            {'repoze.who.identity': {'repoze.who.userid': 'fred'}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.unauthenticated_userid(request), 'fred')
@@ -233,48 +266,61 @@
    def test_authenticated_userid(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred'}})
            {'repoze.who.identity': {'repoze.who.userid': 'fred'}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.authenticated_userid(request), 'fred')
    def test_authenticated_userid_repoze_who_userid_is_None(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':None}})
            {'repoze.who.identity': {'repoze.who.userid': None}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid_with_callback_returns_None(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred'}})
            {'repoze.who.identity': {'repoze.who.userid': 'fred'}}
        )
        def callback(identity, request):
            return None
        policy = self._makeOne(callback=callback)
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid_with_callback_returns_something(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred'}})
            {'repoze.who.identity': {'repoze.who.userid': 'fred'}}
        )
        def callback(identity, request):
            return ['agroup']
        policy = self._makeOne(callback=callback)
        self.assertEqual(policy.authenticated_userid(request), 'fred')
    def test_authenticated_userid_unclean_principal_Authenticated(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}}
            )
            {
                'repoze.who.identity': {
                    'repoze.who.userid': 'system.Authenticated'
                }
            }
        )
        policy = self._makeOne()
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid_unclean_principal_Everyone(self):
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}}
            )
            {'repoze.who.identity': {'repoze.who.userid': 'system.Everyone'}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_effective_principals_None(self):
        from pyramid.security import Everyone
        request = DummyRequest({})
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
@@ -282,56 +328,86 @@
    def test_effective_principals_userid_only(self):
        from pyramid.security import Everyone
        from pyramid.security import Authenticated
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred'}})
            {'repoze.who.identity': {'repoze.who.userid': 'fred'}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request),
                         [Everyone, Authenticated, 'fred'])
        self.assertEqual(
            policy.effective_principals(request),
            [Everyone, Authenticated, 'fred'],
        )
    def test_effective_principals_userid_and_groups(self):
        from pyramid.security import Everyone
        from pyramid.security import Authenticated
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred',
                                    'groups':['quux', 'biz']}})
            {
                'repoze.who.identity': {
                    'repoze.who.userid': 'fred',
                    'groups': ['quux', 'biz'],
                }
            }
        )
        def callback(identity, request):
            return identity['groups']
        policy = self._makeOne(callback=callback)
        self.assertEqual(policy.effective_principals(request),
                         [Everyone, Authenticated, 'fred', 'quux', 'biz'])
        self.assertEqual(
            policy.effective_principals(request),
            [Everyone, Authenticated, 'fred', 'quux', 'biz'],
        )
    def test_effective_principals_userid_callback_returns_None(self):
        from pyramid.security import Everyone
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'fred',
                                    'groups':['quux', 'biz']}})
            {
                'repoze.who.identity': {
                    'repoze.who.userid': 'fred',
                    'groups': ['quux', 'biz'],
                }
            }
        )
        def callback(identity, request):
            return None
        policy = self._makeOne(callback=callback)
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals_repoze_who_userid_is_None(self):
        from pyramid.security import Everyone
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':None}}
            )
            {'repoze.who.identity': {'repoze.who.userid': None}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals_repoze_who_userid_is_unclean_Everyone(self):
        from pyramid.security import Everyone
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}}
            )
            {'repoze.who.identity': {'repoze.who.userid': 'system.Everyone'}}
        )
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals_repoze_who_userid_is_unclean_Authenticated(
        self):
        self
    ):
        from pyramid.security import Everyone
        request = DummyRequest(
            {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}}
            )
            {
                'repoze.who.identity': {
                    'repoze.who.userid': 'system.Authenticated'
                }
            }
        )
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
@@ -343,20 +419,20 @@
    def test_remember(self):
        authtkt = DummyWhoPlugin()
        request = DummyRequest(
            {'repoze.who.plugins':{'auth_tkt':authtkt}})
        request = DummyRequest({'repoze.who.plugins': {'auth_tkt': authtkt}})
        policy = self._makeOne()
        result = policy.remember(request, 'fred')
        self.assertEqual(result[0], request.environ)
        self.assertEqual(result[1], {'repoze.who.userid':'fred'})
        self.assertEqual(result[1], {'repoze.who.userid': 'fred'})
    def test_remember_kwargs(self):
        authtkt = DummyWhoPlugin()
        request = DummyRequest(
            {'repoze.who.plugins':{'auth_tkt':authtkt}})
        request = DummyRequest({'repoze.who.plugins': {'auth_tkt': authtkt}})
        policy = self._makeOne()
        result = policy.remember(request, 'fred', max_age=23)
        self.assertEqual(result[1], {'repoze.who.userid':'fred', 'max_age': 23})
        self.assertEqual(
            result[1], {'repoze.who.userid': 'fred', 'max_age': 23}
        )
    def test_forget_no_plugins(self):
        request = DummyRequest({})
@@ -367,17 +443,21 @@
    def test_forget(self):
        authtkt = DummyWhoPlugin()
        request = DummyRequest(
            {'repoze.who.plugins':{'auth_tkt':authtkt},
             'repoze.who.identity':{'repoze.who.userid':'fred'},
             })
            {
                'repoze.who.plugins': {'auth_tkt': authtkt},
                'repoze.who.identity': {'repoze.who.userid': 'fred'},
            }
        )
        policy = self._makeOne()
        result = policy.forget(request)
        self.assertEqual(result[0], request.environ)
        self.assertEqual(result[1], request.environ['repoze.who.identity'])
class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import RemoteUserAuthenticationPolicy
        return RemoteUserAuthenticationPolicy
    def _makeOne(self, environ_key='REMOTE_USER', callback=None):
@@ -386,11 +466,13 @@
    def test_class_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthenticationPolicy
        verifyClass(IAuthenticationPolicy, self._getTargetClass())
    def test_instance_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IAuthenticationPolicy
        verifyObject(IAuthenticationPolicy, self._makeOne())
    def test_unauthenticated_userid_returns_None(self):
@@ -399,7 +481,7 @@
        self.assertEqual(policy.unauthenticated_userid(request), None)
    def test_unauthenticated_userid(self):
        request = DummyRequest({'REMOTE_USER':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne()
        self.assertEqual(policy.unauthenticated_userid(request), 'fred')
@@ -409,12 +491,13 @@
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid(self):
        request = DummyRequest({'REMOTE_USER':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne()
        self.assertEqual(policy.authenticated_userid(request), 'fred')
    def test_effective_principals_None(self):
        from pyramid.security import Everyone
        request = DummyRequest({})
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
@@ -422,26 +505,31 @@
    def test_effective_principals(self):
        from pyramid.security import Everyone
        from pyramid.security import Authenticated
        request = DummyRequest({'REMOTE_USER':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request),
                         [Everyone, Authenticated, 'fred'])
        self.assertEqual(
            policy.effective_principals(request),
            [Everyone, Authenticated, 'fred'],
        )
    def test_remember(self):
        request = DummyRequest({'REMOTE_USER':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne()
        result = policy.remember(request, 'fred')
        self.assertEqual(result, [])
    def test_forget(self):
        request = DummyRequest({'REMOTE_USER':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne()
        result = policy.forget(request)
        self.assertEqual(result, [])
class TestAuthTktAuthenticationPolicy(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import AuthTktAuthenticationPolicy
        return AuthTktAuthenticationPolicy
    def _makeOne(self, callback, cookieidentity, **kw):
@@ -460,10 +548,16 @@
    def test_allargs(self):
        # pass all known args
        inst = self._getTargetClass()(
            'secret', callback=None, cookie_name=None, secure=False,
            include_ip=False, timeout=None, reissue_time=None,
            hashalg='sha512', samesite=None,
            )
            'secret',
            callback=None,
            cookie_name=None,
            secure=False,
            include_ip=False,
            timeout=None,
            reissue_time=None,
            hashalg='sha512',
            samesite=None,
        )
        self.assertEqual(inst.callback, None)
    def test_hashalg_override(self):
@@ -477,8 +571,8 @@
        self.assertEqual(policy.unauthenticated_userid(request), None)
    def test_unauthenticated_userid(self):
        request = DummyRequest({'REMOTE_USER':'fred'})
        policy = self._makeOne(None, {'userid':'fred'})
        request = DummyRequest({'REMOTE_USER': 'fred'})
        policy = self._makeOne(None, {'userid': 'fred'})
        self.assertEqual(policy.unauthenticated_userid(request), 'fred')
    def test_authenticated_userid_no_cookie_identity(self):
@@ -488,41 +582,54 @@
    def test_authenticated_userid_callback_returns_None(self):
        request = DummyRequest({})
        def callback(userid, request):
            return None
        policy = self._makeOne(callback, {'userid':'fred'})
        policy = self._makeOne(callback, {'userid': 'fred'})
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid(self):
        request = DummyRequest({})
        def callback(userid, request):
            return True
        policy = self._makeOne(callback, {'userid':'fred'})
        policy = self._makeOne(callback, {'userid': 'fred'})
        self.assertEqual(policy.authenticated_userid(request), 'fred')
    def test_effective_principals_no_cookie_identity(self):
        from pyramid.security import Everyone
        request = DummyRequest({})
        policy = self._makeOne(None, None)
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals_callback_returns_None(self):
        from pyramid.security import Everyone
        request = DummyRequest({})
        def callback(userid, request):
            return None
        policy = self._makeOne(callback, {'userid':'fred'})
        policy = self._makeOne(callback, {'userid': 'fred'})
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals(self):
        from pyramid.security import Everyone
        from pyramid.security import Authenticated
        request = DummyRequest({})
        def callback(userid, request):
            return ['group.foo']
        policy = self._makeOne(callback, {'userid':'fred'})
        self.assertEqual(policy.effective_principals(request),
                             [Everyone, Authenticated, 'fred', 'group.foo'])
        policy = self._makeOne(callback, {'userid': 'fred'})
        self.assertEqual(
            policy.effective_principals(request),
            [Everyone, Authenticated, 'fred', 'group.foo'],
        )
    def test_remember(self):
        request = DummyRequest({})
@@ -534,7 +641,7 @@
        request = DummyRequest({})
        policy = self._makeOne(None, None)
        result = policy.remember(request, 'fred', a=1, b=2)
        self.assertEqual(policy.cookie.kw, {'a':1, 'b':2})
        self.assertEqual(policy.cookie.kw, {'a': 1, 'b': 2})
        self.assertEqual(result, [])
    def test_forget(self):
@@ -546,16 +653,20 @@
    def test_class_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthenticationPolicy
        verifyClass(IAuthenticationPolicy, self._getTargetClass())
    def test_instance_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IAuthenticationPolicy
        verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
class TestAuthTktCookieHelper(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import AuthTktCookieHelper
        return AuthTktCookieHelper
    def _makeOne(self, *arg, **kw):
@@ -570,7 +681,7 @@
        return helper
    def _makeRequest(self, cookie=None, ipv6=False):
        environ = {'wsgi.version': (1,0)}
        environ = {'wsgi.version': (1, 0)}
        if ipv6 is False:
            environ['REMOTE_ADDR'] = '1.1.1.1'
@@ -588,7 +699,7 @@
        return D
    def _parseHeaders(self, headers):
        return [ self._parseHeader(header) for header in headers ]
        return [self._parseHeader(header) for header in headers]
    def _parseHeader(self, header):
        cookie = self._parseCookie(header[1])
@@ -596,18 +707,25 @@
    def _parseCookie(self, cookie):
        from pyramid.compat import SimpleCookie
        cookies = SimpleCookie()
        cookies.load(cookie)
        return cookies.get('auth_tkt')
    def test_init_cookie_str_reissue_invalid(self):
        self.assertRaises(ValueError, self._makeOne, 'secret', reissue_time='invalid value')
        self.assertRaises(
            ValueError, self._makeOne, 'secret', reissue_time='invalid value'
        )
    def test_init_cookie_str_timeout_invalid(self):
        self.assertRaises(ValueError, self._makeOne, 'secret', timeout='invalid value')
        self.assertRaises(
            ValueError, self._makeOne, 'secret', timeout='invalid value'
        )
    def test_init_cookie_str_max_age_invalid(self):
        self.assertRaises(ValueError, self._makeOne, 'secret', max_age='invalid value')
        self.assertRaises(
            ValueError, self._makeOne, 'secret', max_age='invalid value'
        )
    def test_identify_nocookie(self):
        helper = self._makeOne('secret')
@@ -635,8 +753,8 @@
        self.assertEqual(helper.auth_tkt.secret, 'secret')
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], '')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_include_ipv6(self):
        helper = self._makeOne('secret', include_ip=True)
@@ -652,8 +770,8 @@
        self.assertEqual(helper.auth_tkt.secret, 'secret')
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], '')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_dont_include_ip(self):
        helper = self._makeOne('secret', include_ip=False)
@@ -669,8 +787,8 @@
        self.assertEqual(helper.auth_tkt.secret, 'secret')
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], '')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_int_useridtype(self):
        helper = self._makeOne('secret', include_ip=False)
@@ -685,8 +803,8 @@
        self.assertEqual(result['timestamp'], 0)
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:int')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:int')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_nonuseridtype_user_data(self):
        helper = self._makeOne('secret', include_ip=False)
@@ -701,8 +819,8 @@
        self.assertEqual(result['timestamp'], 0)
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'bogus:int')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], 'bogus:int')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_unknown_useridtype(self):
        helper = self._makeOne('secret', include_ip=False)
@@ -717,11 +835,12 @@
        self.assertEqual(result['timestamp'], 0)
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:unknown')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:unknown')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_b64str_useridtype(self):
        from base64 import b64encode
        helper = self._makeOne('secret', include_ip=False)
        helper.auth_tkt.userid = b64encode(b'encoded').strip()
        helper.auth_tkt.user_data = 'userid_type:b64str'
@@ -734,11 +853,12 @@
        self.assertEqual(result['timestamp'], 0)
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:b64str')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:b64str')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_good_cookie_b64unicode_useridtype(self):
        from base64 import b64encode
        helper = self._makeOne('secret', include_ip=False)
        helper.auth_tkt.userid = b64encode(b'\xc3\xa9ncoded').strip()
        helper.auth_tkt.user_data = 'userid_type:b64unicode'
@@ -751,8 +871,8 @@
        self.assertEqual(result['timestamp'], 0)
        environ = request.environ
        self.assertEqual(environ['REMOTE_USER_TOKENS'], ())
        self.assertEqual(environ['REMOTE_USER_DATA'],'userid_type:b64unicode')
        self.assertEqual(environ['AUTH_TYPE'],'cookie')
        self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:b64unicode')
        self.assertEqual(environ['AUTH_TYPE'], 'cookie')
    def test_identify_bad_cookie(self):
        helper = self._makeOne('secret', include_ip=True)
@@ -771,22 +891,24 @@
    def test_identify_cookie_timeout_aged(self):
        import time
        helper = self._makeOne('secret', timeout=10)
        now = time.time()
        helper.auth_tkt.timestamp = now - 1
        helper.now = now + 10
        helper.auth_tkt.tokens = (text_('a'), )
        helper.auth_tkt.tokens = (text_('a'),)
        request = self._makeRequest('bogus')
        result = helper.identify(request)
        self.assertFalse(result)
    def test_identify_cookie_reissue(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=0)
        now = time.time()
        helper.auth_tkt.timestamp = now
        helper.now = now + 1
        helper.auth_tkt.tokens = (text_('a'), )
        helper.auth_tkt.tokens = (text_('a'),)
        request = self._makeRequest('bogus')
        result = helper.identify(request)
        self.assertTrue(result)
@@ -798,11 +920,12 @@
    def test_identify_cookie_str_reissue(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time='0')
        now = time.time()
        helper.auth_tkt.timestamp = now
        helper.now = now + 1
        helper.auth_tkt.tokens = (text_('a'), )
        helper.auth_tkt.tokens = (text_('a'),)
        request = self._makeRequest('bogus')
        result = helper.identify(request)
        self.assertTrue(result)
@@ -814,6 +937,7 @@
    def test_identify_cookie_reissue_already_reissued_this_request(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=0)
        now = time.time()
        helper.auth_tkt.timestamp = now
@@ -826,6 +950,7 @@
    def test_identify_cookie_reissue_notyet(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=10)
        now = time.time()
        helper.auth_tkt.timestamp = now
@@ -837,6 +962,7 @@
    def test_identify_cookie_reissue_revoked_by_forget(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=0)
        now = time.time()
        helper.auth_tkt.timestamp = now
@@ -854,6 +980,7 @@
    def test_identify_cookie_reissue_revoked_by_remember(self):
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=0)
        now = time.time()
        helper.auth_tkt.timestamp = now
@@ -872,6 +999,7 @@
    def test_identify_cookie_reissue_with_tokens_default(self):
        # see https://github.com/Pylons/pyramid/issues#issue/108
        import time
        helper = self._makeOne('secret', timeout=10, reissue_time=0)
        auth_tkt = DummyAuthTktModule(tokens=[''])
        helper.auth_tkt = auth_tkt
@@ -902,13 +1030,15 @@
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[1][1].endswith('; Domain=localhost; Path=/; SameSite=Lax')
        )
        self.assertTrue(result[1][1].startswith('auth_tkt='))
        self.assertEqual(result[2][0], 'Set-Cookie')
        self.assertTrue(result[2][1].endswith(
            '; Domain=.localhost; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[2][1].endswith('; Domain=.localhost; Path=/; SameSite=Lax')
        )
        self.assertTrue(result[2][1].startswith('auth_tkt='))
    def test_remember_nondefault_samesite(self):
@@ -922,13 +1052,19 @@
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/; SameSite=Strict'))
        self.assertTrue(
            result[1][1].endswith(
                '; Domain=localhost; Path=/; SameSite=Strict'
            )
        )
        self.assertTrue(result[1][1].startswith('auth_tkt='))
        self.assertEqual(result[2][0], 'Set-Cookie')
        self.assertTrue(result[2][1].endswith(
            '; Domain=.localhost; Path=/; SameSite=Strict'))
        self.assertTrue(
            result[2][1].endswith(
                '; Domain=.localhost; Path=/; SameSite=Strict'
            )
        )
        self.assertTrue(result[2][1].startswith('auth_tkt='))
    def test_remember_None_samesite(self):
@@ -938,17 +1074,15 @@
        self.assertEqual(len(result), 3)
        self.assertEqual(result[0][0], 'Set-Cookie')
        self.assertTrue(result[0][1].endswith('; Path=/')) # no samesite
        self.assertTrue(result[0][1].endswith('; Path=/'))  # no samesite
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/'))
        self.assertTrue(result[1][1].endswith('; Domain=localhost; Path=/'))
        self.assertTrue(result[1][1].startswith('auth_tkt='))
        self.assertEqual(result[2][0], 'Set-Cookie')
        self.assertTrue(result[2][1].endswith(
            '; Domain=.localhost; Path=/'))
        self.assertTrue(result[2][1].endswith('; Domain=.localhost; Path=/'))
        self.assertTrue(result[2][1].startswith('auth_tkt='))
    def test_remember_include_ip(self):
@@ -962,35 +1096,45 @@
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[1][1].endswith('; Domain=localhost; Path=/; SameSite=Lax')
        )
        self.assertTrue(result[1][1].startswith('auth_tkt='))
        self.assertEqual(result[2][0], 'Set-Cookie')
        self.assertTrue(result[2][1].endswith(
            '; Domain=.localhost; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[2][1].endswith('; Domain=.localhost; Path=/; SameSite=Lax')
        )
        self.assertTrue(result[2][1].startswith('auth_tkt='))
    def test_remember_path(self):
        helper = self._makeOne('secret', include_ip=True,
                               path="/cgi-bin/app.cgi/")
        helper = self._makeOne(
            'secret', include_ip=True, path="/cgi-bin/app.cgi/"
        )
        request = self._makeRequest()
        result = helper.remember(request, 'other')
        self.assertEqual(len(result), 3)
        self.assertEqual(result[0][0], 'Set-Cookie')
        self.assertTrue(result[0][1].endswith(
            '; Path=/cgi-bin/app.cgi/; SameSite=Lax'))
        self.assertTrue(
            result[0][1].endswith('; Path=/cgi-bin/app.cgi/; SameSite=Lax')
        )
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/cgi-bin/app.cgi/; SameSite=Lax'))
        self.assertTrue(
            result[1][1].endswith(
                '; Domain=localhost; Path=/cgi-bin/app.cgi/; SameSite=Lax'
            )
        )
        self.assertTrue(result[1][1].startswith('auth_tkt='))
        self.assertEqual(result[2][0], 'Set-Cookie')
        self.assertTrue(result[2][1].endswith(
            '; Domain=.localhost; Path=/cgi-bin/app.cgi/; SameSite=Lax'))
        self.assertTrue(
            result[2][1].endswith(
                '; Domain=.localhost; Path=/cgi-bin/app.cgi/; SameSite=Lax'
            )
        )
        self.assertTrue(result[2][1].startswith('auth_tkt='))
    def test_remember_http_only(self):
@@ -1040,8 +1184,9 @@
        self.assertTrue(result[0][1].startswith('auth_tkt='))
        self.assertEqual(result[1][0], 'Set-Cookie')
        self.assertTrue(result[1][1].endswith(
            '; Domain=localhost; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[1][1].endswith('; Domain=localhost; Path=/; SameSite=Lax')
        )
        self.assertTrue(result[1][1].startswith('auth_tkt='))
    def test_remember_parent_domain(self):
@@ -1052,8 +1197,11 @@
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0][0], 'Set-Cookie')
        self.assertTrue(result[0][1].endswith(
            '; Domain=.example.com; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[0][1].endswith(
                '; Domain=.example.com; Path=/; SameSite=Lax'
            )
        )
        self.assertTrue(result[0][1].startswith('auth_tkt='))
    def test_remember_parent_domain_supercedes_wild_domain(self):
@@ -1062,8 +1210,11 @@
        request.domain = 'www.example.com'
        result = helper.remember(request, 'other')
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0][1].endswith(
            '; Domain=.example.com; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[0][1].endswith(
                '; Domain=.example.com; Path=/; SameSite=Lax'
            )
        )
    def test_remember_explicit_domain(self):
        helper = self._makeOne('secret', domain='pyramid.bazinga')
@@ -1073,30 +1224,42 @@
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0][0], 'Set-Cookie')
        self.assertTrue(result[0][1].endswith(
                '; Domain=pyramid.bazinga; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[0][1].endswith(
                '; Domain=pyramid.bazinga; Path=/; SameSite=Lax'
            )
        )
        self.assertTrue(result[0][1].startswith('auth_tkt='))
    def test_remember_domain_supercedes_parent_and_wild_domain(self):
        helper = self._makeOne('secret', domain='pyramid.bazinga',
                parent_domain=True, wild_domain=True)
        helper = self._makeOne(
            'secret',
            domain='pyramid.bazinga',
            parent_domain=True,
            wild_domain=True,
        )
        request = self._makeRequest()
        request.domain = 'www.example.com'
        result = helper.remember(request, 'other')
        self.assertEqual(len(result), 1)
        self.assertTrue(result[0][1].endswith(
                '; Domain=pyramid.bazinga; Path=/; SameSite=Lax'))
        self.assertTrue(
            result[0][1].endswith(
                '; Domain=pyramid.bazinga; Path=/; SameSite=Lax'
            )
        )
    def test_remember_binary_userid(self):
        import base64
        helper = self._makeOne('secret')
        request = self._makeRequest()
        result = helper.remember(request, b'userid')
        values = self._parseHeaders(result)
        self.assertEqual(len(result), 3)
        val = self._cookieValue(values[0])
        self.assertEqual(val['userid'],
                         text_(base64.b64encode(b'userid').strip()))
        self.assertEqual(
            val['userid'], text_(base64.b64encode(b'userid').strip())
        )
        self.assertEqual(val['user_data'], 'userid_type:b64str')
    def test_remember_int_userid(self):
@@ -1111,6 +1274,7 @@
    def test_remember_long_userid(self):
        from pyramid.compat import long
        helper = self._makeOne('secret')
        request = self._makeRequest()
        result = helper.remember(request, long(1))
@@ -1122,6 +1286,7 @@
    def test_remember_unicode_userid(self):
        import base64
        helper = self._makeOne('secret')
        request = self._makeRequest()
        userid = text_(b'\xc2\xa9', 'utf-8')
@@ -1129,8 +1294,9 @@
        values = self._parseHeaders(result)
        self.assertEqual(len(result), 3)
        val = self._cookieValue(values[0])
        self.assertEqual(val['userid'],
                         text_(base64.b64encode(userid.encode('utf-8'))))
        self.assertEqual(
            val['userid'], text_(base64.b64encode(userid.encode('utf-8')))
        )
        self.assertEqual(val['user_data'], 'userid_type:b64unicode')
    def test_remember_insane_userid(self):
@@ -1169,7 +1335,13 @@
    def test_remember_str_max_age_invalid(self):
        helper = self._makeOne('secret')
        request = self._makeRequest()
        self.assertRaises(ValueError, helper.remember, request, 'userid', max_age='invalid value')
        self.assertRaises(
            ValueError,
            helper.remember,
            request,
            'userid',
            max_age='invalid value',
        )
    def test_remember_tokens(self):
        helper = self._makeOne('secret')
@@ -1194,18 +1366,24 @@
        self.assertEqual(result[0][0], 'Set-Cookie')
        cookieval = result[0][1]
        self.assertTrue('SameSite=Strict' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Strict' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
        self.assertEqual(result[1][0], 'Set-Cookie')
        cookieval = result[1][1]
        self.assertTrue('SameSite=Strict' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Strict' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
        self.assertEqual(result[2][0], 'Set-Cookie')
        cookieval = result[2][1]
        self.assertTrue('SameSite=Strict' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Strict' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
    def test_remember_samesite_default(self):
        helper = self._makeOne('secret')
@@ -1215,18 +1393,24 @@
        self.assertEqual(result[0][0], 'Set-Cookie')
        cookieval = result[0][1]
        self.assertTrue('SameSite=Lax' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Lax' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
        self.assertEqual(result[1][0], 'Set-Cookie')
        cookieval = result[1][1]
        self.assertTrue('SameSite=Lax' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Lax' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
        self.assertEqual(result[2][0], 'Set-Cookie')
        cookieval = result[2][1]
        self.assertTrue('SameSite=Lax' in
                        [x.strip() for x in cookieval.split(';')], cookieval)
        self.assertTrue(
            'SameSite=Lax' in [x.strip() for x in cookieval.split(';')],
            cookieval,
        )
    def test_remember_unicode_but_ascii_token(self):
        helper = self._makeOne('secret')
@@ -1240,16 +1424,19 @@
        helper = self._makeOne('secret')
        request = self._makeRequest()
        la = text_(b'La Pe\xc3\xb1a', 'utf-8')
        self.assertRaises(ValueError, helper.remember, request, 'other',
                          tokens=(la,))
        self.assertRaises(
            ValueError, helper.remember, request, 'other', tokens=(la,)
        )
    def test_remember_invalid_token_format(self):
        helper = self._makeOne('secret')
        request = self._makeRequest()
        self.assertRaises(ValueError, helper.remember, request, 'other',
                          tokens=('foo bar',))
        self.assertRaises(ValueError, helper.remember, request, 'other',
                          tokens=('1bar',))
        self.assertRaises(
            ValueError, helper.remember, request, 'other', tokens=('foo bar',)
        )
        self.assertRaises(
            ValueError, helper.remember, request, 'other', tokens=('1bar',)
        )
    def test_forget(self):
        helper = self._makeOne('secret')
@@ -1261,26 +1448,28 @@
        self.assertEqual(
            value,
            'auth_tkt=; Max-Age=0; Path=/; '
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax'
            )
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax',
        )
        name, value = headers[1]
        self.assertEqual(name, 'Set-Cookie')
        self.assertEqual(
            value,
            'auth_tkt=; Domain=localhost; Max-Age=0; Path=/; '
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax'
            )
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax',
        )
        name, value = headers[2]
        self.assertEqual(name, 'Set-Cookie')
        self.assertEqual(
            value,
            'auth_tkt=; Domain=.localhost; Max-Age=0; Path=/; '
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax'
            )
            'expires=Wed, 31-Dec-97 23:59:59 GMT; SameSite=Lax',
        )
class TestAuthTicket(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.authentication import AuthTicket
        return AuthTicket(*arg, **kw)
    def test_ctor_with_tokens(self):
@@ -1297,37 +1486,53 @@
        self.assertEqual(result, '126fd6224912187ee9ffa80e0b81420c')
    def test_digest_sha512(self):
        ticket = self._makeOne('secret', 'userid', '0.0.0.0',
                               time=10, hashalg='sha512')
        ticket = self._makeOne(
            'secret', 'userid', '0.0.0.0', time=10, hashalg='sha512'
        )
        result = ticket.digest()
        self.assertEqual(result, '74770b2e0d5b1a54c2a466ec567a40f7d7823576aa49'\
                                 '3c65fc3445e9b44097f4a80410319ef8cb256a2e60b9'\
                                 'c2002e48a9e33a3e8ee4379352c04ef96d2cb278')
        self.assertEqual(
            result,
            '74770b2e0d5b1a54c2a466ec567a40f7d7823576aa49'
            '3c65fc3445e9b44097f4a80410319ef8cb256a2e60b9'
            'c2002e48a9e33a3e8ee4379352c04ef96d2cb278',
        )
    def test_cookie_value(self):
        ticket = self._makeOne('secret', 'userid', '0.0.0.0', time=10,
                               tokens=('a', 'b'))
        ticket = self._makeOne(
            'secret', 'userid', '0.0.0.0', time=10, tokens=('a', 'b')
        )
        result = ticket.cookie_value()
        self.assertEqual(result,
                         '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!')
        self.assertEqual(
            result, '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!'
        )
    def test_ipv4(self):
        ticket = self._makeOne('secret', 'userid', '198.51.100.1',
                               time=10, hashalg='sha256')
        ticket = self._makeOne(
            'secret', 'userid', '198.51.100.1', time=10, hashalg='sha256'
        )
        result = ticket.cookie_value()
        self.assertEqual(result, 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b'\
                                 '798400ecdade8d76c530000000auserid!')
        self.assertEqual(
            result,
            'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b'
            '798400ecdade8d76c530000000auserid!',
        )
    def test_ipv6(self):
        ticket = self._makeOne('secret', 'userid', '2001:db8::1',
                               time=10, hashalg='sha256')
        ticket = self._makeOne(
            'secret', 'userid', '2001:db8::1', time=10, hashalg='sha256'
        )
        result = ticket.cookie_value()
        self.assertEqual(result, 'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c8'\
                                 '5becf8760cd7a2fa4910000000auserid!')
        self.assertEqual(
            result,
            'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c8'
            '5becf8760cd7a2fa4910000000auserid!',
        )
class TestBadTicket(unittest.TestCase):
    def _makeOne(self, msg, expected=None):
        from pyramid.authentication import BadTicket
        return BadTicket(msg, expected)
    def test_it(self):
@@ -1335,14 +1540,19 @@
        self.assertEqual(exc.expected, True)
        self.assertTrue(isinstance(exc, Exception))
class Test_parse_ticket(unittest.TestCase):
    def _callFUT(self, secret, ticket, ip, hashalg='md5'):
        from pyramid.authentication import parse_ticket
        return parse_ticket(secret, ticket, ip, hashalg)
    def _assertRaisesBadTicket(self, secret, ticket, ip, hashalg='md5'):
        from pyramid.authentication import BadTicket
        self.assertRaises(BadTicket,self._callFUT, secret, ticket, ip, hashalg)
        self.assertRaises(
            BadTicket, self._callFUT, secret, ticket, ip, hashalg
        )
    def test_bad_timestamp(self):
        ticket = 'x' * 64
@@ -1362,27 +1572,35 @@
        self.assertEqual(result, (10, 'userid', ['a', 'b'], ''))
    def test_correct_with_user_data_sha512(self):
        ticket = text_('7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1'
                       '160cab0ec0e6888faa41eba641a18522b26f19109f3ffafb769767'
                       'ba8a26d02aaeae56599a0000000auserid!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 = text_('b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecd'
                       'ade8d76c530000000auserid!')
        ticket = text_(
            'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecd'
            'ade8d76c530000000auserid!'
        )
        result = self._callFUT('secret', ticket, '198.51.100.1', 'sha256')
        self.assertEqual(result, (10, 'userid', [''], ''))
    def test_ipv6(self):
        ticket = text_('d025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760'
                       'cd7a2fa4910000000auserid!')
        ticket = text_(
            'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760'
            'cd7a2fa4910000000auserid!'
        )
        result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256')
        self.assertEqual(result, (10, 'userid', [''], ''))
class TestSessionAuthenticationPolicy(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import SessionAuthenticationPolicy
        return SessionAuthenticationPolicy
    def _makeOne(self, callback=None, prefix=''):
@@ -1391,11 +1609,13 @@
    def test_class_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthenticationPolicy
        verifyClass(IAuthenticationPolicy, self._getTargetClass())
    def test_instance_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IAuthenticationPolicy
        verifyObject(IAuthenticationPolicy, self._makeOne())
    def test_unauthenticated_userid_returns_None(self):
@@ -1404,7 +1624,7 @@
        self.assertEqual(policy.unauthenticated_userid(request), None)
    def test_unauthenticated_userid(self):
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        policy = self._makeOne()
        self.assertEqual(policy.unauthenticated_userid(request), 'fred')
@@ -1414,42 +1634,55 @@
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid_callback_returns_None(self):
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        def callback(userid, request):
            return None
        policy = self._makeOne(callback)
        self.assertEqual(policy.authenticated_userid(request), None)
    def test_authenticated_userid(self):
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        def callback(userid, request):
            return True
        policy = self._makeOne(callback)
        self.assertEqual(policy.authenticated_userid(request), 'fred')
    def test_effective_principals_no_identity(self):
        from pyramid.security import Everyone
        request = DummyRequest()
        policy = self._makeOne()
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals_callback_returns_None(self):
        from pyramid.security import Everyone
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        def callback(userid, request):
            return None
        policy = self._makeOne(callback)
        self.assertEqual(policy.effective_principals(request), [Everyone])
    def test_effective_principals(self):
        from pyramid.security import Everyone
        from pyramid.security import Authenticated
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        def callback(userid, request):
            return ['group.foo']
        policy = self._makeOne(callback)
        self.assertEqual(policy.effective_principals(request),
                         [Everyone, Authenticated, 'fred', 'group.foo'])
        self.assertEqual(
            policy.effective_principals(request),
            [Everyone, Authenticated, 'fred', 'group.foo'],
        )
    def test_remember(self):
        request = DummyRequest()
@@ -1459,7 +1692,7 @@
        self.assertEqual(result, [])
    def test_forget(self):
        request = DummyRequest(session={'userid':'fred'})
        request = DummyRequest(session={'userid': 'fred'})
        policy = self._makeOne()
        result = policy.forget(request)
        self.assertEqual(request.session.get('userid'), None)
@@ -1472,9 +1705,11 @@
        self.assertEqual(request.session.get('userid'), None)
        self.assertEqual(result, [])
class TestBasicAuthAuthenticationPolicy(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.authentication import BasicAuthAuthenticationPolicy as cls
        return cls
    def _makeOne(self, check):
@@ -1483,13 +1718,16 @@
    def test_class_implements_IAuthenticationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthenticationPolicy
        verifyClass(IAuthenticationPolicy, self._getTargetClass())
    def test_unauthenticated_userid(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
            bytes_('chrisr:password')).decode('ascii')
            bytes_('chrisr:password')
        ).decode('ascii')
        policy = self._makeOne(None)
        self.assertEqual(policy.unauthenticated_userid(request), 'chrisr')
@@ -1518,45 +1756,65 @@
    def test_authenticated_userid(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
            bytes_('chrisr:password')).decode('ascii')
            bytes_('chrisr:password')
        ).decode('ascii')
        def check(username, password, request):
            return []
        policy = self._makeOne(check)
        self.assertEqual(policy.authenticated_userid(request), 'chrisr')
    def test_authenticated_userid_utf8(self):
        import base64
        request = testing.DummyRequest()
        inputs = (b'm\xc3\xb6rk\xc3\xb6:'
                  b'm\xc3\xb6rk\xc3\xb6password').decode('utf-8')
        inputs = (
            b'm\xc3\xb6rk\xc3\xb6:' b'm\xc3\xb6rk\xc3\xb6password'
        ).decode('utf-8')
        request.headers['Authorization'] = 'Basic %s' % (
            base64.b64encode(inputs.encode('utf-8')).decode('latin-1'))
            base64.b64encode(inputs.encode('utf-8')).decode('latin-1')
        )
        def check(username, password, request):
            return []
        policy = self._makeOne(check)
        self.assertEqual(policy.authenticated_userid(request),
                         b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'))
        self.assertEqual(
            policy.authenticated_userid(request),
            b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
        )
    def test_authenticated_userid_latin1(self):
        import base64
        request = testing.DummyRequest()
        inputs = (b'm\xc3\xb6rk\xc3\xb6:'
                  b'm\xc3\xb6rk\xc3\xb6password').decode('utf-8')
        inputs = (
            b'm\xc3\xb6rk\xc3\xb6:' b'm\xc3\xb6rk\xc3\xb6password'
        ).decode('utf-8')
        request.headers['Authorization'] = 'Basic %s' % (
            base64.b64encode(inputs.encode('latin-1')).decode('latin-1'))
            base64.b64encode(inputs.encode('latin-1')).decode('latin-1')
        )
        def check(username, password, request):
            return []
        policy = self._makeOne(check)
        self.assertEqual(policy.authenticated_userid(request),
                         b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'))
        self.assertEqual(
            policy.authenticated_userid(request),
            b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
        )
    def test_unauthenticated_userid_invalid_payload(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
            bytes_('chrisrpassword')).decode('ascii')
            bytes_('chrisrpassword')
        ).decode('ascii')
        policy = self._makeOne(None)
        self.assertEqual(policy.unauthenticated_userid(request), None)
@@ -1566,13 +1824,16 @@
    def test_forget(self):
        policy = self._makeOne(None)
        self.assertEqual(policy.forget(None), [
            ('WWW-Authenticate', 'Basic realm="SomeRealm"')])
        self.assertEqual(
            policy.forget(None),
            [('WWW-Authenticate', 'Basic realm="SomeRealm"')],
        )
class TestExtractHTTPBasicCredentials(unittest.TestCase):
    def _get_func(self):
        from pyramid.authentication import extract_http_basic_credentials
        return extract_http_basic_credentials
    def test_no_auth_header(self):
@@ -1583,17 +1844,21 @@
    def test_invalid_payload(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
            bytes_('chrisrpassword')).decode('ascii')
            bytes_('chrisrpassword')
        ).decode('ascii')
        fn = self._get_func()
        self.assertIsNone(fn(request))
    def test_not_a_basic_auth_scheme(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'OtherScheme %s' % base64.b64encode(
            bytes_('chrisr:password')).decode('ascii')
            bytes_('chrisr:password')
        ).decode('ascii')
        fn = self._get_func()
        self.assertIsNone(fn(request))
@@ -1605,43 +1870,59 @@
    def test_latin1_payload(self):
        import base64
        request = testing.DummyRequest()
        inputs = (b'm\xc3\xb6rk\xc3\xb6:'
                  b'm\xc3\xb6rk\xc3\xb6password').decode('utf-8')
        inputs = (
            b'm\xc3\xb6rk\xc3\xb6:' b'm\xc3\xb6rk\xc3\xb6password'
        ).decode('utf-8')
        request.headers['Authorization'] = 'Basic %s' % (
            base64.b64encode(inputs.encode('latin-1')).decode('latin-1'))
            base64.b64encode(inputs.encode('latin-1')).decode('latin-1')
        )
        fn = self._get_func()
        self.assertEqual(fn(request), (
            b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
            b'm\xc3\xb6rk\xc3\xb6password'.decode('utf-8')
        ))
        self.assertEqual(
            fn(request),
            (
                b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
                b'm\xc3\xb6rk\xc3\xb6password'.decode('utf-8'),
            ),
        )
    def test_utf8_payload(self):
        import base64
        request = testing.DummyRequest()
        inputs = (b'm\xc3\xb6rk\xc3\xb6:'
                  b'm\xc3\xb6rk\xc3\xb6password').decode('utf-8')
        inputs = (
            b'm\xc3\xb6rk\xc3\xb6:' b'm\xc3\xb6rk\xc3\xb6password'
        ).decode('utf-8')
        request.headers['Authorization'] = 'Basic %s' % (
            base64.b64encode(inputs.encode('utf-8')).decode('latin-1'))
            base64.b64encode(inputs.encode('utf-8')).decode('latin-1')
        )
        fn = self._get_func()
        self.assertEqual(fn(request), (
            b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
            b'm\xc3\xb6rk\xc3\xb6password'.decode('utf-8')
        ))
        self.assertEqual(
            fn(request),
            (
                b'm\xc3\xb6rk\xc3\xb6'.decode('utf-8'),
                b'm\xc3\xb6rk\xc3\xb6password'.decode('utf-8'),
            ),
        )
    def test_namedtuple_return(self):
        import base64
        request = testing.DummyRequest()
        request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
            bytes_('chrisr:pass')).decode('ascii')
            bytes_('chrisr:pass')
        ).decode('ascii')
        fn = self._get_func()
        result = fn(request)
        self.assertEqual(result.username, 'chrisr')
        self.assertEqual(result.password, 'pass')
class DummyContext:
    pass
class DummyCookies(object):
    def __init__(self, cookie):
@@ -1650,8 +1931,10 @@
    def get(self, name):
        return self.cookie
class DummyRequest:
    domain = 'localhost'
    def __init__(self, environ=None, session=None, registry=None, cookie=None):
        self.environ = environ or {}
        self.session = session or {}
@@ -1662,12 +1945,14 @@
    def add_response_callback(self, callback):
        self.callbacks.append(callback)
class DummyWhoPlugin:
    def remember(self, environ, identity):
        return environ, identity
    def forget(self, environ, identity):
        return environ, identity
class DummyCookieHelper:
    def __init__(self, result):
@@ -1683,15 +1968,24 @@
    def forget(self, *arg):
        return []
class DummyAuthTktModule(object):
    def __init__(self, timestamp=0, userid='userid', tokens=(), user_data='',
                 parse_raise=False, hashalg="md5"):
    def __init__(
        self,
        timestamp=0,
        userid='userid',
        tokens=(),
        user_data='',
        parse_raise=False,
        hashalg="md5",
    ):
        self.timestamp = timestamp
        self.userid = userid
        self.tokens = tokens
        self.user_data = user_data
        self.parse_raise = parse_raise
        self.hashalg = hashalg
        def parse_ticket(secret, value, remote_addr, hashalg):
            self.secret = secret
            self.value = value
@@ -1699,6 +1993,7 @@
            if self.parse_raise:
                raise self.BadTicket()
            return self.timestamp, self.userid, self.tokens, self.user_data
        self.parse_ticket = parse_ticket
        class AuthTicket(object):
@@ -1710,10 +2005,10 @@
            def cookie_value(self):
                result = {
                    'secret':self.secret,
                    'userid':self.userid,
                    'remote_addr':self.remote_addr
                    }
                    'secret': self.secret,
                    'userid': self.userid,
                    'remote_addr': self.remote_addr,
                }
                result.update(self.kw)
                tokens = result.pop('tokens', None)
                if tokens is not None:
@@ -1724,15 +2019,16 @@
                for k, v in items:
                    if isinstance(v, bytes):
                        v = text_(v)
                    new_items.append((k,v))
                result = '/'.join(['%s=%s' % (k, v) for k,v in new_items ])
                    new_items.append((k, v))
                result = '/'.join(['%s=%s' % (k, v) for k, v in new_items])
                return result
        self.AuthTicket = AuthTicket
    class BadTicket(Exception):
        pass
class DummyResponse:
    def __init__(self):
        self.headerlist = []
tests/test_authorization.py
@@ -2,6 +2,7 @@
from pyramid.testing import cleanUp
class TestACLAuthorizationPolicy(unittest.TestCase):
    def setUp(self):
        cleanUp()
@@ -11,6 +12,7 @@
    def _getTargetClass(self):
        from pyramid.authorization import ACLAuthorizationPolicy
        return ACLAuthorizationPolicy
    def _makeOne(self):
@@ -19,18 +21,20 @@
    def test_class_implements_IAuthorizationPolicy(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IAuthorizationPolicy
        verifyClass(IAuthorizationPolicy, self._getTargetClass())
    def test_instance_implements_IAuthorizationPolicy(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IAuthorizationPolicy
        verifyObject(IAuthorizationPolicy, self._makeOne())
    def test_permits_no_acl(self):
        context = DummyContext()
        policy = self._makeOne()
        self.assertEqual(policy.permits(context, [], 'view'), False)
    def test_permits(self):
        from pyramid.security import Deny
        from pyramid.security import Allow
@@ -38,68 +42,76 @@
        from pyramid.security import Authenticated
        from pyramid.security import ALL_PERMISSIONS
        from pyramid.security import DENY_ALL
        root = DummyContext()
        community = DummyContext(__name__='community', __parent__=root)
        blog = DummyContext(__name__='blog', __parent__=community)
        root.__acl__ = [
            (Allow, Authenticated, VIEW),
            ]
        root.__acl__ = [(Allow, Authenticated, VIEW)]
        community.__acl__ = [
            (Allow, 'fred', ALL_PERMISSIONS),
            (Allow, 'wilma', VIEW),
            DENY_ALL,
            ]
        ]
        blog.__acl__ = [
            (Allow, 'barney', MEMBER_PERMS),
            (Allow, 'wilma', VIEW),
            ]
        ]
        policy = self._makeOne()
        result = policy.permits(blog, [Everyone, Authenticated, 'wilma'],
                                'view')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'wilma'], 'view'
        )
        self.assertEqual(result, True)
        self.assertEqual(result.context, blog)
        self.assertEqual(result.ace, (Allow, 'wilma', VIEW))
        self.assertEqual(result.acl, blog.__acl__)
        result = policy.permits(blog, [Everyone, Authenticated, 'wilma'],
                                'delete')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'wilma'], 'delete'
        )
        self.assertEqual(result, False)
        self.assertEqual(result.context, community)
        self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
        self.assertEqual(result.acl, community.__acl__)
        result = policy.permits(blog, [Everyone, Authenticated, 'fred'], 'view')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'fred'], 'view'
        )
        self.assertEqual(result, True)
        self.assertEqual(result.context, community)
        self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS))
        result = policy.permits(blog, [Everyone, Authenticated, 'fred'],
                                'doesntevenexistyet')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'fred'], 'doesntevenexistyet'
        )
        self.assertEqual(result, True)
        self.assertEqual(result.context, community)
        self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS))
        self.assertEqual(result.acl, community.__acl__)
        result = policy.permits(blog, [Everyone, Authenticated, 'barney'],
                                'view')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'barney'], 'view'
        )
        self.assertEqual(result, True)
        self.assertEqual(result.context, blog)
        self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS))
        result = policy.permits(blog, [Everyone, Authenticated, 'barney'],
                                'administer')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'barney'], 'administer'
        )
        self.assertEqual(result, False)
        self.assertEqual(result.context, community)
        self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
        self.assertEqual(result.acl, community.__acl__)
        result = policy.permits(root, [Everyone, Authenticated, 'someguy'],
                                'view')
        result = policy.permits(
            root, [Everyone, Authenticated, 'someguy'], 'view'
        )
        self.assertEqual(result, True)
        self.assertEqual(result.context, root)
        self.assertEqual(result.ace, (Allow, Authenticated, VIEW))
        result = policy.permits(blog,
                                [Everyone, Authenticated, 'someguy'], 'view')
        result = policy.permits(
            blog, [Everyone, Authenticated, 'someguy'], 'view'
        )
        self.assertEqual(result, False)
        self.assertEqual(result.context, community)
        self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS))
@@ -116,128 +128,149 @@
        self.assertEqual(result, False)
        self.assertEqual(result.ace, '<default deny>')
        self.assertEqual(
            result.acl,
            '<No ACL found on any object in resource lineage>')
            result.acl, '<No ACL found on any object in resource lineage>'
        )
    def test_permits_string_permissions_in_acl(self):
        from pyramid.security import Allow
        root = DummyContext()
        root.__acl__ = [
            (Allow, 'wilma', 'view_stuff'),
            ]
        root.__acl__ = [(Allow, 'wilma', 'view_stuff')]
        policy = self._makeOne()
        result = policy.permits(root, ['wilma'], 'view')
        # would be True if matching against 'view_stuff' instead of against
        # ['view_stuff']
        self.assertEqual(result, False)
        self.assertEqual(result, False)
    def test_principals_allowed_by_permission_direct(self):
        from pyramid.security import Allow
        from pyramid.security import DENY_ALL
        context = DummyContext()
        acl = [ (Allow, 'chrism', ('read', 'write')),
                DENY_ALL,
                (Allow, 'other', 'read') ]
        acl = [
            (Allow, 'chrism', ('read', 'write')),
            DENY_ALL,
            (Allow, 'other', 'read'),
        ]
        context.__acl__ = acl
        policy = self._makeOne()
        result = sorted(
            policy.principals_allowed_by_permission(context, 'read'))
            policy.principals_allowed_by_permission(context, 'read')
        )
        self.assertEqual(result, ['chrism'])
    def test_principals_allowed_by_permission_callable_acl(self):
        from pyramid.security import Allow
        from pyramid.security import DENY_ALL
        context = DummyContext()
        acl = lambda: [ (Allow, 'chrism', ('read', 'write')),
                        DENY_ALL,
                        (Allow, 'other', 'read') ]
        acl = lambda: [
            (Allow, 'chrism', ('read', 'write')),
            DENY_ALL,
            (Allow, 'other', 'read'),
        ]
        context.__acl__ = acl
        policy = self._makeOne()
        result = sorted(
            policy.principals_allowed_by_permission(context, 'read'))
            policy.principals_allowed_by_permission(context, 'read')
        )
        self.assertEqual(result, ['chrism'])
    def test_principals_allowed_by_permission_string_permission(self):
        from pyramid.security import Allow
        context = DummyContext()
        acl = [ (Allow, 'chrism', 'read_it')]
        acl = [(Allow, 'chrism', 'read_it')]
        context.__acl__ = acl
        policy = self._makeOne()
        result = policy.principals_allowed_by_permission(context, 'read')
        # would be ['chrism'] if 'read' were compared against 'read_it' instead
        # of against ['read_it']
        self.assertEqual(list(result), [])
    def test_principals_allowed_by_permission(self):
        from pyramid.security import Allow
        from pyramid.security import Deny
        from pyramid.security import DENY_ALL
        from pyramid.security import ALL_PERMISSIONS
        root = DummyContext(__name__='', __parent__=None)
        community = DummyContext(__name__='community', __parent__=root)
        blog = DummyContext(__name__='blog', __parent__=community)
        root.__acl__ = [ (Allow, 'chrism', ('read', 'write')),
                         (Allow, 'other', ('read',)),
                         (Allow, 'jim', ALL_PERMISSIONS)]
        community.__acl__ = [  (Deny, 'flooz', 'read'),
                               (Allow, 'flooz', 'read'),
                               (Allow, 'mork', 'read'),
                               (Deny, 'jim', 'read'),
                               (Allow, 'someguy', 'manage')]
        blog.__acl__ = [ (Allow, 'fred', 'read'),
                         DENY_ALL]
        root.__acl__ = [
            (Allow, 'chrism', ('read', 'write')),
            (Allow, 'other', ('read',)),
            (Allow, 'jim', ALL_PERMISSIONS),
        ]
        community.__acl__ = [
            (Deny, 'flooz', 'read'),
            (Allow, 'flooz', 'read'),
            (Allow, 'mork', 'read'),
            (Deny, 'jim', 'read'),
            (Allow, 'someguy', 'manage'),
        ]
        blog.__acl__ = [(Allow, 'fred', 'read'), DENY_ALL]
        policy = self._makeOne()
        result = sorted(policy.principals_allowed_by_permission(blog, 'read'))
        self.assertEqual(result, ['fred'])
        result = sorted(policy.principals_allowed_by_permission(community,
            'read'))
        result = sorted(
            policy.principals_allowed_by_permission(community, 'read')
        )
        self.assertEqual(result, ['chrism', 'mork', 'other'])
        result = sorted(policy.principals_allowed_by_permission(community,
            'read'))
        result = sorted(
            policy.principals_allowed_by_permission(community, 'read')
        )
        result = sorted(policy.principals_allowed_by_permission(root, 'read'))
        self.assertEqual(result, ['chrism', 'jim', 'other'])
    def test_principals_allowed_by_permission_no_acls(self):
        context = DummyContext()
        policy = self._makeOne()
        result = sorted(policy.principals_allowed_by_permission(context,'read'))
        result = sorted(
            policy.principals_allowed_by_permission(context, 'read')
        )
        self.assertEqual(result, [])
    def test_principals_allowed_by_permission_deny_not_permission_in_acl(self):
        from pyramid.security import Deny
        from pyramid.security import Everyone
        context = DummyContext()
        acl = [ (Deny, Everyone, 'write') ]
        acl = [(Deny, Everyone, 'write')]
        context.__acl__ = acl
        policy = self._makeOne()
        result = sorted(
            policy.principals_allowed_by_permission(context, 'read'))
            policy.principals_allowed_by_permission(context, 'read')
        )
        self.assertEqual(result, [])
    def test_principals_allowed_by_permission_deny_permission_in_acl(self):
        from pyramid.security import Deny
        from pyramid.security import Everyone
        context = DummyContext()
        acl = [ (Deny, Everyone, 'read') ]
        acl = [(Deny, Everyone, 'read')]
        context.__acl__ = acl
        policy = self._makeOne()
        result = sorted(
            policy.principals_allowed_by_permission(context, 'read'))
            policy.principals_allowed_by_permission(context, 'read')
        )
        self.assertEqual(result, [])
    def test_callable_acl(self):
        from pyramid.security import Allow
        context = DummyContext()
        fn = lambda self: [(Allow, 'bob', 'read')]
        context.__acl__ = fn.__get__(context, context.__class__)
        policy = self._makeOne()
        result = policy.permits(context, ['bob'], 'read')
        self.assertTrue(result)
class DummyContext:
    def __init__(self, *arg, **kw):
@@ -256,4 +289,3 @@
MEMBER_PERMS = GUEST_PERMS + (EDIT, CREATE, DELETE)
MODERATOR_PERMS = MEMBER_PERMS + (MODERATE,)
ADMINISTRATOR_PERMS = MODERATOR_PERMS + (ADMINISTER,)
tests/test_compat.py
@@ -1,6 +1,7 @@
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))
@@ -15,12 +16,17 @@
        self.assertTrue(is_unbound_method(NewStyle.run))
    def test_normal_func_unbound(self):
        def func(): return 'OK'
        def func():  # pragma: no cover
            return 'OK'
        self.assertFalse(is_unbound_method(func))
class OldStyle:
    def run(self): return 'OK'
    def run(self):  # pragma: no cover
        return 'OK'
class NewStyle(object):
    def run(self): return 'OK'
    def run(self):  # pragma: no cover
        return 'OK'
tests/test_config/__init__.py
@@ -1,53 +1,70 @@
# package
from functools import partial
from zope.interface import implementer
from zope.interface import Interface
class IFactory(Interface):
    pass
def dummy_tween_factory(handler, registry): pass
def dummy_tween_factory2(handler, registry): pass
def dummy_tween_factory(handler, registry):  # pragma: no cover
    pass
def dummy_tween_factory2(handler, registry):  # pragma: no cover
    pass
def dummy_include(config):
    config.registry.included = True
    config.action('discrim', None, config.package)
def dummy_include2(config):
    config.registry.also_included = True
    config.action('discrim', None, config.package)
includeme = dummy_include
class DummyContext:
    pass
@implementer(IFactory)
class DummyFactory(object):
    def __call__(self):
        """ """
def dummyfactory(request):
    """ """
class IDummy(Interface):
    pass
def dummy_view(request):
    return 'OK'
def dummy_extend(config, discrim):
    config.action(discrim, None, config.package)
def dummy_extend2(config, discrim):
    config.action(discrim, None, config.registry)
from functools import partial
dummy_partial = partial(dummy_extend, discrim='partial')
class DummyCallable(object):
    def __call__(self, config, discrim):
        config.action(discrim, None, config.package)
dummy_callable = DummyCallable()
dummy_callable = DummyCallable()
tests/test_config/path/scanerror/__init__.py
@@ -1,3 +1 @@
# scan error package
tests/test_config/pkgs/__init__.py
@@ -1,2 +1 @@
# package
tests/test_config/pkgs/asset/__init__.py
@@ -1,3 +1 @@
# package
tests/test_config/pkgs/asset/subpackage/__init__.py
@@ -1 +1 @@
#package
# package
tests/test_config/pkgs/scanextrakw/__init__.py
@@ -1,14 +1,17 @@
import venusian
def foo(wrapped):
    def bar(scanner, name, wrapped):
        scanner.config.a = scanner.a
    venusian.attach(wrapped, bar)
    return wrapped
@foo
def hello():
    pass
hello() # appease coverage
hello()  # appease coverage
tests/test_config/pkgs/scannable/__init__.py
@@ -1,18 +1,22 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(renderer=null_renderer)
def grokked(context, request):
    return 'grokked'
@view_config(request_method='POST', renderer=null_renderer)
def grokked_post(context, request):
    return 'grokked_post'
@view_config(name='stacked2', renderer=null_renderer)
@view_config(name='stacked1', renderer=null_renderer)
def stacked(context, request):
    return 'stacked'
class stacked_class(object):
    def __init__(self, context, request):
@@ -22,11 +26,15 @@
    def __call__(self):
        return 'stacked_class'
stacked_class = view_config(name='stacked_class1',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(name='stacked_class2',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(name='stacked_class1', renderer=null_renderer)(
    stacked_class
)
stacked_class = view_config(name='stacked_class2', renderer=null_renderer)(
    stacked_class
)
class oldstyle_grokked_class:
    def __init__(self, context, request):
        self.context = context
@@ -34,10 +42,12 @@
    def __call__(self):
        return 'oldstyle_grokked_class'
oldstyle_grokked_class = view_config(name='oldstyle_grokked_class',
                                     renderer=null_renderer)(
    oldstyle_grokked_class)
oldstyle_grokked_class = view_config(
    name='oldstyle_grokked_class', renderer=null_renderer
)(oldstyle_grokked_class)
class grokked_class(object):
    def __init__(self, context, request):
@@ -46,23 +56,30 @@
    def __call__(self):
        return 'grokked_class'
grokked_class = view_config(name='grokked_class',
                            renderer=null_renderer)(grokked_class)
grokked_class = view_config(name='grokked_class', renderer=null_renderer)(
    grokked_class
)
class Foo(object):
    def __call__(self, context, request):
        return 'grokked_instance'
grokked_instance = Foo()
grokked_instance = view_config(name='grokked_instance',
                               renderer=null_renderer)(grokked_instance)
grokked_instance = view_config(
    name='grokked_instance', renderer=null_renderer
)(grokked_instance)
class Base(object):
    @view_config(name='basemethod', renderer=null_renderer)
    def basemethod(self):
        """ """
class MethodViews(Base):
    def __init__(self, context, request):
        self.context = context
@@ -81,16 +98,20 @@
    def stacked(self):
        return 'stacked_method'
# ungrokkable
A = 1
B = {}
def stuff():
    """ """
class Whatever(object):
    pass
class Whatever2:
    pass
tests/test_config/pkgs/scannable/another.py
@@ -1,18 +1,22 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='another', renderer=null_renderer)
def grokked(context, request):
    return 'another_grokked'
@view_config(request_method='POST', name='another', renderer=null_renderer)
def grokked_post(context, request):
    return 'another_grokked_post'
@view_config(name='another_stacked2', renderer=null_renderer)
@view_config(name='another_stacked1', renderer=null_renderer)
def stacked(context, request):
    return 'another_stacked'
class stacked_class(object):
    def __init__(self, context, request):
@@ -22,10 +26,14 @@
    def __call__(self):
        return 'another_stacked_class'
stacked_class = view_config(name='another_stacked_class1',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(name='another_stacked_class2',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(
    name='another_stacked_class1', renderer=null_renderer
)(stacked_class)
stacked_class = view_config(
    name='another_stacked_class2', renderer=null_renderer
)(stacked_class)
class oldstyle_grokked_class:
    def __init__(self, context, request):
@@ -34,10 +42,12 @@
    def __call__(self):
        return 'another_oldstyle_grokked_class'
oldstyle_grokked_class = view_config(name='another_oldstyle_grokked_class',
                                     renderer=null_renderer)(
    oldstyle_grokked_class)
oldstyle_grokked_class = view_config(
    name='another_oldstyle_grokked_class', renderer=null_renderer
)(oldstyle_grokked_class)
class grokked_class(object):
    def __init__(self, context, request):
@@ -46,24 +56,28 @@
    def __call__(self):
        return 'another_grokked_class'
grokked_class = view_config(name='another_grokked_class',
                            renderer=null_renderer)(grokked_class)
grokked_class = view_config(
    name='another_grokked_class', renderer=null_renderer
)(grokked_class)
class Foo(object):
    def __call__(self, context, request):
        return 'another_grokked_instance'
grokked_instance = Foo()
grokked_instance = view_config(name='another_grokked_instance',
                               renderer=null_renderer)(
    grokked_instance)
grokked_instance = view_config(
    name='another_grokked_instance', renderer=null_renderer
)(grokked_instance)
# ungrokkable
A = 1
B = {}
def stuff():
    """ """
tests/test_config/pkgs/scannable/pod/notinit.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='pod_notinit', renderer=null_renderer)
def subpackage_notinit(context, request):
    return 'pod_notinit'
tests/test_config/pkgs/scannable/subpackage/__init__.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subpackage_init', renderer=null_renderer)
def subpackage_init(context, request):
    return 'subpackage_init'
tests/test_config/pkgs/scannable/subpackage/notinit.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subpackage_notinit', renderer=null_renderer)
def subpackage_notinit(context, request):
    return 'subpackage_notinit'
tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py
@@ -1,6 +1,7 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subsubpackage_init', renderer=null_renderer)
def subpackage_init(context, request):
    return 'subsubpackage_init'
tests/test_config/pkgs/selfscan/__init__.py
@@ -1,11 +1,14 @@
from pyramid.view import view_config
@view_config(renderer='string')
def abc(request):
    return 'root'
def main():
    from pyramid.config import Configurator
    c = Configurator()
    c.scan()
    return c
tests/test_config/pkgs/selfscan/another.py
@@ -1,6 +1,6 @@
from pyramid.view import view_config
@view_config(name='two', renderer='string')
def two(request):
    return 'two'
tests/test_config/test_adapters.py
@@ -3,23 +3,30 @@
from pyramid.compat import PY2
from . import IDummy
class AdaptersConfiguratorMixinTests(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_add_subscriber_defaults(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event):
            L.append(event)
        config = self._makeOne(autocommit=True)
        config.add_subscriber(subscriber)
        event = Event()
@@ -32,14 +39,19 @@
    def test_add_subscriber_iface_specified(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event):
            L.append(event)
        config = self._makeOne(autocommit=True)
        config.add_subscriber(subscriber, IEvent)
        event = Event()
@@ -52,9 +64,11 @@
    def test_add_subscriber_dottednames(self):
        import tests.test_config
        from pyramid.interfaces import INewRequest
        config = self._makeOne(autocommit=True)
        config.add_subscriber('tests.test_config',
                              'pyramid.interfaces.INewRequest')
        config.add_subscriber(
            'tests.test_config', 'pyramid.interfaces.INewRequest'
        )
        handlers = list(config.registry.registeredHandlers())
        self.assertEqual(len(handlers), 1)
        handler = handlers[0]
@@ -64,15 +78,20 @@
    def test_add_object_event_subscriber(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            object = 'foo'
        event = Event()
        L = []
        def subscriber(object, event):
            L.append(event)
        config = self._makeOne(autocommit=True)
        config.add_subscriber(subscriber, (Interface, IEvent))
        config.registry.subscribers((event.object, event), None)
@@ -84,14 +103,19 @@
    def test_add_subscriber_with_specific_type_and_predicates_True(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event):
            L.append(event)
        config = self._makeOne(autocommit=True)
        predlist = config.get_predlist('subscriber')
        jam_predicate = predicate_maker('jam')
@@ -111,14 +135,19 @@
    def test_add_subscriber_with_default_type_predicates_True(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event):
            L.append(event)
        config = self._makeOne(autocommit=True)
        predlist = config.get_predlist('subscriber')
        jam_predicate = predicate_maker('jam')
@@ -138,13 +167,19 @@
    def test_add_subscriber_with_specific_type_and_predicates_False(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event): L.append(event)
        def subscriber(event):  # pragma: no cover
            L.append(event)
        config = self._makeOne(autocommit=True)
        predlist = config.get_predlist('subscriber')
        jam_predicate = predicate_maker('jam')
@@ -161,13 +196,19 @@
    def test_add_subscriber_with_default_type_predicates_False(self):
        from zope.interface import implementer
        from zope.interface import Interface
        class IEvent(Interface):
            pass
        @implementer(IEvent)
        class Event:
            pass
        L = []
        def subscriber(event): L.append(event)
        def subscriber(event):  # pragma: no cover
            L.append(event)
        config = self._makeOne(autocommit=True)
        predlist = config.get_predlist('subscriber')
        jam_predicate = predicate_maker('jam')
@@ -184,33 +225,42 @@
    def test_add_subscriber_predicate(self):
        config = self._makeOne()
        L = []
        def add_predicate(type, name, factory, weighs_less_than=None,
                          weighs_more_than=None):
        def add_predicate(
            type, name, factory, weighs_less_than=None, weighs_more_than=None
        ):
            self.assertEqual(type, 'subscriber')
            self.assertEqual(name, 'name')
            self.assertEqual(factory, 'factory')
            self.assertEqual(weighs_more_than, 1)
            self.assertEqual(weighs_less_than, 2)
            L.append(1)
        config._add_predicate = add_predicate
        config.add_subscriber_predicate('name', 'factory', 1, 2)
        self.assertTrue(L)
    def test_add_response_adapter(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        class Adapter(object):
            def __init__(self, other):
                self.other = other
        config.add_response_adapter(Adapter, str)
        result = config.registry.queryAdapter('foo', IResponse)
        self.assertTrue(result.other, 'foo')
    def test_add_response_adapter_self(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        class Adapter(object):
            pass
        config.add_response_adapter(None, Adapter)
        adapter = Adapter()
        result = config.registry.queryAdapter(adapter, IResponse)
@@ -218,6 +268,7 @@
    def test_add_response_adapter_dottednames(self):
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        if PY2:
            str_name = '__builtin__.str'
@@ -229,10 +280,12 @@
    def test_add_traverser_dotted_names(self):
        from pyramid.interfaces import ITraverser
        config = self._makeOne(autocommit=True)
        config.add_traverser(
            'tests.test_config.test_adapters.DummyTraverser',
            'tests.test_config.test_adapters.DummyIface')
            'tests.test_config.test_adapters.DummyIface',
        )
        iface = DummyIface()
        traverser = config.registry.getAdapter(iface, ITraverser)
        self.assertEqual(traverser.__class__, DummyTraverser)
@@ -240,6 +293,7 @@
    def test_add_traverser_default_iface_means_Interface(self):
        from pyramid.interfaces import ITraverser
        config = self._makeOne(autocommit=True)
        config.add_traverser(DummyTraverser)
        traverser = config.registry.getAdapter(None, ITraverser)
@@ -247,19 +301,20 @@
    def test_add_traverser_nondefault_iface(self):
        from pyramid.interfaces import ITraverser
        config = self._makeOne(autocommit=True)
        config.add_traverser(DummyTraverser, DummyIface)
        iface = DummyIface()
        traverser = config.registry.getAdapter(iface, ITraverser)
        self.assertEqual(traverser.__class__, DummyTraverser)
        self.assertEqual(traverser.root, iface)
    def test_add_traverser_introspectables(self):
        config = self._makeOne()
        config.add_traverser(DummyTraverser, DummyIface)
        actions = config.action_state.actions
        self.assertEqual(len(actions), 1)
        intrs  = actions[0]['introspectables']
        intrs = actions[0]['introspectables']
        self.assertEqual(len(intrs), 1)
        intr = intrs[0]
        self.assertEqual(intr.type_name, 'traverser')
@@ -271,25 +326,25 @@
    def test_add_resource_url_adapter_dotted_names(self):
        from pyramid.interfaces import IResourceURL
        config = self._makeOne(autocommit=True)
        config.add_resource_url_adapter(
            'tests.test_config.test_adapters.DummyResourceURL',
            'tests.test_config.test_adapters.DummyIface',
            )
        )
        iface = DummyIface()
        adapter = config.registry.getMultiAdapter((iface, iface),
                                                    IResourceURL)
        adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL)
        self.assertEqual(adapter.__class__, DummyResourceURL)
        self.assertEqual(adapter.resource, iface)
        self.assertEqual(adapter.request, iface)
    def test_add_resource_url_default_resource_iface_means_Interface(self):
        from pyramid.interfaces import IResourceURL
        config = self._makeOne(autocommit=True)
        config.add_resource_url_adapter(DummyResourceURL)
        iface = DummyIface()
        adapter = config.registry.getMultiAdapter((iface, iface),
                                                    IResourceURL)
        adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL)
        self.assertEqual(adapter.__class__, DummyResourceURL)
        self.assertEqual(adapter.resource, iface)
        self.assertEqual(adapter.request, iface)
@@ -297,18 +352,17 @@
    def test_add_resource_url_nodefault_resource_iface(self):
        from zope.interface import Interface
        from pyramid.interfaces import IResourceURL
        config = self._makeOne(autocommit=True)
        config.add_resource_url_adapter(DummyResourceURL, DummyIface)
        iface = DummyIface()
        adapter = config.registry.getMultiAdapter((iface, iface),
                                                    IResourceURL)
        adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL)
        self.assertEqual(adapter.__class__, DummyResourceURL)
        self.assertEqual(adapter.resource, iface)
        self.assertEqual(adapter.request, iface)
        bad_result = config.registry.queryMultiAdapter(
            (Interface, Interface),
            IResourceURL,
            )
            (Interface, Interface), IResourceURL
        )
        self.assertEqual(bad_result, None)
    def test_add_resource_url_adapter_introspectables(self):
@@ -316,50 +370,62 @@
        config.add_resource_url_adapter(DummyResourceURL, DummyIface)
        actions = config.action_state.actions
        self.assertEqual(len(actions), 1)
        intrs  = actions[0]['introspectables']
        intrs = actions[0]['introspectables']
        self.assertEqual(len(intrs), 1)
        intr = intrs[0]
        self.assertEqual(intr.type_name, 'resource url adapter')
        self.assertEqual(intr.discriminator,
                         ('resource url adapter', DummyIface))
        self.assertEqual(
            intr.discriminator, ('resource url adapter', DummyIface)
        )
        self.assertEqual(intr.category_name, 'resource url adapters')
        self.assertEqual(
            intr.title,
            "resource url adapter for resource iface "
            "<class 'tests.test_config.test_adapters.DummyIface'>"
            )
            "<class 'tests.test_config.test_adapters.DummyIface'>",
        )
        self.assertEqual(intr['adapter'], DummyResourceURL)
        self.assertEqual(intr['resource_iface'], DummyIface)
class Test_eventonly(unittest.TestCase):
    def _callFUT(self, callee):
        from pyramid.config.adapters import eventonly
        return eventonly(callee)
    def test_defaults(self):
        def acallable(event, a=1, b=2): pass
        def acallable(event, a=1, b=2):  # pragma: no cover
            pass
        self.assertTrue(self._callFUT(acallable))
class DummyTraverser(object):
    def __init__(self, root):
        self.root = root
class DummyIface(object):
    pass
class DummyResourceURL(object):
    def __init__(self, resource, request):
        self.resource = resource
        self.request = request
def predicate_maker(name):
    class Predicate(object):
        def __init__(self, val, config):
            self.val = val
        def phash(self):
            return 'phash'
        text = phash
        def __call__(self, event):
            return getattr(event, name, None) == self.val
    return Predicate
    return Predicate
tests/test_config/test_assets.py
@@ -5,48 +5,66 @@
# we use this folder
here = os.path.dirname(os.path.abspath(__file__))
class TestAssetsConfiguratorMixin(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_override_asset_samename(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne()
        self.assertRaises(ConfigurationError, config.override_asset, 'a', 'a')
    def test_override_asset_directory_with_file(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne()
        self.assertRaises(ConfigurationError, config.override_asset,
                          'a:foo/',
                          'tests.test_config.pkgs.asset:foo.pt')
        self.assertRaises(
            ConfigurationError,
            config.override_asset,
            'a:foo/',
            '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',
                          'tests.test_config.pkgs.asset:templates/')
        self.assertRaises(
            ConfigurationError,
            config.override_asset,
            'a:foo.pt',
            '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',
                          'tests.test_config.pkgs.asset')
        self.assertRaises(
            ConfigurationError,
            config.override_asset,
            'a:foo.pt',
            '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(
            'tests.test_config.pkgs.asset:templates/foo.pt',
            'tests.test_config.pkgs.asset.subpackage:templates/bar.pt',
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        from tests.test_config.pkgs.asset import subpackage
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, 'templates/foo.pt')
        source = override.source
@@ -55,21 +73,24 @@
        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)
        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(
            'tests.test_config.pkgs.asset',
            'tests.test_config.pkgs.asset.subpackage',
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        from tests.test_config.pkgs.asset import subpackage
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, '')
        source = override.source
@@ -78,21 +99,24 @@
        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)
        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(
            'tests.test_config.pkgs.asset:templates/',
            'tests.test_config.pkgs.asset.subpackage:templates/',
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        from tests.test_config.pkgs.asset import subpackage
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, 'templates/')
        source = override.source
@@ -101,21 +125,24 @@
        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)
        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(
            'tests.test_config.pkgs.asset:templates/',
            'tests.test_config.pkgs.asset.subpackage',
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        from tests.test_config.pkgs.asset import subpackage
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, 'templates/')
        source = override.source
@@ -124,21 +151,24 @@
        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)
        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(
            'tests.test_config.pkgs.asset',
            'tests.test_config.pkgs.asset.subpackage:templates/',
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        from tests.test_config.pkgs.asset import subpackage
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, '')
        source = override.source
@@ -147,44 +177,59 @@
        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)
        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'))
        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)
        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'))
        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')
        abspath = os.path.join(
            here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt'
        )
        config.override_asset(
            'tests.test_config.pkgs.asset:templates/foo.pt',
            abspath,
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, 'templates/foo.pt')
        source = override.source
@@ -192,21 +237,26 @@
        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)
        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')
        abspath = os.path.join(
            here, 'pkgs', 'asset', 'subpackage', 'templates'
        )
        config.override_asset(
            'tests.test_config.pkgs.asset:templates/',
            abspath,
            _override=override)
            _override=override,
        )
        from tests.test_config.pkgs import asset
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, 'templates/')
        source = override.source
@@ -214,21 +264,24 @@
        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)
        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')
        abspath = os.path.join(
            here, 'pkgs', 'asset', 'subpackage', 'templates'
        )
        config.override_asset(
            'tests.test_config.pkgs.asset',
            abspath,
            _override=override)
            'tests.test_config.pkgs.asset', abspath, _override=override
        )
        from tests.test_config.pkgs import asset
        self.assertEqual(override.package, asset)
        self.assertEqual(override.path, '')
        source = override.source
@@ -236,33 +289,39 @@
        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)
        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')
        source = DummyAssetSource()
        config = self._makeOne()
        config._override(package, 'path', source,
                         PackageOverrides=DummyPackageOverrides)
        overrides = config.registry.queryUtility(IPackageOverrides,
                                                 name='package')
        config._override(
            package, 'path', source, PackageOverrides=DummyPackageOverrides
        )
        overrides = config.registry.queryUtility(
            IPackageOverrides, name='package'
        )
        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')
        source = DummyAssetSource()
        overrides = DummyPackageOverrides(package)
        config = self._makeOne()
        config.registry.registerUtility(overrides, IPackageOverrides,
                                        name='package')
        config._override(package, 'path', source,
                         PackageOverrides=DummyPackageOverrides)
        config.registry.registerUtility(
            overrides, IPackageOverrides, name='package'
        )
        config._override(
            package, 'path', source, PackageOverrides=DummyPackageOverrides
        )
        self.assertEqual(overrides.inserted, [('path', source)])
        self.assertEqual(overrides.package, package)
@@ -276,6 +335,7 @@
    def _getTargetClass(self):
        from pyramid.config.assets import OverrideProvider
        return OverrideProvider
    def _makeOne(self, module):
@@ -285,12 +345,14 @@
    def _registerOverrides(self, overrides, name='tests.test_config'):
        from pyramid.interfaces import IPackageOverrides
        from pyramid.threadlocal import get_current_registry
        reg = get_current_registry()
        reg.registerUtility(overrides, IPackageOverrides, name=name)
    def test_get_resource_filename_no_overrides(self):
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        expected = os.path.join(here, resource_name)
        result = provider.get_resource_filename(None, resource_name)
@@ -299,6 +361,7 @@
    def test_get_resource_stream_no_overrides(self):
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        with provider.get_resource_stream(None, resource_name) as result:
            _assertBody(result.read(), os.path.join(here, resource_name))
@@ -306,6 +369,7 @@
    def test_get_resource_string_no_overrides(self):
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.get_resource_string(None, resource_name)
        _assertBody(result, os.path.join(here, resource_name))
@@ -313,6 +377,7 @@
    def test_has_resource_no_overrides(self):
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.has_resource(resource_name)
        self.assertEqual(result, True)
@@ -321,6 +386,7 @@
        file_resource_name = 'test_assets.py'
        directory_resource_name = 'files'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.resource_isdir(file_resource_name)
        self.assertEqual(result, False)
@@ -330,6 +396,7 @@
    def test_resource_listdir_no_overrides(self):
        resource_name = 'files'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.resource_listdir(resource_name)
        self.assertTrue(result)
@@ -339,16 +406,18 @@
        self._registerOverrides(overrides)
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        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)
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        with provider.get_resource_stream(None, resource_name) as result:
            _assertBody(result.read(), os.path.join(here, resource_name))
@@ -358,6 +427,7 @@
        self._registerOverrides(overrides)
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.get_resource_string(None, resource_name)
        _assertBody(result, os.path.join(here, resource_name))
@@ -367,6 +437,7 @@
        self._registerOverrides(overrides)
        resource_name = 'test_assets.py'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.has_resource(resource_name)
        self.assertEqual(result, True)
@@ -376,6 +447,7 @@
        self._registerOverrides(overrides)
        resource_name = 'files'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.resource_isdir(resource_name)
        self.assertEqual(result, True)
@@ -385,6 +457,7 @@
        self._registerOverrides(overrides)
        resource_name = 'files'
        import tests.test_config
        provider = self._makeOne(tests.test_config)
        result = provider.resource_listdir(resource_name)
        self.assertTrue(result)
@@ -392,6 +465,7 @@
    def test_get_resource_filename_override_returns_value(self):
        overrides = DummyOverrides('value')
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        result = provider.get_resource_filename(None, 'test_assets.py')
@@ -399,8 +473,10 @@
    def test_get_resource_stream_override_returns_value(self):
        from io import BytesIO
        overrides = DummyOverrides(BytesIO(b'value'))
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        with provider.get_resource_stream(None, 'test_assets.py') as stream:
@@ -409,6 +485,7 @@
    def test_get_resource_string_override_returns_value(self):
        overrides = DummyOverrides('value')
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        result = provider.get_resource_string(None, 'test_assets.py')
@@ -417,6 +494,7 @@
    def test_has_resource_override_returns_True(self):
        overrides = DummyOverrides(True)
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        result = provider.has_resource('test_assets.py')
@@ -425,6 +503,7 @@
    def test_resource_isdir_override_returns_False(self):
        overrides = DummyOverrides(False)
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        result = provider.resource_isdir('files')
@@ -433,14 +512,17 @@
    def test_resource_listdir_override_returns_values(self):
        overrides = DummyOverrides(['a'])
        import tests.test_config
        self._registerOverrides(overrides)
        provider = self._makeOne(tests.test_config)
        result = provider.resource_listdir('files')
        self.assertEqual(result, ['a'])
class TestPackageOverrides(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.config.assets import PackageOverrides
        return PackageOverrides
    def _makeOne(self, package=None, pkg_resources=None):
@@ -454,21 +536,25 @@
    def test_class_conforms_to_IPackageOverrides(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IPackageOverrides
        verifyClass(IPackageOverrides, self._getTargetClass())
    def test_instance_conforms_to_IPackageOverrides(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IPackageOverrides
        verifyObject(IPackageOverrides, self._makeOne())
    def test_class_conforms_to_IPEP302Loader(self):
        from zope.interface.verify import verifyClass
        from pyramid.interfaces import IPEP302Loader
        verifyClass(IPEP302Loader, self._getTargetClass())
    def test_instance_conforms_to_IPEP302Loader(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IPEP302Loader
        verifyObject(IPEP302Loader, self._makeOne())
    def test_ctor_package_already_has_loader_of_different_type(self):
@@ -491,11 +577,13 @@
    def test_ctor_registers_loader_type(self):
        from pyramid.config.assets import OverrideProvider
        dummy_pkg_resources = DummyPkgResources()
        package = DummyPackage('package')
        po = self._makeOne(package, dummy_pkg_resources)
        self.assertEqual(dummy_pkg_resources.registered, [(po.__class__,
                         OverrideProvider)])
        self.assertEqual(
            dummy_pkg_resources.registered, [(po.__class__, OverrideProvider)]
        )
    def test_ctor_sets_local_state(self):
        package = DummyPackage('package')
@@ -505,6 +593,7 @@
    def test_insert_directory(self):
        from pyramid.config.assets import DirectoryOverride
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = [None]
@@ -515,6 +604,7 @@
    def test_insert_file(self):
        from pyramid.config.assets import FileOverride
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = [None]
@@ -526,6 +616,7 @@
    def test_insert_emptystring(self):
        # XXX is this a valid case for a directory?
        from pyramid.config.assets import DirectoryOverride
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = [None]
@@ -536,7 +627,7 @@
        self.assertEqual(override.__class__, DirectoryOverride)
    def test_filtered_sources(self):
        overrides = [ DummyOverride(None), DummyOverride('foo')]
        overrides = [DummyOverride(None), DummyOverride('foo')]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -544,7 +635,7 @@
    def test_get_filename(self):
        source = DummyAssetSource(filename='foo.pt')
        overrides = [ DummyOverride(None), DummyOverride((source, ''))]
        overrides = [DummyOverride(None), DummyOverride((source, ''))]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -554,7 +645,10 @@
    def test_get_filename_file_doesnt_exist(self):
        source = DummyAssetSource(filename=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -569,10 +663,13 @@
        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):
        source = DummyAssetSource(stream=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -587,10 +684,13 @@
        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):
        source = DummyAssetSource(string=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -608,7 +708,10 @@
    def test_has_resource_file_doesnt_exist(self):
        source = DummyAssetSource(exists=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -635,7 +738,10 @@
    def test_isdir_doesnt_exist(self):
        source = DummyAssetSource(isdir=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -653,7 +759,10 @@
    def test_listdir_doesnt_exist(self):
        source = DummyAssetSource(listdir=None)
        overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))]
        overrides = [
            DummyOverride(None),
            DummyOverride((source, 'wont_exist')),
        ]
        package = DummyPackage('package')
        po = self._makeOne(package)
        po.overrides = overrides
@@ -668,7 +777,7 @@
    def test_get_data_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        loader = package.__loader__ = DummyLoader()
        po = self._makeOne(package)
        self.assertEqual(po.get_data('whatever'), b'DEADBEEF')
        self.assertEqual(loader._got_data, 'whatever')
@@ -680,7 +789,7 @@
    def test_is_package_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        loader = package.__loader__ = DummyLoader()
        po = self._makeOne(package)
        self.assertTrue(po.is_package('whatever'))
        self.assertEqual(loader._is_package, 'whatever')
@@ -692,7 +801,7 @@
    def test_get_code_pkg_has___loader__(self):
        package = DummyPackage('package')
        loader = package.__loader__  = DummyLoader()
        loader = package.__loader__ = DummyLoader()
        po = self._makeOne(package)
        self.assertEqual(po.get_code('whatever'), b'DEADBEEF')
        self.assertEqual(loader._got_code, 'whatever')
@@ -709,17 +818,20 @@
        self.assertEqual(po.get_source('whatever'), 'def foo():\n    pass')
        self.assertEqual(loader._got_source, 'whatever')
class AssetSourceIntegrationTests(object):
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'))
        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'))
        self.assertEqual(
            source.get_filename(''), os.path.join(here, 'test_assets.py')
        )
    def test_get_filename_file_doesnt_exist(self):
        source = self._makeOne('')
@@ -741,13 +853,16 @@
    def test_get_string(self):
        source = self._makeOne('')
        _assertBody(source.get_string('test_assets.py'),
                    os.path.join(here, 'test_assets.py'))
        _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'))
        _assertBody(
            source.get_string(''), os.path.join(here, 'test_assets.py')
        )
    def test_get_string_file_doesnt_exist(self):
        source = self._makeOne('')
@@ -785,28 +900,33 @@
        source = self._makeOne('')
        self.assertEqual(source.listdir('wont_exist'), None)
class TestPackageAssetSource(AssetSourceIntegrationTests, unittest.TestCase):
class TestPackageAssetSource(AssetSourceIntegrationTests, unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.config.assets import PackageAssetSource
        return PackageAssetSource
    def _makeOne(self, prefix, package='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, source):
@@ -818,16 +938,18 @@
        o = self._makeOne('foo/', source)
        result = o('foo/something.pt')
        self.assertEqual(result, (source, 'something.pt'))
    def test_it_no_match(self):
        source = DummyAssetSource()
        o = self._makeOne('foo/', source)
        result = o('baz/notfound.pt')
        self.assertEqual(result, None)
class TestFileOverride(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.config.assets import FileOverride
        return FileOverride
    def _makeOne(self, path, source):
@@ -839,12 +961,13 @@
        o = self._makeOne('foo.pt', source)
        result = o('foo.pt')
        self.assertEqual(result, (source, ''))
    def test_it_no_match(self):
        source = DummyAssetSource()
        o = self._makeOne('foo.pt', source)
        result = o('notfound.pt')
        self.assertEqual(result, None)
class DummyOverride:
    def __init__(self, result):
@@ -852,6 +975,7 @@
    def __call__(self, resource_name):
        return self.result
class DummyOverrides:
    def __init__(self, result):
@@ -862,6 +986,7 @@
    listdir = isdir = has_resource = get_stream = get_string = get_filename
class DummyPackageOverrides:
    def __init__(self, package):
        self.package = package
@@ -869,7 +994,8 @@
    def insert(self, path, source):
        self.inserted.append((path, source))
class DummyPkgResources:
    def __init__(self):
        self.registered = []
@@ -877,9 +1003,11 @@
    def register_loader_type(self, typ, inst):
        self.registered.append((typ, inst))
class DummyPackage:
    def __init__(self, name):
        self.__name__ = name
class DummyAssetSource:
    def __init__(self, **kw):
@@ -908,21 +1036,27 @@
    def listdir(self, resource_name):
        self.resource_name = resource_name
        return self.kw['listdir']
class DummyLoader:
    _got_data = _is_package = None
    def get_data(self, path):
        self._got_data = path
        return b'DEADBEEF'
    def is_package(self, fullname):
        self._is_package = fullname
        return True
    def get_code(self, fullname):
        self._got_code = fullname
        return b'DEADBEEF'
    def get_source(self, fullname):
        self._got_source = fullname
        return 'def foo():\n    pass'
class DummyUnderOverride:
    def __call__(self, package, path, source, _info=''):
@@ -930,10 +1064,12 @@
        self.path = path
        self.source = source
def read_(src):
    with open(src, 'rb') as f:
        contents = f.read()
    return contents
def _assertBody(body, filename):
    # strip both \n and \r for windows
@@ -942,4 +1078,4 @@
    data = read_(filename)
    data = data.replace(b'\r', b'')
    data = data.replace(b'\n', b'')
    assert(body == data)
    assert body == data
tests/test_config/test_factories.py
@@ -2,14 +2,17 @@
from . import dummyfactory
class TestFactoriesMixin(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_set_request_factory(self):
        from pyramid.interfaces import IRequestFactory
        config = self._makeOne(autocommit=True)
        factory = object()
        config.set_request_factory(factory)
@@ -17,14 +20,16 @@
    def test_set_request_factory_dottedname(self):
        from pyramid.interfaces import IRequestFactory
        config = self._makeOne(autocommit=True)
        config.set_request_factory(
            'tests.test_config.dummyfactory')
        self.assertEqual(config.registry.getUtility(IRequestFactory),
                         dummyfactory)
        config.set_request_factory('tests.test_config.dummyfactory')
        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)
@@ -32,58 +37,72 @@
    def test_set_response_factory_dottedname(self):
        from pyramid.interfaces import IResponseFactory
        config = self._makeOne(autocommit=True)
        config.set_response_factory(
            'tests.test_config.dummyfactory')
        self.assertEqual(config.registry.getUtility(IResponseFactory),
                         dummyfactory)
        config.set_response_factory('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.set_root_factory(dummyfactory)
        self.assertEqual(config.registry.queryUtility(IRootFactory), None)
        config.commit()
        self.assertEqual(config.registry.getUtility(IRootFactory), dummyfactory)
        self.assertEqual(
            config.registry.getUtility(IRootFactory), dummyfactory
        )
    def test_set_root_factory_as_None(self):
        from pyramid.interfaces import IRootFactory
        from pyramid.traversal import DefaultRootFactory
        config = self._makeOne()
        config.set_root_factory(None)
        self.assertEqual(config.registry.queryUtility(IRootFactory), None)
        config.commit()
        self.assertEqual(config.registry.getUtility(IRootFactory),
                         DefaultRootFactory)
        self.assertEqual(
            config.registry.getUtility(IRootFactory), DefaultRootFactory
        )
    def test_set_root_factory_dottedname(self):
        from pyramid.interfaces import IRootFactory
        config = self._makeOne()
        config.set_root_factory('tests.test_config.dummyfactory')
        self.assertEqual(config.registry.queryUtility(IRootFactory), None)
        config.commit()
        self.assertEqual(config.registry.getUtility(IRootFactory), dummyfactory)
        self.assertEqual(
            config.registry.getUtility(IRootFactory), dummyfactory
        )
    def test_set_session_factory(self):
        from pyramid.interfaces import ISessionFactory
        config = self._makeOne()
        config.set_session_factory(dummyfactory)
        self.assertEqual(config.registry.queryUtility(ISessionFactory), None)
        config.commit()
        self.assertEqual(config.registry.getUtility(ISessionFactory),
                         dummyfactory)
        self.assertEqual(
            config.registry.getUtility(ISessionFactory), dummyfactory
        )
    def test_set_session_factory_dottedname(self):
        from pyramid.interfaces import ISessionFactory
        config = self._makeOne()
        config.set_session_factory('tests.test_config.dummyfactory')
        self.assertEqual(config.registry.queryUtility(ISessionFactory), None)
        config.commit()
        self.assertEqual(config.registry.getUtility(ISessionFactory),
                         dummyfactory)
        self.assertEqual(
            config.registry.getUtility(ISessionFactory), dummyfactory
        )
    def test_add_request_method_with_callable(self):
        from pyramid.interfaces import IRequestExtensions
        config = self._makeOne(autocommit=True)
        callable = lambda x: None
        config.add_request_method(callable, name='foo')
@@ -92,23 +111,34 @@
    def test_add_request_method_with_unnamed_callable(self):
        from pyramid.interfaces import IRequestExtensions
        config = self._makeOne(autocommit=True)
        def foo(self): pass
        def foo(self):  # pragma: no cover
            pass
        config.add_request_method(foo)
        exts = config.registry.getUtility(IRequestExtensions)
        self.assertTrue('foo' in exts.methods)
    def test_set_multiple_request_methods_conflict(self):
        from pyramid.exceptions import ConfigurationConflictError
        config = self._makeOne()
        def foo(self): pass
        def bar(self): pass
        def foo(self):  # pragma: no cover
            pass
        def bar(self):  # pragma: no cover
            pass
        config.add_request_method(foo, name='bar')
        config.add_request_method(bar, name='bar')
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_add_request_method_with_None_callable(self):
        from pyramid.interfaces import IRequestExtensions
        config = self._makeOne(autocommit=True)
        config.add_request_method(name='foo')
        exts = config.registry.queryUtility(IRequestExtensions)
@@ -116,8 +146,12 @@
    def test_add_request_method_with_None_callable_conflict(self):
        from pyramid.exceptions import ConfigurationConflictError
        config = self._makeOne()
        def bar(self): pass
        def bar(self):  # pragma: no cover
            pass
        config.add_request_method(name='foo')
        config.add_request_method(bar, name='foo')
        self.assertRaises(ConfigurationConflictError, config.commit)
@@ -127,12 +161,13 @@
        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_, PY2
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne(autocommit=True)
        def boomshaka(r): pass
        def boomshaka(r):  # pragma: no cover
            pass
        def get_bad_name():
            if PY2:
@@ -146,8 +181,12 @@
    def test_set_execution_policy(self):
        from pyramid.interfaces import IExecutionPolicy
        config = self._makeOne(autocommit=True)
        def dummy_policy(environ, router): pass
        def dummy_policy(environ, router):  # pragma: no cover
            pass
        config.set_execution_policy(dummy_policy)
        registry = config.registry
        result = registry.queryUtility(IExecutionPolicy)
@@ -156,6 +195,7 @@
    def test_set_execution_policy_to_None(self):
        from pyramid.interfaces import IExecutionPolicy
        from pyramid.router import default_execution_policy
        config = self._makeOne(autocommit=True)
        config.set_execution_policy(None)
        registry = config.registry
tests/test_config/test_i18n.py
@@ -5,56 +5,73 @@
here = os.path.dirname(__file__)
locale = os.path.abspath(
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale'))
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale')
)
locale2 = os.path.abspath(
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale2'))
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale2')
)
locale3 = os.path.abspath(
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale3'))
    os.path.join(here, '..', 'pkgs', 'localeapp', 'locale3')
)
class TestI18NConfiguratorMixin(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_set_locale_negotiator(self):
        from pyramid.interfaces import ILocaleNegotiator
        config = self._makeOne(autocommit=True)
        def negotiator(request): pass
        def negotiator(request):  # pragma: no cover
            pass
        config.set_locale_negotiator(negotiator)
        self.assertEqual(config.registry.getUtility(ILocaleNegotiator),
                         negotiator)
        self.assertEqual(
            config.registry.getUtility(ILocaleNegotiator), negotiator
        )
    def test_set_locale_negotiator_dottedname(self):
        from pyramid.interfaces import ILocaleNegotiator
        config = self._makeOne(autocommit=True)
        config.set_locale_negotiator(
            'tests.test_config.dummyfactory')
        self.assertEqual(config.registry.getUtility(ILocaleNegotiator),
                         dummyfactory)
        config.set_locale_negotiator('tests.test_config.dummyfactory')
        self.assertEqual(
            config.registry.getUtility(ILocaleNegotiator), dummyfactory
        )
    def test_add_translation_dirs_missing_dir(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne()
        config.add_translation_dirs('/wont/exist/on/my/system')
        self.assertRaises(ConfigurationError, config.commit)
    def test_add_translation_dirs_no_specs(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne()
        config.add_translation_dirs()
        self.assertEqual(config.registry.queryUtility(ITranslationDirectories),
                         None)
        self.assertEqual(
            config.registry.queryUtility(ITranslationDirectories), None
        )
    def test_add_translation_dirs_asset_spec(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs('tests.pkgs.localeapp:locale')
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale])
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories), [locale]
        )
    def test_add_translation_dirs_asset_spec_existing_translation_dirs(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        directories = ['abc']
        config.registry.registerUtility(directories, ITranslationDirectories)
@@ -64,69 +81,91 @@
    def test_add_translation_dirs_multiple_specs(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs('tests.pkgs.localeapp:locale',
                                    'tests.pkgs.localeapp:locale2')
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale, locale2])
        config.add_translation_dirs(
            'tests.pkgs.localeapp:locale', 'tests.pkgs.localeapp:locale2'
        )
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories),
            [locale, locale2],
        )
    def test_add_translation_dirs_multiple_specs_multiple_calls(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs('tests.pkgs.localeapp:locale',
                                    'tests.pkgs.localeapp:locale2')
        config.add_translation_dirs(
            'tests.pkgs.localeapp:locale', 'tests.pkgs.localeapp:locale2'
        )
        config.add_translation_dirs('tests.pkgs.localeapp:locale3')
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale3, locale, locale2])
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories),
            [locale3, locale, locale2],
        )
    def test_add_translation_dirs_override_multiple_specs_multiple_calls(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs('tests.pkgs.localeapp:locale',
                                    'tests.pkgs.localeapp:locale2')
        config.add_translation_dirs('tests.pkgs.localeapp:locale3',
                                    override=True)
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale, locale2, locale3])
        config.add_translation_dirs(
            'tests.pkgs.localeapp:locale', 'tests.pkgs.localeapp:locale2'
        )
        config.add_translation_dirs(
            'tests.pkgs.localeapp:locale3', override=True
        )
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories),
            [locale, locale2, locale3],
        )
    def test_add_translation_dirs_invalid_kwargs(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        with self.assertRaises(TypeError):
            config.add_translation_dirs('tests.pkgs.localeapp:locale',
                                        foo=1)
            config.add_translation_dirs('tests.pkgs.localeapp:locale', foo=1)
    def test_add_translation_dirs_abspath(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs(locale)
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale])
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories), [locale]
        )
    def test_add_translation_dirs_uses_override_out_of_order(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne()
        config.add_translation_dirs('tests.pkgs.localeapp:locale')
        config.override_asset('tests.pkgs.localeapp:locale/',
                              'tests.pkgs.localeapp:locale2/')
        config.override_asset(
            'tests.pkgs.localeapp:locale/', 'tests.pkgs.localeapp:locale2/'
        )
        config.commit()
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale2])
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories), [locale2]
        )
    def test_add_translation_dirs_doesnt_use_override_w_autocommit(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.add_translation_dirs('tests.pkgs.localeapp:locale')
        config.override_asset('tests.pkgs.localeapp:locale/',
                              'tests.pkgs.localeapp:locale2/')
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale])
        config.override_asset(
            'tests.pkgs.localeapp:locale/', 'tests.pkgs.localeapp:locale2/'
        )
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories), [locale]
        )
    def test_add_translation_dirs_uses_override_w_autocommit(self):
        from pyramid.interfaces import ITranslationDirectories
        config = self._makeOne(autocommit=True)
        config.override_asset('tests.pkgs.localeapp:locale/',
                              'tests.pkgs.localeapp:locale2/')
        config.override_asset(
            'tests.pkgs.localeapp:locale/', 'tests.pkgs.localeapp:locale2/'
        )
        config.add_translation_dirs('tests.pkgs.localeapp:locale')
        self.assertEqual(config.registry.getUtility(ITranslationDirectories),
                         [locale2])
        self.assertEqual(
            config.registry.getUtility(ITranslationDirectories), [locale2]
        )
tests/test_config/test_init.py
@@ -1,6 +1,7 @@
import unittest
import os
import unittest
from zope.interface import Interface
from zope.interface import implementer
from pyramid.compat import im_func
from pyramid.testing import skip_on
@@ -17,20 +18,29 @@
from pyramid.interfaces import IRequest
class ConfiguratorTests(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def _getViewCallable(self, config, ctx_iface=None, request_iface=None,
                         name='', exception_view=False):
    def _getViewCallable(
        self,
        config,
        ctx_iface=None,
        request_iface=None,
        name='',
        exception_view=False,
    ):
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IExceptionViewClassifier
        if exception_view: # pragma: no cover
        if exception_view:  # pragma: no cover
            classifier = IExceptionViewClassifier
        else:
            classifier = IViewClassifier
@@ -39,16 +49,22 @@
        if request_iface is None:
            request_iface = IRequest
        return config.registry.adapters.lookup(
            (classifier, request_iface, ctx_iface), IView, name=name,
            default=None)
            (classifier, request_iface, ctx_iface),
            IView,
            name=name,
            default=None,
        )
    def _registerEventListener(self, config, event_iface=None):
        if event_iface is None: # pragma: no cover
        if event_iface is None:  # pragma: no cover
            from zope.interface import Interface
            event_iface = Interface
        L = []
        def subscriber(*event):
            L.extend(event)
        config.registry.registerHandler(subscriber, (event_iface,))
        return L
@@ -62,6 +78,7 @@
        from pyramid.interfaces import ISettings
        from pyramid.config import Configurator
        from pyramid.interfaces import IRendererFactory
        config = Configurator()
        this_pkg = sys.modules['tests.test_config']
        self.assertTrue(config.registry.getUtility(ISettings))
@@ -72,27 +89,32 @@
    def test_begin(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        config.manager = manager
        config.begin()
        self.assertEqual(manager.pushed,
                         {'registry':config.registry, 'request':None})
        self.assertEqual(
            manager.pushed, {'registry': config.registry, 'request': None}
        )
        self.assertEqual(manager.popped, False)
    def test_begin_with_request(self):
        from pyramid.config import Configurator
        config = Configurator()
        request = object()
        manager = DummyThreadLocalManager()
        config.manager = manager
        config.begin(request=request)
        self.assertEqual(manager.pushed,
                         {'registry':config.registry, 'request':request})
        self.assertEqual(
            manager.pushed, {'registry': config.registry, 'request': request}
        )
        self.assertEqual(manager.popped, False)
    def test_begin_overrides_request(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        req = object()
@@ -107,6 +129,7 @@
    def test_begin_propagates_request_for_same_registry(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        req = object()
@@ -120,6 +143,7 @@
    def test_begin_does_not_propagate_request_for_diff_registry(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        req = object()
@@ -133,6 +157,7 @@
    def test_end(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        pushed = manager.pushed
@@ -143,14 +168,16 @@
    def test_context_manager(self):
        from pyramid.config import Configurator
        config = Configurator()
        manager = DummyThreadLocalManager()
        config.manager = manager
        view = lambda r: None
        with config as ctx:
            self.assertTrue(config is ctx)
            self.assertEqual(manager.pushed,
                             {'registry': config.registry, 'request': None})
            self.assertEqual(
                manager.pushed, {'registry': config.registry, 'request': None}
            )
            self.assertFalse(manager.popped)
            config.add_view(view)
        self.assertTrue(manager.popped)
@@ -160,14 +187,15 @@
    def test_ctor_with_package_registry(self):
        import sys
        from pyramid.config import Configurator
        pkg = sys.modules['pyramid']
        config = Configurator(package=pkg)
        self.assertEqual(config.package, pkg)
    def test_ctor_noreg_custom_settings(self):
        from pyramid.interfaces import ISettings
        settings = {'reload_templates':True,
                    'mysetting':True}
        settings = {'reload_templates': True, 'mysetting': True}
        config = self._makeOne(settings=settings)
        settings = config.registry.getUtility(ISettings)
        self.assertEqual(settings['reload_templates'], True)
@@ -176,12 +204,14 @@
    def test_ctor_noreg_debug_logger_None_default(self):
        from pyramid.interfaces import IDebugLogger
        config = self._makeOne()
        logger = config.registry.getUtility(IDebugLogger)
        self.assertEqual(logger.name, 'tests.test_config')
    def test_ctor_noreg_debug_logger_non_None(self):
        from pyramid.interfaces import IDebugLogger
        logger = object()
        config = self._makeOne(debug_logger=logger)
        result = config.registry.getUtility(IDebugLogger)
@@ -189,6 +219,7 @@
    def test_ctor_authentication_policy(self):
        from pyramid.interfaces import IAuthenticationPolicy
        policy = object()
        config = self._makeOne(authentication_policy=policy)
        config.commit()
@@ -202,6 +233,7 @@
    def test_ctor_no_root_factory(self):
        from pyramid.interfaces import IRootFactory
        config = self._makeOne()
        self.assertEqual(config.registry.queryUtility(IRootFactory), None)
        config.commit()
@@ -209,6 +241,7 @@
    def test_ctor_with_root_factory(self):
        from pyramid.interfaces import IRootFactory
        factory = object()
        config = self._makeOne(root_factory=factory)
        self.assertEqual(config.registry.queryUtility(IRootFactory), None)
@@ -217,27 +250,36 @@
    def test_ctor_alternate_renderers(self):
        from pyramid.interfaces import IRendererFactory
        renderer = object()
        config = self._makeOne(renderers=[('yeah', renderer)])
        config.commit()
        self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'),
                         renderer)
        self.assertEqual(
            config.registry.getUtility(IRendererFactory, 'yeah'), renderer
        )
    def test_ctor_default_renderers(self):
        from pyramid.interfaces import IRendererFactory
        from pyramid.renderers import json_renderer_factory
        config = self._makeOne()
        self.assertEqual(config.registry.getUtility(IRendererFactory, 'json'),
                         json_renderer_factory)
        self.assertEqual(
            config.registry.getUtility(IRendererFactory, 'json'),
            json_renderer_factory,
        )
    def test_ctor_default_permission(self):
        from pyramid.interfaces import IDefaultPermission
        config = self._makeOne(default_permission='view')
        config.commit()
        self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view')
        self.assertEqual(
            config.registry.getUtility(IDefaultPermission), 'view'
        )
    def test_ctor_session_factory(self):
        from pyramid.interfaces import ISessionFactory
        factory = object()
        config = self._makeOne(session_factory=factory)
        self.assertEqual(config.registry.queryUtility(ISessionFactory), None)
@@ -246,39 +288,46 @@
    def test_ctor_default_view_mapper(self):
        from pyramid.interfaces import IViewMapperFactory
        mapper = object()
        config = self._makeOne(default_view_mapper=mapper)
        config.commit()
        self.assertEqual(config.registry.getUtility(IViewMapperFactory),
                         mapper)
        self.assertEqual(
            config.registry.getUtility(IViewMapperFactory), mapper
        )
    def test_ctor_httpexception_view_default(self):
        from pyramid.interfaces import IExceptionResponse
        from pyramid.httpexceptions import default_exceptionresponse_view
        from pyramid.interfaces import IRequest
        config = self._makeOne()
        view = self._getViewCallable(config,
                                     ctx_iface=IExceptionResponse,
                                     request_iface=IRequest)
        view = self._getViewCallable(
            config, ctx_iface=IExceptionResponse, request_iface=IRequest
        )
        self.assertTrue(view.__wraps__ is default_exceptionresponse_view)
    def test_ctor_exceptionresponse_view_None(self):
        from pyramid.interfaces import IExceptionResponse
        from pyramid.interfaces import IRequest
        config = self._makeOne(exceptionresponse_view=None)
        view = self._getViewCallable(config,
                                     ctx_iface=IExceptionResponse,
                                     request_iface=IRequest)
        view = self._getViewCallable(
            config, ctx_iface=IExceptionResponse, request_iface=IRequest
        )
        self.assertTrue(view is None)
    def test_ctor_exceptionresponse_view_custom(self):
        from pyramid.interfaces import IExceptionResponse
        from pyramid.interfaces import IRequest
        def exceptionresponse_view(context, request): pass
        def exceptionresponse_view(context, request):  # pragma: no cover
            pass
        config = self._makeOne(exceptionresponse_view=exceptionresponse_view)
        view = self._getViewCallable(config,
                                     ctx_iface=IExceptionResponse,
                                     request_iface=IRequest)
        view = self._getViewCallable(
            config, ctx_iface=IExceptionResponse, request_iface=IRequest
        )
        self.assertTrue(view.__wraps__ is exceptionresponse_view)
    def test_ctor_with_introspection(self):
@@ -287,27 +336,33 @@
    def test_ctor_default_webob_response_adapter_registered(self):
        from webob import Response as WebobResponse
        response = WebobResponse()
        from pyramid.interfaces import IResponse
        config = self._makeOne(autocommit=True)
        result = config.registry.queryAdapter(response, IResponse)
        self.assertEqual(result, response)
    def test_with_package_module(self):
        from . import test_init
        config = self._makeOne()
        newconfig = config.with_package(test_init)
        import tests.test_config
        self.assertEqual(newconfig.package, tests.test_config)
    def test_with_package_package(self):
        from tests import test_config
        config = self._makeOne()
        newconfig = config.with_package(test_config)
        self.assertEqual(newconfig.package, test_config)
    def test_with_package(self):
        import tests
        config = self._makeOne()
        config.basepath = 'basepath'
        config.info = 'info'
@@ -325,6 +380,7 @@
    def test_maybe_dotted_string_success(self):
        import tests.test_config
        config = self._makeOne()
        result = config.maybe_dotted('tests.test_config')
        self.assertEqual(result, tests.test_config)
@@ -335,24 +391,28 @@
    def test_maybe_dotted_notstring_success(self):
        import tests.test_config
        config = self._makeOne()
        result = config.maybe_dotted(tests.test_config)
        self.assertEqual(result, tests.test_config)
    def test_absolute_asset_spec_already_absolute(self):
        import tests.test_config
        config = self._makeOne(package=tests.test_config)
        result = config.absolute_asset_spec('already:absolute')
        self.assertEqual(result, 'already:absolute')
    def test_absolute_asset_spec_notastring(self):
        import tests.test_config
        config = self._makeOne(package=tests.test_config)
        result = config.absolute_asset_spec(None)
        self.assertEqual(result, None)
    def test_absolute_asset_spec_relative(self):
        import tests.test_config
        config = self._makeOne(package=tests.test_config)
        result = config.absolute_asset_spec('files')
        self.assertEqual(result, 'tests.test_config:files')
@@ -373,13 +433,17 @@
    def test__fix_registry_queryAdapterOrSelf(self):
        from zope.interface import Interface
        from zope.interface import implementer
        class IFoo(Interface):
            pass
        @implementer(IFoo)
        class Foo(object):
            pass
        class Bar(object):
            pass
        adaptation = ()
        foo = Foo()
        bar = Bar()
@@ -397,9 +461,16 @@
        self.assertEqual(len(reg.adapters), 1)
        args, kw = reg.adapters[0]
        self.assertEqual(args[0]('abc'), 'abc')
        self.assertEqual(kw,
                         {'info': '', 'provided': 'provided',
                          'required': 'required', 'name': 'abc', 'event': True})
        self.assertEqual(
            kw,
            {
                'info': '',
                'provided': 'provided',
                'required': 'required',
                'name': 'abc',
                'event': True,
            },
        )
    def test__fix_registry_adds__lock(self):
        reg = DummyRegistry()
@@ -428,6 +499,7 @@
        from webob.exc import WSGIHTTPException
        from pyramid.interfaces import IExceptionResponse
        from pyramid.view import default_exceptionresponse_view
        reg = DummyRegistry()
        config = self._makeOne(reg)
        views = []
@@ -435,17 +507,29 @@
        config.add_default_view_predicates = lambda *arg: None
        config._add_tween = lambda *arg, **kw: False
        config.setup_registry()
        self.assertEqual(views[0], ((default_exceptionresponse_view,),
                                    {'context':IExceptionResponse}))
        self.assertEqual(views[1], ((default_exceptionresponse_view,),
                                    {'context':WSGIHTTPException}))
        self.assertEqual(
            views[0],
            (
                (default_exceptionresponse_view,),
                {'context': IExceptionResponse},
            ),
        )
        self.assertEqual(
            views[1],
            (
                (default_exceptionresponse_view,),
                {'context': WSGIHTTPException},
            ),
        )
    def test_setup_registry_registers_default_view_predicates(self):
        reg = DummyRegistry()
        config = self._makeOne(reg)
        vp_called = []
        config.add_view = lambda *arg, **kw: None
        config.add_default_view_predicates = lambda *arg: vp_called.append(True)
        config.add_default_view_predicates = lambda *arg: vp_called.append(
            True
        )
        config._add_tween = lambda *arg, **kw: False
        config.setup_registry()
        self.assertTrue(vp_called)
@@ -453,11 +537,13 @@
    def test_setup_registry_registers_default_webob_iresponse_adapter(self):
        from webob import Response
        from pyramid.interfaces import IResponse
        config = self._makeOne()
        config.setup_registry()
        response = Response()
        self.assertTrue(
            config.registry.queryAdapter(response, IResponse) is response)
            config.registry.queryAdapter(response, IResponse) is response
        )
    def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self):
        from pyramid.renderers import null_renderer
@@ -465,24 +551,29 @@
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPNotFound
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg, autocommit=True)
        config.setup_registry() # registers IExceptionResponse default view
        config.setup_registry()  # registers IExceptionResponse default view
        def myview(context, request):
            return 'OK'
        config.add_view(myview, context=HTTPNotFound, renderer=null_renderer)
        request = self._makeRequest(config)
        view = self._getViewCallable(config,
                                     ctx_iface=implementedBy(HTTPNotFound),
                                     request_iface=IRequest)
        view = self._getViewCallable(
            config,
            ctx_iface=implementedBy(HTTPNotFound),
            request_iface=IRequest,
        )
        result = view(None, request)
        self.assertEqual(result, 'OK')
    def test_setup_registry_custom_settings(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import ISettings
        settings = {'reload_templates':True,
                    'mysetting':True}
        settings = {'reload_templates': True, 'mysetting': True}
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(settings=settings)
@@ -494,6 +585,7 @@
    def test_setup_registry_debug_logger_None_default(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IDebugLogger
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry()
@@ -503,6 +595,7 @@
    def test_setup_registry_debug_logger_non_None(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IDebugLogger
        logger = object()
        reg = Registry()
        config = self._makeOne(reg)
@@ -513,6 +606,7 @@
    def test_setup_registry_debug_logger_name(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IDebugLogger
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(debug_logger='foo')
@@ -522,6 +616,7 @@
    def test_setup_registry_authentication_policy(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IAuthenticationPolicy
        policy = object()
        reg = Registry()
        config = self._makeOne(reg)
@@ -533,29 +628,36 @@
    def test_setup_registry_authentication_policy_dottedname(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IAuthenticationPolicy
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(authentication_policy='tests.test_config')
        config.commit()
        result = reg.getUtility(IAuthenticationPolicy)
        import tests.test_config
        self.assertEqual(result, tests.test_config)
    def test_setup_registry_authorization_policy_dottedname(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IAuthorizationPolicy
        reg = Registry()
        config = self._makeOne(reg)
        dummy = object()
        config.setup_registry(authentication_policy=dummy,
                              authorization_policy='tests.test_config')
        config.setup_registry(
            authentication_policy=dummy,
            authorization_policy='tests.test_config',
        )
        config.commit()
        result = reg.getUtility(IAuthorizationPolicy)
        import tests.test_config
        self.assertEqual(result, tests.test_config)
    def test_setup_registry_authorization_policy_only(self):
        from pyramid.registry import Registry
        policy = object()
        reg = Registry()
        config = self._makeOne(reg)
@@ -565,6 +667,7 @@
    def test_setup_registry_no_default_root_factory(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IRootFactory
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry()
@@ -574,9 +677,11 @@
    def test_setup_registry_dottedname_root_factory(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IRootFactory
        reg = Registry()
        config = self._makeOne(reg)
        import tests.test_config
        config.setup_registry(root_factory='tests.test_config')
        self.assertEqual(reg.queryUtility(IRootFactory), None)
        config.commit()
@@ -585,9 +690,11 @@
    def test_setup_registry_locale_negotiator_dottedname(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import ILocaleNegotiator
        reg = Registry()
        config = self._makeOne(reg)
        import tests.test_config
        config.setup_registry(locale_negotiator='tests.test_config')
        self.assertEqual(reg.queryUtility(ILocaleNegotiator), None)
        config.commit()
@@ -597,6 +704,7 @@
    def test_setup_registry_locale_negotiator(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import ILocaleNegotiator
        reg = Registry()
        config = self._makeOne(reg)
        negotiator = object()
@@ -609,6 +717,7 @@
    def test_setup_registry_request_factory(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IRequestFactory
        reg = Registry()
        config = self._makeOne(reg)
        factory = object()
@@ -621,6 +730,7 @@
    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()
@@ -633,9 +743,11 @@
    def test_setup_registry_request_factory_dottedname(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IRequestFactory
        reg = Registry()
        config = self._makeOne(reg)
        import tests.test_config
        config.setup_registry(request_factory='tests.test_config')
        self.assertEqual(reg.queryUtility(IRequestFactory), None)
        config.commit()
@@ -645,17 +757,18 @@
    def test_setup_registry_alternate_renderers(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IRendererFactory
        renderer = object()
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(renderers=[('yeah', renderer)])
        config.commit()
        self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'),
                         renderer)
        self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), renderer)
    def test_setup_registry_default_permission(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IDefaultPermission
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(default_permission='view')
@@ -664,12 +777,12 @@
    def test_setup_registry_includes(self):
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg)
        settings = {
            'pyramid.includes':
"""tests.test_config.dummy_include
tests.test_config.dummy_include2""",
            'pyramid.includes': """tests.test_config.dummy_include
tests.test_config.dummy_include2"""
        }
        config.setup_registry(settings=settings)
        self.assertTrue(reg.included)
@@ -677,11 +790,12 @@
    def test_setup_registry_includes_spaces(self):
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg)
        settings = {
            'pyramid.includes':
"""tests.test_config.dummy_include tests.test_config.dummy_include2""",
            'pyramid.includes': """tests.test_config.dummy_include tests.\
test_config.dummy_include2"""
        }
        config.setup_registry(settings=settings)
        self.assertTrue(reg.included)
@@ -690,19 +804,17 @@
    def test_setup_registry_tweens(self):
        from pyramid.interfaces import ITweens
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg)
        settings = {
            'pyramid.tweens':
                    'tests.test_config.dummy_tween_factory'
        }
        settings = {'pyramid.tweens': 'tests.test_config.dummy_tween_factory'}
        config.setup_registry(settings=settings)
        config.commit()
        tweens = config.registry.getUtility(ITweens)
        self.assertEqual(
            tweens.explicit,
            [('tests.test_config.dummy_tween_factory',
              dummy_tween_factory)])
            [('tests.test_config.dummy_tween_factory', dummy_tween_factory)],
        )
    def test_introspector_decorator(self):
        inst = self._makeOne()
@@ -723,6 +835,7 @@
        import pyramid.config
        from pyramid.router import Router
        from pyramid.interfaces import IApplicationCreated
        manager = DummyThreadLocalManager()
        config = self._makeOne()
        subscriber = self._registerEventListener(config, IApplicationCreated)
@@ -739,6 +852,7 @@
    def test_include_with_dotted_name(self):
        from tests import test_config
        config = self._makeOne()
        config.include('tests.test_config.dummy_include')
        after = config.action_state
@@ -751,6 +865,7 @@
    def test_include_with_python_callable(self):
        from tests import test_config
        config = self._makeOne()
        config.include(dummy_include)
        after = config.action_state
@@ -763,6 +878,7 @@
    def test_include_with_module_defaults_to_includeme(self):
        from tests import test_config
        config = self._makeOne()
        config.include('tests.test_config')
        after = config.action_state
@@ -775,24 +891,31 @@
    def test_include_with_module_defaults_to_includeme_missing(self):
        from pyramid.exceptions import ConfigurationError
        config = self._makeOne()
        self.assertRaises(ConfigurationError, config.include, 'tests')
    def test_include_with_route_prefix(self):
        root_config = self._makeOne(autocommit=True)
        def dummy_subapp(config):
            self.assertEqual(config.route_prefix, 'root')
        root_config.include(dummy_subapp, route_prefix='root')
    def test_include_with_nested_route_prefix(self):
        root_config = self._makeOne(autocommit=True, route_prefix='root')
        def dummy_subapp2(config):
            self.assertEqual(config.route_prefix, 'root/nested')
        def dummy_subapp3(config):
            self.assertEqual(config.route_prefix, 'root/nested/nested2')
            config.include(dummy_subapp4)
        def dummy_subapp4(config):
            self.assertEqual(config.route_prefix, 'root/nested/nested2')
        def dummy_subapp(config):
            self.assertEqual(config.route_prefix, 'root/nested')
            config.include(dummy_subapp2)
@@ -803,41 +926,51 @@
    def test_include_with_missing_source_file(self):
        from pyramid.exceptions import ConfigurationError
        import inspect
        config = self._makeOne()
        class DummyInspect(object):
            def getmodule(self, c):
                return inspect.getmodule(c)
            def getsourcefile(self, c):
                return None
        config.inspect = DummyInspect()
        try:
            config.include('tests.test_config.dummy_include')
        except ConfigurationError as e:
            self.assertEqual(
                e.args[0],
                e.args[0],
                "No source file for module 'tests.test_config' (.py "
                "file must exist, refusing to use orphan .pyc or .pyo file).")
        else: # pragma: no cover
                "file must exist, refusing to use orphan .pyc or .pyo file).",
            )
        else:  # pragma: no cover
            raise AssertionError
    def test_include_constant_root_package(self):
        import tests
        from 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_include_threadlocals_active(self):
        from pyramid.threadlocal import get_current_registry
        from tests import test_config
        stack = []
        def include(config):
            stack.append(get_current_registry())
        config = self._makeOne()
        config.include(include)
        self.assertTrue(stack[0] is config.registry)
@@ -848,10 +981,11 @@
    def test_action_branching_kw_is_not_None(self):
        config = self._makeOne(autocommit=True)
        self.assertEqual(config.action('discrim', kw={'a':1}), None)
        self.assertEqual(config.action('discrim', kw={'a': 1}), None)
    def test_action_autocommit_with_introspectables(self):
        from pyramid.config.util import ActionInfo
        config = self._makeOne(autocommit=True)
        intr = DummyIntrospectable()
        config.action('discrim', introspectables=(intr,))
@@ -865,25 +999,32 @@
        intr = DummyIntrospectable()
        config.action('discrim', introspectables=(intr,))
        self.assertEqual(len(intr.registered), 0)
    def test_action_branching_nonautocommit_with_config_info(self):
        config = self._makeOne(autocommit=False)
        config.info = 'abc'
        state = DummyActionState()
        state.autocommit = False
        config.action_state = state
        config.action('discrim', kw={'a':1})
        config.action('discrim', kw={'a': 1})
        self.assertEqual(
            state.actions,
            [((),
             {'args': (),
             'callable': None,
             'discriminator': 'discrim',
             'includepath': (),
             'info': 'abc',
             'introspectables': (),
             'kw': {'a': 1},
             'order': 0})])
            [
                (
                    (),
                    {
                        'args': (),
                        'callable': None,
                        'discriminator': 'discrim',
                        'includepath': (),
                        'info': 'abc',
                        'introspectables': (),
                        'kw': {'a': 1},
                        'order': 0,
                    },
                )
            ],
        )
    def test_action_branching_nonautocommit_without_config_info(self):
        config = self._makeOne(autocommit=False)
@@ -892,18 +1033,25 @@
        state = DummyActionState()
        config.action_state = state
        state.autocommit = False
        config.action('discrim', kw={'a':1})
        config.action('discrim', kw={'a': 1})
        self.assertEqual(
            state.actions,
            [((),
             {'args': (),
             'callable': None,
             'discriminator': 'discrim',
             'includepath': (),
             'info': 'z',
             'introspectables': (),
             'kw': {'a': 1},
             'order': 0})])
            [
                (
                    (),
                    {
                        'args': (),
                        'callable': None,
                        'discriminator': 'discrim',
                        'includepath': (),
                        'info': 'z',
                        'introspectables': (),
                        'kw': {'a': 1},
                        'order': 0,
                    },
                )
            ],
        )
    def test_action_branching_nonautocommit_with_introspectables(self):
        config = self._makeOne(autocommit=False)
@@ -914,8 +1062,7 @@
        state.autocommit = False
        intr = DummyIntrospectable()
        config.action('discrim', introspectables=(intr,))
        self.assertEqual(
            state.actions[0][1]['introspectables'], (intr,))
        self.assertEqual(state.actions[0][1]['introspectables'], (intr,))
    def test_action_nonautocommit_with_introspectables_introspection_off(self):
        config = self._makeOne(autocommit=False)
@@ -927,14 +1074,14 @@
        state.autocommit = False
        intr = DummyIntrospectable()
        config.action('discrim', introspectables=(intr,))
        self.assertEqual(
            state.actions[0][1]['introspectables'], ())
        self.assertEqual(state.actions[0][1]['introspectables'], ())
    def test_scan_integration(self):
        from zope.interface import alsoProvides
        from pyramid.interfaces import IRequest
        from pyramid.view import render_view_to_response
        import tests.test_config.pkgs.scannable as package
        config = self._makeOne(autocommit=True)
        config.scan(package)
@@ -951,13 +1098,13 @@
        result = render_view_to_response(ctx, req, '')
        self.assertEqual(result, 'grokked_post')
        result= render_view_to_response(ctx, req, 'grokked_class')
        result = render_view_to_response(ctx, req, 'grokked_class')
        self.assertEqual(result, 'grokked_class')
        result= render_view_to_response(ctx, req, 'grokked_instance')
        result = render_view_to_response(ctx, req, 'grokked_instance')
        self.assertEqual(result, 'grokked_instance')
        result= render_view_to_response(ctx, req, 'oldstyle_grokked_class')
        result = render_view_to_response(ctx, req, 'oldstyle_grokked_class')
        self.assertEqual(result, 'oldstyle_grokked_class')
        req.method = 'GET'
@@ -968,14 +1115,15 @@
        result = render_view_to_response(ctx, req, 'another')
        self.assertEqual(result, 'another_grokked_post')
        result= render_view_to_response(ctx, req, 'another_grokked_class')
        result = render_view_to_response(ctx, req, 'another_grokked_class')
        self.assertEqual(result, 'another_grokked_class')
        result= render_view_to_response(ctx, req, 'another_grokked_instance')
        result = render_view_to_response(ctx, req, 'another_grokked_instance')
        self.assertEqual(result, 'another_grokked_instance')
        result= render_view_to_response(ctx, req,
                                        'another_oldstyle_grokked_class')
        result = render_view_to_response(
            ctx, req, 'another_oldstyle_grokked_class'
        )
        self.assertEqual(result, 'another_oldstyle_grokked_class')
        result = render_view_to_response(ctx, req, 'stacked1')
@@ -1007,8 +1155,9 @@
        # assertion may fail there.  We don't support Jython at the moment,
        # this is just a note to a future self.
        self.assertRaises(TypeError,
                          render_view_to_response, ctx, req, 'basemethod')
        self.assertRaises(
            TypeError, render_view_to_response, ctx, req, 'basemethod'
        )
        result = render_view_to_response(ctx, req, 'method1')
        self.assertEqual(result, 'method1')
@@ -1039,9 +1188,9 @@
        from pyramid.interfaces import IRequest
        from pyramid.view import render_view_to_response
        import tests.test_config.pkgs.scannable as package
        config = self._makeOne(autocommit=True)
        config.scan(package,
                    ignore='tests.test_config.pkgs.scannable.another')
        config.scan(package, ignore='tests.test_config.pkgs.scannable.another')
        ctx = DummyContext()
        req = DummyRequest()
@@ -1055,11 +1204,12 @@
        # ignored
        v = render_view_to_response(ctx, req, 'another_stacked_class2')
        self.assertEqual(v, None)
    def test_scan_integration_dottedname_package(self):
        from zope.interface import alsoProvides
        from pyramid.interfaces import IRequest
        from pyramid.view import render_view_to_response
        config = self._makeOne(autocommit=True)
        config.scan('tests.test_config.pkgs.scannable')
@@ -1081,45 +1231,56 @@
        # fancy sys.path manipulation here to appease "setup.py test" which
        # fails miserably when it can't import something in the package
        import sys
        try:
            here = os.path.dirname(__file__)
            path = os.path.join(here, 'path')
            sys.path.append(path)
            config = self._makeOne(autocommit=True)
            class FooException(Exception):
                pass
            def onerror(name):
                raise FooException
            self.assertRaises(FooException, config.scan, 'scanerror',
                              onerror=onerror)
            self.assertRaises(
                FooException, config.scan, 'scanerror', onerror=onerror
            )
        finally:
            sys.path.remove(path)
    def test_scan_integration_conflict(self):
        from tests.test_config.pkgs import selfscan
        from pyramid.config import Configurator
        c = Configurator()
        c.scan(selfscan)
        c.scan(selfscan)
        try:
            c.commit()
        except ConfigurationConflictError as why:
            def scanconflicts(e):
                conflicts = e._conflicts.values()
                for conflict in conflicts:
                    for confinst in conflict:
                        yield confinst.src
            which = list(scanconflicts(why))
            self.assertEqual(len(which), 4)
            self.assertTrue("@view_config(renderer='string')" in which)
            self.assertTrue("@view_config(name='two', renderer='string')" in
                            which)
            self.assertTrue(
                "@view_config(name='two', renderer='string')" in which
            )
    @skip_on('py3')
    def test_hook_zca(self):
        from zope.component import getSiteManager
        def foo():
            '123'
        try:
            config = self._makeOne()
            config.hook_zca()
@@ -1132,8 +1293,10 @@
    @skip_on('py3')
    def test_unhook_zca(self):
        from zope.component import getSiteManager
        def foo():
            '123'
        try:
            getSiteManager.sethook(foo)
            config = self._makeOne()
@@ -1145,18 +1308,29 @@
    def test_commit_conflict_simple(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        config.add_view(view1)
        config.add_view(view2)
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_commit_conflict_resolved_with_include(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        def includeme(config):
            config.add_view(view2)
        config.add_view(view1)
        config.include(includeme)
        config.commit()
@@ -1165,12 +1339,19 @@
    def test_commit_conflict_with_two_includes(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        def includeme1(config):
            config.add_view(view1)
        def includeme2(config):
            config.add_view(view2)
        config.include(includeme1)
        config.include(includeme2)
        try:
@@ -1179,18 +1360,27 @@
            c1, c2 = _conflictFunctions(why)
            self.assertEqual(c1, 'includeme1')
            self.assertEqual(c2, 'includeme2')
        else: #pragma: no cover
        else:  # pragma: no cover
            raise AssertionError
    def test_commit_conflict_resolved_with_two_includes_and_local(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view3(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        def view3(request):  # pragma: no cover
            pass
        def includeme1(config):
            config.add_view(view1)
        def includeme2(config):
            config.add_view(view2)
        config.include(includeme1)
        config.include(includeme2)
        config.add_view(view3)
@@ -1200,10 +1390,18 @@
    def test_autocommit_no_conflicts(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        def view1(request): pass
        def view2(request): pass
        def view3(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        def view3(request):  # pragma: no cover
            pass
        config.add_view(view1, renderer=null_renderer)
        config.add_view(view2, renderer=null_renderer)
        config.add_view(view3, renderer=null_renderer)
@@ -1213,8 +1411,13 @@
    def test_conflict_set_notfound_view(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        config.set_notfound_view(view1)
        config.set_notfound_view(view2)
        try:
@@ -1223,13 +1426,18 @@
            c1, c2 = _conflictFunctions(why)
            self.assertEqual(c1, 'test_conflict_set_notfound_view')
            self.assertEqual(c2, 'test_conflict_set_notfound_view')
        else: # pragma: no cover
        else:  # pragma: no cover
            raise AssertionError
    def test_conflict_set_forbidden_view(self):
        config = self._makeOne()
        def view1(request): pass
        def view2(request): pass
        def view1(request):  # pragma: no cover
            pass
        def view2(request):  # pragma: no cover
            pass
        config.set_forbidden_view(view1)
        config.set_forbidden_view(view2)
        try:
@@ -1238,7 +1446,7 @@
            c1, c2 = _conflictFunctions(why)
            self.assertEqual(c1, 'test_conflict_set_forbidden_view')
            self.assertEqual(c2, 'test_conflict_set_forbidden_view')
        else: # pragma: no cover
        else:  # pragma: no cover
            raise AssertionError
    def test___getattr__missing_when_directives_exist(self):
@@ -1253,31 +1461,38 @@
    def test___getattr__matches(self):
        config = self._makeOne()
        def foo(config): pass
        directives = {'foo':(foo, True)}
        def foo(config):  # pragma: no cover
            pass
        directives = {'foo': (foo, True)}
        config.registry._directives = directives
        foo_meth = config.foo
        self.assertTrue(getattr(foo_meth, im_func).__docobj__ is foo)
    def test___getattr__matches_no_action_wrap(self):
        config = self._makeOne()
        def foo(config): pass
        directives = {'foo':(foo, False)}
        def foo(config):  # pragma: no cover
            pass
        directives = {'foo': (foo, False)}
        config.registry._directives = directives
        foo_meth = config.foo
        self.assertTrue(getattr(foo_meth, im_func) is foo)
class TestConfigurator_add_directive(unittest.TestCase):
class TestConfigurator_add_directive(unittest.TestCase):
    def setUp(self):
        from pyramid.config import Configurator
        self.config = Configurator()
    def test_extend_with_dotted_name(self):
        from tests import test_config
        config = self.config
        config.add_directive(
            'dummy_extend', 'tests.test_config.dummy_extend')
        config.add_directive('dummy_extend', 'tests.test_config.dummy_extend')
        self.assertTrue(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        after = config.action_state
@@ -1288,9 +1503,11 @@
    def test_add_directive_with_partial(self):
        from tests import test_config
        config = self.config
        config.add_directive(
                'dummy_partial', 'tests.test_config.dummy_partial')
            'dummy_partial', 'tests.test_config.dummy_partial'
        )
        self.assertTrue(hasattr(config, 'dummy_partial'))
        config.dummy_partial()
        after = config.action_state
@@ -1301,9 +1518,11 @@
    def test_add_directive_with_custom_callable(self):
        from tests import test_config
        config = self.config
        config.add_directive(
                'dummy_callable', 'tests.test_config.dummy_callable')
            'dummy_callable', 'tests.test_config.dummy_callable'
        )
        self.assertTrue(hasattr(config, 'dummy_callable'))
        config.dummy_callable('discrim')
        after = config.action_state
@@ -1314,9 +1533,9 @@
    def test_extend_with_python_callable(self):
        from tests import test_config
        config = self.config
        config.add_directive(
            'dummy_extend', dummy_extend)
        config.add_directive('dummy_extend', dummy_extend)
        self.assertTrue(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        after = config.action_state
@@ -1327,10 +1546,8 @@
    def test_extend_same_name_doesnt_conflict(self):
        config = self.config
        config.add_directive(
            'dummy_extend', dummy_extend)
        config.add_directive(
            'dummy_extend', dummy_extend2)
        config.add_directive('dummy_extend', dummy_extend)
        config.add_directive('dummy_extend', dummy_extend2)
        self.assertTrue(hasattr(config, 'dummy_extend'))
        config.dummy_extend('discrim')
        after = config.action_state
@@ -1341,8 +1558,7 @@
    def test_extend_action_method_successful(self):
        config = self.config
        config.add_directive(
            'dummy_extend', dummy_extend)
        config.add_directive('dummy_extend', dummy_extend)
        config.dummy_extend('discrim')
        config.dummy_extend('discrim')
        self.assertRaises(ConfigurationConflictError, config.commit)
@@ -1360,16 +1576,25 @@
        self.assertEqual(action['callable'], None)
        self.assertEqual(action['args'], config2.package)
class TestConfigurator__add_predicate(unittest.TestCase):
    def _makeOne(self):
        from pyramid.config import Configurator
        return Configurator()
    def test_factory_as_object(self):
        config = self._makeOne()
        def _fakeAction(discriminator, callable=None, args=(), kw=None,
                        order=0, introspectables=(), **extra):
        def _fakeAction(
            discriminator,
            callable=None,
            args=(),
            kw=None,
            order=0,
            introspectables=(),
            **extra
        ):
            self.assertEqual(len(introspectables), 1)
            self.assertEqual(introspectables[0]['name'], 'testing')
            self.assertEqual(introspectables[0]['factory'], DummyPredicate)
@@ -1380,63 +1605,82 @@
    def test_factory_as_dotted_name(self):
        config = self._makeOne()
        def _fakeAction(discriminator, callable=None, args=(),
                        kw=None, order=0, introspectables=(), **extra):
        def _fakeAction(
            discriminator,
            callable=None,
            args=(),
            kw=None,
            order=0,
            introspectables=(),
            **extra
        ):
            self.assertEqual(len(introspectables), 1)
            self.assertEqual(introspectables[0]['name'], 'testing')
            self.assertEqual(introspectables[0]['factory'], DummyPredicate)
        config.action = _fakeAction
        config._add_predicate(
            'route',
            'testing',
            'tests.test_config.test_init.DummyPredicate'
            )
            'route', 'testing', 'tests.test_config.test_init.DummyPredicate'
        )
class TestActionState(unittest.TestCase):
    def _makeOne(self):
        from pyramid.config import ActionState
        return ActionState()
    def test_it(self):
        c = self._makeOne()
        self.assertEqual(c.actions, [])
    def test_action_simple(self):
        from . import dummyfactory as f
        c = self._makeOne()
        c.actions = []
        c.action(1, f, (1,), {'x':1})
        c.action(1, f, (1,), {'x': 1})
        self.assertEqual(
            c.actions,
            [{'args': (1,),
             'callable': f,
             'discriminator': 1,
             'includepath': (),
             'info': None,
             'introspectables': (),
             'kw': {'x': 1},
             'order': 0}])
            [
                {
                    'args': (1,),
                    'callable': f,
                    'discriminator': 1,
                    'includepath': (),
                    'info': None,
                    'introspectables': (),
                    'kw': {'x': 1},
                    'order': 0,
                }
            ],
        )
        c.action(None)
        self.assertEqual(
            c.actions,
            [{'args': (1,),
             'callable': f,
             'discriminator': 1,
             'includepath': (),
             'info': None,
             'introspectables': (),
             'kw': {'x': 1},
             'order': 0},
             {'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': (),
             'info': None,
             'introspectables': (),
             'kw': {},
             'order': 0},])
            [
                {
                    'args': (1,),
                    'callable': f,
                    'discriminator': 1,
                    'includepath': (),
                    'info': None,
                    'introspectables': (),
                    'kw': {'x': 1},
                    'order': 0,
                },
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': (),
                    'info': None,
                    'introspectables': (),
                    'kw': {},
                    'order': 0,
                },
            ],
        )
    def test_action_with_includepath(self):
        c = self._makeOne()
@@ -1444,42 +1688,57 @@
        c.action(None, includepath=('abc',))
        self.assertEqual(
            c.actions,
            [{'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': ('abc',),
             'info': None,
             'introspectables': (),
             'kw': {},
             'order': 0}])
            [
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': ('abc',),
                    'info': None,
                    'introspectables': (),
                    'kw': {},
                    'order': 0,
                }
            ],
        )
    def test_action_with_info(self):
        c = self._makeOne()
        c.action(None, info='abc')
        self.assertEqual(
            c.actions,
            [{'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': (),
             'info': 'abc',
             'introspectables': (),
             'kw': {},
             'order': 0}])
            [
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': (),
                    'info': 'abc',
                    'introspectables': (),
                    'kw': {},
                    'order': 0,
                }
            ],
        )
    def test_action_with_includepath_and_info(self):
        c = self._makeOne()
        c.action(None, includepath=('spec',), info='bleh')
        self.assertEqual(
            c.actions,
            [{'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': ('spec',),
             'info': 'bleh',
             'introspectables': (),
             'kw': {},
             'order': 0}])
            [
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': ('spec',),
                    'info': 'bleh',
                    'introspectables': (),
                    'kw': {},
                    'order': 0,
                }
            ],
        )
    def test_action_with_order(self):
        c = self._makeOne()
@@ -1487,14 +1746,19 @@
        c.action(None, order=99999)
        self.assertEqual(
            c.actions,
            [{'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': (),
             'info': None,
             'introspectables': (),
             'kw': {},
             'order': 99999}])
            [
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': (),
                    'info': None,
                    'introspectables': (),
                    'kw': {},
                    'order': 99999,
                }
            ],
        )
    def test_action_with_introspectables(self):
        c = self._makeOne()
@@ -1503,14 +1767,19 @@
        c.action(None, introspectables=(intr,))
        self.assertEqual(
            c.actions,
            [{'args': (),
             'callable': None,
             'discriminator': None,
             'includepath': (),
             'info': None,
             'introspectables': (intr,),
             'kw': {},
             'order': 0}])
            [
                {
                    'args': (),
                    'callable': None,
                    'discriminator': None,
                    'includepath': (),
                    'info': None,
                    'introspectables': (intr,),
                    'kw': {},
                    'order': 0,
                }
            ],
        )
    def test_processSpec(self):
        c = self._makeOne()
@@ -1519,190 +1788,269 @@
    def test_execute_actions_tuples(self):
        output = []
        def f(*a, **k):
            output.append((a, k))
        c = self._makeOne()
        c.actions = [
            (1, f, (1,)),
            (1, f, (11,), {}, ('x', )),
            (1, f, (11,), {}, ('x',)),
            (2, f, (2,)),
            (None, None),
            ]
        ]
        c.execute_actions()
        self.assertEqual(output,  [((1,), {}), ((2,), {})])
        self.assertEqual(output, [((1,), {}), ((2,), {})])
    def test_execute_actions_dicts(self):
        output = []
        def f(*a, **k):
            output.append((a, k))
        c = self._makeOne()
        c.actions = [
            {'discriminator':1, 'callable':f, 'args':(1,), 'kw':{},
             'order':0, 'includepath':(), 'info':None,
             'introspectables':()},
            {'discriminator':1, 'callable':f, 'args':(11,), 'kw':{},
             'includepath':('x',), 'order': 0, 'info':None,
             'introspectables':()},
            {'discriminator':2, 'callable':f, 'args':(2,), 'kw':{},
             'order':0, 'includepath':(), 'info':None,
             'introspectables':()},
            {'discriminator':None, 'callable':None, 'args':(), 'kw':{},
             'order':0, 'includepath':(), 'info':None,
             'introspectables':()},
            ]
            {
                'discriminator': 1,
                'callable': f,
                'args': (1,),
                'kw': {},
                'order': 0,
                'includepath': (),
                'info': None,
                'introspectables': (),
            },
            {
                'discriminator': 1,
                'callable': f,
                'args': (11,),
                'kw': {},
                'includepath': ('x',),
                'order': 0,
                'info': None,
                'introspectables': (),
            },
            {
                'discriminator': 2,
                'callable': f,
                'args': (2,),
                'kw': {},
                'order': 0,
                'includepath': (),
                'info': None,
                'introspectables': (),
            },
            {
                'discriminator': None,
                'callable': None,
                'args': (),
                'kw': {},
                'order': 0,
                'includepath': (),
                'info': None,
                'introspectables': (),
            },
        ]
        c.execute_actions()
        self.assertEqual(output,  [((1,), {}), ((2,), {})])
        self.assertEqual(output, [((1,), {}), ((2,), {})])
    def test_execute_actions_with_introspectables(self):
        output = []
        def f(*a, **k):
            output.append((a, k))
        c = self._makeOne()
        intr = DummyIntrospectable()
        c.actions = [
            {'discriminator':1, 'callable':f, 'args':(1,), 'kw':{},
             'order':0, 'includepath':(), 'info':None,
             'introspectables':(intr,)},
            ]
            {
                'discriminator': 1,
                'callable': f,
                'args': (1,),
                'kw': {},
                'order': 0,
                'includepath': (),
                'info': None,
                'introspectables': (intr,),
            }
        ]
        introspector = object()
        c.execute_actions(introspector=introspector)
        self.assertEqual(output,  [((1,), {})])
        self.assertEqual(output, [((1,), {})])
        self.assertEqual(intr.registered, [(introspector, None)])
    def test_execute_actions_with_introspectable_no_callable(self):
        c = self._makeOne()
        intr = DummyIntrospectable()
        c.actions = [
            {'discriminator':1, 'callable':None, 'args':(1,), 'kw':{},
             'order':0, 'includepath':(), 'info':None,
             'introspectables':(intr,)},
            ]
            {
                'discriminator': 1,
                'callable': None,
                'args': (1,),
                'kw': {},
                'order': 0,
                'includepath': (),
                'info': None,
                'introspectables': (intr,),
            }
        ]
        introspector = object()
        c.execute_actions(introspector=introspector)
        self.assertEqual(intr.registered, [(introspector, None)])
    def test_execute_actions_error(self):
        output = []
        def f(*a, **k):
            output.append(('f', a, k))
        def bad():
            raise NotImplementedError
        c = self._makeOne()
        c.actions = [
            (1, f, (1,)),
            (1, f, (11,), {}, ('x', )),
            (1, f, (11,), {}, ('x',)),
            (2, f, (2,)),
            (3, bad, (), {}, (), 'oops')
            ]
            (3, bad, (), {}, (), 'oops'),
        ]
        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.actions = [(1, f, (1,))]
        c.execute_actions()
        self.assertEqual(output, [('f', (1,), {}), ('g', (8,), {})])
    def test_reentrant_action_with_deferred_discriminator(self):
        # see https://github.com/Pylons/pyramid/issues/2697
        from pyramid.registry import Deferred
        output = []
        c = self._makeOne()
        def f(*a, **k):
            output.append(('f', a, k))
            c.actions.append((4, g, (4,), {}, (), None, 2))
        def g(*a, **k):
            output.append(('g', a, k))
        def h(*a, **k):
            output.append(('h', a, k))
        def discrim():
            self.assertEqual(output, [('f', (1,), {}), ('g', (2,), {})])
            return 3
        d = Deferred(discrim)
        c.actions = [
            (d, h, (3,), {}, (), None, 1), # order 1
            (1, f, (1,)), # order 0
            (2, g, (2,)), # order 0
            (d, h, (3,), {}, (), None, 1),  # order 1
            (1, f, (1,)),  # order 0
            (2, g, (2,)),  # order 0
        ]
        c.execute_actions()
        self.assertEqual(output, [
            ('f', (1,), {}), ('g', (2,), {}), ('h', (3,), {}), ('g', (4,), {})])
        self.assertEqual(
            output,
            [
                ('f', (1,), {}),
                ('g', (2,), {}),
                ('h', (3,), {}),
                ('g', (4,), {}),
            ],
        )
    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,)),
        ]
        def g(*a, **k):  # pragma: no cover
            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,)),
        ]
        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,)),
        ])
        self.assertEqual(c.actions, [(1, f, (1,)), (3, g, (8,))])
    def test_executing_conflicting_action_across_orders(self):
        from pyramid.exceptions import ConfigurationConflictError
        c = self._makeOne()
        def f(*a, **k): pass
        def g(*a, **k): pass
        c.actions = [
            (1, f, (1,), {}, (), None, -1),
            (1, g, (2,)),
        ]
        def f(*a, **k):
            pass
        def g(*a, **k):  # pragma: no cover
            pass
        c.actions = [(1, f, (1,), {}, (), None, -1), (1, g, (2,))]
        self.assertRaises(ConfigurationConflictError, c.execute_actions)
    def test_executing_conflicting_action_across_reentrant_orders(self):
        from pyramid.exceptions import ConfigurationConflictError
        c = self._makeOne()
        def f(*a, **k):
            c.actions.append((1, g, (8,)))
        def g(*a, **k): pass
        c.actions = [
            (1, f, (1,), {}, (), None, -1),
        ]
        def g(*a, **k):  # pragma: no cover
            pass
        c.actions = [(1, f, (1,), {}, (), None, -1)]
        self.assertRaises(ConfigurationConflictError, c.execute_actions)
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
                   )
            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
        def my_view(request):  # pragma: no cover
            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]
@@ -1713,291 +2061,352 @@
    def test_deferred_discriminator(self):
        # see https://github.com/Pylons/pyramid/issues/2697
        from pyramid.config import PHASE0_CONFIG
        config = self._makeConfigurator()
        def deriver(view, info): return view
        def deriver(view, info):
            return view
        deriver.options = ('foo',)
        config.add_view_deriver(deriver, 'foo_view')
        # add_view uses a deferred discriminator and will fail if executed
        # prior to add_view_deriver executing its action
        config.add_view(lambda r: r.response, name='', foo=1)
        def dummy_action():
            # trigger a re-entrant action
            config.action(None, lambda: None)
        config.action(None, dummy_action, order=PHASE0_CONFIG)
        config.commit()
class Test_resolveConflicts(unittest.TestCase):
    def _callFUT(self, actions):
        from pyramid.config import resolveConflicts
        return resolveConflicts(actions)
    def test_it_success_tuples(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (None, f),
            (1, f, (1,), {}, (), 'first'),
            (1, f, (2,), {}, ('x',), 'second'),
            (1, f, (3,), {}, ('y',), 'third'),
            (4, f, (4,), {}, ('y',), 'should be last', 99999),
            (3, f, (3,), {}, ('y',)),
            (None, f, (5,), {}, ('y',)),
            ])
        result = self._callFUT(
            [
                (None, f),
                (1, f, (1,), {}, (), 'first'),
                (1, f, (2,), {}, ('x',), 'second'),
                (1, f, (3,), {}, ('y',), 'third'),
                (4, f, (4,), {}, ('y',), 'should be last', 99999),
                (3, f, (3,), {}, ('y',)),
                (None, f, (5,), {}, ('y',)),
            ]
        )
        result = list(result)
        self.assertEqual(
            result,
            [{'info': None,
              'args': (),
              'callable': f,
              'introspectables': (),
              'kw': {},
              'discriminator': None,
              'includepath': (),
              'order': 0},
              {'info': 'first',
               'args': (1,),
               'callable': f,
               'introspectables': (),
               'kw': {},
               'discriminator': 1,
               'includepath': (),
               'order': 0},
               {'info': None,
                'args': (3,),
                'callable': f,
                'introspectables': (),
                'kw': {},
                'discriminator': 3,
                'includepath': ('y',),
                'order': 0},
                {'info': None,
                 'args': (5,),
                 'callable': f,
                 'introspectables': (),
                 'kw': {},
                 'discriminator': None,
                 'includepath': ('y',),
                 'order': 0},
                 {'info': 'should be last',
                  'args': (4,),
                  'callable': f,
                  'introspectables': (),
                  'kw': {},
                  'discriminator': 4,
                  'includepath': ('y',),
                  'order': 99999}
                  ]
                  )
            [
                {
                    'info': None,
                    'args': (),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': None,
                    'includepath': (),
                    'order': 0,
                },
                {
                    'info': 'first',
                    'args': (1,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 1,
                    'includepath': (),
                    'order': 0,
                },
                {
                    'info': None,
                    'args': (3,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 3,
                    'includepath': ('y',),
                    'order': 0,
                },
                {
                    'info': None,
                    'args': (5,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': None,
                    'includepath': ('y',),
                    'order': 0,
                },
                {
                    'info': 'should be last',
                    'args': (4,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 4,
                    'includepath': ('y',),
                    'order': 99999,
                },
            ],
        )
    def test_it_success_dicts(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (None, f),
            (1, f, (1,), {}, (), 'first'),
            (1, f, (2,), {}, ('x',), 'second'),
            (1, f, (3,), {}, ('y',), 'third'),
            (4, f, (4,), {}, ('y',), 'should be last', 99999),
            (3, f, (3,), {}, ('y',)),
            (None, f, (5,), {}, ('y',)),
            ])
        result = self._callFUT(
            [
                (None, f),
                (1, f, (1,), {}, (), 'first'),
                (1, f, (2,), {}, ('x',), 'second'),
                (1, f, (3,), {}, ('y',), 'third'),
                (4, f, (4,), {}, ('y',), 'should be last', 99999),
                (3, f, (3,), {}, ('y',)),
                (None, f, (5,), {}, ('y',)),
            ]
        )
        result = list(result)
        self.assertEqual(
            result,
            [{'info': None,
              'args': (),
              'callable': f,
              'introspectables': (),
              'kw': {},
              'discriminator': None,
              'includepath': (),
              'order': 0},
              {'info': 'first',
               'args': (1,),
               'callable': f,
               'introspectables': (),
               'kw': {},
               'discriminator': 1,
               'includepath': (),
               'order': 0},
               {'info': None,
                'args': (3,),
                'callable': f,
                'introspectables': (),
                'kw': {},
                'discriminator': 3,
                'includepath': ('y',),
                'order': 0},
                {'info': None,
                 'args': (5,),
                 'callable': f,
                 'introspectables': (),
                 'kw': {},
                 'discriminator': None,
                 'includepath': ('y',),
                 'order': 0},
                 {'info': 'should be last',
                  'args': (4,),
                  'callable': f,
                  'introspectables': (),
                  'kw': {},
                  'discriminator': 4,
                  'includepath': ('y',),
                  'order': 99999}
                  ]
                  )
            [
                {
                    'info': None,
                    'args': (),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': None,
                    'includepath': (),
                    'order': 0,
                },
                {
                    'info': 'first',
                    'args': (1,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 1,
                    'includepath': (),
                    'order': 0,
                },
                {
                    'info': None,
                    'args': (3,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 3,
                    'includepath': ('y',),
                    'order': 0,
                },
                {
                    'info': None,
                    'args': (5,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': None,
                    'includepath': ('y',),
                    'order': 0,
                },
                {
                    'info': 'should be last',
                    'args': (4,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 4,
                    'includepath': ('y',),
                    'order': 99999,
                },
            ],
        )
    def test_it_conflict(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (None, f),
            (1, f, (2,), {}, ('x',), 'eek'),     # will conflict
            (1, f, (3,), {}, ('y',), 'ack'),     # will conflict
            (4, f, (4,), {}, ('y',)),
            (3, f, (3,), {}, ('y',)),
            (None, f, (5,), {}, ('y',)),
            ])
        result = self._callFUT(
            [
                (None, f),
                (1, f, (2,), {}, ('x',), 'eek'),  # will conflict
                (1, f, (3,), {}, ('y',), 'ack'),  # will conflict
                (4, f, (4,), {}, ('y',)),
                (3, f, (3,), {}, ('y',)),
                (None, f, (5,), {}, ('y',)),
            ]
        )
        self.assertRaises(ConfigurationConflictError, list, result)
    def test_it_with_actions_grouped_by_order(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (None, f),                                 # X
            (1, f, (1,), {}, (), 'third', 10),         # X
            (1, f, (2,), {}, ('x',), 'fourth', 10),
            (1, f, (3,), {}, ('y',), 'fifth', 10),
            (2, f, (1,), {}, (), 'sixth', 10),         # X
            (3, f, (1,), {}, (), 'seventh', 10),       # X
            (5, f, (4,), {}, ('y',), 'eighth', 99999), # X
            (4, f, (3,), {}, (), 'first', 5),          # X
            (4, f, (5,), {}, ('y',), 'second', 5),
            ])
        result = self._callFUT(
            [
                (None, f),  # X
                (1, f, (1,), {}, (), 'third', 10),  # X
                (1, f, (2,), {}, ('x',), 'fourth', 10),
                (1, f, (3,), {}, ('y',), 'fifth', 10),
                (2, f, (1,), {}, (), 'sixth', 10),  # X
                (3, f, (1,), {}, (), 'seventh', 10),  # X
                (5, f, (4,), {}, ('y',), 'eighth', 99999),  # X
                (4, f, (3,), {}, (), 'first', 5),  # X
                (4, f, (5,), {}, ('y',), 'second', 5),
            ]
        )
        result = list(result)
        self.assertEqual(len(result), 6)
        # resolved actions should be grouped by (order, i)
        self.assertEqual(
            result,
            [{'info': None,
              'args': (),
              'callable': f,
              'introspectables': (),
              'kw': {},
              'discriminator': None,
              'includepath': (),
              'order': 0},
              {'info': 'first',
               'args': (3,),
               'callable': f,
               'introspectables': (),
               'kw': {},
               'discriminator': 4,
               'includepath': (),
               'order': 5},
               {'info': 'third',
                'args': (1,),
                'callable': f,
                'introspectables': (),
                'kw': {},
                'discriminator': 1,
                'includepath': (),
                'order': 10},
               {'info': 'sixth',
                'args': (1,),
                'callable': f,
                'introspectables': (),
                'kw': {},
                'discriminator': 2,
                'includepath': (),
                'order': 10},
               {'info': 'seventh',
                'args': (1,),
                'callable': f,
                'introspectables': (),
                'kw': {},
                'discriminator': 3,
                'includepath': (),
                'order': 10},
                 {'info': 'eighth',
                  'args': (4,),
                  'callable': f,
                  'introspectables': (),
                  'kw': {},
                  'discriminator': 5,
                  'includepath': ('y',),
                  'order': 99999}
                  ]
                  )
            [
                {
                    'info': None,
                    'args': (),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': None,
                    'includepath': (),
                    'order': 0,
                },
                {
                    'info': 'first',
                    'args': (3,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 4,
                    'includepath': (),
                    'order': 5,
                },
                {
                    'info': 'third',
                    'args': (1,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 1,
                    'includepath': (),
                    'order': 10,
                },
                {
                    'info': 'sixth',
                    'args': (1,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 2,
                    'includepath': (),
                    'order': 10,
                },
                {
                    'info': 'seventh',
                    'args': (1,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 3,
                    'includepath': (),
                    'order': 10,
                },
                {
                    'info': 'eighth',
                    'args': (4,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 5,
                    'includepath': ('y',),
                    'order': 99999,
                },
            ],
        )
    def test_override_success_across_orders(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (1, f, (2,), {}, ('x',), 'eek', 0),
            (1, f, (3,), {}, ('x', 'y'), 'ack', 10),
            ])
        result = self._callFUT(
            [
                (1, f, (2,), {}, ('x',), 'eek', 0),
                (1, f, (3,), {}, ('x', 'y'), 'ack', 10),
            ]
        )
        result = list(result)
        self.assertEqual(result, [
            {'info': 'eek',
            'args': (2,),
            'callable': f,
            'introspectables': (),
            'kw': {},
            'discriminator': 1,
            'includepath': ('x',),
            'order': 0},
        ])
        self.assertEqual(
            result,
            [
                {
                    'info': 'eek',
                    'args': (2,),
                    'callable': f,
                    'introspectables': (),
                    'kw': {},
                    'discriminator': 1,
                    'includepath': ('x',),
                    'order': 0,
                }
            ],
        )
    def test_conflicts_across_orders(self):
        from . import dummyfactory as f
        result = self._callFUT([
            (1, f, (2,), {}, ('x', 'y'), 'eek', 0),
            (1, f, (3,), {}, ('x'), 'ack', 10),
            ])
        result = self._callFUT(
            [
                (1, f, (2,), {}, ('x', 'y'), 'eek', 0),
                (1, f, (3,), {}, ('x'), 'ack', 10),
            ]
        )
        self.assertRaises(ConfigurationConflictError, list, result)
class TestGlobalRegistriesIntegration(unittest.TestCase):
    def setUp(self):
        from pyramid.config import global_registries
        global_registries.empty()
    tearDown = setUp
    def _makeConfigurator(self, *arg, **kw):
        from pyramid.config import Configurator
        config = Configurator(*arg, **kw)
        return config
    def test_global_registries_empty(self):
        from pyramid.config import global_registries
        self.assertEqual(global_registries.last, None)
    def test_global_registries(self):
        from pyramid.config import global_registries
        config1 = self._makeConfigurator()
        config1.make_wsgi_app()
        self.assertEqual(global_registries.last, config1.registry)
        config2 = self._makeConfigurator()
        config2.make_wsgi_app()
        self.assertEqual(global_registries.last, config2.registry)
        self.assertEqual(list(global_registries),
                         [config1.registry, config2.registry])
        self.assertEqual(
            list(global_registries), [config1.registry, config2.registry]
        )
        global_registries.remove(config2.registry)
        self.assertEqual(global_registries.last, config1.registry)
class DummyRequest:
    subpath = ()
    matchdict = None
    request_iface = IRequest
    def __init__(self, environ=None):
        if environ is None:
            environ = {}
@@ -2005,21 +2414,26 @@
        self.params = {}
        self.cookies = {}
class DummyThreadLocalManager(object):
    def __init__(self):
        self.pushed = {'registry': None, 'request': None}
        self.popped = False
    def push(self, d):
        self.pushed = d
    def get(self):
        return self.pushed
    def pop(self):
        self.popped = True
from zope.interface import implementer
@implementer(IDummy)
class DummyEvent:
    pass
class DummyRegistry(object):
    def __init__(self, adaptation=None, util=None):
@@ -2027,21 +2441,27 @@
        self.adapters = []
        self.adaptation = adaptation
        self.util = util
    def subscribers(self, events, name):
        self.events = events
        return events
    def registerUtility(self, *arg, **kw):
        self.utilities.append((arg, kw))
    def registerAdapter(self, *arg, **kw):
        self.adapters.append((arg, kw))
    def queryAdapter(self, *arg, **kw):
        return self.adaptation
    def queryUtility(self, *arg, **kw):
        return self.util
from zope.interface import Interface
class IOther(Interface):
    pass
def _conflictFunctions(e):
    conflicts = e._conflicts.values()
@@ -2049,19 +2469,25 @@
        for confinst in conflict:
            yield confinst.function
class DummyActionState(object):
    autocommit = False
    info = ''
    def __init__(self):
        self.actions = []
    def action(self, *arg, **kw):
        self.actions.append((arg, kw))
class DummyIntrospectable(object):
    def __init__(self):
        self.registered = []
    def register(self, introspector, action_info):
        self.registered.append((introspector, action_info))
class DummyPredicate(object):
    pass
Diff truncated after the above file
tests/test_config/test_rendering.py tests/test_config/test_routes.py tests/test_config/test_security.py tests/test_config/test_settings.py tests/test_config/test_testing.py tests/test_config/test_tweens.py tests/test_config/test_util.py tests/test_config/test_views.py tests/test_csrf.py tests/test_decorator.py tests/test_docs.py tests/test_encode.py tests/test_events.py tests/test_exceptions.py tests/test_httpexceptions.py tests/test_i18n.py tests/test_integration.py tests/test_location.py tests/test_paster.py tests/test_path.py tests/test_predicates.py tests/test_registry.py tests/test_renderers.py tests/test_request.py tests/test_response.py tests/test_router.py tests/test_scaffolds/test_copydir.py tests/test_scaffolds/test_init.py tests/test_scaffolds/test_template.py tests/test_scripting.py tests/test_scripts/dummy.py tests/test_scripts/test_common.py tests/test_scripts/test_pcreate.py tests/test_scripts/test_pdistreport.py tests/test_scripts/test_prequest.py tests/test_scripts/test_proutes.py tests/test_scripts/test_pserve.py tests/test_scripts/test_pshell.py tests/test_scripts/test_ptweens.py tests/test_scripts/test_pviews.py tests/test_security.py tests/test_session.py tests/test_settings.py tests/test_static.py tests/test_testing.py tests/test_threadlocal.py tests/test_traversal.py tests/test_tweens.py tests/test_url.py tests/test_urldispatch.py tests/test_util.py tests/test_view.py tests/test_viewderivers.py tests/test_wsgi.py tox.ini