diff options
Diffstat (limited to 'tests')
150 files changed, 37428 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..944343e82 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,2 @@ +def dummy_extend(*args): + """used to test Configurator.extend""" diff --git a/tests/fixtures/dummy.ini b/tests/fixtures/dummy.ini new file mode 100644 index 000000000..fffaf75c8 --- /dev/null +++ b/tests/fixtures/dummy.ini @@ -0,0 +1,4 @@ +[app:myapp] +use = call:tests.test_paster:make_dummyapp + +foo = %(bar)s diff --git a/tests/fixtures/manifest.json b/tests/fixtures/manifest.json new file mode 100644 index 000000000..0a43bc5e3 --- /dev/null +++ b/tests/fixtures/manifest.json @@ -0,0 +1,4 @@ +{ + "css/main.css": "css/main-test.css", + "images/background.png": "images/background-a8169106.png" +} diff --git a/tests/fixtures/manifest2.json b/tests/fixtures/manifest2.json new file mode 100644 index 000000000..fd6b9a7bb --- /dev/null +++ b/tests/fixtures/manifest2.json @@ -0,0 +1,4 @@ +{ + "css/main.css": "css/main-678b7c80.css", + "images/background.png": "images/background-a8169106.png" +} diff --git a/tests/fixtures/minimal.jpg b/tests/fixtures/minimal.jpg Binary files differnew file mode 100644 index 000000000..1cda9a53d --- /dev/null +++ b/tests/fixtures/minimal.jpg diff --git a/tests/fixtures/minimal.pdf b/tests/fixtures/minimal.pdf Binary files differnew file mode 100755 index 000000000..e267be996 --- /dev/null +++ b/tests/fixtures/minimal.pdf diff --git a/tests/fixtures/minimal.txt b/tests/fixtures/minimal.txt new file mode 100644 index 000000000..18832d351 --- /dev/null +++ b/tests/fixtures/minimal.txt @@ -0,0 +1 @@ +Hello. diff --git a/tests/fixtures/minimal.xml b/tests/fixtures/minimal.xml new file mode 100644 index 000000000..1972c155d --- /dev/null +++ b/tests/fixtures/minimal.xml @@ -0,0 +1 @@ +<hello/>
\ No newline at end of file diff --git a/tests/fixtures/nonminimal.txt b/tests/fixtures/nonminimal.txt new file mode 100644 index 000000000..9de95ec92 --- /dev/null +++ b/tests/fixtures/nonminimal.txt @@ -0,0 +1 @@ +Hello, ${name}! diff --git a/tests/fixtures/static/.hiddenfile b/tests/fixtures/static/.hiddenfile new file mode 100644 index 000000000..86d345000 --- /dev/null +++ b/tests/fixtures/static/.hiddenfile @@ -0,0 +1,2 @@ +<html>I'm hidden</html> + diff --git a/tests/fixtures/static/arcs.svg.tgz b/tests/fixtures/static/arcs.svg.tgz new file mode 100644 index 000000000..376c42ac8 --- /dev/null +++ b/tests/fixtures/static/arcs.svg.tgz @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + id="canvas" + width="2000" + height="2000" + onload="init()"> + + <style type="text/css"> + .ellipse + { + stroke: red; + stroke-width: 2; + fill: blue; + fill-opacity: 0.1; + } + + .axes + { + stroke: blue; + stroke-width: 1; + } + </style> + + <script> + <![CDATA[ + var COLS = 4; + var ROWS = 4; + var RX = 80; + var RY = 30; + + function init() + { + var canvas = document.getElementById("canvas"); + + var angleStep = 360.0/(COLS*ROWS); + var spacing = 2*Math.max(RX, RY)+10; + for (var c = 0; c < COLS; ++c) { + for (var r = 0; r < ROWS; ++r) { + var ellipse = createEllipse((c+ COLS*r)*angleStep, spacing*(c+0.5), spacing*(r+0.5), RX, RY); + canvas.appendChild(ellipse); + } + } + } + + function createEllipse(phi, x, y, rx, ry) + { + var degPerRad = Math.PI/180.0; + var e1x = rx*Math.cos(phi*degPerRad); + var e1y = rx*Math.sin(phi*degPerRad); + var e2x = ry*Math.cos((phi+90)*degPerRad); + var e2y = ry*Math.sin((phi+90)*degPerRad); + + var axes = document.createElementNS("http://www.w3.org/2000/svg", "path"); + axes.setAttribute("class", "axes"); + axes.setAttribute("d", "M"+x+","+y+" l"+e1x+","+e1y+"M"+x+","+y+" l"+e2x+","+e2y); + var ellipse = document.createElementNS("http://www.w3.org/2000/svg", "path"); + ellipse.setAttribute("class", "ellipse"); + ellipse.setAttribute("d", "M" + (x+e1x) + "," + (y+e1y) + + "A" + rx + "," + ry + " " + phi + " 0,1 " + (x+e2x) + "," + (y+e2y) + + "A" + rx + "," + ry + " " + phi + " 1,1 " + (x+e1x) + "," + (y+e1y) +"z"); + + var group = document.createElementNS("http://www.w3.org/2000/svg", "g"); + group.appendChild(axes); + group.appendChild(ellipse); + return group; + } + + ]]> + </script> +</svg> diff --git a/tests/fixtures/static/index.html b/tests/fixtures/static/index.html new file mode 100644 index 000000000..0470710b2 --- /dev/null +++ b/tests/fixtures/static/index.html @@ -0,0 +1 @@ +<html>static</html>
\ No newline at end of file diff --git a/tests/fixtures/static/subdir/index.html b/tests/fixtures/static/subdir/index.html new file mode 100644 index 000000000..bb84fad04 --- /dev/null +++ b/tests/fixtures/static/subdir/index.html @@ -0,0 +1 @@ +<html>subdir</html> diff --git a/tests/pkgs/__init__.py b/tests/pkgs/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/pkgs/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/pkgs/ccbugapp/__init__.py b/tests/pkgs/ccbugapp/__init__.py new file mode 100644 index 000000000..908a36e3e --- /dev/null +++ b/tests/pkgs/ccbugapp/__init__.py @@ -0,0 +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_view(rdf_view, route_name='rdf') + config.add_view(juri_view, route_name='juri') diff --git a/tests/pkgs/conflictapp/__init__.py b/tests/pkgs/conflictapp/__init__.py new file mode 100644 index 000000000..840813994 --- /dev/null +++ b/tests/pkgs/conflictapp/__init__.py @@ -0,0 +1,29 @@ +from pyramid.response import Response +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy + + +def aview(request): + return Response('a view') + + +def routeview(request): + return Response('route view') + + +def protectedview(request): + return Response('protected view') + + +def includeme(config): + # purposely sorta-randomly ordered (route comes after view naming it, + # authz comes after views) + config.add_view(aview) + config.add_view(protectedview, name='protected', permission='view') + config.add_view(routeview, route_name='aroute') + config.add_route('aroute', '/route') + config.set_authentication_policy( + AuthTktAuthenticationPolicy('seekri1t', hashalg='sha512') + ) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.include('tests.pkgs.conflictapp.included') diff --git a/tests/pkgs/conflictapp/included.py b/tests/pkgs/conflictapp/included.py new file mode 100644 index 000000000..fc42c317e --- /dev/null +++ b/tests/pkgs/conflictapp/included.py @@ -0,0 +1,9 @@ +from webob import Response + + +def bview(request): # pragma: no cover + return Response('b view') + + +def includeme(config): + config.add_view(bview) diff --git a/tests/pkgs/defpermbugapp/__init__.py b/tests/pkgs/defpermbugapp/__init__.py new file mode 100644 index 000000000..81897e86a --- /dev/null +++ b/tests/pkgs/defpermbugapp/__init__.py @@ -0,0 +1,30 @@ +from webob import Response +from pyramid.security import NO_PERMISSION_REQUIRED +from pyramid.view import view_config + + +@view_config(name='x') +def x_view(request): # pragma: no cover + return Response('this is private!') + + +@view_config(name='y', permission='private2') +def y_view(request): # pragma: no cover + return Response('this is private too!') + + +@view_config(name='z', permission=NO_PERMISSION_REQUIRED) +def z_view(request): + return Response('this is public') + + +def includeme(config): + from pyramid.authorization import ACLAuthorizationPolicy + from pyramid.authentication import AuthTktAuthenticationPolicy + + authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') + authz_policy = ACLAuthorizationPolicy() + config.scan('tests.pkgs.defpermbugapp') + config._set_authentication_policy(authn_policy) + config._set_authorization_policy(authz_policy) + config.set_default_permission('private') diff --git a/tests/pkgs/eventonly/__init__.py b/tests/pkgs/eventonly/__init__.py new file mode 100644 index 000000000..c48b539a1 --- /dev/null +++ b/tests/pkgs/eventonly/__init__.py @@ -0,0 +1,75 @@ +from pyramid.view import view_config +from pyramid.events import subscriber + + +class Yup(object): + def __init__(self, val, config): + self.val = val + + def text(self): + return 'path_startswith = %s' % (self.val,) + + phash = text + + def __call__(self, event): + return getattr(event.response, 'yup', False) + + +class Foo(object): + def __init__(self, response): + self.response = response + + +class Bar(object): + pass + + +@subscriber(Foo) +def foo(event): + event.response.text += 'foo ' + + +@subscriber(Foo, yup=True) +def fooyup(event): + event.response.text += 'fooyup ' + + +@subscriber([Foo, Bar]) +def foobar(event): + event.response.text += 'foobar ' + + +@subscriber([Foo, Bar]) +def foobar2(event, context): + event.response.text += 'foobar2 ' + + +@subscriber([Foo, Bar], yup=True) +def foobaryup(event): + event.response.text += 'foobaryup ' + + +@subscriber([Foo, Bar], yup=True) +def foobaryup2(event, context): + event.response.text += 'foobaryup2 ' + + +@view_config(name='sendfoo') +def sendfoo(request): + response = request.response + response.yup = True + request.registry.notify(Foo(response)) + return response + + +@view_config(name='sendfoobar') +def sendfoobar(request): + response = request.response + response.yup = True + request.registry.notify(Foo(response), Bar()) + return response + + +def includeme(config): + config.add_subscriber_predicate('yup', Yup) + config.scan('tests.pkgs.eventonly') diff --git a/tests/pkgs/exceptionviewapp/__init__.py b/tests/pkgs/exceptionviewapp/__init__.py new file mode 100644 index 000000000..19804d242 --- /dev/null +++ b/tests/pkgs/exceptionviewapp/__init__.py @@ -0,0 +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_exception4', 'route_raise_exception4') + config.add_view('.views.maybe') + config.add_view('.views.no', context='.models.NotAnException') + config.add_view('.views.yes', context=".models.AnException") + config.add_view('.views.raise_exception', name='raise_exception') + config.add_view( + '.views.raise_exception', route_name='route_raise_exception' + ) + config.add_view( + '.views.raise_exception', route_name='route_raise_exception2' + ) + config.add_view( + '.views.raise_exception', route_name='route_raise_exception3' + ) + config.add_view( + '.views.whoa', + context='.models.AnException', + route_name='route_raise_exception3', + ) + config.add_view( + '.views.raise_exception', route_name='route_raise_exception4' + ) + config.add_view( + '.views.whoa', + context='.models.AnException', + route_name='route_raise_exception4', + ) + config.add_view( + '.views.raise_httpexception', route_name='route_raise_httpexception' + ) + config.add_view('.views.catch_httpexception', context=HTTPException) diff --git a/tests/pkgs/exceptionviewapp/models.py b/tests/pkgs/exceptionviewapp/models.py new file mode 100644 index 000000000..25f8e9156 --- /dev/null +++ b/tests/pkgs/exceptionviewapp/models.py @@ -0,0 +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() diff --git a/tests/pkgs/exceptionviewapp/views.py b/tests/pkgs/exceptionviewapp/views.py new file mode 100644 index 000000000..ca2c4fffb --- /dev/null +++ b/tests/pkgs/exceptionviewapp/views.py @@ -0,0 +1,31 @@ +from webob import Response +from .models import AnException +from pyramid.httpexceptions import HTTPBadRequest + + +def no(request): + return Response('no') + + +def yes(request): + return Response('yes') + + +def maybe(request): + return Response('maybe') + + +def whoa(request): + return Response('whoa') + + +def raise_exception(request): + raise AnException() + + +def raise_httpexception(request): + raise HTTPBadRequest + + +def catch_httpexception(request): + return Response('caught') diff --git a/tests/pkgs/fixtureapp/__init__.py b/tests/pkgs/fixtureapp/__init__.py new file mode 100644 index 000000000..ffc8adb4a --- /dev/null +++ b/tests/pkgs/fixtureapp/__init__.py @@ -0,0 +1,14 @@ +def includeme(config): + config.add_view('.views.fixture_view') + config.add_view('.views.exception_view', context=RuntimeError) + config.add_view('.views.protected_view', name='protected.html') + config.add_view('.views.erroneous_view', name='error.html') + config.add_view( + '.views.fixture_view', + name='dummyskin.html', + request_type='.views.IDummy', + ) + from .models import fixture, IFixture + + config.registry.registerUtility(fixture, IFixture) + config.add_view('.views.fixture_view', name='another.html') diff --git a/tests/pkgs/fixtureapp/models.py b/tests/pkgs/fixtureapp/models.py new file mode 100644 index 000000000..5ad640df9 --- /dev/null +++ b/tests/pkgs/fixtureapp/models.py @@ -0,0 +1,9 @@ +from zope.interface import Interface + + +class IFixture(Interface): + pass + + +def fixture(): + """ """ diff --git a/tests/pkgs/fixtureapp/subpackage/__init__.py b/tests/pkgs/fixtureapp/subpackage/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/pkgs/fixtureapp/subpackage/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/pkgs/fixtureapp/views.py b/tests/pkgs/fixtureapp/views.py new file mode 100644 index 000000000..78df81c6f --- /dev/null +++ b/tests/pkgs/fixtureapp/views.py @@ -0,0 +1,27 @@ +from zope.interface import Interface +from webob import Response +from pyramid.httpexceptions import HTTPForbidden + + +def fixture_view(context, request): + """ """ + return Response('fixture') + + +def erroneous_view(context, request): + """ """ + raise RuntimeError() + + +def exception_view(context, request): + """ """ + return Response('supressed') + + +def protected_view(context, request): + """ """ + raise HTTPForbidden() + + +class IDummy(Interface): + pass diff --git a/tests/pkgs/forbiddenapp/__init__.py b/tests/pkgs/forbiddenapp/__init__.py new file mode 100644 index 000000000..9ebf62a9d --- /dev/null +++ b/tests/pkgs/forbiddenapp/__init__.py @@ -0,0 +1,28 @@ +from webob import Response +from pyramid.httpexceptions import HTTPForbidden +from pyramid.compat import bytes_ + + +def x_view(request): # pragma: no cover + return Response('this is private!') + + +def forbidden_view(context, request): + msg = context.message + result = context.result + message = msg + '\n' + str(result) + resp = HTTPForbidden() + resp.body = bytes_(message) + return resp + + +def includeme(config): + from pyramid.authentication import AuthTktAuthenticationPolicy + from pyramid.authorization import ACLAuthorizationPolicy + + authn_policy = AuthTktAuthenticationPolicy('seekr1t', hashalg='sha512') + authz_policy = ACLAuthorizationPolicy() + config._set_authentication_policy(authn_policy) + config._set_authorization_policy(authz_policy) + config.add_view(x_view, name='x', permission='private') + config.add_view(forbidden_view, context=HTTPForbidden) diff --git a/tests/pkgs/forbiddenview/__init__.py b/tests/pkgs/forbiddenview/__init__.py new file mode 100644 index 000000000..4c78d961c --- /dev/null +++ b/tests/pkgs/forbiddenview/__init__.py @@ -0,0 +1,35 @@ +from pyramid.view import forbidden_view_config, view_config +from pyramid.response import Response +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy + + +@forbidden_view_config(route_name='foo') +def foo_forbidden(request): # pragma: no cover + return Response('foo_forbidden') + + +@forbidden_view_config() +def forbidden(request): + return Response('generic_forbidden') + + +@view_config(route_name='foo') +def foo(request): # pragma: no cover + return Response('OK foo') + + +@view_config(route_name='bar') +def bar(request): # pragma: no cover + return Response('OK bar') + + +def includeme(config): + authn_policy = AuthTktAuthenticationPolicy('seekri1', hashalg='sha512') + authz_policy = ACLAuthorizationPolicy() + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) + config.set_default_permission('a') + config.add_route('foo', '/foo') + config.add_route('bar', '/bar') + config.scan('tests.pkgs.forbiddenview') diff --git a/tests/pkgs/hybridapp/__init__.py b/tests/pkgs/hybridapp/__init__.py new file mode 100644 index 000000000..001e1b882 --- /dev/null +++ b/tests/pkgs/hybridapp/__init__.py @@ -0,0 +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') + + # <!-- 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') diff --git a/tests/pkgs/hybridapp/views.py b/tests/pkgs/hybridapp/views.py new file mode 100644 index 000000000..695a79531 --- /dev/null +++ b/tests/pkgs/hybridapp/views.py @@ -0,0 +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): + """ """ diff --git a/tests/pkgs/includeapp1/__init__.py b/tests/pkgs/includeapp1/__init__.py new file mode 100644 index 000000000..eaeeb7ef6 --- /dev/null +++ b/tests/pkgs/includeapp1/__init__.py @@ -0,0 +1 @@ +# include app diff --git a/tests/pkgs/includeapp1/root.py b/tests/pkgs/includeapp1/root.py new file mode 100644 index 000000000..369ab9f38 --- /dev/null +++ b/tests/pkgs/includeapp1/root.py @@ -0,0 +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() diff --git a/tests/pkgs/includeapp1/three.py b/tests/pkgs/includeapp1/three.py new file mode 100644 index 000000000..b8d881244 --- /dev/null +++ b/tests/pkgs/includeapp1/three.py @@ -0,0 +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 diff --git a/tests/pkgs/includeapp1/two.py b/tests/pkgs/includeapp1/two.py new file mode 100644 index 000000000..727161c8e --- /dev/null +++ b/tests/pkgs/includeapp1/two.py @@ -0,0 +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 diff --git a/tests/pkgs/localeapp/__init__.py b/tests/pkgs/localeapp/__init__.py new file mode 100644 index 000000000..1a35cdb4a --- /dev/null +++ b/tests/pkgs/localeapp/__init__.py @@ -0,0 +1 @@ +# a file diff --git a/tests/pkgs/localeapp/locale/GARBAGE b/tests/pkgs/localeapp/locale/GARBAGE new file mode 100644 index 000000000..032c55584 --- /dev/null +++ b/tests/pkgs/localeapp/locale/GARBAGE @@ -0,0 +1 @@ +Garbage file. diff --git a/tests/pkgs/localeapp/locale/be/LC_MESSAGES b/tests/pkgs/localeapp/locale/be/LC_MESSAGES new file mode 100644 index 000000000..909cf6a3b --- /dev/null +++ b/tests/pkgs/localeapp/locale/be/LC_MESSAGES @@ -0,0 +1 @@ +busted. diff --git a/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale/de/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..e3b2b0881 --- /dev/null +++ b/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..be055bed9 --- /dev/null +++ b/tests/pkgs/localeapp/locale/de_DE/LC_MESSAGES/deformsite.po @@ -0,0 +1,26 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "different" diff --git a/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale/en/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/localeapp/locale2/GARBAGE b/tests/pkgs/localeapp/locale2/GARBAGE new file mode 100644 index 000000000..032c55584 --- /dev/null +++ b/tests/pkgs/localeapp/locale2/GARBAGE @@ -0,0 +1 @@ +Garbage file. diff --git a/tests/pkgs/localeapp/locale2/be/LC_MESSAGES b/tests/pkgs/localeapp/locale2/be/LC_MESSAGES new file mode 100644 index 000000000..909cf6a3b --- /dev/null +++ b/tests/pkgs/localeapp/locale2/be/LC_MESSAGES @@ -0,0 +1 @@ +busted. diff --git a/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale2/de/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale2/en/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/localeapp/locale3/GARBAGE b/tests/pkgs/localeapp/locale3/GARBAGE new file mode 100644 index 000000000..032c55584 --- /dev/null +++ b/tests/pkgs/localeapp/locale3/GARBAGE @@ -0,0 +1 @@ +Garbage file. diff --git a/tests/pkgs/localeapp/locale3/be/LC_MESSAGES b/tests/pkgs/localeapp/locale3/be/LC_MESSAGES new file mode 100644 index 000000000..909cf6a3b --- /dev/null +++ b/tests/pkgs/localeapp/locale3/be/LC_MESSAGES @@ -0,0 +1 @@ +busted. diff --git a/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale3/de/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.mo b/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.mo Binary files differnew file mode 100644 index 000000000..2924a5eb5 --- /dev/null +++ b/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.mo diff --git a/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po b/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/tests/pkgs/localeapp/locale3/en/LC_MESSAGES/deformsite.po @@ -0,0 +1,31 @@ +# German translations for deformsite. +# Copyright (C) 2010 ORGANIZATION +# This file is distributed under the same license as the deformsite project. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: de <LL@li.org>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/tests/pkgs/notfoundview/__init__.py b/tests/pkgs/notfoundview/__init__.py new file mode 100644 index 000000000..f606ec671 --- /dev/null +++ b/tests/pkgs/notfoundview/__init__.py @@ -0,0 +1,35 @@ +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 + return Response('foo_notfound') + + +@notfound_view_config(route_name='baz') +def baz_notfound(request): + return Response('baz_notfound') + + +@notfound_view_config(append_slash=True) +def notfound(request): + return Response('generic_notfound') + + +@view_config(route_name='bar') +def bar(request): + return Response('OK bar') + + +@view_config(route_name='foo2') +def foo2(request): + return Response('OK foo2') + + +def includeme(config): + config.add_route('foo', '/foo') + config.add_route('foo2', '/foo/') + config.add_route('bar', '/bar/') + config.add_route('baz', '/baz') + config.scan('tests.pkgs.notfoundview') diff --git a/tests/pkgs/permbugapp/__init__.py b/tests/pkgs/permbugapp/__init__.py new file mode 100644 index 000000000..aedd405f8 --- /dev/null +++ b/tests/pkgs/permbugapp/__init__.py @@ -0,0 +1,27 @@ +from pyramid.compat import escape +from pyramid.security import view_execution_permitted +from pyramid.response import Response + + +def x_view(request): # pragma: no cover + return Response('this is private!') + + +def test(context, request): + # should return false + msg = 'Allow ./x? %s' % repr( + view_execution_permitted(context, request, 'x') + ) + return Response(escape(msg)) + + +def includeme(config): + from pyramid.authentication import AuthTktAuthenticationPolicy + from pyramid.authorization import ACLAuthorizationPolicy + + authn_policy = AuthTktAuthenticationPolicy('seekt1t', hashalg='sha512') + authz_policy = ACLAuthorizationPolicy() + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) + config.add_view(test, name='test') + config.add_view(x_view, name='x', permission='private') diff --git a/tests/pkgs/rendererscanapp/__init__.py b/tests/pkgs/rendererscanapp/__init__.py new file mode 100644 index 000000000..1fc831e66 --- /dev/null +++ b/tests/pkgs/rendererscanapp/__init__.py @@ -0,0 +1,10 @@ +from pyramid.view import view_config + + +@view_config(name='one', renderer='json') +def one(request): + return {'name': 'One!'} + + +def includeme(config): + config.scan() diff --git a/tests/pkgs/rendererscanapp/two/__init__.py b/tests/pkgs/rendererscanapp/two/__init__.py new file mode 100644 index 000000000..7d3990317 --- /dev/null +++ b/tests/pkgs/rendererscanapp/two/__init__.py @@ -0,0 +1,6 @@ +from pyramid.view import view_config + + +@view_config(name='two', renderer='json') +def two(request): + return {'nameagain': 'Two!'} diff --git a/tests/pkgs/restbugapp/__init__.py b/tests/pkgs/restbugapp/__init__.py new file mode 100644 index 000000000..ae0a80f01 --- /dev/null +++ b/tests/pkgs/restbugapp/__init__.py @@ -0,0 +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', + ) diff --git a/tests/pkgs/restbugapp/views.py b/tests/pkgs/restbugapp/views.py new file mode 100644 index 000000000..161321aed --- /dev/null +++ b/tests/pkgs/restbugapp/views.py @@ -0,0 +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') diff --git a/tests/pkgs/static_abspath/__init__.py b/tests/pkgs/static_abspath/__init__.py new file mode 100644 index 000000000..0c875b96f --- /dev/null +++ b/tests/pkgs/static_abspath/__init__.py @@ -0,0 +1,7 @@ +import os + + +def includeme(config): + here = here = os.path.dirname(__file__) + fixtures = os.path.normpath(os.path.join(here, '..', '..', 'fixtures')) + config.add_static_view('/', fixtures) diff --git a/tests/pkgs/static_assetspec/__init__.py b/tests/pkgs/static_assetspec/__init__.py new file mode 100644 index 000000000..e7d3e9373 --- /dev/null +++ b/tests/pkgs/static_assetspec/__init__.py @@ -0,0 +1,2 @@ +def includeme(config): + config.add_static_view('/', 'tests:fixtures') diff --git a/tests/pkgs/static_routeprefix/__init__.py b/tests/pkgs/static_routeprefix/__init__.py new file mode 100644 index 000000000..f64f31292 --- /dev/null +++ b/tests/pkgs/static_routeprefix/__init__.py @@ -0,0 +1,7 @@ +def includeme(config): + config.add_static_view('/static', 'tests:fixtures') + config.include(includeme2, route_prefix='/prefix') + + +def includeme2(config): + config.add_static_view('/static', 'tests:fixtures/static') diff --git a/tests/pkgs/staticpermapp/__init__.py b/tests/pkgs/staticpermapp/__init__.py new file mode 100644 index 000000000..ffc87d39a --- /dev/null +++ b/tests/pkgs/staticpermapp/__init__.py @@ -0,0 +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, + ) diff --git a/tests/pkgs/subrequestapp/__init__.py b/tests/pkgs/subrequestapp/__init__.py new file mode 100644 index 000000000..177f5637b --- /dev/null +++ b/tests/pkgs/subrequestapp/__init__.py @@ -0,0 +1,58 @@ +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 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') + try: + return request.invoke_subrequest(subreq, use_tweens=False) + except ValueError: + request.response.body = b'Value error raised' + return request.response + + +def excview(request): + request.response.status_int = 500 + request.response.body = b'Bad stuff happened' + return request.response + + +def main(): + config = Configurator() + config.add_route('one', '/view_one') + config.add_route('two', '/view_two') + config.add_route('three', '/view_three') + config.add_route('four', '/view_four') + config.add_route('five', '/view_five') + config.add_view(excview, context=Exception) + config.add_view(view_one, route_name='one') + config.add_view(view_two, route_name='two', renderer='string') + config.add_view(view_three, route_name='three') + config.add_view(view_four, route_name='four') + config.add_view(view_five, route_name='five') + config.add_request_method(lambda r: 'bar', 'foo', property=True) + return config diff --git a/tests/pkgs/viewdecoratorapp/__init__.py b/tests/pkgs/viewdecoratorapp/__init__.py new file mode 100644 index 000000000..99b7ea9c7 --- /dev/null +++ b/tests/pkgs/viewdecoratorapp/__init__.py @@ -0,0 +1,2 @@ +def includeme(config): + config.scan('tests.pkgs.viewdecoratorapp') diff --git a/tests/pkgs/viewdecoratorapp/views/__init__.py b/tests/pkgs/viewdecoratorapp/views/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/pkgs/viewdecoratorapp/views/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/pkgs/viewdecoratorapp/views/views.py b/tests/pkgs/viewdecoratorapp/views/views.py new file mode 100644 index 000000000..6879acd2b --- /dev/null +++ b/tests/pkgs/viewdecoratorapp/views/views.py @@ -0,0 +1,11 @@ +from pyramid.view import view_config + + +@view_config(renderer='json', name='first') +def first(request): + return {'result': 'OK1'} + + +@view_config(renderer='json', name='second') +def second(request): + return {'result': 'OK2'} diff --git a/tests/pkgs/wsgiapp2app/__init__.py b/tests/pkgs/wsgiapp2app/__init__.py new file mode 100644 index 000000000..f7452f52c --- /dev/null +++ b/tests/pkgs/wsgiapp2app/__init__.py @@ -0,0 +1,20 @@ +from pyramid.view import view_config +from pyramid.wsgi import wsgiapp2 + + +@view_config(name='hello', renderer='string') +@wsgiapp2 +def hello(environ, start_response): + assert environ['PATH_INFO'] == '/' + assert environ['SCRIPT_NAME'] == '/hello' + response_headers = [('Content-Type', 'text/plain')] + start_response('200 OK', response_headers) + return [b'Hello!'] + + +def main(): + from pyramid.config import Configurator + + c = Configurator() + c.scan() + return c diff --git a/tests/test_asset.py b/tests/test_asset.py new file mode 100644 index 000000000..55a3c0336 --- /dev/null +++ b/tests/test_asset.py @@ -0,0 +1,94 @@ +import unittest +import os + +here = os.path.abspath(os.path.dirname(__file__)) + + +class Test_resolve_asset_spec(unittest.TestCase): + def _callFUT(self, spec, package_name='__main__'): + from pyramid.resource import resolve_asset_spec + + return resolve_asset_spec(spec, package_name) + + def test_abspath(self): + package_name, filename = self._callFUT(here, 'apackage') + self.assertEqual(filename, here) + self.assertEqual(package_name, None) + + def test_rel_spec(self): + pkg = 'tests' + path = 'test_asset.py' + 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' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, 'pyramid.nottests') + self.assertEqual(filename, 'test_asset.py') + + def test_package_name_is_None(self): + pkg = None + path = 'test_asset.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, None) + self.assertEqual(filename, 'test_asset.py') + + def test_package_name_is_package_object(self): + import tests + + pkg = tests + path = 'test_asset.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, 'tests') + self.assertEqual(filename, 'test_asset.py') + + +class Test_abspath_from_asset_spec(unittest.TestCase): + def _callFUT(self, spec, pname='__main__'): + from pyramid.resource import abspath_from_asset_spec + + return abspath_from_asset_spec(spec, pname) + + def test_pname_is_None_before_resolve_asset_spec(self): + result = self._callFUT('abc', None) + self.assertEqual(result, 'abc') + + def test_pname_is_None_after_resolve_asset_spec(self): + result = self._callFUT('/abc', '__main__') + self.assertEqual(result, '/abc') + + def test_pkgrelative(self): + result = self._callFUT('abc', 'tests') + self.assertEqual(result, os.path.join(here, 'abc')) + + +class Test_asset_spec_from_abspath(unittest.TestCase): + def _callFUT(self, abspath, package): + from pyramid.asset import asset_spec_from_abspath + + return asset_spec_from_abspath(abspath, package) + + def test_package_name_is_main(self): + pkg = DummyPackage('__main__') + result = self._callFUT('abspath', pkg) + self.assertEqual(result, 'abspath') + + def test_abspath_startswith_package_path(self): + abspath = os.path.join(here, 'fixtureapp') + pkg = DummyPackage('tests') + pkg.__file__ = 'file' + result = self._callFUT(abspath, pkg) + self.assertEqual(result, 'tests:fixtureapp') + + def test_abspath_doesnt_startwith_package_path(self): + pkg = DummyPackage('tests') + result = self._callFUT(here, pkg) + self.assertEqual(result, here) + + +class DummyPackage: + def __init__(self, name): + self.__name__ = name diff --git a/tests/test_authentication.py b/tests/test_authentication.py new file mode 100644 index 000000000..fc3e60587 --- /dev/null +++ b/tests/test_authentication.py @@ -0,0 +1,2034 @@ +import unittest +import warnings +from pyramid import testing +from pyramid.compat import text_, bytes_ + + +class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase): + def setUp(self): + from pyramid.interfaces import IDebugLogger + + self.config = testing.setUp() + self.config.registry.registerUtility(self, IDebugLogger) + self.messages = [] + + def tearDown(self): + del self.config + + def debug(self, msg): + self.messages.append(msg) + + def _makeOne(self, userid=None, callback=None): + from pyramid.authentication import CallbackAuthenticationPolicy + + class MyAuthenticationPolicy(CallbackAuthenticationPolicy): + def unauthenticated_userid(self, request): + return userid + + policy = MyAuthenticationPolicy() + policy.debug = True + policy.callback = callback + return policy + + def test_authenticated_userid_no_unauthenticated_userid(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + 'tests.test_authentication.MyAuthenticationPolicy.' + 'authenticated_userid: call to unauthenticated_userid returned ' + 'None; returning None', + ) + + def test_authenticated_userid_no_callback(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='fred') + self.assertEqual(policy.authenticated_userid(request), 'fred') + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "authenticated_userid: there was no groupfinder callback; " + "returning 'fred'", + ) + + def test_authenticated_userid_with_callback_fail(self): + request = DummyRequest(registry=self.config.registry) + + def callback(userid, request): + return None + + policy = self._makeOne(userid='fred', callback=callback) + self.assertEqual(policy.authenticated_userid(request), None) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + 'tests.test_authentication.MyAuthenticationPolicy.' + 'authenticated_userid: groupfinder callback returned None; ' + 'returning None', + ) + + def test_authenticated_userid_with_callback_success(self): + request = DummyRequest(registry=self.config.registry) + + def callback(userid, request): + return [] + + policy = self._makeOne(userid='fred', callback=callback) + self.assertEqual(policy.authenticated_userid(request), 'fred') + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "authenticated_userid: groupfinder callback returned []; " + "returning 'fred'", + ) + + def test_authenticated_userid_fails_cleaning_as_Authenticated(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='system.Authenticated') + self.assertEqual(policy.authenticated_userid(request), None) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "authenticated_userid: use of userid 'system.Authenticated' is " + "disallowed by any built-in Pyramid security policy, returning " + "None", + ) + + def test_authenticated_userid_fails_cleaning_as_Everyone(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='system.Everyone') + self.assertEqual(policy.authenticated_userid(request), None) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "authenticated_userid: use of userid 'system.Everyone' is " + "disallowed by any built-in Pyramid security policy, returning " + "None", + ) + + def test_effective_principals_no_unauthenticated_userid(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne() + self.assertEqual( + policy.effective_principals(request), ['system.Everyone'] + ) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "effective_principals: unauthenticated_userid returned None; " + "returning ['system.Everyone']", + ) + + def test_effective_principals_no_callback(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='fred') + self.assertEqual( + policy.effective_principals(request), + ['system.Everyone', 'system.Authenticated', 'fred'], + ) + self.assertEqual(len(self.messages), 2) + self.assertEqual( + self.messages[0], + 'tests.test_authentication.MyAuthenticationPolicy.' + 'effective_principals: groupfinder callback is None, so groups ' + 'is []', + ) + self.assertEqual( + self.messages[1], + "tests.test_authentication.MyAuthenticationPolicy." + "effective_principals: returning effective principals: " + "['system.Everyone', 'system.Authenticated', 'fred']", + ) + + def test_effective_principals_with_callback_fail(self): + request = DummyRequest(registry=self.config.registry) + + def callback(userid, request): + return None + + policy = self._makeOne(userid='fred', callback=callback) + self.assertEqual( + policy.effective_principals(request), ['system.Everyone'] + ) + self.assertEqual(len(self.messages), 2) + self.assertEqual( + self.messages[0], + 'tests.test_authentication.MyAuthenticationPolicy.' + 'effective_principals: groupfinder callback returned None as ' + 'groups', + ) + self.assertEqual( + self.messages[1], + "tests.test_authentication.MyAuthenticationPolicy." + "effective_principals: returning effective principals: " + "['system.Everyone']", + ) + + def test_effective_principals_with_callback_success(self): + request = DummyRequest(registry=self.config.registry) + + def callback(userid, request): + return [] + + policy = self._makeOne(userid='fred', callback=callback) + self.assertEqual( + policy.effective_principals(request), + ['system.Everyone', 'system.Authenticated', 'fred'], + ) + self.assertEqual(len(self.messages), 2) + self.assertEqual( + self.messages[0], + 'tests.test_authentication.MyAuthenticationPolicy.' + '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']", + ) + + def test_effective_principals_with_unclean_principal_Authenticated(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='system.Authenticated') + self.assertEqual( + policy.effective_principals(request), ['system.Everyone'] + ) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "effective_principals: unauthenticated_userid returned disallowed " + "'system.Authenticated'; returning ['system.Everyone'] as if it " + "was None", + ) + + def test_effective_principals_with_unclean_principal_Everyone(self): + request = DummyRequest(registry=self.config.registry) + policy = self._makeOne(userid='system.Everyone') + self.assertEqual( + policy.effective_principals(request), ['system.Everyone'] + ) + self.assertEqual(len(self.messages), 1) + self.assertEqual( + self.messages[0], + "tests.test_authentication.MyAuthenticationPolicy." + "effective_principals: unauthenticated_userid returned disallowed " + "'system.Everyone'; returning ['system.Everyone'] as if it " + "was None", + ) + + +class TestRepozeWho1AuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from pyramid.authentication import RepozeWho1AuthenticationPolicy + + return RepozeWho1AuthenticationPolicy + + def _makeOne(self, identifier_name='auth_tkt', callback=None): + return self._getTargetClass()(identifier_name, callback) + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthenticationPolicy + + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAuthenticationPolicy + + verifyObject(IAuthenticationPolicy, self._makeOne()) + + def test_unauthenticated_userid_returns_None(self): + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'fred'}} + ) + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), 'fred') + + def test_authenticated_userid_None(self): + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'fred'}} + ) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), 'fred') + + def test_authenticated_userid_repoze_who_userid_is_None(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': None}} + ) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid_with_callback_returns_None(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'fred'}} + ) + + def callback(identity, request): + return None + + policy = self._makeOne(callback=callback) + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid_with_callback_returns_something(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'fred'}} + ) + + def callback(identity, request): + return ['agroup'] + + policy = self._makeOne(callback=callback) + self.assertEqual(policy.authenticated_userid(request), 'fred') + + def test_authenticated_userid_unclean_principal_Authenticated(self): + request = DummyRequest( + { + 'repoze.who.identity': { + 'repoze.who.userid': 'system.Authenticated' + } + } + ) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid_unclean_principal_Everyone(self): + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'system.Everyone'}} + ) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_effective_principals_None(self): + from pyramid.security import Everyone + + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_userid_only(self): + from pyramid.security import Everyone + from pyramid.security import Authenticated + + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'fred'}} + ) + policy = self._makeOne() + self.assertEqual( + policy.effective_principals(request), + [Everyone, Authenticated, 'fred'], + ) + + def test_effective_principals_userid_and_groups(self): + from pyramid.security import Everyone + from pyramid.security import Authenticated + + request = DummyRequest( + { + 'repoze.who.identity': { + 'repoze.who.userid': 'fred', + 'groups': ['quux', 'biz'], + } + } + ) + + def callback(identity, request): + return identity['groups'] + + policy = self._makeOne(callback=callback) + self.assertEqual( + policy.effective_principals(request), + [Everyone, Authenticated, 'fred', 'quux', 'biz'], + ) + + def test_effective_principals_userid_callback_returns_None(self): + from pyramid.security import Everyone + + request = DummyRequest( + { + 'repoze.who.identity': { + 'repoze.who.userid': 'fred', + 'groups': ['quux', 'biz'], + } + } + ) + + def callback(identity, request): + return None + + policy = self._makeOne(callback=callback) + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_repoze_who_userid_is_None(self): + from pyramid.security import Everyone + + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': None}} + ) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_repoze_who_userid_is_unclean_Everyone(self): + from pyramid.security import Everyone + + request = DummyRequest( + {'repoze.who.identity': {'repoze.who.userid': 'system.Everyone'}} + ) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_repoze_who_userid_is_unclean_Authenticated( + self + ): + from pyramid.security import Everyone + + request = DummyRequest( + { + 'repoze.who.identity': { + 'repoze.who.userid': 'system.Authenticated' + } + } + ) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_remember_no_plugins(self): + request = DummyRequest({}) + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(result, []) + + def test_remember(self): + authtkt = DummyWhoPlugin() + request = DummyRequest({'repoze.who.plugins': {'auth_tkt': authtkt}}) + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(result[0], request.environ) + self.assertEqual(result[1], {'repoze.who.userid': 'fred'}) + + def test_remember_kwargs(self): + authtkt = DummyWhoPlugin() + 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} + ) + + def test_forget_no_plugins(self): + request = DummyRequest({}) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(result, []) + + def test_forget(self): + authtkt = DummyWhoPlugin() + request = DummyRequest( + { + 'repoze.who.plugins': {'auth_tkt': authtkt}, + 'repoze.who.identity': {'repoze.who.userid': 'fred'}, + } + ) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(result[0], request.environ) + self.assertEqual(result[1], request.environ['repoze.who.identity']) + + +class TestRemoteUserAuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from pyramid.authentication import RemoteUserAuthenticationPolicy + + return RemoteUserAuthenticationPolicy + + def _makeOne(self, environ_key='REMOTE_USER', callback=None): + return self._getTargetClass()(environ_key, callback) + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthenticationPolicy + + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAuthenticationPolicy + + verifyObject(IAuthenticationPolicy, self._makeOne()) + + def test_unauthenticated_userid_returns_None(self): + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid(self): + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), 'fred') + + def test_authenticated_userid_None(self): + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid(self): + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), 'fred') + + def test_effective_principals_None(self): + from pyramid.security import Everyone + + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals(self): + from pyramid.security import Everyone + from pyramid.security import Authenticated + + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne() + self.assertEqual( + policy.effective_principals(request), + [Everyone, Authenticated, 'fred'], + ) + + def test_remember(self): + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(result, []) + + def test_forget(self): + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(result, []) + + +class TestAuthTktAuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from pyramid.authentication import AuthTktAuthenticationPolicy + + return AuthTktAuthenticationPolicy + + def _makeOne(self, callback, cookieidentity, **kw): + inst = self._getTargetClass()('secret', callback, **kw) + inst.cookie = DummyCookieHelper(cookieidentity) + return inst + + def setUp(self): + self.warnings = warnings.catch_warnings() + self.warnings.__enter__() + warnings.simplefilter('ignore', DeprecationWarning) + + def tearDown(self): + self.warnings.__exit__(None, None, None) + + def test_allargs(self): + # pass all known args + inst = self._getTargetClass()( + 'secret', + callback=None, + cookie_name=None, + secure=False, + include_ip=False, + timeout=None, + reissue_time=None, + hashalg='sha512', + samesite=None, + ) + self.assertEqual(inst.callback, None) + + def test_hashalg_override(self): + # important to ensure hashalg is passed to cookie helper + inst = self._getTargetClass()('secret', hashalg='sha512') + self.assertEqual(inst.cookie.hashalg, 'sha512') + + def test_unauthenticated_userid_returns_None(self): + request = DummyRequest({}) + policy = self._makeOne(None, None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid(self): + request = DummyRequest({'REMOTE_USER': 'fred'}) + policy = self._makeOne(None, {'userid': 'fred'}) + self.assertEqual(policy.unauthenticated_userid(request), 'fred') + + def test_authenticated_userid_no_cookie_identity(self): + request = DummyRequest({}) + policy = self._makeOne(None, None) + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid_callback_returns_None(self): + request = DummyRequest({}) + + def callback(userid, request): + return None + + policy = self._makeOne(callback, {'userid': 'fred'}) + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid(self): + request = DummyRequest({}) + + def callback(userid, request): + return True + + policy = self._makeOne(callback, {'userid': 'fred'}) + self.assertEqual(policy.authenticated_userid(request), 'fred') + + def test_effective_principals_no_cookie_identity(self): + from pyramid.security import Everyone + + request = DummyRequest({}) + policy = self._makeOne(None, None) + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_callback_returns_None(self): + from pyramid.security import Everyone + + request = DummyRequest({}) + + def callback(userid, request): + return None + + policy = self._makeOne(callback, {'userid': 'fred'}) + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals(self): + from pyramid.security import Everyone + from pyramid.security import Authenticated + + request = DummyRequest({}) + + def callback(userid, request): + return ['group.foo'] + + policy = self._makeOne(callback, {'userid': 'fred'}) + self.assertEqual( + policy.effective_principals(request), + [Everyone, Authenticated, 'fred', 'group.foo'], + ) + + def test_remember(self): + request = DummyRequest({}) + policy = self._makeOne(None, None) + result = policy.remember(request, 'fred') + self.assertEqual(result, []) + + def test_remember_with_extra_kargs(self): + request = DummyRequest({}) + policy = self._makeOne(None, None) + result = policy.remember(request, 'fred', a=1, b=2) + self.assertEqual(policy.cookie.kw, {'a': 1, 'b': 2}) + self.assertEqual(result, []) + + def test_forget(self): + request = DummyRequest({}) + policy = self._makeOne(None, None) + result = policy.forget(request) + self.assertEqual(result, []) + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthenticationPolicy + + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAuthenticationPolicy + + verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) + + +class TestAuthTktCookieHelper(unittest.TestCase): + def _getTargetClass(self): + from pyramid.authentication import AuthTktCookieHelper + + return AuthTktCookieHelper + + def _makeOne(self, *arg, **kw): + helper = self._getTargetClass()(*arg, **kw) + # laziness after moving auth_tkt classes and funcs into + # authentication module + auth_tkt = DummyAuthTktModule() + helper.auth_tkt = auth_tkt + helper.AuthTicket = auth_tkt.AuthTicket + helper.parse_ticket = auth_tkt.parse_ticket + helper.BadTicket = auth_tkt.BadTicket + return helper + + def _makeRequest(self, cookie=None, ipv6=False): + environ = {'wsgi.version': (1, 0)} + + if ipv6 is False: + environ['REMOTE_ADDR'] = '1.1.1.1' + else: + environ['REMOTE_ADDR'] = '::1' + environ['SERVER_NAME'] = 'localhost' + return DummyRequest(environ, cookie=cookie) + + def _cookieValue(self, cookie): + items = cookie.value.split('/') + D = {} + for item in items: + k, v = item.split('=', 1) + D[k] = v + return D + + def _parseHeaders(self, headers): + return [self._parseHeader(header) for header in headers] + + def _parseHeader(self, header): + cookie = self._parseCookie(header[1]) + return cookie + + def _parseCookie(self, cookie): + from pyramid.compat import SimpleCookie + + cookies = SimpleCookie() + cookies.load(cookie) + return cookies.get('auth_tkt') + + def test_init_cookie_str_reissue_invalid(self): + 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' + ) + + def test_init_cookie_str_max_age_invalid(self): + self.assertRaises( + ValueError, self._makeOne, 'secret', max_age='invalid value' + ) + + def test_identify_nocookie(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.identify(request) + self.assertEqual(result, None) + + def test_identify_cookie_value_is_None(self): + helper = self._makeOne('secret') + request = self._makeRequest(None) + result = helper.identify(request) + self.assertEqual(result, None) + + def test_identify_good_cookie_include_ip(self): + helper = self._makeOne('secret', include_ip=True) + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 'userid') + self.assertEqual(result['userdata'], '') + self.assertEqual(result['timestamp'], 0) + self.assertEqual(helper.auth_tkt.value, 'ticket') + self.assertEqual(helper.auth_tkt.remote_addr, '1.1.1.1') + self.assertEqual(helper.auth_tkt.secret, 'secret') + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], '') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_include_ipv6(self): + helper = self._makeOne('secret', include_ip=True) + request = self._makeRequest('ticket', ipv6=True) + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 'userid') + self.assertEqual(result['userdata'], '') + self.assertEqual(result['timestamp'], 0) + self.assertEqual(helper.auth_tkt.value, 'ticket') + self.assertEqual(helper.auth_tkt.remote_addr, '::1') + self.assertEqual(helper.auth_tkt.secret, 'secret') + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], '') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_dont_include_ip(self): + helper = self._makeOne('secret', include_ip=False) + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 'userid') + self.assertEqual(result['userdata'], '') + self.assertEqual(result['timestamp'], 0) + self.assertEqual(helper.auth_tkt.value, 'ticket') + self.assertEqual(helper.auth_tkt.remote_addr, '0.0.0.0') + self.assertEqual(helper.auth_tkt.secret, 'secret') + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], '') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_int_useridtype(self): + helper = self._makeOne('secret', include_ip=False) + helper.auth_tkt.userid = '1' + helper.auth_tkt.user_data = 'userid_type:int' + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 1) + self.assertEqual(result['userdata'], 'userid_type:int') + self.assertEqual(result['timestamp'], 0) + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:int') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_nonuseridtype_user_data(self): + helper = self._makeOne('secret', include_ip=False) + helper.auth_tkt.userid = '1' + helper.auth_tkt.user_data = 'bogus:int' + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], '1') + self.assertEqual(result['userdata'], 'bogus:int') + self.assertEqual(result['timestamp'], 0) + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], 'bogus:int') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_unknown_useridtype(self): + helper = self._makeOne('secret', include_ip=False) + helper.auth_tkt.userid = 'abc' + helper.auth_tkt.user_data = 'userid_type:unknown' + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], 'abc') + self.assertEqual(result['userdata'], 'userid_type:unknown') + self.assertEqual(result['timestamp'], 0) + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:unknown') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_b64str_useridtype(self): + from base64 import b64encode + + helper = self._makeOne('secret', include_ip=False) + helper.auth_tkt.userid = b64encode(b'encoded').strip() + helper.auth_tkt.user_data = 'userid_type:b64str' + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], b'encoded') + self.assertEqual(result['userdata'], 'userid_type:b64str') + self.assertEqual(result['timestamp'], 0) + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:b64str') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_good_cookie_b64unicode_useridtype(self): + from base64 import b64encode + + helper = self._makeOne('secret', include_ip=False) + helper.auth_tkt.userid = b64encode(b'\xc3\xa9ncoded').strip() + helper.auth_tkt.user_data = 'userid_type:b64unicode' + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], text_(b'\xc3\xa9ncoded', 'utf-8')) + self.assertEqual(result['userdata'], 'userid_type:b64unicode') + self.assertEqual(result['timestamp'], 0) + environ = request.environ + self.assertEqual(environ['REMOTE_USER_TOKENS'], ()) + self.assertEqual(environ['REMOTE_USER_DATA'], 'userid_type:b64unicode') + self.assertEqual(environ['AUTH_TYPE'], 'cookie') + + def test_identify_bad_cookie(self): + helper = self._makeOne('secret', include_ip=True) + helper.auth_tkt.parse_raise = True + request = self._makeRequest('ticket') + result = helper.identify(request) + self.assertEqual(result, None) + + def test_identify_cookie_timeout(self): + helper = self._makeOne('secret', timeout=1) + self.assertEqual(helper.timeout, 1) + + def test_identify_cookie_str_timeout(self): + helper = self._makeOne('secret', timeout='1') + self.assertEqual(helper.timeout, 1) + + 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'),) + 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'),) + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + response = DummyResponse() + request.callbacks[0](request, response) + self.assertEqual(len(response.headerlist), 3) + self.assertEqual(response.headerlist[0][0], 'Set-Cookie') + + def test_identify_cookie_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'),) + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + response = DummyResponse() + request.callbacks[0](request, response) + self.assertEqual(len(response.headerlist), 3) + self.assertEqual(response.headerlist[0][0], 'Set-Cookie') + + def test_identify_cookie_reissue_already_reissued_this_request(self): + import time + + helper = self._makeOne('secret', timeout=10, reissue_time=0) + now = time.time() + helper.auth_tkt.timestamp = now + helper.now = now + 1 + request = self._makeRequest('bogus') + request._authtkt_reissued = True + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 0) + + def test_identify_cookie_reissue_notyet(self): + import time + + helper = self._makeOne('secret', timeout=10, reissue_time=10) + now = time.time() + helper.auth_tkt.timestamp = now + helper.now = now + 1 + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 0) + + def test_identify_cookie_reissue_revoked_by_forget(self): + import time + + helper = self._makeOne('secret', timeout=10, reissue_time=0) + now = time.time() + helper.auth_tkt.timestamp = now + helper.now = now + 1 + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + result = helper.forget(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + response = DummyResponse() + request.callbacks[0](request, response) + self.assertEqual(len(response.headerlist), 0) + + def test_identify_cookie_reissue_revoked_by_remember(self): + import time + + helper = self._makeOne('secret', timeout=10, reissue_time=0) + now = time.time() + helper.auth_tkt.timestamp = now + helper.now = now + 1 + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + result = helper.remember(request, 'bob') + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + response = DummyResponse() + request.callbacks[0](request, response) + self.assertEqual(len(response.headerlist), 0) + + def test_identify_cookie_reissue_with_tokens_default(self): + # see https://github.com/Pylons/pyramid/issues#issue/108 + import time + + helper = self._makeOne('secret', timeout=10, reissue_time=0) + auth_tkt = DummyAuthTktModule(tokens=['']) + helper.auth_tkt = auth_tkt + helper.AuthTicket = auth_tkt.AuthTicket + helper.parse_ticket = auth_tkt.parse_ticket + helper.BadTicket = auth_tkt.BadTicket + now = time.time() + helper.auth_tkt.timestamp = now + helper.now = now + 1 + request = self._makeRequest('bogus') + result = helper.identify(request) + self.assertTrue(result) + self.assertEqual(len(request.callbacks), 1) + response = DummyResponse() + request.callbacks[0](None, response) + self.assertEqual(len(response.headerlist), 3) + self.assertEqual(response.headerlist[0][0], 'Set-Cookie') + self.assertTrue("/tokens=/" in response.headerlist[0][1]) + + def test_remember(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 'userid') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; Path=/; 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=/; 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].startswith('auth_tkt=')) + + def test_remember_nondefault_samesite(self): + helper = self._makeOne('secret', samesite='Strict') + request = self._makeRequest() + result = helper.remember(request, 'userid') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; Path=/; SameSite=Strict')) + 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].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].startswith('auth_tkt=')) + + def test_remember_None_samesite(self): + helper = self._makeOne('secret', samesite=None) + request = self._makeRequest() + result = helper.remember(request, 'userid') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; Path=/')) # 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].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.assertTrue(result[2][1].endswith('; Domain=.localhost; Path=/')) + self.assertTrue(result[2][1].startswith('auth_tkt=')) + + def test_remember_include_ip(self): + helper = self._makeOne('secret', include_ip=True) + request = self._makeRequest() + result = helper.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; Path=/; 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=/; 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].startswith('auth_tkt=')) + + def test_remember_path(self): + helper = self._makeOne( + 'secret', include_ip=True, path="/cgi-bin/app.cgi/" + ) + request = self._makeRequest() + result = helper.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue( + result[0][1].endswith('; Path=/cgi-bin/app.cgi/; 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].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].startswith('auth_tkt=')) + + def test_remember_http_only(self): + helper = self._makeOne('secret', include_ip=True, http_only=True) + request = self._makeRequest() + result = helper.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; HttpOnly; SameSite=Lax')) + self.assertTrue(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.assertTrue('; HttpOnly' in result[1][1]) + self.assertTrue(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.assertTrue('; HttpOnly' in result[2][1]) + self.assertTrue(result[2][1].startswith('auth_tkt=')) + + def test_remember_secure(self): + helper = self._makeOne('secret', include_ip=True, secure=True) + request = self._makeRequest() + result = helper.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue('; secure' in result[0][1]) + self.assertTrue(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.assertTrue('; secure' in result[1][1]) + self.assertTrue(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.assertTrue('; secure' in result[2][1]) + self.assertTrue(result[2][1].startswith('auth_tkt=')) + + def test_remember_wild_domain_disabled(self): + helper = self._makeOne('secret', wild_domain=False) + request = self._makeRequest() + result = helper.remember(request, 'other') + self.assertEqual(len(result), 2) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue(result[0][1].endswith('; Path=/; 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=/; SameSite=Lax') + ) + self.assertTrue(result[1][1].startswith('auth_tkt=')) + + def test_remember_parent_domain(self): + helper = self._makeOne('secret', parent_domain=True) + request = self._makeRequest() + request.domain = 'www.example.com' + result = helper.remember(request, 'other') + 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].startswith('auth_tkt=')) + + def test_remember_parent_domain_supercedes_wild_domain(self): + helper = self._makeOne('secret', 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=.example.com; Path=/; SameSite=Lax' + ) + ) + + def test_remember_explicit_domain(self): + helper = self._makeOne('secret', domain='pyramid.bazinga') + request = self._makeRequest() + request.domain = 'www.example.com' + result = helper.remember(request, 'other') + 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].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, + ) + 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' + ) + ) + + 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['user_data'], 'userid_type:b64str') + + def test_remember_int_userid(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 1) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + val = self._cookieValue(values[0]) + self.assertEqual(val['userid'], '1') + self.assertEqual(val['user_data'], 'userid_type:int') + + def test_remember_long_userid(self): + from pyramid.compat import long + + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, long(1)) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + val = self._cookieValue(values[0]) + self.assertEqual(val['userid'], '1') + self.assertEqual(val['user_data'], 'userid_type:int') + + def test_remember_unicode_userid(self): + import base64 + + helper = self._makeOne('secret') + request = self._makeRequest() + userid = text_(b'\xc2\xa9', 'utf-8') + result = helper.remember(request, userid) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + val = self._cookieValue(values[0]) + self.assertEqual( + val['userid'], text_(base64.b64encode(userid.encode('utf-8'))) + ) + self.assertEqual(val['user_data'], 'userid_type:b64unicode') + + def test_remember_insane_userid(self): + helper = self._makeOne('secret') + request = self._makeRequest() + userid = object() + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always', RuntimeWarning) + result = helper.remember(request, userid) + self.assertTrue(str(w[-1].message).startswith('userid is of type')) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + value = values[0] + self.assertTrue('userid' in value.value) + + def test_remember_max_age(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 'userid', max_age=500) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + + self.assertEqual(values[0]['max-age'], '500') + self.assertTrue(values[0]['expires']) + + def test_remember_str_max_age(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 'userid', max_age='500') + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + + self.assertEqual(values[0]['max-age'], '500') + self.assertTrue(values[0]['expires']) + + def test_remember_str_max_age_invalid(self): + helper = self._makeOne('secret') + request = self._makeRequest() + self.assertRaises( + ValueError, + helper.remember, + request, + 'userid', + max_age='invalid value', + ) + + def test_remember_tokens(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 'other', tokens=('foo', 'bar')) + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.assertTrue("/tokens=foo|bar/" in result[0][1]) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.assertTrue("/tokens=foo|bar/" in result[1][1]) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.assertTrue("/tokens=foo|bar/" in result[2][1]) + + def test_remember_samesite_nondefault(self): + helper = self._makeOne('secret', samesite='Strict') + request = self._makeRequest() + result = helper.remember(request, 'userid') + self.assertEqual(len(result), 3) + + 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.assertEqual(result[1][0], 'Set-Cookie') + cookieval = result[1][1] + 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, + ) + + def test_remember_samesite_default(self): + helper = self._makeOne('secret') + request = self._makeRequest() + result = helper.remember(request, 'userid') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + cookieval = result[0][1] + 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.assertEqual(result[2][0], 'Set-Cookie') + cookieval = result[2][1] + 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') + request = self._makeRequest() + la = text_(b'foo', 'utf-8') + result = helper.remember(request, 'other', tokens=(la,)) + # tokens must be str type on both Python 2 and 3 + self.assertTrue("/tokens=foo/" in result[0][1]) + + def test_remember_nonascii_token(self): + helper = self._makeOne('secret') + request = self._makeRequest() + la = text_(b'La Pe\xc3\xb1a', 'utf-8') + self.assertRaises( + ValueError, helper.remember, request, 'other', tokens=(la,) + ) + + def test_remember_invalid_token_format(self): + helper = self._makeOne('secret') + request = self._makeRequest() + self.assertRaises( + ValueError, helper.remember, request, 'other', tokens=('foo bar',) + ) + self.assertRaises( + ValueError, helper.remember, request, 'other', tokens=('1bar',) + ) + + def test_forget(self): + helper = self._makeOne('secret') + request = self._makeRequest() + headers = helper.forget(request) + self.assertEqual(len(headers), 3) + name, value = headers[0] + self.assertEqual(name, 'Set-Cookie') + self.assertEqual( + value, + 'auth_tkt=; Max-Age=0; Path=/; ' + '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', + ) + 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', + ) + + +class TestAuthTicket(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.authentication import AuthTicket + + return AuthTicket(*arg, **kw) + + def test_ctor_with_tokens(self): + ticket = self._makeOne('secret', 'userid', 'ip', tokens=('a', 'b')) + self.assertEqual(ticket.tokens, 'a,b') + + def test_ctor_with_time(self): + ticket = self._makeOne('secret', 'userid', 'ip', time='time') + self.assertEqual(ticket.time, 'time') + + def test_digest(self): + ticket = self._makeOne('secret', 'userid', '0.0.0.0', time=10) + result = ticket.digest() + self.assertEqual(result, '126fd6224912187ee9ffa80e0b81420c') + + def test_digest_sha512(self): + ticket = self._makeOne( + 'secret', 'userid', '0.0.0.0', time=10, hashalg='sha512' + ) + result = ticket.digest() + self.assertEqual( + result, + '74770b2e0d5b1a54c2a466ec567a40f7d7823576aa49' + '3c65fc3445e9b44097f4a80410319ef8cb256a2e60b9' + 'c2002e48a9e33a3e8ee4379352c04ef96d2cb278', + ) + + def test_cookie_value(self): + ticket = self._makeOne( + 'secret', 'userid', '0.0.0.0', time=10, tokens=('a', 'b') + ) + result = ticket.cookie_value() + self.assertEqual( + result, '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!' + ) + + def test_ipv4(self): + ticket = self._makeOne( + 'secret', 'userid', '198.51.100.1', time=10, hashalg='sha256' + ) + result = ticket.cookie_value() + self.assertEqual( + result, + 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b' + '798400ecdade8d76c530000000auserid!', + ) + + def test_ipv6(self): + ticket = self._makeOne( + 'secret', 'userid', '2001:db8::1', time=10, hashalg='sha256' + ) + result = ticket.cookie_value() + 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): + exc = self._makeOne('msg', expected=True) + self.assertEqual(exc.expected, True) + self.assertTrue(isinstance(exc, Exception)) + + +class Test_parse_ticket(unittest.TestCase): + def _callFUT(self, secret, ticket, ip, hashalg='md5'): + from pyramid.authentication import parse_ticket + + return parse_ticket(secret, ticket, ip, hashalg) + + def _assertRaisesBadTicket(self, secret, ticket, ip, hashalg='md5'): + from pyramid.authentication import BadTicket + + self.assertRaises( + BadTicket, self._callFUT, secret, ticket, ip, hashalg + ) + + def test_bad_timestamp(self): + ticket = 'x' * 64 + self._assertRaisesBadTicket('secret', ticket, 'ip') + + def test_bad_userid_or_data(self): + ticket = 'x' * 32 + '11111111' + 'x' * 10 + self._assertRaisesBadTicket('secret', ticket, 'ip') + + def test_digest_sig_incorrect(self): + ticket = 'x' * 32 + '11111111' + 'a!b!c' + self._assertRaisesBadTicket('secret', ticket, '0.0.0.0') + + def test_correct_with_user_data(self): + ticket = text_('66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!') + result = self._callFUT('secret', ticket, '0.0.0.0') + self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) + + def test_correct_with_user_data_sha512(self): + ticket = 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!' + ) + result = self._callFUT('secret', ticket, '198.51.100.1', 'sha256') + self.assertEqual(result, (10, 'userid', [''], '')) + + def test_ipv6(self): + 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=''): + return self._getTargetClass()(prefix=prefix, callback=callback) + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthenticationPolicy + + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAuthenticationPolicy + + verifyObject(IAuthenticationPolicy, self._makeOne()) + + def test_unauthenticated_userid_returns_None(self): + request = DummyRequest() + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid(self): + request = DummyRequest(session={'userid': 'fred'}) + policy = self._makeOne() + self.assertEqual(policy.unauthenticated_userid(request), 'fred') + + def test_authenticated_userid_no_cookie_identity(self): + request = DummyRequest() + policy = self._makeOne() + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid_callback_returns_None(self): + request = DummyRequest(session={'userid': 'fred'}) + + def callback(userid, request): + return None + + policy = self._makeOne(callback) + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_userid(self): + request = DummyRequest(session={'userid': 'fred'}) + + def callback(userid, request): + return True + + policy = self._makeOne(callback) + self.assertEqual(policy.authenticated_userid(request), 'fred') + + def test_effective_principals_no_identity(self): + from pyramid.security import Everyone + + request = DummyRequest() + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_callback_returns_None(self): + from pyramid.security import Everyone + + request = DummyRequest(session={'userid': 'fred'}) + + def callback(userid, request): + return None + + policy = self._makeOne(callback) + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals(self): + from pyramid.security import Everyone + from pyramid.security import Authenticated + + request = DummyRequest(session={'userid': 'fred'}) + + def callback(userid, request): + return ['group.foo'] + + policy = self._makeOne(callback) + self.assertEqual( + policy.effective_principals(request), + [Everyone, Authenticated, 'fred', 'group.foo'], + ) + + def test_remember(self): + request = DummyRequest() + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(request.session.get('userid'), 'fred') + self.assertEqual(result, []) + + def test_forget(self): + request = DummyRequest(session={'userid': 'fred'}) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(request.session.get('userid'), None) + self.assertEqual(result, []) + + def test_forget_no_identity(self): + request = DummyRequest() + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(request.session.get('userid'), None) + self.assertEqual(result, []) + + +class TestBasicAuthAuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from pyramid.authentication import BasicAuthAuthenticationPolicy as cls + + return cls + + def _makeOne(self, check): + return self._getTargetClass()(check, realm='SomeRealm') + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthenticationPolicy + + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_unauthenticated_userid(self): + import base64 + + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic %s' % base64.b64encode( + bytes_('chrisr:password') + ).decode('ascii') + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), 'chrisr') + + def test_unauthenticated_userid_no_credentials(self): + request = testing.DummyRequest() + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_bad_header(self): + request = testing.DummyRequest() + request.headers['Authorization'] = '...' + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid_not_basic(self): + request = testing.DummyRequest() + request.headers['Authorization'] = 'Complicated things' + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_unauthenticated_userid_corrupt_base64(self): + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic chrisr:password' + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_authenticated_userid(self): + import base64 + + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic %s' % base64.b64encode( + bytes_('chrisr:password') + ).decode('ascii') + + def check(username, password, request): + return [] + + policy = self._makeOne(check) + self.assertEqual(policy.authenticated_userid(request), 'chrisr') + + def test_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') + request.headers['Authorization'] = 'Basic %s' % ( + 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'), + ) + + 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') + request.headers['Authorization'] = 'Basic %s' % ( + 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'), + ) + + def test_unauthenticated_userid_invalid_payload(self): + import base64 + + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic %s' % base64.b64encode( + bytes_('chrisrpassword') + ).decode('ascii') + policy = self._makeOne(None) + self.assertEqual(policy.unauthenticated_userid(request), None) + + def test_remember(self): + policy = self._makeOne(None) + self.assertEqual(policy.remember(None, None), []) + + def test_forget(self): + policy = self._makeOne(None) + self.assertEqual( + policy.forget(None), + [('WWW-Authenticate', 'Basic realm="SomeRealm"')], + ) + + +class 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): + request = testing.DummyRequest() + fn = self._get_func() + + self.assertIsNone(fn(request)) + + def test_invalid_payload(self): + import base64 + + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic %s' % base64.b64encode( + 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') + fn = self._get_func() + self.assertIsNone(fn(request)) + + def test_no_base64_encoding(self): + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic ...' + fn = self._get_func() + self.assertIsNone(fn(request)) + + 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') + request.headers['Authorization'] = 'Basic %s' % ( + 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'), + ), + ) + + 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') + request.headers['Authorization'] = 'Basic %s' % ( + 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'), + ), + ) + + def test_namedtuple_return(self): + import base64 + + request = testing.DummyRequest() + request.headers['Authorization'] = 'Basic %s' % base64.b64encode( + 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): + self.cookie = cookie + + 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 {} + self.registry = registry + self.callbacks = [] + self.cookies = DummyCookies(cookie) + + def add_response_callback(self, callback): + self.callbacks.append(callback) + + +class DummyWhoPlugin: + def remember(self, environ, identity): + return environ, identity + + def forget(self, environ, identity): + return environ, identity + + +class DummyCookieHelper: + def __init__(self, result): + self.result = result + + def identify(self, *arg, **kw): + return self.result + + def remember(self, *arg, **kw): + self.kw = kw + return [] + + def forget(self, *arg): + return [] + + +class DummyAuthTktModule(object): + def __init__( + self, + timestamp=0, + userid='userid', + tokens=(), + user_data='', + parse_raise=False, + hashalg="md5", + ): + self.timestamp = timestamp + self.userid = userid + self.tokens = tokens + self.user_data = user_data + self.parse_raise = parse_raise + self.hashalg = hashalg + + def parse_ticket(secret, value, remote_addr, hashalg): + self.secret = secret + self.value = value + self.remote_addr = remote_addr + if self.parse_raise: + raise self.BadTicket() + return self.timestamp, self.userid, self.tokens, self.user_data + + self.parse_ticket = parse_ticket + + class AuthTicket(object): + def __init__(self, secret, userid, remote_addr, **kw): + self.secret = secret + self.userid = userid + self.remote_addr = remote_addr + self.kw = kw + + def cookie_value(self): + result = { + 'secret': self.secret, + 'userid': self.userid, + 'remote_addr': self.remote_addr, + } + result.update(self.kw) + tokens = result.pop('tokens', None) + if tokens is not None: + tokens = '|'.join(tokens) + result['tokens'] = tokens + items = sorted(result.items()) + new_items = [] + 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]) + return result + + self.AuthTicket = AuthTicket + + class BadTicket(Exception): + pass + + +class DummyResponse: + def __init__(self): + self.headerlist = [] diff --git a/tests/test_authorization.py b/tests/test_authorization.py new file mode 100644 index 000000000..efb84b203 --- /dev/null +++ b/tests/test_authorization.py @@ -0,0 +1,291 @@ +import unittest + +from pyramid.testing import cleanUp + + +class TestACLAuthorizationPolicy(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from pyramid.authorization import ACLAuthorizationPolicy + + return ACLAuthorizationPolicy + + def _makeOne(self): + return self._getTargetClass()() + + def test_class_implements_IAuthorizationPolicy(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAuthorizationPolicy + + verifyClass(IAuthorizationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthorizationPolicy(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAuthorizationPolicy + + verifyObject(IAuthorizationPolicy, self._makeOne()) + + def test_permits_no_acl(self): + context = DummyContext() + policy = self._makeOne() + self.assertEqual(policy.permits(context, [], 'view'), False) + + def test_permits(self): + from pyramid.security import Deny + from pyramid.security import Allow + from pyramid.security import Everyone + from pyramid.security import Authenticated + from pyramid.security import ALL_PERMISSIONS + from pyramid.security import DENY_ALL + + root = DummyContext() + community = DummyContext(__name__='community', __parent__=root) + blog = DummyContext(__name__='blog', __parent__=community) + root.__acl__ = [(Allow, Authenticated, VIEW)] + community.__acl__ = [ + (Allow, 'fred', ALL_PERMISSIONS), + (Allow, 'wilma', VIEW), + DENY_ALL, + ] + blog.__acl__ = [ + (Allow, 'barney', MEMBER_PERMS), + (Allow, 'wilma', VIEW), + ] + + policy = self._makeOne() + + result = policy.permits( + blog, [Everyone, Authenticated, 'wilma'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'wilma', VIEW)) + self.assertEqual(result.acl, blog.__acl__) + + result = policy.permits( + blog, [Everyone, Authenticated, 'wilma'], 'delete' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits( + blog, [Everyone, Authenticated, 'fred'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + result = policy.permits( + blog, [Everyone, Authenticated, 'fred'], 'doesntevenexistyet' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits( + blog, [Everyone, Authenticated, 'barney'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS)) + result = policy.permits( + blog, [Everyone, Authenticated, 'barney'], 'administer' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits( + root, [Everyone, Authenticated, 'someguy'], 'view' + ) + self.assertEqual(result, True) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, (Allow, Authenticated, VIEW)) + result = policy.permits( + blog, [Everyone, Authenticated, 'someguy'], 'view' + ) + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits(root, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, '<default deny>') + self.assertEqual(result.acl, root.__acl__) + + context = DummyContext() + result = policy.permits(context, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.ace, '<default deny>') + self.assertEqual( + 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')] + + policy = self._makeOne() + + result = policy.permits(root, ['wilma'], 'view') + # would be True if matching against 'view_stuff' instead of against + # ['view_stuff'] + self.assertEqual(result, False) + + def test_principals_allowed_by_permission_direct(self): + from pyramid.security import Allow + from pyramid.security import DENY_ALL + + context = DummyContext() + acl = [ + (Allow, 'chrism', ('read', 'write')), + DENY_ALL, + (Allow, 'other', 'read'), + ] + context.__acl__ = acl + policy = self._makeOne() + result = sorted( + policy.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, ['chrism']) + + def test_principals_allowed_by_permission_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'), + ] + context.__acl__ = acl + policy = self._makeOne() + result = sorted( + policy.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, ['chrism']) + + def test_principals_allowed_by_permission_string_permission(self): + from pyramid.security import Allow + + context = DummyContext() + acl = [(Allow, 'chrism', 'read_it')] + context.__acl__ = acl + policy = self._makeOne() + result = policy.principals_allowed_by_permission(context, 'read') + # would be ['chrism'] if 'read' were compared against 'read_it' instead + # of against ['read_it'] + self.assertEqual(list(result), []) + + def test_principals_allowed_by_permission(self): + from pyramid.security import Allow + from pyramid.security import Deny + from pyramid.security import DENY_ALL + from pyramid.security import ALL_PERMISSIONS + + root = DummyContext(__name__='', __parent__=None) + community = DummyContext(__name__='community', __parent__=root) + blog = DummyContext(__name__='blog', __parent__=community) + root.__acl__ = [ + (Allow, 'chrism', ('read', 'write')), + (Allow, 'other', ('read',)), + (Allow, 'jim', ALL_PERMISSIONS), + ] + community.__acl__ = [ + (Deny, 'flooz', 'read'), + (Allow, 'flooz', 'read'), + (Allow, 'mork', 'read'), + (Deny, 'jim', 'read'), + (Allow, 'someguy', 'manage'), + ] + blog.__acl__ = [(Allow, 'fred', 'read'), DENY_ALL] + + policy = self._makeOne() + + result = sorted(policy.principals_allowed_by_permission(blog, 'read')) + self.assertEqual(result, ['fred']) + result = sorted( + policy.principals_allowed_by_permission(community, 'read') + ) + self.assertEqual(result, ['chrism', 'mork', 'other']) + result = sorted( + policy.principals_allowed_by_permission(community, 'read') + ) + result = sorted(policy.principals_allowed_by_permission(root, 'read')) + self.assertEqual(result, ['chrism', 'jim', 'other']) + + def test_principals_allowed_by_permission_no_acls(self): + context = DummyContext() + policy = self._makeOne() + result = sorted( + policy.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + def test_principals_allowed_by_permission_deny_not_permission_in_acl(self): + from pyramid.security import Deny + from pyramid.security import Everyone + + context = DummyContext() + acl = [(Deny, Everyone, 'write')] + context.__acl__ = acl + policy = self._makeOne() + result = sorted( + policy.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + def test_principals_allowed_by_permission_deny_permission_in_acl(self): + from pyramid.security import Deny + from pyramid.security import Everyone + + context = DummyContext() + acl = [(Deny, Everyone, 'read')] + context.__acl__ = acl + policy = self._makeOne() + result = sorted( + policy.principals_allowed_by_permission(context, 'read') + ) + self.assertEqual(result, []) + + 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): + self.__dict__.update(kw) + + +VIEW = 'view' +EDIT = 'edit' +CREATE = 'create' +DELETE = 'delete' +MODERATE = 'moderate' +ADMINISTER = 'administer' +COMMENT = 'comment' + +GUEST_PERMS = (VIEW, COMMENT) +MEMBER_PERMS = GUEST_PERMS + (EDIT, CREATE, DELETE) +MODERATOR_PERMS = MEMBER_PERMS + (MODERATE,) +ADMINISTRATOR_PERMS = MODERATOR_PERMS + (ADMINISTER,) diff --git a/tests/test_compat.py b/tests/test_compat.py new file mode 100644 index 000000000..4a14caedf --- /dev/null +++ b/tests/test_compat.py @@ -0,0 +1,32 @@ +import unittest +from pyramid.compat import is_unbound_method + + +class TestUnboundMethods(unittest.TestCase): + def test_old_style_bound(self): + self.assertFalse(is_unbound_method(OldStyle().run)) + + def test_new_style_bound(self): + self.assertFalse(is_unbound_method(NewStyle().run)) + + def test_old_style_unbound(self): + self.assertTrue(is_unbound_method(OldStyle.run)) + + def test_new_style_unbound(self): + self.assertTrue(is_unbound_method(NewStyle.run)) + + def test_normal_func_unbound(self): + def func(): # pragma: no cover + return 'OK' + + self.assertFalse(is_unbound_method(func)) + + +class OldStyle: + def run(self): # pragma: no cover + return 'OK' + + +class NewStyle(object): + def run(self): # pragma: no cover + return 'OK' diff --git a/tests/test_config/__init__.py b/tests/test_config/__init__.py new file mode 100644 index 000000000..ac1f19667 --- /dev/null +++ b/tests/test_config/__init__.py @@ -0,0 +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): # 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) + + +dummy_partial = partial(dummy_extend, discrim='partial') + + +class DummyCallable(object): + def __call__(self, config, discrim): + config.action(discrim, None, config.package) + + +dummy_callable = DummyCallable() diff --git a/tests/test_config/files/assets/dummy.txt b/tests/test_config/files/assets/dummy.txt new file mode 100644 index 000000000..18832d351 --- /dev/null +++ b/tests/test_config/files/assets/dummy.txt @@ -0,0 +1 @@ +Hello. diff --git a/tests/test_config/files/minimal.txt b/tests/test_config/files/minimal.txt new file mode 100644 index 000000000..19fe66dfa --- /dev/null +++ b/tests/test_config/files/minimal.txt @@ -0,0 +1 @@ +<div clas="header"></div> diff --git a/tests/test_config/path/scanerror/__init__.py b/tests/test_config/path/scanerror/__init__.py new file mode 100644 index 000000000..934d6d3ad --- /dev/null +++ b/tests/test_config/path/scanerror/__init__.py @@ -0,0 +1 @@ +# scan error package diff --git a/tests/test_config/path/scanerror/will_raise_error.py b/tests/test_config/path/scanerror/will_raise_error.py new file mode 100644 index 000000000..9098ff1fe --- /dev/null +++ b/tests/test_config/path/scanerror/will_raise_error.py @@ -0,0 +1 @@ +import wont.exist diff --git a/tests/test_config/pkgs/__init__.py b/tests/test_config/pkgs/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/test_config/pkgs/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/test_config/pkgs/asset/__init__.py b/tests/test_config/pkgs/asset/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/test_config/pkgs/asset/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/test_config/pkgs/asset/subpackage/__init__.py b/tests/test_config/pkgs/asset/subpackage/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/test_config/pkgs/asset/subpackage/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/test_config/pkgs/asset/subpackage/templates/bar.pt b/tests/test_config/pkgs/asset/subpackage/templates/bar.pt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/test_config/pkgs/asset/subpackage/templates/bar.pt diff --git a/tests/test_config/pkgs/scanextrakw/__init__.py b/tests/test_config/pkgs/scanextrakw/__init__.py new file mode 100644 index 000000000..ddda504e1 --- /dev/null +++ b/tests/test_config/pkgs/scanextrakw/__init__.py @@ -0,0 +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 diff --git a/tests/test_config/pkgs/scannable/__init__.py b/tests/test_config/pkgs/scannable/__init__.py new file mode 100644 index 000000000..585f4357b --- /dev/null +++ b/tests/test_config/pkgs/scannable/__init__.py @@ -0,0 +1,117 @@ +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): + self.context = context + self.request = request + + def __call__(self): + return 'stacked_class' + + +stacked_class = view_config(name='stacked_class1', renderer=null_renderer)( + stacked_class +) +stacked_class = view_config(name='stacked_class2', renderer=null_renderer)( + stacked_class +) + + +class oldstyle_grokked_class: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'oldstyle_grokked_class' + + +oldstyle_grokked_class = view_config( + name='oldstyle_grokked_class', renderer=null_renderer +)(oldstyle_grokked_class) + + +class grokked_class(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'grokked_class' + + +grokked_class = view_config(name='grokked_class', renderer=null_renderer)( + grokked_class +) + + +class Foo(object): + def __call__(self, context, request): + return 'grokked_instance' + + +grokked_instance = Foo() +grokked_instance = view_config( + name='grokked_instance', renderer=null_renderer +)(grokked_instance) + + +class Base(object): + @view_config(name='basemethod', renderer=null_renderer) + def basemethod(self): + """ """ + + +class MethodViews(Base): + def __init__(self, context, request): + self.context = context + self.request = request + + @view_config(name='method1', renderer=null_renderer) + def method1(self): + return 'method1' + + @view_config(name='method2', renderer=null_renderer) + def method2(self): + return 'method2' + + @view_config(name='stacked_method2', renderer=null_renderer) + @view_config(name='stacked_method1', renderer=null_renderer) + def stacked(self): + return 'stacked_method' + + +# ungrokkable + +A = 1 +B = {} + + +def stuff(): + """ """ + + +class Whatever(object): + pass + + +class Whatever2: + pass diff --git a/tests/test_config/pkgs/scannable/another.py b/tests/test_config/pkgs/scannable/another.py new file mode 100644 index 000000000..e8b71e5e3 --- /dev/null +++ b/tests/test_config/pkgs/scannable/another.py @@ -0,0 +1,83 @@ +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): + self.context = context + self.request = request + + def __call__(self): + return 'another_stacked_class' + + +stacked_class = view_config( + name='another_stacked_class1', renderer=null_renderer +)(stacked_class) +stacked_class = view_config( + name='another_stacked_class2', renderer=null_renderer +)(stacked_class) + + +class oldstyle_grokked_class: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'another_oldstyle_grokked_class' + + +oldstyle_grokked_class = view_config( + name='another_oldstyle_grokked_class', renderer=null_renderer +)(oldstyle_grokked_class) + + +class grokked_class(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'another_grokked_class' + + +grokked_class = view_config( + name='another_grokked_class', renderer=null_renderer +)(grokked_class) + + +class Foo(object): + def __call__(self, context, request): + return 'another_grokked_instance' + + +grokked_instance = Foo() +grokked_instance = view_config( + name='another_grokked_instance', renderer=null_renderer +)(grokked_instance) + +# ungrokkable + +A = 1 +B = {} + + +def stuff(): + """ """ diff --git a/tests/test_config/pkgs/scannable/pod/notinit.py b/tests/test_config/pkgs/scannable/pod/notinit.py new file mode 100644 index 000000000..03c93857f --- /dev/null +++ b/tests/test_config/pkgs/scannable/pod/notinit.py @@ -0,0 +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' diff --git a/tests/test_config/pkgs/scannable/subpackage/__init__.py b/tests/test_config/pkgs/scannable/subpackage/__init__.py new file mode 100644 index 000000000..f89ca33f7 --- /dev/null +++ b/tests/test_config/pkgs/scannable/subpackage/__init__.py @@ -0,0 +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' diff --git a/tests/test_config/pkgs/scannable/subpackage/notinit.py b/tests/test_config/pkgs/scannable/subpackage/notinit.py new file mode 100644 index 000000000..65c2a4929 --- /dev/null +++ b/tests/test_config/pkgs/scannable/subpackage/notinit.py @@ -0,0 +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' diff --git a/tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py b/tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py new file mode 100644 index 000000000..ec4bab818 --- /dev/null +++ b/tests/test_config/pkgs/scannable/subpackage/subsubpackage/__init__.py @@ -0,0 +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' diff --git a/tests/test_config/pkgs/selfscan/__init__.py b/tests/test_config/pkgs/selfscan/__init__.py new file mode 100644 index 000000000..8bc8761ca --- /dev/null +++ b/tests/test_config/pkgs/selfscan/__init__.py @@ -0,0 +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 diff --git a/tests/test_config/pkgs/selfscan/another.py b/tests/test_config/pkgs/selfscan/another.py new file mode 100644 index 000000000..79e0b08de --- /dev/null +++ b/tests/test_config/pkgs/selfscan/another.py @@ -0,0 +1,6 @@ +from pyramid.view import view_config + + +@view_config(name='two', renderer='string') +def two(request): + return 'two' diff --git a/tests/test_config/test_actions.py b/tests/test_config/test_actions.py new file mode 100644 index 000000000..a72d0d7b1 --- /dev/null +++ b/tests/test_config/test_actions.py @@ -0,0 +1,1090 @@ +import unittest + +from pyramid.exceptions import ConfigurationConflictError +from pyramid.exceptions import ConfigurationExecutionError + +from pyramid.interfaces import IRequest + + +class ActionConfiguratorMixinTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def _getViewCallable( + self, + config, + ctx_iface=None, + request_iface=None, + name='', + exception_view=False, + ): + from zope.interface import Interface + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + if exception_view: # pragma: no cover + classifier = IExceptionViewClassifier + else: + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), + IView, + name=name, + default=None, + ) + + def test_action_branching_kw_is_None(self): + config = self._makeOne(autocommit=True) + self.assertEqual(config.action('discrim'), None) + + def test_action_branching_kw_is_not_None(self): + config = self._makeOne(autocommit=True) + self.assertEqual(config.action('discrim', kw={'a': 1}), None) + + def test_action_autocommit_with_introspectables(self): + from pyramid.config.actions import ActionInfo + + config = self._makeOne(autocommit=True) + intr = DummyIntrospectable() + config.action('discrim', introspectables=(intr,)) + self.assertEqual(len(intr.registered), 1) + self.assertEqual(intr.registered[0][0], config.introspector) + self.assertEqual(intr.registered[0][1].__class__, ActionInfo) + + def test_action_autocommit_with_introspectables_introspection_off(self): + config = self._makeOne(autocommit=True) + config.introspection = False + intr = DummyIntrospectable() + config.action('discrim', introspectables=(intr,)) + self.assertEqual(len(intr.registered), 0) + + def test_action_branching_nonautocommit_with_config_info(self): + config = self._makeOne(autocommit=False) + config.info = 'abc' + state = DummyActionState() + state.autocommit = False + config.action_state = state + config.action('discrim', kw={'a': 1}) + self.assertEqual( + state.actions, + [ + ( + (), + { + 'args': (), + 'callable': None, + 'discriminator': 'discrim', + 'includepath': (), + 'info': 'abc', + 'introspectables': (), + 'kw': {'a': 1}, + 'order': 0, + }, + ) + ], + ) + + def test_action_branching_nonautocommit_without_config_info(self): + config = self._makeOne(autocommit=False) + config.info = '' + config._ainfo = ['z'] + state = DummyActionState() + config.action_state = state + state.autocommit = False + config.action('discrim', kw={'a': 1}) + self.assertEqual( + state.actions, + [ + ( + (), + { + 'args': (), + 'callable': None, + 'discriminator': 'discrim', + 'includepath': (), + 'info': 'z', + 'introspectables': (), + 'kw': {'a': 1}, + 'order': 0, + }, + ) + ], + ) + + def test_action_branching_nonautocommit_with_introspectables(self): + config = self._makeOne(autocommit=False) + config.info = '' + config._ainfo = [] + state = DummyActionState() + config.action_state = state + state.autocommit = False + intr = DummyIntrospectable() + config.action('discrim', introspectables=(intr,)) + self.assertEqual(state.actions[0][1]['introspectables'], (intr,)) + + def test_action_nonautocommit_with_introspectables_introspection_off(self): + config = self._makeOne(autocommit=False) + config.info = '' + config._ainfo = [] + config.introspection = False + state = DummyActionState() + config.action_state = state + state.autocommit = False + intr = DummyIntrospectable() + config.action('discrim', introspectables=(intr,)) + self.assertEqual(state.actions[0][1]['introspectables'], ()) + + def test_commit_conflict_simple(self): + config = self._makeOne() + + 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): # 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() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view1') + + def test_commit_conflict_with_two_includes(self): + config = self._makeOne() + + 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: + config.commit() + except ConfigurationConflictError as why: + c1, c2 = _conflictFunctions(why) + self.assertEqual(c1, 'includeme1') + self.assertEqual(c2, 'includeme2') + else: # pragma: no cover + raise AssertionError + + def test_commit_conflict_resolved_with_two_includes_and_local(self): + config = self._makeOne() + + def view1(request): # 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) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + + def test_autocommit_no_conflicts(self): + from pyramid.renderers import null_renderer + + config = self._makeOne(autocommit=True) + + def view1(request): # 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) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + + def test_conflict_set_notfound_view(self): + config = self._makeOne() + + 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: + config.commit() + except ConfigurationConflictError as why: + c1, c2 = _conflictFunctions(why) + self.assertEqual(c1, 'test_conflict_set_notfound_view') + self.assertEqual(c2, 'test_conflict_set_notfound_view') + else: # pragma: no cover + raise AssertionError + + def test_conflict_set_forbidden_view(self): + config = self._makeOne() + + def view1(request): # pragma: no cover + pass + + def view2(request): # pragma: no cover + pass + + config.set_forbidden_view(view1) + config.set_forbidden_view(view2) + try: + config.commit() + except ConfigurationConflictError as why: + c1, c2 = _conflictFunctions(why) + self.assertEqual(c1, 'test_conflict_set_forbidden_view') + self.assertEqual(c2, 'test_conflict_set_forbidden_view') + else: # pragma: no cover + raise AssertionError + + +class TestActionState(unittest.TestCase): + def _makeOne(self): + from pyramid.config.actions 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}) + self.assertEqual( + c.actions, + [ + { + 'args': (1,), + 'callable': f, + 'discriminator': 1, + 'includepath': (), + 'info': None, + 'introspectables': (), + 'kw': {'x': 1}, + 'order': 0, + } + ], + ) + c.action(None) + self.assertEqual( + c.actions, + [ + { + 'args': (1,), + 'callable': f, + 'discriminator': 1, + 'includepath': (), + 'info': None, + 'introspectables': (), + 'kw': {'x': 1}, + 'order': 0, + }, + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': (), + 'info': None, + 'introspectables': (), + 'kw': {}, + 'order': 0, + }, + ], + ) + + def test_action_with_includepath(self): + c = self._makeOne() + c.actions = [] + c.action(None, includepath=('abc',)) + self.assertEqual( + c.actions, + [ + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': ('abc',), + 'info': None, + 'introspectables': (), + 'kw': {}, + 'order': 0, + } + ], + ) + + def test_action_with_info(self): + c = self._makeOne() + c.action(None, info='abc') + self.assertEqual( + c.actions, + [ + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': (), + 'info': 'abc', + 'introspectables': (), + 'kw': {}, + 'order': 0, + } + ], + ) + + def test_action_with_includepath_and_info(self): + c = self._makeOne() + c.action(None, includepath=('spec',), info='bleh') + self.assertEqual( + c.actions, + [ + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': ('spec',), + 'info': 'bleh', + 'introspectables': (), + 'kw': {}, + 'order': 0, + } + ], + ) + + def test_action_with_order(self): + c = self._makeOne() + c.actions = [] + c.action(None, order=99999) + self.assertEqual( + c.actions, + [ + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': (), + 'info': None, + 'introspectables': (), + 'kw': {}, + 'order': 99999, + } + ], + ) + + def test_action_with_introspectables(self): + c = self._makeOne() + c.actions = [] + intr = DummyIntrospectable() + c.action(None, introspectables=(intr,)) + self.assertEqual( + c.actions, + [ + { + 'args': (), + 'callable': None, + 'discriminator': None, + 'includepath': (), + 'info': None, + 'introspectables': (intr,), + 'kw': {}, + 'order': 0, + } + ], + ) + + def test_processSpec(self): + c = self._makeOne() + self.assertTrue(c.processSpec('spec')) + self.assertFalse(c.processSpec('spec')) + + def test_execute_actions_tuples(self): + output = [] + + def f(*a, **k): + output.append((a, k)) + + c = self._makeOne() + c.actions = [ + (1, f, (1,)), + (1, f, (11,), {}, ('x',)), + (2, f, (2,)), + (None, None), + ] + c.execute_actions() + self.assertEqual(output, [((1,), {}), ((2,), {})]) + + def test_execute_actions_dicts(self): + output = [] + + def f(*a, **k): + output.append((a, k)) + + c = self._makeOne() + c.actions = [ + { + 'discriminator': 1, + 'callable': f, + 'args': (1,), + 'kw': {}, + 'order': 0, + 'includepath': (), + 'info': None, + 'introspectables': (), + }, + { + 'discriminator': 1, + 'callable': f, + 'args': (11,), + 'kw': {}, + 'includepath': ('x',), + 'order': 0, + 'info': None, + 'introspectables': (), + }, + { + 'discriminator': 2, + 'callable': f, + 'args': (2,), + 'kw': {}, + 'order': 0, + 'includepath': (), + 'info': None, + 'introspectables': (), + }, + { + 'discriminator': None, + 'callable': None, + 'args': (), + 'kw': {}, + 'order': 0, + 'includepath': (), + 'info': None, + 'introspectables': (), + }, + ] + c.execute_actions() + self.assertEqual(output, [((1,), {}), ((2,), {})]) + + def test_execute_actions_with_introspectables(self): + output = [] + + def f(*a, **k): + output.append((a, k)) + + c = self._makeOne() + intr = DummyIntrospectable() + c.actions = [ + { + 'discriminator': 1, + 'callable': f, + 'args': (1,), + 'kw': {}, + 'order': 0, + 'includepath': (), + 'info': None, + 'introspectables': (intr,), + } + ] + introspector = object() + c.execute_actions(introspector=introspector) + self.assertEqual(output, [((1,), {})]) + self.assertEqual(intr.registered, [(introspector, None)]) + + def test_execute_actions_with_introspectable_no_callable(self): + c = self._makeOne() + intr = DummyIntrospectable() + c.actions = [ + { + 'discriminator': 1, + 'callable': None, + 'args': (1,), + 'kw': {}, + 'order': 0, + 'includepath': (), + 'info': None, + 'introspectables': (intr,), + } + ] + introspector = object() + c.execute_actions(introspector=introspector) + self.assertEqual(intr.registered, [(introspector, None)]) + + def test_execute_actions_error(self): + output = [] + + def f(*a, **k): + output.append(('f', a, k)) + + def bad(): + raise NotImplementedError + + c = self._makeOne() + c.actions = [ + (1, f, (1,)), + (1, f, (11,), {}, ('x',)), + (2, f, (2,)), + (3, bad, (), {}, (), 'oops'), + ] + self.assertRaises(ConfigurationExecutionError, c.execute_actions) + self.assertEqual(output, [('f', (1,), {}), ('f', (2,), {})]) + + def test_reentrant_action(self): + output = [] + c = self._makeOne() + + def f(*a, **k): + output.append(('f', a, k)) + c.actions.append((3, g, (8,), {})) + + def g(*a, **k): + output.append(('g', a, k)) + + c.actions = [(1, f, (1,))] + c.execute_actions() + self.assertEqual(output, [('f', (1,), {}), ('g', (8,), {})]) + + def test_reentrant_action_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 + ] + c.execute_actions() + 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): # 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,))] + c.execute_actions(clear=False) + 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): # 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): # 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) + + config = self._makeConfigurator() + config.add_directive('add_auto_route', add_auto_route) + + 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] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, 'foo') + self.assertEqual(route.path, '/foo') + + 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 + + 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.actions 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 = list(result) + self.assertEqual( + result, + [ + { + 'info': None, + 'args': (), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': None, + 'includepath': (), + 'order': 0, + }, + { + 'info': 'first', + 'args': (1,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 1, + 'includepath': (), + 'order': 0, + }, + { + 'info': None, + 'args': (3,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 3, + 'includepath': ('y',), + 'order': 0, + }, + { + 'info': None, + 'args': (5,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': None, + 'includepath': ('y',), + 'order': 0, + }, + { + 'info': 'should be last', + 'args': (4,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 4, + 'includepath': ('y',), + 'order': 99999, + }, + ], + ) + + def test_it_success_dicts(self): + from . import dummyfactory as f + + result = self._callFUT( + [ + (None, f), + (1, f, (1,), {}, (), 'first'), + (1, f, (2,), {}, ('x',), 'second'), + (1, f, (3,), {}, ('y',), 'third'), + (4, f, (4,), {}, ('y',), 'should be last', 99999), + (3, f, (3,), {}, ('y',)), + (None, f, (5,), {}, ('y',)), + ] + ) + result = list(result) + self.assertEqual( + result, + [ + { + 'info': None, + 'args': (), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': None, + 'includepath': (), + 'order': 0, + }, + { + 'info': 'first', + 'args': (1,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 1, + 'includepath': (), + 'order': 0, + }, + { + 'info': None, + 'args': (3,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 3, + 'includepath': ('y',), + 'order': 0, + }, + { + 'info': None, + 'args': (5,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': None, + 'includepath': ('y',), + 'order': 0, + }, + { + 'info': 'should be last', + 'args': (4,), + 'callable': f, + 'introspectables': (), + 'kw': {}, + 'discriminator': 4, + 'includepath': ('y',), + 'order': 99999, + }, + ], + ) + + def test_it_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',)), + ] + ) + 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 = 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, + }, + ], + ) + + 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 = list(result) + 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), + ] + ) + self.assertRaises(ConfigurationConflictError, list, result) + + +class TestActionInfo(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.actions import ActionInfo + + return ActionInfo + + def _makeOne(self, filename, lineno, function, linerepr): + return self._getTargetClass()(filename, lineno, function, linerepr) + + def test_class_conforms(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IActionInfo + + verifyClass(IActionInfo, self._getTargetClass()) + + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IActionInfo + + verifyObject(IActionInfo, self._makeOne('f', 0, 'f', 'f')) + + def test_ctor(self): + inst = self._makeOne('filename', 10, 'function', 'src') + self.assertEqual(inst.file, 'filename') + self.assertEqual(inst.line, 10) + self.assertEqual(inst.function, 'function') + self.assertEqual(inst.src, 'src') + + def test___str__(self): + inst = self._makeOne('filename', 0, 'function', ' linerepr ') + self.assertEqual( + str(inst), "Line 0 of file filename:\n linerepr " + ) + + +def _conflictFunctions(e): + conflicts = e._conflicts.values() + for conflict in conflicts: + for confinst in conflict: + yield confinst.function + + +class DummyActionState(object): + autocommit = False + info = '' + + def __init__(self): + self.actions = [] + + def action(self, *arg, **kw): + self.actions.append((arg, kw)) + + +class DummyIntrospectable(object): + def __init__(self): + self.registered = [] + + def register(self, introspector, action_info): + self.registered.append((introspector, action_info)) diff --git a/tests/test_config/test_adapters.py b/tests/test_config/test_adapters.py new file mode 100644 index 000000000..d871e8825 --- /dev/null +++ b/tests/test_config/test_adapters.py @@ -0,0 +1,431 @@ +import unittest + +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() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 2) + + def test_add_subscriber_iface_specified(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + pass + + L = [] + + def subscriber(event): + L.append(event) + + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, IEvent) + event = Event() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_add_subscriber_dottednames(self): + import tests.test_config + from pyramid.interfaces import INewRequest + + config = self._makeOne(autocommit=True) + config.add_subscriber( + 'tests.test_config', 'pyramid.interfaces.INewRequest' + ) + handlers = list(config.registry.registeredHandlers()) + self.assertEqual(len(handlers), 1) + handler = handlers[0] + self.assertEqual(handler.handler, tests.test_config) + self.assertEqual(handler.required, (INewRequest,)) + + def test_add_object_event_subscriber(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + object = 'foo' + + event = Event() + L = [] + + def subscriber(object, event): + L.append(event) + + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, (Interface, IEvent)) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.subscribers((event.object, IDummy), None) + self.assertEqual(len(L), 1) + + def test_add_subscriber_with_specific_type_and_predicates_True(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + pass + + L = [] + + def subscriber(event): + L.append(event) + + config = self._makeOne(autocommit=True) + predlist = config.get_predlist('subscriber') + jam_predicate = predicate_maker('jam') + jim_predicate = predicate_maker('jim') + predlist.add('jam', jam_predicate) + predlist.add('jim', jim_predicate) + config.add_subscriber(subscriber, IEvent, jam=True, jim=True) + event = Event() + event.jam = True + event.jim = True + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_add_subscriber_with_default_type_predicates_True(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + pass + + L = [] + + def subscriber(event): + L.append(event) + + config = self._makeOne(autocommit=True) + predlist = config.get_predlist('subscriber') + jam_predicate = predicate_maker('jam') + jim_predicate = predicate_maker('jim') + predlist.add('jam', jam_predicate) + predlist.add('jim', jim_predicate) + config.add_subscriber(subscriber, jam=True, jim=True) + event = Event() + event.jam = True + event.jim = True + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_add_subscriber_with_specific_type_and_predicates_False(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + pass + + L = [] + + def subscriber(event): # pragma: no cover + L.append(event) + + config = self._makeOne(autocommit=True) + predlist = config.get_predlist('subscriber') + jam_predicate = predicate_maker('jam') + jim_predicate = predicate_maker('jim') + predlist.add('jam', jam_predicate) + predlist.add('jim', jim_predicate) + config.add_subscriber(subscriber, IEvent, jam=True, jim=True) + event = Event() + event.jam = True + event.jim = False + config.registry.notify(event) + self.assertEqual(len(L), 0) + + def test_add_subscriber_with_default_type_predicates_False(self): + from zope.interface import implementer + from zope.interface import Interface + + class IEvent(Interface): + pass + + @implementer(IEvent) + class Event: + pass + + L = [] + + def subscriber(event): # pragma: no cover + L.append(event) + + config = self._makeOne(autocommit=True) + predlist = config.get_predlist('subscriber') + jam_predicate = predicate_maker('jam') + jim_predicate = predicate_maker('jim') + predlist.add('jam', jam_predicate) + predlist.add('jim', jim_predicate) + config.add_subscriber(subscriber, jam=True, jim=True) + event = Event() + event.jam = False + event.jim = True + config.registry.notify(event) + self.assertEqual(len(L), 0) + + def test_add_subscriber_predicate(self): + config = self._makeOne() + L = [] + + def add_predicate( + type, name, factory, weighs_less_than=None, weighs_more_than=None + ): + self.assertEqual(type, 'subscriber') + self.assertEqual(name, 'name') + self.assertEqual(factory, 'factory') + self.assertEqual(weighs_more_than, 1) + self.assertEqual(weighs_less_than, 2) + L.append(1) + + config._add_predicate = add_predicate + config.add_subscriber_predicate('name', 'factory', 1, 2) + self.assertTrue(L) + + def test_add_response_adapter(self): + from pyramid.interfaces import IResponse + + config = self._makeOne(autocommit=True) + + class Adapter(object): + def __init__(self, other): + self.other = other + + config.add_response_adapter(Adapter, str) + result = config.registry.queryAdapter('foo', IResponse) + self.assertTrue(result.other, 'foo') + + def test_add_response_adapter_self(self): + from pyramid.interfaces import IResponse + + config = self._makeOne(autocommit=True) + + class Adapter(object): + pass + + config.add_response_adapter(None, Adapter) + adapter = Adapter() + result = config.registry.queryAdapter(adapter, IResponse) + self.assertTrue(result is adapter) + + def test_add_response_adapter_dottednames(self): + from pyramid.interfaces import IResponse + + config = self._makeOne(autocommit=True) + if PY2: + str_name = '__builtin__.str' + else: + str_name = 'builtins.str' + config.add_response_adapter('pyramid.response.Response', str_name) + result = config.registry.queryAdapter('foo', IResponse) + self.assertTrue(result.body, b'foo') + + def test_add_traverser_dotted_names(self): + from pyramid.interfaces import ITraverser + + config = self._makeOne(autocommit=True) + config.add_traverser( + 'tests.test_config.test_adapters.DummyTraverser', + 'tests.test_config.test_adapters.DummyIface', + ) + iface = DummyIface() + traverser = config.registry.getAdapter(iface, ITraverser) + self.assertEqual(traverser.__class__, DummyTraverser) + self.assertEqual(traverser.root, iface) + + def test_add_traverser_default_iface_means_Interface(self): + from pyramid.interfaces import ITraverser + + config = self._makeOne(autocommit=True) + config.add_traverser(DummyTraverser) + traverser = config.registry.getAdapter(None, ITraverser) + self.assertEqual(traverser.__class__, DummyTraverser) + + def test_add_traverser_nondefault_iface(self): + from pyramid.interfaces import ITraverser + + config = self._makeOne(autocommit=True) + config.add_traverser(DummyTraverser, DummyIface) + iface = DummyIface() + traverser = config.registry.getAdapter(iface, ITraverser) + self.assertEqual(traverser.__class__, DummyTraverser) + self.assertEqual(traverser.root, iface) + + def test_add_traverser_introspectables(self): + config = self._makeOne() + config.add_traverser(DummyTraverser, DummyIface) + actions = config.action_state.actions + self.assertEqual(len(actions), 1) + intrs = actions[0]['introspectables'] + self.assertEqual(len(intrs), 1) + intr = intrs[0] + self.assertEqual(intr.type_name, 'traverser') + self.assertEqual(intr.discriminator, ('traverser', DummyIface)) + self.assertEqual(intr.category_name, 'traversers') + self.assertEqual(intr.title, 'traverser for %r' % DummyIface) + self.assertEqual(intr['adapter'], DummyTraverser) + self.assertEqual(intr['iface'], DummyIface) + + def test_add_resource_url_adapter_dotted_names(self): + from pyramid.interfaces import IResourceURL + + config = self._makeOne(autocommit=True) + config.add_resource_url_adapter( + 'tests.test_config.test_adapters.DummyResourceURL', + 'tests.test_config.test_adapters.DummyIface', + ) + iface = DummyIface() + adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) + self.assertEqual(adapter.__class__, DummyResourceURL) + self.assertEqual(adapter.resource, iface) + self.assertEqual(adapter.request, iface) + + def test_add_resource_url_default_resource_iface_means_Interface(self): + from pyramid.interfaces import IResourceURL + + config = self._makeOne(autocommit=True) + config.add_resource_url_adapter(DummyResourceURL) + iface = DummyIface() + adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) + self.assertEqual(adapter.__class__, DummyResourceURL) + self.assertEqual(adapter.resource, iface) + self.assertEqual(adapter.request, iface) + + def test_add_resource_url_nodefault_resource_iface(self): + from zope.interface import Interface + from pyramid.interfaces import IResourceURL + + config = self._makeOne(autocommit=True) + config.add_resource_url_adapter(DummyResourceURL, DummyIface) + iface = DummyIface() + adapter = config.registry.getMultiAdapter((iface, iface), IResourceURL) + self.assertEqual(adapter.__class__, DummyResourceURL) + self.assertEqual(adapter.resource, iface) + self.assertEqual(adapter.request, iface) + bad_result = config.registry.queryMultiAdapter( + (Interface, Interface), IResourceURL + ) + self.assertEqual(bad_result, None) + + def test_add_resource_url_adapter_introspectables(self): + config = self._makeOne() + config.add_resource_url_adapter(DummyResourceURL, DummyIface) + actions = config.action_state.actions + self.assertEqual(len(actions), 1) + intrs = actions[0]['introspectables'] + self.assertEqual(len(intrs), 1) + intr = intrs[0] + self.assertEqual(intr.type_name, 'resource url adapter') + self.assertEqual( + intr.discriminator, ('resource url adapter', DummyIface) + ) + self.assertEqual(intr.category_name, 'resource url adapters') + self.assertEqual( + intr.title, + "resource url adapter for resource iface " + "<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): # 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 diff --git a/tests/test_config/test_assets.py b/tests/test_config/test_assets.py new file mode 100644 index 000000000..875846dbd --- /dev/null +++ b/tests/test_config/test_assets.py @@ -0,0 +1,1081 @@ +import os.path +import unittest +from pyramid.testing import cleanUp + +# we use this folder +here = os.path.dirname(os.path.abspath(__file__)) + + +class TestAssetsConfiguratorMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + 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', + ) + + 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/', + ) + + 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', + ) + + 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, + ) + 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 + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/bar.pt') + + resource_name = '' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_package_with_package(self): + from pyramid.config.assets import PackageAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_asset( + 'tests.test_config.pkgs.asset', + 'tests.test_config.pkgs.asset.subpackage', + _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 + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, '') + + resource_name = 'templates/bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_directory_with_directory(self): + from pyramid.config.assets import PackageAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_asset( + 'tests.test_config.pkgs.asset:templates/', + 'tests.test_config.pkgs.asset.subpackage:templates/', + _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 + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/') + + resource_name = 'bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_directory_with_package(self): + from pyramid.config.assets import PackageAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_asset( + 'tests.test_config.pkgs.asset:templates/', + 'tests.test_config.pkgs.asset.subpackage', + _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 + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, '') + + resource_name = 'templates/bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_package_with_directory(self): + from pyramid.config.assets import PackageAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_asset( + 'tests.test_config.pkgs.asset', + 'tests.test_config.pkgs.asset.subpackage:templates/', + _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 + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/') + + resource_name = 'bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_directory_with_absfile(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.override_asset, + 'a:foo/', + os.path.join(here, 'pkgs', 'asset', 'foo.pt'), + ) + + def test_override_asset_file_with_absdirectory(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + abspath = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates' + ) + self.assertRaises( + ConfigurationError, config.override_asset, 'a:foo.pt', abspath + ) + + def test_override_asset_file_with_missing_abspath(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.override_asset, + 'a:foo.pt', + os.path.join(here, 'wont_exist'), + ) + + def test_override_asset_file_with_absfile(self): + from pyramid.config.assets import FSAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + config.override_asset( + 'tests.test_config.pkgs.asset:templates/foo.pt', + abspath, + _override=override, + ) + from tests.test_config.pkgs import asset + + self.assertEqual(override.package, asset) + self.assertEqual(override.path, 'templates/foo.pt') + source = override.source + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) + + resource_name = '' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_directory_with_absdirectory(self): + from pyramid.config.assets import FSAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates' + ) + config.override_asset( + 'tests.test_config.pkgs.asset:templates/', + abspath, + _override=override, + ) + from tests.test_config.pkgs import asset + + self.assertEqual(override.package, asset) + self.assertEqual(override.path, 'templates/') + source = override.source + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) + + resource_name = 'bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test_override_asset_package_with_absdirectory(self): + from pyramid.config.assets import FSAssetSource + + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates' + ) + config.override_asset( + '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 + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) + + resource_name = 'bar.pt' + expected = os.path.join( + here, 'pkgs', 'asset', 'subpackage', 'templates', 'bar.pt' + ) + self.assertEqual(override.source.get_filename(resource_name), expected) + + def test__override_not_yet_registered(self): + from pyramid.interfaces import IPackageOverrides + + package = DummyPackage('package') + source = DummyAssetSource() + config = self._makeOne() + 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 + ) + self.assertEqual(overrides.inserted, [('path', source)]) + self.assertEqual(overrides.package, package) + + +class TestOverrideProvider(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from pyramid.config.assets import OverrideProvider + + return OverrideProvider + + def _makeOne(self, module): + klass = self._getTargetClass() + return klass(module) + + def _registerOverrides(self, overrides, name='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) + self.assertEqual(result, expected) + + 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)) + + 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)) + + 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) + + def test_resource_isdir_no_overrides(self): + 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) + result = provider.resource_isdir(directory_resource_name) + self.assertEqual(result, True) + + 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) + + def test_get_resource_filename_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) + 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)) + + def test_get_resource_string_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) + result = provider.get_resource_string(None, resource_name) + _assertBody(result, os.path.join(here, resource_name)) + + def test_has_resource_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + resource_name = 'test_assets.py' + import tests.test_config + + provider = self._makeOne(tests.test_config) + result = provider.has_resource(resource_name) + self.assertEqual(result, True) + + def test_resource_isdir_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + resource_name = 'files' + import tests.test_config + + provider = self._makeOne(tests.test_config) + result = provider.resource_isdir(resource_name) + self.assertEqual(result, True) + + def test_resource_listdir_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + resource_name = 'files' + import tests.test_config + + provider = self._makeOne(tests.test_config) + result = provider.resource_listdir(resource_name) + self.assertTrue(result) + + def test_get_resource_filename_override_returns_value(self): + overrides = DummyOverrides('value') + import tests.test_config + + self._registerOverrides(overrides) + provider = self._makeOne(tests.test_config) + result = provider.get_resource_filename(None, 'test_assets.py') + self.assertEqual(result, 'value') + + def test_get_resource_stream_override_returns_value(self): + from io import BytesIO + + overrides = DummyOverrides(BytesIO(b'value')) + import tests.test_config + + self._registerOverrides(overrides) + provider = self._makeOne(tests.test_config) + with provider.get_resource_stream(None, 'test_assets.py') as stream: + self.assertEqual(stream.getvalue(), b'value') + + def test_get_resource_string_override_returns_value(self): + overrides = DummyOverrides('value') + import tests.test_config + + self._registerOverrides(overrides) + provider = self._makeOne(tests.test_config) + result = provider.get_resource_string(None, 'test_assets.py') + self.assertEqual(result, 'value') + + def test_has_resource_override_returns_True(self): + overrides = DummyOverrides(True) + import tests.test_config + + self._registerOverrides(overrides) + provider = self._makeOne(tests.test_config) + result = provider.has_resource('test_assets.py') + self.assertEqual(result, True) + + def test_resource_isdir_override_returns_False(self): + overrides = DummyOverrides(False) + import tests.test_config + + self._registerOverrides(overrides) + provider = self._makeOne(tests.test_config) + result = provider.resource_isdir('files') + self.assertEqual(result, False) + + 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): + if package is None: + package = DummyPackage('package') + klass = self._getTargetClass() + if pkg_resources is None: + pkg_resources = DummyPkgResources() + return klass(package, pkg_resources=pkg_resources) + + def test_class_conforms_to_IPackageOverrides(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IPackageOverrides + + verifyClass(IPackageOverrides, self._getTargetClass()) + + def test_instance_conforms_to_IPackageOverrides(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IPackageOverrides + + verifyObject(IPackageOverrides, self._makeOne()) + + def test_class_conforms_to_IPEP302Loader(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IPEP302Loader + + verifyClass(IPEP302Loader, self._getTargetClass()) + + def test_instance_conforms_to_IPEP302Loader(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IPEP302Loader + + verifyObject(IPEP302Loader, self._makeOne()) + + def test_ctor_package_already_has_loader_of_different_type(self): + package = DummyPackage('package') + loader = package.__loader__ = DummyLoader() + po = self._makeOne(package) + self.assertTrue(package.__loader__ is po) + self.assertTrue(po.real_loader is loader) + + def test_ctor_package_already_has_loader_of_same_type(self): + package = DummyPackage('package') + package.__loader__ = self._makeOne(package) + po = self._makeOne(package) + self.assertEqual(package.__loader__, po) + + def test_ctor_sets_loader(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertEqual(package.__loader__, po) + + def test_ctor_registers_loader_type(self): + from pyramid.config.assets import OverrideProvider + + dummy_pkg_resources = DummyPkgResources() + package = DummyPackage('package') + po = self._makeOne(package, dummy_pkg_resources) + self.assertEqual( + dummy_pkg_resources.registered, [(po.__class__, OverrideProvider)] + ) + + def test_ctor_sets_local_state(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertEqual(po.overrides, []) + self.assertEqual(po.overridden_package_name, 'package') + + def test_insert_directory(self): + from pyramid.config.assets import DirectoryOverride + + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = [None] + po.insert('foo/', DummyAssetSource()) + self.assertEqual(len(po.overrides), 2) + override = po.overrides[0] + self.assertEqual(override.__class__, DirectoryOverride) + + def test_insert_file(self): + from pyramid.config.assets import FileOverride + + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = [None] + po.insert('foo.pt', DummyAssetSource()) + self.assertEqual(len(po.overrides), 2) + override = po.overrides[0] + self.assertEqual(override.__class__, FileOverride) + + def test_insert_emptystring(self): + # XXX is this a valid case for a directory? + from pyramid.config.assets import DirectoryOverride + + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = [None] + source = DummyAssetSource() + po.insert('', source) + self.assertEqual(len(po.overrides), 2) + override = po.overrides[0] + self.assertEqual(override.__class__, DirectoryOverride) + + def test_filtered_sources(self): + overrides = [DummyOverride(None), DummyOverride('foo')] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(list(po.filtered_sources('whatever')), ['foo']) + + def test_get_filename(self): + source = DummyAssetSource(filename='foo.pt') + overrides = [DummyOverride(None), DummyOverride((source, ''))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + result = po.get_filename('whatever') + self.assertEqual(result, 'foo.pt') + self.assertEqual(source.resource_name, '') + + def test_get_filename_file_doesnt_exist(self): + source = DummyAssetSource(filename=None) + overrides = [ + DummyOverride(None), + DummyOverride((source, 'wont_exist')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.get_filename('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + def test_get_stream(self): + source = DummyAssetSource(stream='a stream?') + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + 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')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.get_stream('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + def test_get_string(self): + source = DummyAssetSource(string='a string') + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + 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')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.get_string('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + def test_has_resource(self): + source = DummyAssetSource(exists=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.has_resource('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') + + def test_has_resource_file_doesnt_exist(self): + source = DummyAssetSource(exists=None) + overrides = [ + DummyOverride(None), + DummyOverride((source, 'wont_exist')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.has_resource('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + def test_isdir_false(self): + source = DummyAssetSource(isdir=False) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.isdir('whatever'), False) + self.assertEqual(source.resource_name, 'foo.pt') + + def test_isdir_true(self): + source = DummyAssetSource(isdir=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.isdir('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') + + def test_isdir_doesnt_exist(self): + source = DummyAssetSource(isdir=None) + overrides = [ + DummyOverride(None), + DummyOverride((source, 'wont_exist')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.isdir('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + def test_listdir(self): + source = DummyAssetSource(listdir=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.listdir('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') + + def test_listdir_doesnt_exist(self): + source = DummyAssetSource(listdir=None) + overrides = [ + DummyOverride(None), + DummyOverride((source, 'wont_exist')), + ] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides = overrides + self.assertEqual(po.listdir('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') + + # PEP 302 __loader__ extensions: use the "real" __loader__, if present. + def test_get_data_pkg_has_no___loader__(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertRaises(NotImplementedError, po.get_data, 'whatever') + + def test_get_data_pkg_has___loader__(self): + package = DummyPackage('package') + loader = package.__loader__ = DummyLoader() + po = self._makeOne(package) + self.assertEqual(po.get_data('whatever'), b'DEADBEEF') + self.assertEqual(loader._got_data, 'whatever') + + def test_is_package_pkg_has_no___loader__(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertRaises(NotImplementedError, po.is_package, 'whatever') + + def test_is_package_pkg_has___loader__(self): + package = DummyPackage('package') + loader = package.__loader__ = DummyLoader() + po = self._makeOne(package) + self.assertTrue(po.is_package('whatever')) + self.assertEqual(loader._is_package, 'whatever') + + def test_get_code_pkg_has_no___loader__(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertRaises(NotImplementedError, po.get_code, 'whatever') + + def test_get_code_pkg_has___loader__(self): + package = DummyPackage('package') + loader = package.__loader__ = DummyLoader() + po = self._makeOne(package) + self.assertEqual(po.get_code('whatever'), b'DEADBEEF') + self.assertEqual(loader._got_code, 'whatever') + + def test_get_source_pkg_has_no___loader__(self): + package = DummyPackage('package') + po = self._makeOne(package) + self.assertRaises(NotImplementedError, po.get_source, 'whatever') + + def test_get_source_pkg_has___loader__(self): + package = DummyPackage('package') + loader = package.__loader__ = DummyLoader() + po = self._makeOne(package) + self.assertEqual(po.get_source('whatever'), 'def foo():\n pass') + self.assertEqual(loader._got_source, 'whatever') + + +class AssetSourceIntegrationTests(object): + def test_get_filename(self): + source = self._makeOne('') + self.assertEqual( + source.get_filename('test_assets.py'), + os.path.join(here, 'test_assets.py'), + ) + + def test_get_filename_with_prefix(self): + source = self._makeOne('test_assets.py') + self.assertEqual( + source.get_filename(''), os.path.join(here, 'test_assets.py') + ) + + def test_get_filename_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_filename('wont_exist'), None) + + def test_get_stream(self): + source = self._makeOne('') + with source.get_stream('test_assets.py') as stream: + _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) + + def test_get_stream_with_prefix(self): + source = self._makeOne('test_assets.py') + with source.get_stream('') as stream: + _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) + + def test_get_stream_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_stream('wont_exist'), None) + + def test_get_string(self): + source = self._makeOne('') + _assertBody( + source.get_string('test_assets.py'), + os.path.join(here, 'test_assets.py'), + ) + + def test_get_string_with_prefix(self): + source = self._makeOne('test_assets.py') + _assertBody( + source.get_string(''), os.path.join(here, 'test_assets.py') + ) + + def test_get_string_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_string('wont_exist'), None) + + def test_exists(self): + source = self._makeOne('') + self.assertEqual(source.exists('test_assets.py'), True) + + def test_exists_with_prefix(self): + source = self._makeOne('test_assets.py') + self.assertEqual(source.exists(''), True) + + def test_exists_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.exists('wont_exist'), None) + + def test_isdir_false(self): + source = self._makeOne('') + self.assertEqual(source.isdir('test_assets.py'), False) + + def test_isdir_true(self): + source = self._makeOne('') + self.assertEqual(source.isdir('files'), True) + + def test_isdir_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.isdir('wont_exist'), None) + + def test_listdir(self): + source = self._makeOne('') + self.assertTrue(source.listdir('files')) + + def test_listdir_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.listdir('wont_exist'), None) + + +class TestPackageAssetSource(AssetSourceIntegrationTests, unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.assets import PackageAssetSource + + return PackageAssetSource + + def _makeOne(self, prefix, package='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): + klass = self._getTargetClass() + return klass(path, source) + + def test_it_match(self): + source = DummyAssetSource() + 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): + klass = self._getTargetClass() + return klass(path, source) + + def test_it_match(self): + source = DummyAssetSource() + 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): + self.result = result + + def __call__(self, resource_name): + return self.result + + +class DummyOverrides: + def __init__(self, result): + self.result = result + + def get_filename(self, resource_name): + return self.result + + listdir = isdir = has_resource = get_stream = get_string = get_filename + + +class DummyPackageOverrides: + def __init__(self, package): + self.package = package + self.inserted = [] + + def insert(self, path, source): + self.inserted.append((path, source)) + + +class DummyPkgResources: + def __init__(self): + self.registered = [] + + def register_loader_type(self, typ, inst): + self.registered.append((typ, inst)) + + +class DummyPackage: + def __init__(self, name): + self.__name__ = name + + +class DummyAssetSource: + def __init__(self, **kw): + self.kw = kw + + def get_filename(self, resource_name): + self.resource_name = resource_name + return self.kw['filename'] + + def get_stream(self, resource_name): + self.resource_name = resource_name + return self.kw['stream'] + + def get_string(self, resource_name): + self.resource_name = resource_name + return self.kw['string'] + + def exists(self, resource_name): + self.resource_name = resource_name + return self.kw['exists'] + + def isdir(self, resource_name): + self.resource_name = resource_name + return self.kw['isdir'] + + def listdir(self, resource_name): + self.resource_name = resource_name + return self.kw['listdir'] + + +class DummyLoader: + _got_data = _is_package = None + + 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=''): + self.package = package + 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 + body = body.replace(b'\r', b'') + body = body.replace(b'\n', b'') + data = read_(filename) + data = data.replace(b'\r', b'') + data = data.replace(b'\n', b'') + assert body == data diff --git a/tests/test_config/test_factories.py b/tests/test_config/test_factories.py new file mode 100644 index 000000000..c03d3f68b --- /dev/null +++ b/tests/test_config/test_factories.py @@ -0,0 +1,203 @@ +import unittest + +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) + self.assertEqual(config.registry.getUtility(IRequestFactory), factory) + + def test_set_request_factory_dottedname(self): + from pyramid.interfaces import IRequestFactory + + config = self._makeOne(autocommit=True) + config.set_request_factory('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) + self.assertEqual(config.registry.getUtility(IResponseFactory), factory) + + def test_set_response_factory_dottedname(self): + from pyramid.interfaces import IResponseFactory + + config = self._makeOne(autocommit=True) + config.set_response_factory('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 + ) + + def test_set_root_factory_as_None(self): + from pyramid.interfaces import IRootFactory + from pyramid.traversal import DefaultRootFactory + + config = self._makeOne() + config.set_root_factory(None) + self.assertEqual(config.registry.queryUtility(IRootFactory), None) + config.commit() + self.assertEqual( + config.registry.getUtility(IRootFactory), DefaultRootFactory + ) + + def test_set_root_factory_dottedname(self): + from pyramid.interfaces import IRootFactory + + config = self._makeOne() + config.set_root_factory('tests.test_config.dummyfactory') + self.assertEqual(config.registry.queryUtility(IRootFactory), None) + config.commit() + self.assertEqual( + config.registry.getUtility(IRootFactory), dummyfactory + ) + + def test_set_session_factory(self): + from pyramid.interfaces import ISessionFactory + + config = self._makeOne() + config.set_session_factory(dummyfactory) + self.assertEqual(config.registry.queryUtility(ISessionFactory), None) + config.commit() + self.assertEqual( + config.registry.getUtility(ISessionFactory), dummyfactory + ) + + def test_set_session_factory_dottedname(self): + from pyramid.interfaces import ISessionFactory + + config = self._makeOne() + config.set_session_factory('tests.test_config.dummyfactory') + self.assertEqual(config.registry.queryUtility(ISessionFactory), None) + config.commit() + 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') + exts = config.registry.getUtility(IRequestExtensions) + self.assertTrue('foo' in exts.methods) + + def test_add_request_method_with_unnamed_callable(self): + from pyramid.interfaces import IRequestExtensions + + config = self._makeOne(autocommit=True) + + def foo(self): # 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): # 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) + self.assertTrue(exts is None) + + def test_add_request_method_with_None_callable_conflict(self): + from pyramid.exceptions import ConfigurationConflictError + + config = self._makeOne() + + def bar(self): # pragma: no cover + pass + + config.add_request_method(name='foo') + config.add_request_method(bar, name='foo') + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_request_method_with_None_callable_and_no_name(self): + config = self._makeOne(autocommit=True) + self.assertRaises(AttributeError, config.add_request_method) + + def test_add_request_method_with_text_type_name(self): + from pyramid.compat import text_, PY2 + from pyramid.exceptions import ConfigurationError + + config = self._makeOne(autocommit=True) + + def boomshaka(r): # pragma: no cover + pass + + def get_bad_name(): + if PY2: + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + else: + name = b'La Pe\xc3\xb1a' + + config.add_request_method(boomshaka, name=name) + + self.assertRaises(ConfigurationError, get_bad_name) + + def test_set_execution_policy(self): + from pyramid.interfaces import IExecutionPolicy + + config = self._makeOne(autocommit=True) + + def dummy_policy(environ, router): # pragma: no cover + pass + + config.set_execution_policy(dummy_policy) + registry = config.registry + result = registry.queryUtility(IExecutionPolicy) + self.assertEqual(result, dummy_policy) + + 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 + result = registry.queryUtility(IExecutionPolicy) + self.assertEqual(result, default_execution_policy) diff --git a/tests/test_config/test_i18n.py b/tests/test_config/test_i18n.py new file mode 100644 index 000000000..b840c1976 --- /dev/null +++ b/tests/test_config/test_i18n.py @@ -0,0 +1,171 @@ +import os +import unittest + +from . import dummyfactory + +here = os.path.dirname(__file__) +locale = os.path.abspath( + os.path.join(here, '..', 'pkgs', 'localeapp', 'locale') +) +locale2 = os.path.abspath( + os.path.join(here, '..', 'pkgs', 'localeapp', 'locale2') +) +locale3 = os.path.abspath( + os.path.join(here, '..', 'pkgs', 'localeapp', 'locale3') +) + + +class TestI18NConfiguratorMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_set_locale_negotiator(self): + from pyramid.interfaces import ILocaleNegotiator + + config = self._makeOne(autocommit=True) + + def negotiator(request): # pragma: no cover + pass + + config.set_locale_negotiator(negotiator) + self.assertEqual( + config.registry.getUtility(ILocaleNegotiator), negotiator + ) + + def test_set_locale_negotiator_dottedname(self): + from pyramid.interfaces import ILocaleNegotiator + + config = self._makeOne(autocommit=True) + config.set_locale_negotiator('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 + ) + + 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] + ) + + def test_add_translation_dirs_asset_spec_existing_translation_dirs(self): + from pyramid.interfaces import ITranslationDirectories + + config = self._makeOne(autocommit=True) + directories = ['abc'] + config.registry.registerUtility(directories, ITranslationDirectories) + config.add_translation_dirs('tests.pkgs.localeapp:locale') + result = config.registry.getUtility(ITranslationDirectories) + self.assertEqual(result, [locale, 'abc']) + + def test_add_translation_dirs_multiple_specs(self): + from pyramid.interfaces import ITranslationDirectories + + config = self._makeOne(autocommit=True) + config.add_translation_dirs( + '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:locale3') + 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], + ) + + def test_add_translation_dirs_invalid_kwargs(self): + config = self._makeOne(autocommit=True) + with self.assertRaises(TypeError): + 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] + ) + + 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.commit() + 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] + ) + + 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.add_translation_dirs('tests.pkgs.localeapp:locale') + self.assertEqual( + config.registry.getUtility(ITranslationDirectories), [locale2] + ) diff --git a/tests/test_config/test_init.py b/tests/test_config/test_init.py new file mode 100644 index 000000000..811672fb3 --- /dev/null +++ b/tests/test_config/test_init.py @@ -0,0 +1,1455 @@ +import os +import unittest + +from pyramid.compat import im_func +from pyramid.testing import skip_on + +from . import dummy_tween_factory +from . import dummy_include +from . import dummy_extend +from . import dummy_extend2 +from . import DummyContext + +from pyramid.exceptions import ConfigurationExecutionError +from pyramid.exceptions import ConfigurationConflictError + +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, + ): + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + if exception_view: # pragma: no cover + classifier = IExceptionViewClassifier + else: + classifier = IViewClassifier + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), + IView, + name=name, + default=None, + ) + + def _registerEventListener(self, config, event_iface=None): + if event_iface is None: # pragma: no cover + from zope.interface import Interface + + event_iface = Interface + L = [] + + def subscriber(*event): + L.extend(event) + + config.registry.registerHandler(subscriber, (event_iface,)) + return L + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def test_ctor_no_registry(self): + import sys + from pyramid.interfaces import ISettings + from pyramid.config import Configurator + from pyramid.interfaces import IRendererFactory + + config = Configurator() + this_pkg = sys.modules['tests.test_config'] + self.assertTrue(config.registry.getUtility(ISettings)) + self.assertEqual(config.package, this_pkg) + config.commit() + self.assertTrue(config.registry.getUtility(IRendererFactory, 'json')) + self.assertTrue(config.registry.getUtility(IRendererFactory, 'string')) + + def test_begin(self): + from pyramid.config import Configurator + + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin() + self.assertEqual( + manager.pushed, {'registry': config.registry, 'request': None} + ) + self.assertEqual(manager.popped, False) + + def test_begin_with_request(self): + from pyramid.config import Configurator + + config = Configurator() + request = object() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin(request=request) + self.assertEqual( + manager.pushed, {'registry': config.registry, 'request': request} + ) + self.assertEqual(manager.popped, False) + + def test_begin_overrides_request(self): + from pyramid.config import Configurator + + config = Configurator() + manager = DummyThreadLocalManager() + req = object() + # set it up for auto-propagation + pushed = {'registry': config.registry, 'request': None} + manager.pushed = pushed + config.manager = manager + config.begin(req) + self.assertTrue(manager.pushed is not pushed) + self.assertEqual(manager.pushed['request'], req) + self.assertEqual(manager.pushed['registry'], config.registry) + + def test_begin_propagates_request_for_same_registry(self): + from pyramid.config import Configurator + + config = Configurator() + manager = DummyThreadLocalManager() + req = object() + pushed = {'registry': config.registry, 'request': req} + manager.pushed = pushed + config.manager = manager + config.begin() + self.assertTrue(manager.pushed is not pushed) + self.assertEqual(manager.pushed['request'], req) + self.assertEqual(manager.pushed['registry'], config.registry) + + def test_begin_does_not_propagate_request_for_diff_registry(self): + from pyramid.config import Configurator + + config = Configurator() + manager = DummyThreadLocalManager() + req = object() + pushed = {'registry': object(), 'request': req} + manager.pushed = pushed + config.manager = manager + config.begin() + self.assertTrue(manager.pushed is not pushed) + self.assertEqual(manager.pushed['request'], None) + self.assertEqual(manager.pushed['registry'], config.registry) + + def test_end(self): + from pyramid.config import Configurator + + config = Configurator() + manager = DummyThreadLocalManager() + pushed = manager.pushed + config.manager = manager + config.end() + self.assertEqual(manager.pushed, pushed) + self.assertEqual(manager.popped, True) + + 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.assertFalse(manager.popped) + config.add_view(view) + self.assertTrue(manager.popped) + config.add_view(view) # did not raise a conflict because of commit + config.commit() + + def test_ctor_with_package_registry(self): + import sys + from pyramid.config import Configurator + + pkg = sys.modules['pyramid'] + config = Configurator(package=pkg) + self.assertEqual(config.package, pkg) + + def test_ctor_noreg_custom_settings(self): + from pyramid.interfaces import ISettings + + settings = {'reload_templates': True, 'mysetting': True} + config = self._makeOne(settings=settings) + settings = config.registry.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_ctor_noreg_debug_logger_None_default(self): + from pyramid.interfaces import IDebugLogger + + config = self._makeOne() + logger = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'tests.test_config') + + def test_ctor_noreg_debug_logger_non_None(self): + from pyramid.interfaces import IDebugLogger + + logger = object() + config = self._makeOne(debug_logger=logger) + result = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_ctor_authentication_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + + policy = object() + config = self._makeOne(authentication_policy=policy) + config.commit() + result = config.registry.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_ctor_authorization_policy_only(self): + policy = object() + config = self._makeOne(authorization_policy=policy) + self.assertRaises(ConfigurationExecutionError, config.commit) + + def test_ctor_no_root_factory(self): + from pyramid.interfaces import IRootFactory + + config = self._makeOne() + self.assertEqual(config.registry.queryUtility(IRootFactory), None) + config.commit() + self.assertEqual(config.registry.queryUtility(IRootFactory), None) + + def test_ctor_with_root_factory(self): + from pyramid.interfaces import IRootFactory + + factory = object() + config = self._makeOne(root_factory=factory) + self.assertEqual(config.registry.queryUtility(IRootFactory), None) + config.commit() + self.assertEqual(config.registry.queryUtility(IRootFactory), factory) + + def test_ctor_alternate_renderers(self): + from pyramid.interfaces import IRendererFactory + + renderer = object() + config = self._makeOne(renderers=[('yeah', renderer)]) + config.commit() + self.assertEqual( + config.registry.getUtility(IRendererFactory, 'yeah'), renderer + ) + + def test_ctor_default_renderers(self): + from pyramid.interfaces import IRendererFactory + from pyramid.renderers import json_renderer_factory + + config = self._makeOne() + self.assertEqual( + config.registry.getUtility(IRendererFactory, 'json'), + json_renderer_factory, + ) + + def test_ctor_default_permission(self): + from pyramid.interfaces import IDefaultPermission + + config = self._makeOne(default_permission='view') + config.commit() + self.assertEqual( + config.registry.getUtility(IDefaultPermission), 'view' + ) + + def test_ctor_session_factory(self): + from pyramid.interfaces import ISessionFactory + + factory = object() + config = self._makeOne(session_factory=factory) + self.assertEqual(config.registry.queryUtility(ISessionFactory), None) + config.commit() + self.assertEqual(config.registry.getUtility(ISessionFactory), factory) + + def test_ctor_default_view_mapper(self): + from pyramid.interfaces import IViewMapperFactory + + mapper = object() + config = self._makeOne(default_view_mapper=mapper) + config.commit() + self.assertEqual( + config.registry.getUtility(IViewMapperFactory), mapper + ) + + def test_ctor_httpexception_view_default(self): + from pyramid.interfaces import IExceptionResponse + from pyramid.httpexceptions import default_exceptionresponse_view + + config = self._makeOne() + view = self._getViewCallable( + config, ctx_iface=IExceptionResponse, request_iface=IRequest + ) + self.assertTrue(view.__wraps__ is default_exceptionresponse_view) + + def test_ctor_exceptionresponse_view_None(self): + from pyramid.interfaces import IExceptionResponse + + config = self._makeOne(exceptionresponse_view=None) + view = self._getViewCallable( + config, ctx_iface=IExceptionResponse, request_iface=IRequest + ) + self.assertTrue(view is None) + + def test_ctor_exceptionresponse_view_custom(self): + from pyramid.interfaces import IExceptionResponse + + 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 + ) + self.assertTrue(view.__wraps__ is exceptionresponse_view) + + def test_ctor_with_introspection(self): + config = self._makeOne(introspection=False) + self.assertEqual(config.introspection, False) + + def test_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' + config.includepath = ('spec',) + config.autocommit = True + config.route_prefix = 'prefix' + newconfig = config.with_package(tests) + self.assertEqual(newconfig.package, tests) + self.assertEqual(newconfig.registry, config.registry) + self.assertEqual(newconfig.autocommit, True) + self.assertEqual(newconfig.route_prefix, 'prefix') + self.assertEqual(newconfig.info, 'info') + self.assertEqual(newconfig.basepath, 'basepath') + self.assertEqual(newconfig.includepath, ('spec',)) + + def test_maybe_dotted_string_success(self): + import tests.test_config + + config = self._makeOne() + result = config.maybe_dotted('tests.test_config') + self.assertEqual(result, tests.test_config) + + def test_maybe_dotted_string_fail(self): + config = self._makeOne() + self.assertRaises(ImportError, config.maybe_dotted, 'cant.be.found') + + def test_maybe_dotted_notstring_success(self): + import 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') + + def test__fix_registry_has_listeners(self): + reg = DummyRegistry() + config = self._makeOne(reg) + config._fix_registry() + self.assertEqual(reg.has_listeners, True) + + def test__fix_registry_notify(self): + reg = DummyRegistry() + config = self._makeOne(reg) + config._fix_registry() + self.assertEqual(reg.notify(1), None) + self.assertEqual(reg.events, (1,)) + + def test__fix_registry_queryAdapterOrSelf(self): + from zope.interface import Interface + from zope.interface import implementer + + class IFoo(Interface): + pass + + @implementer(IFoo) + class Foo(object): + pass + + class Bar(object): + pass + + adaptation = () + foo = Foo() + bar = Bar() + reg = DummyRegistry(adaptation) + config = self._makeOne(reg) + config._fix_registry() + self.assertTrue(reg.queryAdapterOrSelf(foo, IFoo) is foo) + self.assertTrue(reg.queryAdapterOrSelf(bar, IFoo) is adaptation) + + def test__fix_registry_registerSelfAdapter(self): + reg = DummyRegistry() + config = self._makeOne(reg) + config._fix_registry() + reg.registerSelfAdapter('required', 'provided', name='abc') + self.assertEqual(len(reg.adapters), 1) + args, kw = reg.adapters[0] + self.assertEqual(args[0]('abc'), 'abc') + self.assertEqual( + kw, + { + 'info': '', + 'provided': 'provided', + 'required': 'required', + 'name': 'abc', + 'event': True, + }, + ) + + def test__fix_registry_adds__lock(self): + reg = DummyRegistry() + config = self._makeOne(reg) + config._fix_registry() + self.assertTrue(hasattr(reg, '_lock')) + + def test__fix_registry_adds_clear_view_lookup_cache(self): + reg = DummyRegistry() + config = self._makeOne(reg) + self.assertFalse(hasattr(reg, '_clear_view_lookup_cache')) + config._fix_registry() + self.assertFalse(hasattr(reg, '_view_lookup_cache')) + reg._clear_view_lookup_cache() + self.assertEqual(reg._view_lookup_cache, {}) + + def test_setup_registry_calls_fix_registry(self): + reg = DummyRegistry() + config = self._makeOne(reg) + config.add_view = lambda *arg, **kw: False + config._add_tween = lambda *arg, **kw: False + config.setup_registry() + self.assertEqual(reg.has_listeners, True) + + def test_setup_registry_registers_default_exceptionresponse_views(self): + from webob.exc import WSGIHTTPException + from pyramid.interfaces import IExceptionResponse + from pyramid.view import default_exceptionresponse_view + + reg = DummyRegistry() + config = self._makeOne(reg) + views = [] + config.add_view = lambda *arg, **kw: views.append((arg, kw)) + config.add_default_view_predicates = lambda *arg: None + config._add_tween = lambda *arg, **kw: False + config.setup_registry() + self.assertEqual( + views[0], + ( + (default_exceptionresponse_view,), + {'context': IExceptionResponse}, + ), + ) + self.assertEqual( + views[1], + ( + (default_exceptionresponse_view,), + {'context': WSGIHTTPException}, + ), + ) + + def test_setup_registry_registers_default_view_predicates(self): + reg = DummyRegistry() + config = self._makeOne(reg) + vp_called = [] + config.add_view = lambda *arg, **kw: None + config.add_default_view_predicates = lambda *arg: vp_called.append( + True + ) + config._add_tween = lambda *arg, **kw: False + config.setup_registry() + self.assertTrue(vp_called) + + def test_setup_registry_registers_default_webob_iresponse_adapter(self): + from webob import Response + from pyramid.interfaces import IResponse + + config = self._makeOne() + config.setup_registry() + response = Response() + self.assertTrue( + config.registry.queryAdapter(response, IResponse) is response + ) + + def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.httpexceptions import HTTPNotFound + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg, autocommit=True) + config.setup_registry() # registers IExceptionResponse default view + + def myview(context, request): + return 'OK' + + config.add_view(myview, context=HTTPNotFound, renderer=null_renderer) + request = self._makeRequest(config) + view = self._getViewCallable( + config, + ctx_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_setup_registry_custom_settings(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + + settings = {'reload_templates': True, 'mysetting': True} + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(settings=settings) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_setup_registry_debug_logger_None_default(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + logger = reg.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'tests.test_config') + + def test_setup_registry_debug_logger_non_None(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + + logger = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger=logger) + result = reg.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_setup_registry_debug_logger_name(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger='foo') + result = reg.getUtility(IDebugLogger) + self.assertEqual(result.name, 'foo') + + def test_setup_registry_authentication_policy(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + + policy = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy=policy) + config.commit() + result = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_setup_registry_authentication_policy_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy='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.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) + config.setup_registry(authorization_policy=policy) + config = self.assertRaises(ConfigurationExecutionError, config.commit) + + def test_setup_registry_no_default_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + config.commit() + self.assertEqual(reg.queryUtility(IRootFactory), None) + + def test_setup_registry_dottedname_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + + reg = Registry() + config = self._makeOne(reg) + import tests.test_config + + config.setup_registry(root_factory='tests.test_config') + self.assertEqual(reg.queryUtility(IRootFactory), None) + config.commit() + self.assertEqual(reg.getUtility(IRootFactory), tests.test_config) + + def test_setup_registry_locale_negotiator_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + + reg = Registry() + config = self._makeOne(reg) + import tests.test_config + + config.setup_registry(locale_negotiator='tests.test_config') + self.assertEqual(reg.queryUtility(ILocaleNegotiator), None) + config.commit() + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, tests.test_config) + + def test_setup_registry_locale_negotiator(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + + reg = Registry() + config = self._makeOne(reg) + negotiator = object() + config.setup_registry(locale_negotiator=negotiator) + self.assertEqual(reg.queryUtility(ILocaleNegotiator), None) + config.commit() + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, negotiator) + + def test_setup_registry_request_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(request_factory=factory) + self.assertEqual(reg.queryUtility(IRequestFactory), None) + config.commit() + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_response_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IResponseFactory + + reg = Registry() + config = self._makeOne(reg) + factory = lambda r: object() + config.setup_registry(response_factory=factory) + self.assertEqual(reg.queryUtility(IResponseFactory), None) + config.commit() + utility = reg.getUtility(IResponseFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_request_factory_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + + 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() + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, tests.test_config) + + def test_setup_registry_alternate_renderers(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererFactory + + renderer = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(renderers=[('yeah', renderer)]) + config.commit() + self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), renderer) + + def test_setup_registry_default_permission(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDefaultPermission + + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(default_permission='view') + config.commit() + self.assertEqual(reg.getUtility(IDefaultPermission), 'view') + + def test_setup_registry_includes(self): + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg) + settings = { + 'pyramid.includes': """tests.test_config.dummy_include +tests.test_config.dummy_include2""" + } + config.setup_registry(settings=settings) + self.assertTrue(reg.included) + self.assertTrue(reg.also_included) + + def test_setup_registry_includes_spaces(self): + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg) + settings = { + 'pyramid.includes': """tests.test_config.dummy_include tests.\ +test_config.dummy_include2""" + } + config.setup_registry(settings=settings) + self.assertTrue(reg.included) + self.assertTrue(reg.also_included) + + def test_setup_registry_tweens(self): + from pyramid.interfaces import ITweens + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg) + settings = {'pyramid.tweens': '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)], + ) + + def test_introspector_decorator(self): + inst = self._makeOne() + default = inst.introspector + self.assertTrue(hasattr(default, 'add')) + self.assertEqual(inst.introspector, inst.registry.introspector) + introspector = object() + inst.introspector = introspector + new = inst.introspector + self.assertTrue(new is introspector) + self.assertEqual(inst.introspector, inst.registry.introspector) + del inst.introspector + default = inst.introspector + self.assertFalse(default is new) + self.assertTrue(hasattr(default, 'add')) + + def test_make_wsgi_app(self): + import pyramid.config + from pyramid.router import Router + from pyramid.interfaces import IApplicationCreated + + manager = DummyThreadLocalManager() + config = self._makeOne() + subscriber = self._registerEventListener(config, IApplicationCreated) + config.manager = manager + app = config.make_wsgi_app() + self.assertEqual(app.__class__, Router) + self.assertEqual(manager.pushed['registry'], config.registry) + self.assertEqual(manager.pushed['request'], None) + self.assertTrue(manager.popped) + self.assertEqual(pyramid.config.global_registries.last, app.registry) + self.assertEqual(len(subscriber), 1) + self.assertTrue(IApplicationCreated.providedBy(subscriber[0])) + pyramid.config.global_registries.empty() + + def test_include_with_dotted_name(self): + from tests import test_config + + config = self._makeOne() + config.include('tests.test_config.dummy_include') + after = config.action_state + actions = after.actions + self.assertEqual(len(actions), 1) + action = after.actions[0] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_include_with_python_callable(self): + from tests import test_config + + config = self._makeOne() + config.include(dummy_include) + after = config.action_state + actions = after.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_include_with_module_defaults_to_includeme(self): + from tests import test_config + + config = self._makeOne() + config.include('tests.test_config') + after = config.action_state + actions = after.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_include_with_module_defaults_to_includeme_missing(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises(ConfigurationError, config.include, 'tests') + + def test_include_with_route_prefix(self): + root_config = self._makeOne(autocommit=True) + + def dummy_subapp(config): + self.assertEqual(config.route_prefix, 'root') + + root_config.include(dummy_subapp, route_prefix='root') + + def test_include_with_nested_route_prefix(self): + root_config = self._makeOne(autocommit=True, route_prefix='root') + + def dummy_subapp2(config): + self.assertEqual(config.route_prefix, 'root/nested') + + def dummy_subapp3(config): + self.assertEqual(config.route_prefix, 'root/nested/nested2') + config.include(dummy_subapp4) + + def dummy_subapp4(config): + self.assertEqual(config.route_prefix, 'root/nested/nested2') + + def dummy_subapp(config): + self.assertEqual(config.route_prefix, 'root/nested') + config.include(dummy_subapp2) + config.include(dummy_subapp3, route_prefix='nested2') + + root_config.include(dummy_subapp, route_prefix='nested') + + def test_include_with_missing_source_file(self): + from pyramid.exceptions import ConfigurationError + import inspect + + config = self._makeOne() + + class DummyInspect(object): + def getmodule(self, c): + return inspect.getmodule(c) + + def getsourcefile(self, c): + return None + + config.inspect = DummyInspect() + try: + config.include('tests.test_config.dummy_include') + except ConfigurationError as e: + self.assertEqual( + 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 + 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 + + stack = [] + + def include(config): + stack.append(get_current_registry()) + + config = self._makeOne() + config.include(include) + self.assertTrue(stack[0] is config.registry) + + def test_scan_integration(self): + from zope.interface import alsoProvides + from pyramid.view import render_view_to_response + import tests.test_config.pkgs.scannable as package + + config = self._makeOne(autocommit=True) + config.scan(package) + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked_post') + + result = render_view_to_response(ctx, req, 'grokked_class') + self.assertEqual(result, 'grokked_class') + + result = render_view_to_response(ctx, req, 'grokked_instance') + self.assertEqual(result, 'grokked_instance') + + result = render_view_to_response(ctx, req, 'oldstyle_grokked_class') + self.assertEqual(result, 'oldstyle_grokked_class') + + req.method = 'GET' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked_post') + + result = render_view_to_response(ctx, req, 'another_grokked_class') + self.assertEqual(result, 'another_grokked_class') + + result = render_view_to_response(ctx, req, 'another_grokked_instance') + self.assertEqual(result, 'another_grokked_instance') + + result = render_view_to_response( + ctx, req, 'another_oldstyle_grokked_class' + ) + self.assertEqual(result, 'another_oldstyle_grokked_class') + + result = render_view_to_response(ctx, req, 'stacked1') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'stacked2') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'another_stacked1') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'another_stacked2') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'stacked_class1') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'stacked_class2') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class1') + self.assertEqual(result, 'another_stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(result, 'another_stacked_class') + + # NB: on Jython, a class without an __init__ apparently accepts + # any number of arguments without raising a TypeError, so the next + # assertion may fail there. We don't support Jython at the moment, + # this is just a note to a future self. + + self.assertRaises( + TypeError, render_view_to_response, ctx, req, 'basemethod' + ) + + result = render_view_to_response(ctx, req, 'method1') + self.assertEqual(result, 'method1') + + result = render_view_to_response(ctx, req, 'method2') + self.assertEqual(result, 'method2') + + result = render_view_to_response(ctx, req, 'stacked_method1') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'stacked_method2') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'subpackage_init') + self.assertEqual(result, 'subpackage_init') + + result = render_view_to_response(ctx, req, 'subpackage_notinit') + self.assertEqual(result, 'subpackage_notinit') + + result = render_view_to_response(ctx, req, 'subsubpackage_init') + self.assertEqual(result, 'subsubpackage_init') + + result = render_view_to_response(ctx, req, 'pod_notinit') + self.assertEqual(result, None) + + def test_scan_integration_with_ignore(self): + from zope.interface import alsoProvides + from pyramid.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') + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + # ignored + v = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(v, None) + + def test_scan_integration_dottedname_package(self): + from zope.interface import alsoProvides + from pyramid.view import render_view_to_response + + config = self._makeOne(autocommit=True) + config.scan('tests.test_config.pkgs.scannable') + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + def test_scan_integration_with_extra_kw(self): + config = self._makeOne(autocommit=True) + config.scan('tests.test_config.pkgs.scanextrakw', a=1) + self.assertEqual(config.a, 1) + + def test_scan_integration_with_onerror(self): + # fancy sys.path manipulation here to appease "setup.py test" which + # fails miserably when it can't import something in the package + import sys + + try: + here = os.path.dirname(__file__) + path = os.path.join(here, 'path') + sys.path.append(path) + config = self._makeOne(autocommit=True) + + class FooException(Exception): + pass + + def onerror(name): + raise FooException + + self.assertRaises( + FooException, config.scan, 'scanerror', onerror=onerror + ) + finally: + sys.path.remove(path) + + def test_scan_integration_conflict(self): + from tests.test_config.pkgs import selfscan + from pyramid.config import Configurator + + c = Configurator() + c.scan(selfscan) + c.scan(selfscan) + try: + c.commit() + except ConfigurationConflictError as why: + + def scanconflicts(e): + conflicts = e._conflicts.values() + for conflict in conflicts: + for confinst in conflict: + yield confinst.src + + which = list(scanconflicts(why)) + self.assertEqual(len(which), 4) + self.assertTrue("@view_config(renderer='string')" in which) + self.assertTrue( + "@view_config(name='two', renderer='string')" in which + ) + + @skip_on('py3') + def test_hook_zca(self): + from zope.component import getSiteManager + + def foo(): + '123' + + try: + config = self._makeOne() + config.hook_zca() + config.begin() + sm = getSiteManager() + self.assertEqual(sm, config.registry) + finally: + getSiteManager.reset() + + @skip_on('py3') + def test_unhook_zca(self): + from zope.component import getSiteManager + + def foo(): + '123' + + try: + getSiteManager.sethook(foo) + config = self._makeOne() + config.unhook_zca() + sm = getSiteManager() + self.assertNotEqual(sm, '123') + finally: + getSiteManager.reset() + + def test___getattr__missing_when_directives_exist(self): + config = self._makeOne() + directives = {} + config.registry._directives = directives + self.assertRaises(AttributeError, config.__getattr__, 'wontexist') + + def test___getattr__missing_when_directives_dont_exist(self): + config = self._makeOne() + self.assertRaises(AttributeError, config.__getattr__, 'wontexist') + + def test___getattr__matches(self): + config = self._makeOne() + + def foo(config): # 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): # 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): + 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') + self.assertTrue(hasattr(config, 'dummy_extend')) + config.dummy_extend('discrim') + after = config.action_state + action = after.actions[-1] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_add_directive_with_partial(self): + from tests import test_config + + config = self.config + config.add_directive( + 'dummy_partial', 'tests.test_config.dummy_partial' + ) + self.assertTrue(hasattr(config, 'dummy_partial')) + config.dummy_partial() + after = config.action_state + action = after.actions[-1] + self.assertEqual(action['discriminator'], 'partial') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_add_directive_with_custom_callable(self): + from tests import test_config + + config = self.config + config.add_directive( + 'dummy_callable', 'tests.test_config.dummy_callable' + ) + self.assertTrue(hasattr(config, 'dummy_callable')) + config.dummy_callable('discrim') + after = config.action_state + action = after.actions[-1] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_extend_with_python_callable(self): + from tests import test_config + + config = self.config + config.add_directive('dummy_extend', dummy_extend) + self.assertTrue(hasattr(config, 'dummy_extend')) + config.dummy_extend('discrim') + after = config.action_state + action = after.actions[-1] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], test_config) + + def test_extend_same_name_doesnt_conflict(self): + config = self.config + config.add_directive('dummy_extend', dummy_extend) + config.add_directive('dummy_extend', dummy_extend2) + self.assertTrue(hasattr(config, 'dummy_extend')) + config.dummy_extend('discrim') + after = config.action_state + action = after.actions[-1] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], config.registry) + + def test_extend_action_method_successful(self): + config = self.config + config.add_directive('dummy_extend', dummy_extend) + config.dummy_extend('discrim') + config.dummy_extend('discrim') + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_directive_persists_across_configurator_creations(self): + config = self.config + config.add_directive('dummy_extend', dummy_extend) + config2 = config.with_package('tests') + config2.dummy_extend('discrim') + after = config2.action_state + actions = after.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['discriminator'], 'discrim') + self.assertEqual(action['callable'], None) + self.assertEqual(action['args'], config2.package) + + +class 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 + ): + 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', DummyPredicate) + + def test_factory_as_dotted_name(self): + config = self._makeOne() + + 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' + ) + + +class TestGlobalRegistriesIntegration(unittest.TestCase): + def setUp(self): + from pyramid.config import global_registries + + global_registries.empty() + + tearDown = setUp + + def _makeConfigurator(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_global_registries_empty(self): + from pyramid.config import global_registries + + self.assertEqual(global_registries.last, None) + + def test_global_registries(self): + from pyramid.config import global_registries + + config1 = self._makeConfigurator() + config1.make_wsgi_app() + self.assertEqual(global_registries.last, config1.registry) + config2 = self._makeConfigurator() + config2.make_wsgi_app() + self.assertEqual(global_registries.last, config2.registry) + self.assertEqual( + list(global_registries), [config1.registry, config2.registry] + ) + global_registries.remove(config2.registry) + self.assertEqual(global_registries.last, config1.registry) + + +class DummyRequest: + subpath = () + matchdict = None + request_iface = IRequest + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + 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 + + +class DummyRegistry(object): + def __init__(self, adaptation=None, util=None): + self.utilities = [] + self.adapters = [] + self.adaptation = adaptation + self.util = util + + def subscribers(self, events, name): + self.events = events + return events + + def registerUtility(self, *arg, **kw): + self.utilities.append((arg, kw)) + + def registerAdapter(self, *arg, **kw): + self.adapters.append((arg, kw)) + + def queryAdapter(self, *arg, **kw): + return self.adaptation + + def queryUtility(self, *arg, **kw): + return self.util + + +class DummyPredicate(object): + pass diff --git a/tests/test_config/test_predicates.py b/tests/test_config/test_predicates.py new file mode 100644 index 000000000..079652b39 --- /dev/null +++ b/tests/test_config/test_predicates.py @@ -0,0 +1,480 @@ +import unittest + +from pyramid.compat import text_ + + +class TestPredicateList(unittest.TestCase): + def _makeOne(self): + from pyramid.config.predicates import PredicateList + from pyramid import predicates + + inst = PredicateList() + for name, factory in ( + ('xhr', predicates.XHRPredicate), + ('request_method', predicates.RequestMethodPredicate), + ('path_info', predicates.PathInfoPredicate), + ('request_param', predicates.RequestParamPredicate), + ('header', predicates.HeaderPredicate), + ('accept', predicates.AcceptPredicate), + ('containment', predicates.ContainmentPredicate), + ('request_type', predicates.RequestTypePredicate), + ('match_param', predicates.MatchParamPredicate), + ('custom', predicates.CustomPredicate), + ('traverse', predicates.TraversePredicate), + ): + inst.add(name, factory) + return inst + + def _callFUT(self, **kw): + inst = self._makeOne() + config = DummyConfigurator() + return inst.make(config, **kw) + + def test_ordering_xhr_and_request_method_trump_only_containment(self): + order1, _, _ = self._callFUT(xhr=True, request_method='GET') + order2, _, _ = self._callFUT(containment=True) + self.assertTrue(order1 < order2) + + def test_ordering_number_of_predicates(self): + from pyramid.config.predicates import predvalseq + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=predvalseq([DummyCustomPredicate()]), + ) + order2, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=predvalseq([DummyCustomPredicate()]), + ) + order3, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + ) + order4, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + accept='accept', + containment='containment', + ) + order5, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + accept='accept', + ) + order6, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + header='header', + ) + order7, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + match_param='foo=bar', + ) + order8, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + ) + order9, _, _ = self._callFUT( + xhr='xhr', request_method='request_method', path_info='path_info' + ) + order10, _, _ = self._callFUT( + xhr='xhr', request_method='request_method' + ) + order11, _, _ = self._callFUT(xhr='xhr') + order12, _, _ = self._callFUT() + self.assertEqual(order1, order2) + self.assertTrue(order3 > order2) + self.assertTrue(order4 > order3) + self.assertTrue(order5 > order4) + self.assertTrue(order6 > order5) + self.assertTrue(order7 > order6) + self.assertTrue(order8 > order7) + self.assertTrue(order9 > order8) + self.assertTrue(order10 > order9) + self.assertTrue(order11 > order10) + self.assertTrue(order12 > order10) + + def test_ordering_importance_of_predicates(self): + from pyramid.config.predicates import predvalseq + + order1, _, _ = self._callFUT(xhr='xhr') + order2, _, _ = self._callFUT(request_method='request_method') + order3, _, _ = self._callFUT(path_info='path_info') + order4, _, _ = self._callFUT(request_param='param') + order5, _, _ = self._callFUT(header='header') + order6, _, _ = self._callFUT(accept='accept') + order7, _, _ = self._callFUT(containment='containment') + order8, _, _ = self._callFUT(request_type='request_type') + order9, _, _ = self._callFUT(match_param='foo=bar') + order10, _, _ = self._callFUT( + custom=predvalseq([DummyCustomPredicate()]) + ) + self.assertTrue(order1 > order2) + self.assertTrue(order2 > order3) + self.assertTrue(order3 > order4) + self.assertTrue(order4 > order5) + self.assertTrue(order5 > order6) + self.assertTrue(order6 > order7) + self.assertTrue(order7 > order8) + self.assertTrue(order8 > order9) + self.assertTrue(order9 > order10) + + def test_ordering_importance_and_number(self): + from pyramid.config.predicates import predvalseq + + order1, _, _ = self._callFUT( + xhr='xhr', request_method='request_method' + ) + order2, _, _ = self._callFUT( + custom=predvalseq([DummyCustomPredicate()]) + ) + self.assertTrue(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', request_method='request_method' + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=predvalseq([DummyCustomPredicate()]), + ) + self.assertTrue(order1 > order2) + + order1, _, _ = self._callFUT( + xhr='xhr', request_method='request_method', path_info='path_info' + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=predvalseq([DummyCustomPredicate()]), + ) + self.assertTrue(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', request_method='request_method', path_info='path_info' + ) + order2, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + custom=predvalseq([DummyCustomPredicate()]), + ) + self.assertTrue(order1 > order2) + + def test_different_custom_predicates_with_same_hash(self): + from pyramid.config.predicates import predvalseq + + class PredicateWithHash(object): + def __hash__(self): + return 1 + + a = PredicateWithHash() + b = PredicateWithHash() + _, _, a_phash = self._callFUT(custom=predvalseq([a])) + _, _, b_phash = self._callFUT(custom=predvalseq([b])) + self.assertEqual(a_phash, b_phash) + + def test_traverse_has_remainder_already(self): + order, predicates, phash = self._callFUT(traverse='/1/:a/:b') + self.assertEqual(len(predicates), 1) + pred = predicates[0] + info = {'traverse': 'abc'} + request = DummyRequest() + result = pred(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'traverse': 'abc'}) + + def test_traverse_matches(self): + order, predicates, phash = self._callFUT(traverse='/1/:a/:b') + self.assertEqual(len(predicates), 1) + pred = predicates[0] + info = {'match': {'a': 'a', 'b': 'b'}} + request = DummyRequest() + result = pred(info, request) + self.assertEqual(result, True) + self.assertEqual( + info, {'match': {'a': 'a', 'b': 'b', 'traverse': ('1', 'a', 'b')}} + ) + + def test_traverse_matches_with_highorder_chars(self): + order, predicates, phash = self._callFUT( + traverse=text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8') + ) + self.assertEqual(len(predicates), 1) + pred = predicates[0] + info = {'match': {'x': text_(b'Qu\xc3\xa9bec', 'utf-8')}} + request = DummyRequest() + result = pred(info, request) + self.assertEqual(result, True) + self.assertEqual( + info['match']['traverse'], + ( + text_(b'La Pe\xc3\xb1a', 'utf-8'), + text_(b'Qu\xc3\xa9bec', 'utf-8'), + ), + ) + + def test_custom_predicates_can_affect_traversal(self): + from pyramid.config.predicates import predvalseq + + def custom(info, request): + m = info['match'] + m['dummy'] = 'foo' + return True + + _, predicates, _ = self._callFUT( + custom=predvalseq([custom]), traverse='/1/:dummy/:a' + ) + self.assertEqual(len(predicates), 2) + info = {'match': {'a': 'a'}} + request = DummyRequest() + self.assertTrue(all([p(info, request) for p in predicates])) + self.assertEqual( + info, + { + 'match': { + 'a': 'a', + 'dummy': 'foo', + 'traverse': ('1', 'foo', 'a'), + } + }, + ) + + def test_predicate_text_is_correct(self): + from pyramid.config.predicates import predvalseq + + _, predicates, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=predvalseq( + [ + DummyCustomPredicate(), + DummyCustomPredicate.classmethod_predicate, + DummyCustomPredicate.classmethod_predicate_no_text, + ] + ), + match_param='foo=bar', + ) + self.assertEqual(predicates[0].text(), 'xhr = True') + self.assertEqual( + predicates[1].text(), "request_method = request_method" + ) + self.assertEqual(predicates[2].text(), 'path_info = path_info') + self.assertEqual(predicates[3].text(), 'request_param param') + self.assertEqual(predicates[4].text(), 'header header') + self.assertEqual(predicates[5].text(), 'accept = accept') + self.assertEqual(predicates[6].text(), 'containment = containment') + self.assertEqual(predicates[7].text(), 'request_type = request_type') + self.assertEqual(predicates[8].text(), "match_param foo=bar") + self.assertEqual(predicates[9].text(), 'custom predicate') + self.assertEqual(predicates[10].text(), 'classmethod predicate') + self.assertTrue(predicates[11].text().startswith('custom predicate')) + + def test_match_param_from_string(self): + _, predicates, _ = self._callFUT(match_param='foo=bar') + request = DummyRequest() + request.matchdict = {'foo': 'bar', 'baz': 'bum'} + self.assertTrue(predicates[0](Dummy(), request)) + + def test_match_param_from_string_fails(self): + _, predicates, _ = self._callFUT(match_param='foo=bar') + request = DummyRequest() + request.matchdict = {'foo': 'bum', 'baz': 'bum'} + self.assertFalse(predicates[0](Dummy(), request)) + + def test_match_param_from_dict(self): + _, predicates, _ = self._callFUT(match_param=('foo=bar', 'baz=bum')) + request = DummyRequest() + request.matchdict = {'foo': 'bar', 'baz': 'bum'} + self.assertTrue(predicates[0](Dummy(), request)) + + def test_match_param_from_dict_fails(self): + _, predicates, _ = self._callFUT(match_param=('foo=bar', 'baz=bum')) + request = DummyRequest() + request.matchdict = {'foo': 'bar', 'baz': 'foo'} + self.assertFalse(predicates[0](Dummy(), request)) + + def test_request_method_sequence(self): + _, predicates, _ = self._callFUT(request_method=('GET', 'HEAD')) + request = DummyRequest() + request.method = 'HEAD' + self.assertTrue(predicates[0](Dummy(), request)) + request.method = 'GET' + self.assertTrue(predicates[0](Dummy(), request)) + request.method = 'POST' + self.assertFalse(predicates[0](Dummy(), request)) + + def test_request_method_ordering_hashes_same(self): + hash1, _, __ = self._callFUT(request_method=('GET', 'HEAD')) + hash2, _, __ = self._callFUT(request_method=('HEAD', 'GET')) + self.assertEqual(hash1, hash2) + hash1, _, __ = self._callFUT(request_method=('GET',)) + hash2, _, __ = self._callFUT(request_method='GET') + self.assertEqual(hash1, hash2) + + def test_unknown_predicate(self): + from pyramid.exceptions import ConfigurationError + + self.assertRaises(ConfigurationError, self._callFUT, unknown=1) + + def test_predicate_close_matches(self): + from pyramid.exceptions import ConfigurationError + + with self.assertRaises(ConfigurationError) as context: + self._callFUT(method='GET') + expected_msg = ( + "Unknown predicate values: {'method': 'GET'} " + "(did you mean request_method)" + ) + self.assertEqual(context.exception.args[0], expected_msg) + + def test_notted(self): + from pyramid.config import not_ + from pyramid.testing import DummyRequest + + request = DummyRequest() + _, predicates, _ = self._callFUT( + xhr='xhr', request_method=not_('POST'), header=not_('header') + ) + self.assertEqual(predicates[0].text(), 'xhr = True') + self.assertEqual(predicates[1].text(), "!request_method = POST") + self.assertEqual(predicates[2].text(), '!header header') + self.assertEqual(predicates[1](None, request), True) + self.assertEqual(predicates[2](None, request), True) + + +class Test_sort_accept_offers(unittest.TestCase): + def _callFUT(self, offers, order=None): + from pyramid.config.predicates import sort_accept_offers + + return sort_accept_offers(offers, order) + + def test_default_specificities(self): + result = self._callFUT(['text/html', 'text/html;charset=utf8']) + self.assertEqual(result, ['text/html;charset=utf8', 'text/html']) + + def test_specific_type_order(self): + result = self._callFUT( + [ + 'text/html', + 'application/json', + 'text/html;charset=utf8', + 'text/plain', + ], + ['application/json', 'text/html'], + ) + self.assertEqual( + result, + [ + 'application/json', + 'text/html;charset=utf8', + 'text/html', + 'text/plain', + ], + ) + + def test_params_order(self): + result = self._callFUT( + [ + 'text/html;charset=utf8', + 'text/html;charset=latin1', + 'text/html;foo=bar', + ], + ['text/html;charset=latin1', 'text/html;charset=utf8'], + ) + self.assertEqual( + result, + [ + 'text/html;charset=latin1', + 'text/html;charset=utf8', + 'text/html;foo=bar', + ], + ) + + def test_params_inherit_type_prefs(self): + result = self._callFUT( + ['text/html;charset=utf8', 'text/plain;charset=latin1'], + ['text/plain', 'text/html'], + ) + self.assertEqual( + result, ['text/plain;charset=latin1', 'text/html;charset=utf8'] + ) + + +class DummyCustomPredicate(object): + def __init__(self): + self.__text__ = 'custom predicate' + + def classmethod_predicate(*args): # pragma: no cover + pass + + classmethod_predicate.__text__ = 'classmethod predicate' + classmethod_predicate = classmethod(classmethod_predicate) + + @classmethod + def classmethod_predicate_no_text(*args): + pass # pragma: no cover + + +class Dummy(object): + def __init__(self, **kw): + self.__dict__.update(**kw) + + +class DummyRequest: + subpath = () + matchdict = None + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + self.params = {} + self.cookies = {} + + +class DummyConfigurator(object): + def maybe_dotted(self, thing): + return thing diff --git a/tests/test_config/test_rendering.py b/tests/test_config/test_rendering.py new file mode 100644 index 000000000..a33977c28 --- /dev/null +++ b/tests/test_config/test_rendering.py @@ -0,0 +1,43 @@ +import unittest + + +class TestRenderingConfiguratorMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_add_default_renderers(self): + from pyramid.config.rendering import DEFAULT_RENDERERS + from pyramid.interfaces import IRendererFactory + + config = self._makeOne(autocommit=True) + config.add_default_renderers() + for name, impl in DEFAULT_RENDERERS: + self.assertTrue( + config.registry.queryUtility(IRendererFactory, name) + is not None + ) + + def test_add_renderer(self): + from pyramid.interfaces import IRendererFactory + + config = self._makeOne(autocommit=True) + renderer = object() + config.add_renderer('name', renderer) + self.assertEqual( + config.registry.getUtility(IRendererFactory, 'name'), renderer + ) + + def test_add_renderer_dottedname_factory(self): + from pyramid.interfaces import IRendererFactory + + config = self._makeOne(autocommit=True) + import tests.test_config + + config.add_renderer('name', 'tests.test_config') + self.assertEqual( + config.registry.getUtility(IRendererFactory, 'name'), + tests.test_config, + ) diff --git a/tests/test_config/test_routes.py b/tests/test_config/test_routes.py new file mode 100644 index 000000000..1c1ed6700 --- /dev/null +++ b/tests/test_config/test_routes.py @@ -0,0 +1,322 @@ +import unittest + +from . import dummyfactory +from . import DummyContext +from pyramid.compat import text_ + + +class RoutesConfiguratorMixinTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def _assertRoute(self, config, name, path, num_predicates=0): + from pyramid.interfaces import IRoutesMapper + + mapper = config.registry.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.path, path) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def test_get_routes_mapper_not_yet_registered(self): + config = self._makeOne() + mapper = config.get_routes_mapper() + self.assertEqual(mapper.routelist, []) + + def test_get_routes_mapper_already_registered(self): + from pyramid.interfaces import IRoutesMapper + + config = self._makeOne() + mapper = object() + config.registry.registerUtility(mapper, IRoutesMapper) + result = config.get_routes_mapper() + self.assertEqual(result, mapper) + + def test_add_route_defaults(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path') + self._assertRoute(config, 'name', 'path') + + def test_add_route_with_route_prefix(self): + config = self._makeOne(autocommit=True) + config.route_prefix = 'root' + config.add_route('name', 'path') + self._assertRoute(config, 'name', 'root/path') + + def test_add_route_with_empty_string_with_route_prefix(self): + config = self._makeOne(autocommit=True) + config.route_prefix = 'root' + config.add_route('name', '') + self._assertRoute(config, 'name', 'root') + + def test_add_route_with_root_slash_with_route_prefix(self): + config = self._makeOne(autocommit=True) + config.route_prefix = 'root' + config.add_route('name', '/') + self._assertRoute(config, 'name', 'root/') + + def test_add_route_discriminator(self): + config = self._makeOne() + config.add_route('name', 'path') + self.assertEqual( + config.action_state.actions[-1]['discriminator'], ('route', 'name') + ) + + def test_add_route_with_factory(self): + config = self._makeOne(autocommit=True) + factory = object() + config.add_route('name', 'path', factory=factory) + route = self._assertRoute(config, 'name', 'path') + self.assertEqual(route.factory, factory) + + def test_add_route_with_static(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path/{foo}', static=True) + mapper = config.get_routes_mapper() + self.assertEqual(len(mapper.get_routes()), 0) + self.assertEqual(mapper.generate('name', {"foo": "a"}), '/path/a') + + def test_add_route_with_factory_dottedname(self): + config = self._makeOne(autocommit=True) + config.add_route( + 'name', 'path', factory='tests.test_config.dummyfactory' + ) + route = self._assertRoute(config, 'name', 'path') + self.assertEqual(route.factory, dummyfactory) + + def test_add_route_with_xhr(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', xhr=True) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.is_xhr = False + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_method(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_method='GET') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_path_info(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', path_info='/foo') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.upath_info = '/foo' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.upath_info = '/' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_path_info_highorder(self): + config = self._makeOne(autocommit=True) + config.add_route( + 'name', 'path', path_info=text_(b'/La Pe\xc3\xb1a', 'utf-8') + ) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.upath_info = text_(b'/La Pe\xc3\xb1a', 'utf-8') + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.upath_info = text_('/') + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_path_info_regex(self): + config = self._makeOne(autocommit=True) + config.add_route( + 'name', 'path', path_info=text_(br'/La Pe\w*', 'utf-8') + ) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.upath_info = text_(b'/La Pe\xc3\xb1a', 'utf-8') + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.upath_info = text_('/') + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_param(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_param='abc') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.params = {'abc': '123'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.params = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_custom_predicates(self): + import warnings + + config = self._makeOne(autocommit=True) + + def pred1(context, request): # pragma: no cover + pass + + def pred2(context, request): # pragma: no cover + pass + + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + config.add_route('name', 'path', custom_predicates=(pred1, pred2)) + self.assertEqual(len(w), 1) + route = self._assertRoute(config, 'name', 'path', 2) + self.assertEqual(len(route.predicates), 2) + + def test_add_route_with_header(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', header='Host') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.headers = {'Host': 'example.com'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.headers = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_accept(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', accept='text/xml') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.accept = DummyAccept('text/xml') + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.accept = DummyAccept('text/html') + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_accept_list(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', accept=['text/xml', 'text/plain']) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.accept = DummyAccept('text/xml') + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.accept = DummyAccept('text/plain') + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.accept = DummyAccept('text/html') + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_wildcard_accept_raises(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ValueError, + lambda: config.add_route('name', 'path', accept='text/*'), + ) + + def test_add_route_no_pattern_with_path(self): + config = self._makeOne(autocommit=True) + config.add_route('name', path='path') + self._assertRoute(config, 'name', 'path') + + def test_add_route_no_path_no_pattern(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_route, 'name') + + def test_add_route_with_pregenerator(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'pattern', pregenerator='123') + route = self._assertRoute(config, 'name', 'pattern') + self.assertEqual(route.pregenerator, '123') + + def test_add_route_no_view_with_view_attr(self): + config = self._makeOne(autocommit=True) + from pyramid.exceptions import ConfigurationError + + try: + config.add_route('name', '/pattern', view_attr='abc') + except ConfigurationError: + pass + else: # pragma: no cover + raise AssertionError + + def test_add_route_no_view_with_view_context(self): + config = self._makeOne(autocommit=True) + from pyramid.exceptions import ConfigurationError + + try: + config.add_route('name', '/pattern', view_context=DummyContext) + except ConfigurationError: + pass + else: # pragma: no cover + raise AssertionError + + def test_add_route_no_view_with_view_permission(self): + config = self._makeOne(autocommit=True) + from pyramid.exceptions import ConfigurationError + + try: + config.add_route('name', '/pattern', view_permission='edit') + except ConfigurationError: + pass + else: # pragma: no cover + raise AssertionError + + def test_add_route_no_view_with_view_renderer(self): + config = self._makeOne(autocommit=True) + from pyramid.exceptions import ConfigurationError + + try: + config.add_route('name', '/pattern', view_renderer='json') + except ConfigurationError: + pass + else: # pragma: no cover + raise AssertionError + + +class DummyRequest: + subpath = () + matchdict = None + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + self.params = {} + self.cookies = {} + + +class DummyAccept(object): + def __init__(self, *matches, **kw): + self.matches = list(matches) + self.contains = kw.pop('contains', False) + + def acceptable_offers(self, offers): + results = [] + for match in self.matches: + if match in offers: + results.append((match, 1.0)) + return results diff --git a/tests/test_config/test_security.py b/tests/test_config/test_security.py new file mode 100644 index 000000000..5ebd78f8d --- /dev/null +++ b/tests/test_config/test_security.py @@ -0,0 +1,151 @@ +import unittest + +from pyramid.exceptions import ConfigurationExecutionError +from pyramid.exceptions import ConfigurationError + + +class ConfiguratorSecurityMethodsTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_set_authentication_policy_no_authz_policy(self): + config = self._makeOne() + policy = object() + config.set_authentication_policy(policy) + self.assertRaises(ConfigurationExecutionError, config.commit) + + def test_set_authentication_policy_no_authz_policy_autocommit(self): + config = self._makeOne(autocommit=True) + policy = object() + self.assertRaises( + ConfigurationError, config.set_authentication_policy, policy + ) + + def test_set_authentication_policy_with_authz_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + + config = self._makeOne() + authn_policy = object() + authz_policy = object() + config.registry.registerUtility(authz_policy, IAuthorizationPolicy) + config.set_authentication_policy(authn_policy) + config.commit() + self.assertEqual( + config.registry.getUtility(IAuthenticationPolicy), authn_policy + ) + + def test_set_authentication_policy_with_authz_policy_autocommit(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + + config = self._makeOne(autocommit=True) + authn_policy = object() + authz_policy = object() + config.registry.registerUtility(authz_policy, IAuthorizationPolicy) + config.set_authentication_policy(authn_policy) + config.commit() + self.assertEqual( + config.registry.getUtility(IAuthenticationPolicy), authn_policy + ) + + def test_set_authorization_policy_no_authn_policy(self): + config = self._makeOne() + policy = object() + config.set_authorization_policy(policy) + self.assertRaises(ConfigurationExecutionError, config.commit) + + def test_set_authorization_policy_no_authn_policy_autocommit(self): + from pyramid.interfaces import IAuthorizationPolicy + + config = self._makeOne(autocommit=True) + policy = object() + config.set_authorization_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), policy + ) + + def test_set_authorization_policy_with_authn_policy(self): + from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import IAuthenticationPolicy + + config = self._makeOne() + authn_policy = object() + authz_policy = object() + config.registry.registerUtility(authn_policy, IAuthenticationPolicy) + config.set_authorization_policy(authz_policy) + config.commit() + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), authz_policy + ) + + def test_set_authorization_policy_with_authn_policy_autocommit(self): + from pyramid.interfaces import IAuthorizationPolicy + from pyramid.interfaces import IAuthenticationPolicy + + config = self._makeOne(autocommit=True) + authn_policy = object() + authz_policy = object() + config.registry.registerUtility(authn_policy, IAuthenticationPolicy) + config.set_authorization_policy(authz_policy) + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), authz_policy + ) + + def test_set_default_permission(self): + from pyramid.interfaces import IDefaultPermission + + config = self._makeOne(autocommit=True) + config.set_default_permission('view') + self.assertEqual( + config.registry.getUtility(IDefaultPermission), 'view' + ) + + def test_add_permission(self): + config = self._makeOne(autocommit=True) + config.add_permission('perm') + cat = config.registry.introspector.get_category('permissions') + self.assertEqual(len(cat), 1) + D = cat[0] + intr = D['introspectable'] + self.assertEqual(intr['value'], 'perm') + + def test_set_default_csrf_options(self): + from pyramid.interfaces import IDefaultCSRFOptions + + config = self._makeOne(autocommit=True) + config.set_default_csrf_options() + result = config.registry.getUtility(IDefaultCSRFOptions) + self.assertEqual(result.require_csrf, True) + self.assertEqual(result.token, 'csrf_token') + self.assertEqual(result.header, 'X-CSRF-Token') + self.assertEqual( + list(sorted(result.safe_methods)), + ['GET', 'HEAD', 'OPTIONS', 'TRACE'], + ) + self.assertTrue(result.callback is None) + + def test_changing_set_default_csrf_options(self): + from pyramid.interfaces import IDefaultCSRFOptions + + config = self._makeOne(autocommit=True) + + def callback(request): # pragma: no cover + return True + + config.set_default_csrf_options( + require_csrf=False, + token='DUMMY', + header=None, + safe_methods=('PUT',), + callback=callback, + ) + result = config.registry.getUtility(IDefaultCSRFOptions) + self.assertEqual(result.require_csrf, False) + self.assertEqual(result.token, 'DUMMY') + self.assertEqual(result.header, None) + self.assertEqual(list(sorted(result.safe_methods)), ['PUT']) + self.assertTrue(result.callback is callback) diff --git a/tests/test_config/test_settings.py b/tests/test_config/test_settings.py new file mode 100644 index 000000000..2fe769add --- /dev/null +++ b/tests/test_config/test_settings.py @@ -0,0 +1,635 @@ +import unittest + + +class TestSettingsConfiguratorMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test__set_settings_as_None(self): + config = self._makeOne() + settings = config._set_settings(None) + self.assertTrue(settings) + + def test__set_settings_does_not_uses_original_dict(self): + config = self._makeOne() + dummy = {} + result = config._set_settings(dummy) + self.assertTrue(dummy is not result) + self.assertNotIn('pyramid.debug_all', dummy) + + def test__set_settings_as_dictwithvalues(self): + config = self._makeOne() + settings = config._set_settings({'a': '1'}) + self.assertEqual(settings['a'], '1') + + def test_get_settings_nosettings(self): + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg) + self.assertEqual(config.get_settings(), None) + + def test_get_settings_withsettings(self): + settings = {'a': 1} + config = self._makeOne() + config.registry.settings = settings + self.assertEqual(config.get_settings(), settings) + + def test_add_settings_settings_already_registered(self): + from pyramid.registry import Registry + + reg = Registry() + config = self._makeOne(reg) + config._set_settings({'a': 1}) + config.add_settings({'b': 2}) + settings = reg.settings + self.assertEqual(settings['a'], 1) + self.assertEqual(settings['b'], 2) + + def test_add_settings_settings_not_yet_registered(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + + reg = Registry() + config = self._makeOne(reg) + config.add_settings({'a': 1}) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['a'], 1) + + def test_add_settings_settings_None(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + + reg = Registry() + config = self._makeOne(reg) + config.add_settings(None, a=1) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['a'], 1) + + def test_settings_parameter_dict_is_never_updated(self): + class ReadOnlyDict(dict): + def __readonly__(self, *args, **kwargs): # pragma: no cover + raise RuntimeError("Cannot modify ReadOnlyDict") + + __setitem__ = __readonly__ + __delitem__ = __readonly__ + pop = __readonly__ + popitem = __readonly__ + clear = __readonly__ + update = __readonly__ + setdefault = __readonly__ + del __readonly__ + + initial = ReadOnlyDict() + config = self._makeOne(settings=initial) + config._set_settings({'a': '1'}) + + +class TestSettings(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.settings import Settings + + return Settings + + def _makeOne(self, d=None, environ=None): + if environ is None: + environ = {} + klass = self._getTargetClass() + return klass(d, _environ_=environ) + + def test_noargs(self): + settings = self._makeOne() + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['debug_notfound'], False) + self.assertEqual(settings['debug_routematch'], False) + self.assertEqual(settings['reload_templates'], False) + self.assertEqual(settings['reload_resources'], False) + + self.assertEqual(settings['pyramid.debug_authorization'], False) + self.assertEqual(settings['pyramid.debug_notfound'], False) + self.assertEqual(settings['pyramid.debug_routematch'], False) + self.assertEqual(settings['pyramid.reload_templates'], False) + self.assertEqual(settings['pyramid.reload_resources'], False) + + def test_prevent_http_cache(self): + settings = self._makeOne({}) + self.assertEqual(settings['prevent_http_cache'], False) + self.assertEqual(settings['pyramid.prevent_http_cache'], False) + result = self._makeOne({'prevent_http_cache': 'false'}) + self.assertEqual(result['prevent_http_cache'], False) + self.assertEqual(result['pyramid.prevent_http_cache'], False) + result = self._makeOne({'prevent_http_cache': 't'}) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + result = self._makeOne({'prevent_http_cache': '1'}) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + result = self._makeOne({'pyramid.prevent_http_cache': 't'}) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + result = self._makeOne({}, {'PYRAMID_PREVENT_HTTP_CACHE': '1'}) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + result = self._makeOne( + {'prevent_http_cache': 'false', 'pyramid.prevent_http_cache': '1'} + ) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + result = self._makeOne( + {'prevent_http_cache': 'false', 'pyramid.prevent_http_cache': 'f'}, + {'PYRAMID_PREVENT_HTTP_CACHE': '1'}, + ) + self.assertEqual(result['prevent_http_cache'], True) + self.assertEqual(result['pyramid.prevent_http_cache'], True) + + def test_prevent_cachebust(self): + settings = self._makeOne({}) + self.assertEqual(settings['prevent_cachebust'], False) + self.assertEqual(settings['pyramid.prevent_cachebust'], False) + result = self._makeOne({'prevent_cachebust': 'false'}) + self.assertEqual(result['prevent_cachebust'], False) + self.assertEqual(result['pyramid.prevent_cachebust'], False) + result = self._makeOne({'prevent_cachebust': 't'}) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + result = self._makeOne({'prevent_cachebust': '1'}) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + result = self._makeOne({'pyramid.prevent_cachebust': 't'}) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + result = self._makeOne({}, {'PYRAMID_PREVENT_CACHEBUST': '1'}) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + result = self._makeOne( + {'prevent_cachebust': 'false', 'pyramid.prevent_cachebust': '1'} + ) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + result = self._makeOne( + {'prevent_cachebust': 'false', 'pyramid.prevent_cachebust': 'f'}, + {'PYRAMID_PREVENT_CACHEBUST': '1'}, + ) + self.assertEqual(result['prevent_cachebust'], True) + self.assertEqual(result['pyramid.prevent_cachebust'], True) + + def test_reload_templates(self): + settings = self._makeOne({}) + self.assertEqual(settings['reload_templates'], False) + self.assertEqual(settings['pyramid.reload_templates'], False) + result = self._makeOne({'reload_templates': 'false'}) + self.assertEqual(result['reload_templates'], False) + self.assertEqual(result['pyramid.reload_templates'], False) + result = self._makeOne({'reload_templates': 't'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + result = self._makeOne({'reload_templates': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + result = self._makeOne({'pyramid.reload_templates': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + result = self._makeOne({}, {'PYRAMID_RELOAD_TEMPLATES': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + result = self._makeOne( + {'reload_templates': 'false', 'pyramid.reload_templates': '1'} + ) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + result = self._makeOne( + {'reload_templates': 'false'}, {'PYRAMID_RELOAD_TEMPLATES': '1'} + ) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + + def test_reload_resources(self): + # alias for reload_assets + result = self._makeOne({}) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + result = self._makeOne({'reload_resources': 'false'}) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + result = self._makeOne({'reload_resources': 't'}) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({'reload_resources': '1'}) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({'pyramid.reload_resources': '1'}) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({}, {'PYRAMID_RELOAD_RESOURCES': '1'}) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne( + {'reload_resources': 'false', 'pyramid.reload_resources': '1'} + ) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne( + {'reload_resources': 'false', 'pyramid.reload_resources': 'false'}, + {'PYRAMID_RELOAD_RESOURCES': '1'}, + ) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + + def test_reload_assets(self): + # alias for reload_resources + result = self._makeOne({}) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + result = self._makeOne({'reload_assets': 'false'}) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + result = self._makeOne({'reload_assets': 't'}) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + result = self._makeOne({'reload_assets': '1'}) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + result = self._makeOne({'pyramid.reload_assets': '1'}) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + result = self._makeOne({}, {'PYRAMID_RELOAD_ASSETS': '1'}) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + result = self._makeOne( + {'reload_assets': 'false', 'pyramid.reload_assets': '1'} + ) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + result = self._makeOne( + {'reload_assets': 'false', 'pyramid.reload_assets': 'false'}, + {'PYRAMID_RELOAD_ASSETS': '1'}, + ) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + + def test_reload_all(self): + result = self._makeOne({}) + self.assertEqual(result['reload_templates'], False) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['pyramid.reload_templates'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + result = self._makeOne({'reload_all': 'false'}) + self.assertEqual(result['reload_templates'], False) + self.assertEqual(result['reload_resources'], False) + self.assertEqual(result['reload_assets'], False) + self.assertEqual(result['pyramid.reload_templates'], False) + self.assertEqual(result['pyramid.reload_resources'], False) + self.assertEqual(result['pyramid.reload_assets'], False) + result = self._makeOne({'reload_all': 't'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({'reload_all': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({'pyramid.reload_all': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne({}, {'PYRAMID_RELOAD_ALL': '1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne( + {'reload_all': 'false', 'pyramid.reload_all': '1'} + ) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + result = self._makeOne( + {'reload_all': 'false', 'pyramid.reload_all': 'false'}, + {'PYRAMID_RELOAD_ALL': '1'}, + ) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + self.assertEqual(result['reload_assets'], True) + self.assertEqual(result['pyramid.reload_templates'], True) + self.assertEqual(result['pyramid.reload_resources'], True) + self.assertEqual(result['pyramid.reload_assets'], True) + + def test_debug_authorization(self): + result = self._makeOne({}) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['pyramid.debug_authorization'], False) + result = self._makeOne({'debug_authorization': 'false'}) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['pyramid.debug_authorization'], False) + result = self._makeOne({'debug_authorization': 't'}) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + result = self._makeOne({'debug_authorization': '1'}) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + result = self._makeOne({'pyramid.debug_authorization': '1'}) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + result = self._makeOne({}, {'PYRAMID_DEBUG_AUTHORIZATION': '1'}) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + result = self._makeOne( + { + 'debug_authorization': 'false', + 'pyramid.debug_authorization': '1', + } + ) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + result = self._makeOne( + { + 'debug_authorization': 'false', + 'pyramid.debug_authorization': 'false', + }, + {'PYRAMID_DEBUG_AUTHORIZATION': '1'}, + ) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + + def test_debug_notfound(self): + result = self._makeOne({}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['pyramid.debug_notfound'], False) + result = self._makeOne({'debug_notfound': 'false'}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['pyramid.debug_notfound'], False) + result = self._makeOne({'debug_notfound': 't'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + result = self._makeOne({'debug_notfound': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + result = self._makeOne({'pyramid.debug_notfound': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + result = self._makeOne({}, {'PYRAMID_DEBUG_NOTFOUND': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + result = self._makeOne( + {'debug_notfound': 'false', 'pyramid.debug_notfound': '1'} + ) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + result = self._makeOne( + {'debug_notfound': 'false', 'pyramid.debug_notfound': 'false'}, + {'PYRAMID_DEBUG_NOTFOUND': '1'}, + ) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + + def test_debug_routematch(self): + result = self._makeOne({}) + self.assertEqual(result['debug_routematch'], False) + self.assertEqual(result['pyramid.debug_routematch'], False) + result = self._makeOne({'debug_routematch': 'false'}) + self.assertEqual(result['debug_routematch'], False) + self.assertEqual(result['pyramid.debug_routematch'], False) + result = self._makeOne({'debug_routematch': 't'}) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + result = self._makeOne({'debug_routematch': '1'}) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + result = self._makeOne({'pyramid.debug_routematch': '1'}) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + result = self._makeOne({}, {'PYRAMID_DEBUG_ROUTEMATCH': '1'}) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + result = self._makeOne( + {'debug_routematch': 'false', 'pyramid.debug_routematch': '1'} + ) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + result = self._makeOne( + {'debug_routematch': 'false', 'pyramid.debug_routematch': 'false'}, + {'PYRAMID_DEBUG_ROUTEMATCH': '1'}, + ) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + + def test_debug_templates(self): + result = self._makeOne({}) + self.assertEqual(result['debug_templates'], False) + self.assertEqual(result['pyramid.debug_templates'], False) + result = self._makeOne({'debug_templates': 'false'}) + self.assertEqual(result['debug_templates'], False) + self.assertEqual(result['pyramid.debug_templates'], False) + result = self._makeOne({'debug_templates': 't'}) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({'debug_templates': '1'}) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({'pyramid.debug_templates': '1'}) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({}, {'PYRAMID_DEBUG_TEMPLATES': '1'}) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne( + {'debug_templates': 'false', 'pyramid.debug_templates': '1'} + ) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne( + {'debug_templates': 'false', 'pyramid.debug_templates': 'false'}, + {'PYRAMID_DEBUG_TEMPLATES': '1'}, + ) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + + def test_debug_all(self): + result = self._makeOne({}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['debug_routematch'], False) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['debug_templates'], False) + self.assertEqual(result['pyramid.debug_notfound'], False) + self.assertEqual(result['pyramid.debug_routematch'], False) + self.assertEqual(result['pyramid.debug_authorization'], False) + self.assertEqual(result['pyramid.debug_templates'], False) + result = self._makeOne({'debug_all': 'false'}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['debug_routematch'], False) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['debug_templates'], False) + self.assertEqual(result['pyramid.debug_notfound'], False) + self.assertEqual(result['pyramid.debug_routematch'], False) + self.assertEqual(result['pyramid.debug_authorization'], False) + self.assertEqual(result['pyramid.debug_templates'], False) + result = self._makeOne({'debug_all': 't'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({'debug_all': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({'pyramid.debug_all': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne({}, {'PYRAMID_DEBUG_ALL': '1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne( + {'debug_all': 'false', 'pyramid.debug_all': '1'} + ) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + result = self._makeOne( + {'debug_all': 'false', 'pyramid.debug_all': 'false'}, + {'PYRAMID_DEBUG_ALL': '1'}, + ) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_routematch'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + self.assertEqual(result['pyramid.debug_notfound'], True) + self.assertEqual(result['pyramid.debug_routematch'], True) + self.assertEqual(result['pyramid.debug_authorization'], True) + self.assertEqual(result['pyramid.debug_templates'], True) + + def test_default_locale_name(self): + result = self._makeOne({}) + self.assertEqual(result['default_locale_name'], 'en') + self.assertEqual(result['pyramid.default_locale_name'], 'en') + result = self._makeOne({'default_locale_name': 'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + self.assertEqual(result['pyramid.default_locale_name'], 'abc') + result = self._makeOne({'pyramid.default_locale_name': 'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + self.assertEqual(result['pyramid.default_locale_name'], 'abc') + result = self._makeOne({}, {'PYRAMID_DEFAULT_LOCALE_NAME': 'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + self.assertEqual(result['pyramid.default_locale_name'], 'abc') + result = self._makeOne( + { + 'default_locale_name': 'def', + 'pyramid.default_locale_name': 'abc', + } + ) + self.assertEqual(result['default_locale_name'], 'abc') + self.assertEqual(result['pyramid.default_locale_name'], 'abc') + result = self._makeOne( + { + 'default_locale_name': 'def', + 'pyramid.default_locale_name': 'ghi', + }, + {'PYRAMID_DEFAULT_LOCALE_NAME': 'abc'}, + ) + self.assertEqual(result['default_locale_name'], 'abc') + self.assertEqual(result['pyramid.default_locale_name'], 'abc') + + def test_csrf_trusted_origins(self): + result = self._makeOne({}) + self.assertEqual(result['pyramid.csrf_trusted_origins'], []) + result = self._makeOne({'pyramid.csrf_trusted_origins': 'example.com'}) + self.assertEqual( + result['pyramid.csrf_trusted_origins'], ['example.com'] + ) + result = self._makeOne( + {'pyramid.csrf_trusted_origins': ['example.com']} + ) + self.assertEqual( + result['pyramid.csrf_trusted_origins'], ['example.com'] + ) + result = self._makeOne( + { + 'pyramid.csrf_trusted_origins': ( + 'example.com foo.example.com\nasdf.example.com' + ) + } + ) + self.assertEqual( + result['pyramid.csrf_trusted_origins'], + ['example.com', 'foo.example.com', 'asdf.example.com'], + ) + + def test_originals_kept(self): + result = self._makeOne({'a': 'i am so a'}) + self.assertEqual(result['a'], 'i am so a') diff --git a/tests/test_config/test_testing.py b/tests/test_config/test_testing.py new file mode 100644 index 000000000..ede31e1b6 --- /dev/null +++ b/tests/test_config/test_testing.py @@ -0,0 +1,235 @@ +import unittest +from zope.interface import implementer + +from pyramid.compat import text_ +from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin +from . import IDummy + + +class TestingConfiguratorMixinTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_testing_securitypolicy(self): + from pyramid.testing import DummySecurityPolicy + + config = self._makeOne(autocommit=True) + config.testing_securitypolicy( + 'user', ('group1', 'group2'), permissive=False + ) + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + + ut = config.registry.getUtility(IAuthenticationPolicy) + self.assertTrue(isinstance(ut, DummySecurityPolicy)) + ut = config.registry.getUtility(IAuthorizationPolicy) + self.assertEqual(ut.userid, 'user') + self.assertEqual(ut.groupids, ('group1', 'group2')) + self.assertEqual(ut.permissive, False) + + def test_testing_securitypolicy_remember_result(self): + from pyramid.security import remember + + config = self._makeOne(autocommit=True) + pol = config.testing_securitypolicy( + 'user', + ('group1', 'group2'), + permissive=False, + remember_result=True, + ) + request = DummyRequest() + request.registry = config.registry + val = remember(request, 'fred') + self.assertEqual(pol.remembered, 'fred') + self.assertEqual(val, True) + + def test_testing_securitypolicy_forget_result(self): + from pyramid.security import forget + + config = self._makeOne(autocommit=True) + pol = config.testing_securitypolicy( + 'user', ('group1', 'group2'), permissive=False, forget_result=True + ) + request = DummyRequest() + request.registry = config.registry + val = forget(request) + self.assertEqual(pol.forgotten, True) + self.assertEqual(val, True) + + def test_testing_resources(self): + from pyramid.traversal import find_resource + from pyramid.interfaces import ITraverser + + ob1 = object() + ob2 = object() + resources = {'/ob1': ob1, '/ob2': ob2} + config = self._makeOne(autocommit=True) + config.testing_resources(resources) + adapter = config.registry.getAdapter(None, ITraverser) + result = adapter(DummyRequest({'PATH_INFO': '/ob1'})) + self.assertEqual(result['context'], ob1) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (text_('ob1'),)) + self.assertEqual(result['virtual_root'], ob1) + self.assertEqual(result['virtual_root_path'], ()) + result = adapter(DummyRequest({'PATH_INFO': '/ob2'})) + self.assertEqual(result['context'], ob2) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (text_('ob2'),)) + self.assertEqual(result['virtual_root'], ob2) + self.assertEqual(result['virtual_root_path'], ()) + self.assertRaises( + KeyError, adapter, DummyRequest({'PATH_INFO': '/ob3'}) + ) + try: + config.begin() + self.assertEqual(find_resource(None, '/ob1'), ob1) + finally: + config.end() + + def test_testing_add_subscriber_single(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber(IDummy) + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_dottedname(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber('tests.test_config.IDummy') + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_multiple(self): + from zope.interface import Interface + + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber((Interface, IDummy)) + event = DummyEvent() + event.object = 'foo' + # the below is the equivalent of z.c.event.objectEventNotify(event) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 2) + self.assertEqual(L[0], 'foo') + self.assertEqual(L[1], event) + + def test_testing_add_subscriber_defaults(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber() + event = object() + config.registry.notify(event) + self.assertEqual(L[-1], event) + event2 = object() + config.registry.notify(event2) + self.assertEqual(L[-1], event2) + + def test_testing_add_renderer(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_renderer('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + + self.assertTrue(isinstance(renderer, DummyTemplateRenderer)) + from pyramid.renderers import render_to_response + + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + render_to_response( + 'templates/foo.pt', {'foo': 1, 'bar': 2}, request=request + ) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + def test_testing_add_renderer_twice(self): + config = self._makeOne(autocommit=True) + renderer1 = config.testing_add_renderer('templates/foo.pt') + renderer2 = config.testing_add_renderer('templates/bar.pt') + from pyramid.testing import DummyTemplateRenderer + + self.assertTrue(isinstance(renderer1, DummyTemplateRenderer)) + self.assertTrue(isinstance(renderer2, DummyTemplateRenderer)) + from pyramid.renderers import render_to_response + + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + render_to_response( + 'templates/foo.pt', {'foo': 1, 'bar': 2}, request=request + ) + renderer1.assert_(foo=1) + renderer1.assert_(bar=2) + renderer1.assert_(request=request) + render_to_response( + 'templates/bar.pt', {'foo': 1, 'bar': 2}, request=request + ) + renderer2.assert_(foo=1) + renderer2.assert_(bar=2) + renderer2.assert_(request=request) + + def test_testing_add_renderer_explicitrenderer(self): + config = self._makeOne(autocommit=True) + + class E(Exception): + pass + + def renderer(kw, system): + self.assertEqual(kw, {'foo': 1, 'bar': 2}) + raise E + + renderer = config.testing_add_renderer('templates/foo.pt', renderer) + from pyramid.renderers import render_to_response + + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + try: + render_to_response( + 'templates/foo.pt', {'foo': 1, 'bar': 2}, request=request + ) + except E: + pass + else: # pragma: no cover + raise AssertionError + + def test_testing_add_template(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_template('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + + self.assertTrue(isinstance(renderer, DummyTemplateRenderer)) + from pyramid.renderers import render_to_response + + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + render_to_response( + 'templates/foo.pt', dict(foo=1, bar=2), request=request + ) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + +@implementer(IDummy) +class DummyEvent: + pass + + +class DummyRequest(AuthenticationAPIMixin, AuthorizationAPIMixin): + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ diff --git a/tests/test_config/test_tweens.py b/tests/test_config/test_tweens.py new file mode 100644 index 000000000..805310c9a --- /dev/null +++ b/tests/test_config/test_tweens.py @@ -0,0 +1,484 @@ +import unittest + +from . import dummy_tween_factory +from . import dummy_tween_factory2 + +from pyramid.exceptions import ConfigurationConflictError + + +class TestTweensConfiguratorMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_add_tweens_names_distinct(self): + from pyramid.interfaces import ITweens + from pyramid.tweens import excview_tween_factory + + def factory1(handler, registry): # pragma: no cover + return handler + + def factory2(handler, registry): # pragma: no cover + return handler + + config = self._makeOne() + config.add_tween('tests.test_config.dummy_tween_factory') + config.add_tween('tests.test_config.dummy_tween_factory2') + config.commit() + tweens = config.registry.queryUtility(ITweens) + implicit = tweens.implicit() + self.assertEqual( + implicit, + [ + ( + 'tests.test_config.dummy_tween_factory2', + dummy_tween_factory2, + ), + ('tests.test_config.dummy_tween_factory', dummy_tween_factory), + ( + 'pyramid.tweens.excview_tween_factory', + excview_tween_factory, + ), + ], + ) + + def test_add_tweens_names_with_underover(self): + from pyramid.interfaces import ITweens + from pyramid.tweens import excview_tween_factory + from pyramid.tweens import MAIN + + config = self._makeOne() + config.add_tween('tests.test_config.dummy_tween_factory', over=MAIN) + config.add_tween( + 'tests.test_config.dummy_tween_factory2', + over=MAIN, + under='tests.test_config.dummy_tween_factory', + ) + config.commit() + tweens = config.registry.queryUtility(ITweens) + implicit = tweens.implicit() + self.assertEqual( + implicit, + [ + ( + 'pyramid.tweens.excview_tween_factory', + excview_tween_factory, + ), + ('tests.test_config.dummy_tween_factory', dummy_tween_factory), + ( + 'tests.test_config.dummy_tween_factory2', + dummy_tween_factory2, + ), + ], + ) + + def test_add_tweens_names_with_under_nonstringoriter(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + under=False, + ) + + def test_add_tweens_names_with_over_nonstringoriter(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + over=False, + ) + + def test_add_tween_dottedname(self): + from pyramid.interfaces import ITweens + from pyramid.tweens import excview_tween_factory + + config = self._makeOne() + config.add_tween('tests.test_config.dummy_tween_factory') + config.commit() + tweens = config.registry.queryUtility(ITweens) + self.assertEqual( + tweens.implicit(), + [ + ('tests.test_config.dummy_tween_factory', dummy_tween_factory), + ( + 'pyramid.tweens.excview_tween_factory', + excview_tween_factory, + ), + ], + ) + + def test_add_tween_instance(self): + from pyramid.exceptions import ConfigurationError + + class ATween(object): + pass + + atween = ATween() + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_tween, atween) + + def test_add_tween_unsuitable(self): + from pyramid.exceptions import ConfigurationError + import tests.test_config + + config = self._makeOne() + self.assertRaises( + ConfigurationError, config.add_tween, tests.test_config + ) + + def test_add_tween_name_ingress(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import INGRESS + + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_tween, INGRESS) + + def test_add_tween_name_main(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import MAIN + + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_tween, MAIN) + + def test_add_tweens_conflict(self): + config = self._makeOne() + config.add_tween('tests.test_config.dummy_tween_factory') + config.add_tween('tests.test_config.dummy_tween_factory') + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_tween_over_ingress(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import INGRESS + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + over=INGRESS, + ) + + def test_add_tween_over_ingress_iterable(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import INGRESS + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + over=('a', INGRESS), + ) + + def test_add_tween_under_main(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import MAIN + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + under=MAIN, + ) + + def test_add_tween_under_main_iterable(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import MAIN + + config = self._makeOne() + self.assertRaises( + ConfigurationError, + config.add_tween, + 'tests.test_config.dummy_tween_factory', + under=('a', MAIN), + ) + + +class TestTweens(unittest.TestCase): + def _makeOne(self): + from pyramid.config.tweens import Tweens + + return Tweens() + + def test_add_explicit(self): + tweens = self._makeOne() + tweens.add_explicit('name', 'factory') + self.assertEqual(tweens.explicit, [('name', 'factory')]) + tweens.add_explicit('name2', 'factory2') + self.assertEqual( + tweens.explicit, [('name', 'factory'), ('name2', 'factory2')] + ) + + def test_add_implicit(self): + tweens = self._makeOne() + tweens.add_implicit('name', 'factory') + tweens.add_implicit('name2', 'factory2') + self.assertEqual( + tweens.sorter.sorted(), + [('name2', 'factory2'), ('name', 'factory')], + ) + + def test___call___explicit(self): + tweens = self._makeOne() + + def factory1(handler, registry): + return handler + + def factory2(handler, registry): + return '123' + + tweens.explicit = [('name', factory1), ('name', factory2)] + self.assertEqual(tweens(None, None), '123') + + def test___call___implicit(self): + tweens = self._makeOne() + + def factory1(handler, registry): + return handler + + def factory2(handler, registry): + return '123' + + tweens.add_implicit('name2', factory2) + tweens.add_implicit('name1', factory1) + self.assertEqual(tweens(None, None), '123') + + def test_implicit_ordering_1(self): + tweens = self._makeOne() + tweens.add_implicit('name1', 'factory1') + tweens.add_implicit('name2', 'factory2') + self.assertEqual( + tweens.implicit(), [('name2', 'factory2'), ('name1', 'factory1')] + ) + + def test_implicit_ordering_2(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + tweens.add_implicit('name1', 'factory1') + tweens.add_implicit('name2', 'factory2', over=MAIN) + self.assertEqual( + tweens.implicit(), [('name1', 'factory1'), ('name2', 'factory2')] + ) + + def test_implicit_ordering_3(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('auth', 'auth_factory', under='browserid') + add('dbt', 'dbt_factory') + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', under='exceptionview') + add('exceptionview', 'excview_factory', over=MAIN) + self.assertEqual( + tweens.implicit(), + [ + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('dbt', 'dbt_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ], + ) + + def test_implicit_ordering_4(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('auth', 'auth_factory', under='browserid') + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', under='exceptionview') + add('dbt', 'dbt_factory') + self.assertEqual( + tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ], + ) + + def test_implicit_ordering_5(self): + from pyramid.tweens import MAIN, INGRESS + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('auth', 'auth_factory', under=INGRESS) + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory', under=INGRESS) + add('txnmgr', 'txnmgr_factory', under='exceptionview', over=MAIN) + add('dbt', 'dbt_factory') + self.assertEqual( + tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ], + ) + + def test_implicit_ordering_missing_over_partial(self): + from pyramid.exceptions import ConfigurationError + + tweens = self._makeOne() + add = tweens.add_implicit + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', under='browserid') + add('retry', 'retry_factory', over='txnmgr', under='exceptionview') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, tweens.implicit) + + def test_implicit_ordering_missing_under_partial(self): + from pyramid.exceptions import ConfigurationError + + tweens = self._makeOne() + add = tweens.add_implicit + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', under='txnmgr') + add('retry', 'retry_factory', over='dbt', under='exceptionview') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, tweens.implicit) + + def test_implicit_ordering_missing_over_and_under_partials(self): + from pyramid.exceptions import ConfigurationError + + tweens = self._makeOne() + add = tweens.add_implicit + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', under='browserid') + add('retry', 'retry_factory', over='foo', under='txnmgr') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, tweens.implicit) + + def test_implicit_ordering_missing_over_partial_with_fallback(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('auth', 'auth_factory', under='browserid') + add( + 'retry', + 'retry_factory', + over=('txnmgr', MAIN), + under='exceptionview', + ) + add('browserid', 'browserid_factory') + add('dbt', 'dbt_factory') + self.assertEqual( + tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_implicit_ordering_missing_under_partial_with_fallback(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('auth', 'auth_factory', under=('txnmgr', 'browserid')) + add('retry', 'retry_factory', under='exceptionview') + add('browserid', 'browserid_factory') + add('dbt', 'dbt_factory') + self.assertEqual( + tweens.implicit(), + [ + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_implicit_ordering_with_partial_fallbacks(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=('wontbethere', MAIN)) + add('retry', 'retry_factory', under='exceptionview') + add('browserid', 'browserid_factory', over=('wont2', 'exceptionview')) + self.assertEqual( + tweens.implicit(), + [ + ('browserid', 'browserid_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_implicit_ordering_with_multiple_matching_fallbacks(self): + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('retry', 'retry_factory', under='exceptionview') + add('browserid', 'browserid_factory', over=('retry', 'exceptionview')) + self.assertEqual( + tweens.implicit(), + [ + ('browserid', 'browserid_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_implicit_ordering_with_missing_fallbacks(self): + from pyramid.exceptions import ConfigurationError + from pyramid.tweens import MAIN + + tweens = self._makeOne() + add = tweens.add_implicit + add('exceptionview', 'excview_factory', over=MAIN) + add('retry', 'retry_factory', under='exceptionview') + add('browserid', 'browserid_factory', over=('txnmgr', 'auth')) + self.assertRaises(ConfigurationError, tweens.implicit) + + def test_implicit_ordering_conflict_direct(self): + from pyramid.exceptions import CyclicDependencyError + + tweens = self._makeOne() + add = tweens.add_implicit + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', over='browserid', under='browserid') + self.assertRaises(CyclicDependencyError, tweens.implicit) + + def test_implicit_ordering_conflict_indirect(self): + from pyramid.exceptions import CyclicDependencyError + + tweens = self._makeOne() + add = tweens.add_implicit + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', over='browserid') + add('dbt', 'dbt_factory', under='browserid', over='auth') + self.assertRaises(CyclicDependencyError, tweens.implicit) diff --git a/tests/test_config/test_views.py b/tests/test_config/test_views.py new file mode 100644 index 000000000..b72b9b36a --- /dev/null +++ b/tests/test_config/test_views.py @@ -0,0 +1,4360 @@ +import os +import unittest +from zope.interface import implementer + +from pyramid import testing +from pyramid.compat import im_func, text_ +from pyramid.exceptions import ConfigurationError +from pyramid.exceptions import ConfigurationExecutionError +from pyramid.exceptions import ConfigurationConflictError +from pyramid.interfaces import IResponse, IRequest, IMultiView + +from . import IDummy +from . import dummy_view + + +class TestViewsConfigurationMixin(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + config.set_default_csrf_options(require_csrf=False) + return config + + def _getViewCallable( + self, + config, + ctx_iface=None, + exc_iface=None, + request_iface=None, + name='', + ): + 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 exc_iface: + classifier = IExceptionViewClassifier + ctx_iface = exc_iface + else: + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), + IView, + name=name, + default=None, + ) + + def _registerRenderer(self, config, name='.txt'): + from pyramid.interfaces import IRendererFactory + + class Renderer: + def __init__(self, info): + self.__class__.info = info + + def __call__(self, *arg): + return b'Hello!' + + config.registry.registerUtility(Renderer, IRendererFactory, name=name) + return Renderer + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def _assertNotFound(self, wrapper, *arg): + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, wrapper, *arg) + + def _getRouteRequestIface(self, config, name): + from pyramid.interfaces import IRouteRequest + + iface = config.registry.getUtility(IRouteRequest, name) + return iface + + def _assertRoute(self, config, name, path, num_predicates=0): + from pyramid.interfaces import IRoutesMapper + + mapper = config.registry.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.path, path) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def test_add_view_view_callable_None_no_renderer(self): + config = self._makeOne(autocommit=True) + self.assertRaises(ConfigurationError, config.add_view) + + def test_add_view_with_request_type_and_route_name(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + self.assertRaises( + ConfigurationError, + config.add_view, + view, + '', + None, + None, + True, + True, + ) + + def test_add_view_with_request_type(self): + from pyramid.renderers import null_renderer + from zope.interface import directlyProvides + from pyramid.interfaces import IRequest + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, + request_type='pyramid.interfaces.IRequest', + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config) + request = DummyRequest() + self._assertNotFound(wrapper, None, request) + directlyProvides(request, IRequest) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_view_callable_None_with_renderer(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config, name='dummy') + config.add_view(renderer='dummy') + view = self._getViewCallable(config) + self.assertTrue(b'Hello!' in view(None, None).body) + + def test_add_view_with_tmpl_renderer_factory_introspector_missing(self): + config = self._makeOne(autocommit=True) + config.introspection = False + config.introspector = None + config.add_view(renderer='dummy.pt') + view = self._getViewCallable(config) + self.assertRaises(ValueError, view, None, None) + + def test_add_view_with_tmpl_renderer_factory_no_renderer_factory(self): + config = self._makeOne(autocommit=True) + introspector = DummyIntrospector() + config.introspector = introspector + config.add_view(renderer='dummy.pt') + self.assertFalse( + ('renderer factories', '.pt') in introspector.related[-1] + ) + view = self._getViewCallable(config) + self.assertRaises(ValueError, view, None, None) + + def test_add_view_with_tmpl_renderer_factory_with_renderer_factory(self): + config = self._makeOne(autocommit=True) + introspector = DummyIntrospector(True) + config.introspector = introspector + + def dummy_factory(helper): + return lambda val, system_vals: 'Hello!' + + config.add_renderer('.pt', dummy_factory) + config.add_view(renderer='dummy.pt') + self.assertTrue( + ('renderer factories', '.pt') in introspector.related[-1] + ) + view = self._getViewCallable(config) + self.assertTrue(b'Hello!' in view(None, None).body) + + def test_add_view_wrapped_view_is_decorated(self): + def view(request): # request-only wrapper + """ """ + + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper.__module__, view.__module__) + self.assertEqual(wrapper.__name__, view.__name__) + self.assertEqual(wrapper.__doc__, view.__doc__) + self.assertEqual( + wrapper.__discriminator__(None, None).resolve()[0], 'view' + ) + + def test_add_view_view_callable_dottedname(self): + from pyramid.renderers import null_renderer + + config = self._makeOne(autocommit=True) + config.add_view( + view='tests.test_config.dummy_view', renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_function_callable(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_function_callable_requestonly(self): + from pyramid.renderers import null_renderer + + def view(request): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_name(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, name='abc', renderer=null_renderer) + wrapper = self._getViewCallable(config, name='abc') + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_name_unicode(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + config.add_view(view=view, name=name, renderer=null_renderer) + wrapper = self._getViewCallable(config, name=name) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_decorator(self): + from pyramid.renderers import null_renderer + + def view(request): + """ ABC """ + return 'OK' + + def view_wrapper(fn): + def inner(context, request): + return fn(context, request) + + return inner + + config = self._makeOne(autocommit=True) + config.add_view( + view=view, decorator=view_wrapper, renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + self.assertFalse(wrapper is view) + self.assertEqual(wrapper.__doc__, view.__doc__) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_decorator_tuple(self): + from pyramid.renderers import null_renderer + + def view(request): + """ ABC """ + return 'OK' + + def view_wrapper1(fn): + def inner(context, request): + return 'wrapped1' + fn(context, request) + + return inner + + def view_wrapper2(fn): + def inner(context, request): + return 'wrapped2' + fn(context, request) + + return inner + + config = self._makeOne(autocommit=True) + config.add_view( + view=view, + decorator=(view_wrapper2, view_wrapper1), + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config) + self.assertFalse(wrapper is view) + self.assertEqual(wrapper.__doc__, view.__doc__) + result = wrapper(None, None) + self.assertEqual(result, 'wrapped2wrapped1OK') + + def test_add_view_with_http_cache(self): + import datetime + from pyramid.response import Response + + response = Response('OK') + + def view(request): + """ ABC """ + return response + + config = self._makeOne(autocommit=True) + config.add_view(view=view, http_cache=(86400, {'public': True})) + wrapper = self._getViewCallable(config) + self.assertFalse(wrapper is view) + self.assertEqual(wrapper.__doc__, view.__doc__) + request = testing.DummyRequest() + when = datetime.datetime.utcnow() + datetime.timedelta(days=1) + result = wrapper(None, request) + self.assertEqual(result, response) + headers = dict(response.headerlist) + self.assertEqual(headers['Cache-Control'], 'max-age=86400, public') + expires = parse_httpdate(headers['Expires']) + assert_similar_datetime(expires, when) + + def test_add_view_as_instance(self): + from pyramid.renderers import null_renderer + + class AView: + def __call__(self, context, request): + """ """ + return 'OK' + + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instancemethod(self): + from pyramid.renderers import null_renderer + + class View: + def index(self, context, request): + return 'OK' + + view = View() + config = self._makeOne(autocommit=True) + config.add_view(view=view.index, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instancemethod_requestonly(self): + from pyramid.renderers import null_renderer + + class View: + def index(self, request): + return 'OK' + + view = View() + config = self._makeOne(autocommit=True) + config.add_view(view=view.index, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance_requestonly(self): + from pyramid.renderers import null_renderer + + class AView: + def __call__(self, request): + """ """ + return 'OK' + + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class(self): + from pyramid.renderers import null_renderer + + class view: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + self.assertEqual(request.__view__.__class__, view) + + def test_add_view_as_oldstyle_class_requestonly(self): + from pyramid.renderers import null_renderer + + class view: + def __init__(self, request): + self.request = request + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + self.assertEqual(request.__view__.__class__, view) + + def test_add_view_context_as_class(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + + view = lambda *arg: 'OK' + + class Foo: + pass + + config = self._makeOne(autocommit=True) + config.add_view(context=Foo, view=view, renderer=null_renderer) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_iface(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(context=IDummy, view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_dottedname(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + context='tests.test_config.IDummy', + view=view, + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for__as_dottedname(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + for_='tests.test_config.IDummy', view=view, renderer=null_renderer + ) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_class(self): + # ``for_`` is older spelling for ``context`` + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + + view = lambda *arg: 'OK' + + class Foo: + pass + + config = self._makeOne(autocommit=True) + config.add_view(for_=Foo, view=view, renderer=null_renderer) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_iface(self): + # ``for_`` is older spelling for ``context`` + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(for_=IDummy, view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_trumps_for(self): + # ``for_`` is older spelling for ``context`` + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + + class Foo: + pass + + config.add_view( + context=IDummy, for_=Foo, view=view, renderer=null_renderer + ) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_register_secured_view(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IViewClassifier + + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = config.registry.adapters.lookup( + (IViewClassifier, IRequest, Interface), + ISecuredView, + name='', + default=None, + ) + self.assertEqual(wrapper, view) + + def test_add_view_exception_register_secured_view(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view( + view=view, context=RuntimeError, renderer=null_renderer + ) + wrapper = config.registry.adapters.lookup( + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + default=None, + ) + self.assertEqual(wrapper, view) + + def test_add_view_same_phash_overrides_existing_single_view(self): + from pyramid.renderers import null_renderer + from hashlib import md5 + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + phash = md5() + phash.update(b'xhr = True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + + def newview(context, request): + return 'OK' + + config.add_view(view=newview, xhr=True, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_same_phash_overrides_existing_single_view(self): + from pyramid.renderers import null_renderer + from hashlib import md5 + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + + phash = md5() + phash.update(b'xhr = True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + + def newview(context, request): + return 'OK' + + config.add_view( + view=newview, + xhr=True, + context=RuntimeError, + renderer=null_renderer, + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_no_phash(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + + def newview(context, request): + return 'OK' + + config.add_view(view=newview, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_no_phash(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + + def newview(context, request): + return 'OK' + + config.add_view( + view=newview, context=RuntimeError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_default_phash(self): + from pyramid.config.predicates import DEFAULT_PHASH + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + + def newview(context, request): + return 'OK' + + config.add_view(view=newview, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_default_phash(self): + from pyramid.config.predicates import DEFAULT_PHASH + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + + def newview(context, request): + return 'OK' + + config.add_view( + view=newview, context=RuntimeError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertFalse(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_existing_view(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_view(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.add_view( + view=view, context=RuntimeError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_multiview_replaces_existing_securedview(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), ISecuredView, name='' + ) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_securedview(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, + name='', + ) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, + name='', + ) + config.add_view( + view=view, context=RuntimeError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_accept_multiview_replaces_existing_view(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + + def view(context, request): + return 'OK' + + def view2(context, request): + return 'OK2' + + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + config.add_view(view=view2, accept='text/html', renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_mixed_case_replaces_existing_view(self): + from pyramid.renderers import null_renderer + + def view(context, request): + return 'OK' + + def view2(context, request): # pragma: no cover + return 'OK2' + + def view3(context, request): + return 'OK3' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + config.add_view(view=view2, accept='text/html', renderer=null_renderer) + config.add_view(view=view3, accept='text/HTML', renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.media_views.items()), 1) + self.assertFalse('text/HTML' in wrapper.media_views) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK3') + + def test_add_views_with_accept_multiview_replaces_existing(self): + from pyramid.renderers import null_renderer + + def view(context, request): + return 'OK' + + def view2(context, request): # pragma: no cover + return 'OK2' + + def view3(context, request): + return 'OK3' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + config.add_view(view=view2, accept='text/html', renderer=null_renderer) + config.add_view(view=view3, accept='text/html', renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertEqual(len(wrapper.media_views['text/html']), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK3') + + def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + def view(context, request): + return 'OK' + + def view2(context, request): + return 'OK2' + + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.add_view( + view=view2, + accept='text/html', + context=RuntimeError, + renderer=null_renderer, + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_multiview_replaces_existing_view_with___accept__(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + + def view(context, request): + return 'OK' + + def view2(context, request): + return 'OK2' + + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + config.add_view(view=view2, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + def view(context, request): + return 'OK' + + def view2(context, request): + return 'OK2' + + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, + name='', + ) + config.add_view( + view=view2, context=RuntimeError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_multiview(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + + view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IMultiView, name='' + ) + view2 = lambda *arg: 'OK2' + config.add_view(view=view2, renderer=null_renderer) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + self.assertEqual( + [(x[0], x[2]) for x in wrapper.views], [(view2, None)] + ) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_exc_multiview_replaces_multiviews(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + hot_view = DummyMultiView() + exc_view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + hot_view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, + name='', + ) + config.registry.registerAdapter( + exc_view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, + name='', + ) + view2 = lambda *arg: 'OK2' + config.add_view( + view=view2, context=RuntimeError, renderer=null_renderer + ) + hot_wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(hot_wrapper)) + self.assertEqual( + [(x[0], x[2]) for x in hot_wrapper.views], [(view2, None)] + ) + self.assertEqual(hot_wrapper(None, None), 'OK1') + + exc_wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(exc_wrapper)) + self.assertEqual( + [(x[0], x[2]) for x in exc_wrapper.views], [(view2, None)] + ) + self.assertEqual(exc_wrapper(None, None), 'OK1') + + def test_add_view_exc_multiview_replaces_only_exc_multiview(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + hot_view = DummyMultiView() + exc_view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + hot_view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, + name='', + ) + config.registry.registerAdapter( + exc_view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, + name='', + ) + view2 = lambda *arg: 'OK2' + config.add_view( + view=view2, + context=RuntimeError, + exception_only=True, + renderer=null_renderer, + ) + hot_wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(hot_wrapper)) + self.assertEqual(len(hot_wrapper.views), 0) + self.assertEqual(hot_wrapper(None, None), 'OK1') + + exc_wrapper = self._getViewCallable( + config, exc_iface=implementedBy(RuntimeError) + ) + self.assertTrue(IMultiView.providedBy(exc_wrapper)) + self.assertEqual( + [(x[0], x[2]) for x in exc_wrapper.views], [(view2, None)] + ) + self.assertEqual(exc_wrapper(None, None), 'OK1') + + def test_add_view_multiview_context_superclass_then_subclass(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + + class ISuper(Interface): + pass + + class ISub(ISuper): + pass + + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, ISuper), IView, name='' + ) + config.add_view(view=view2, for_=ISub, renderer=null_renderer) + wrapper = self._getViewCallable( + config, ctx_iface=ISuper, request_iface=IRequest + ) + self.assertFalse(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + wrapper = self._getViewCallable( + config, ctx_iface=ISub, request_iface=IRequest + ) + self.assertFalse(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK2') + + def test_add_view_multiview_exception_superclass_then_subclass(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + class Super(Exception): + pass + + class Sub(Super): + pass + + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Super), IView, name='' + ) + config.registry.registerAdapter( + view, (IExceptionViewClassifier, IRequest, Super), IView, name='' + ) + config.add_view(view=view2, for_=Sub, renderer=null_renderer) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(Super), request_iface=IRequest + ) + wrapper_exc_view = self._getViewCallable( + config, exc_iface=implementedBy(Super), request_iface=IRequest + ) + self.assertEqual(wrapper_exc_view, wrapper) + self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(Sub), request_iface=IRequest + ) + wrapper_exc_view = self._getViewCallable( + config, exc_iface=implementedBy(Sub), request_iface=IRequest + ) + self.assertEqual(wrapper_exc_view, wrapper) + self.assertFalse(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK2') + + def test_add_view_multiview_call_ordering(self): + from pyramid.renderers import null_renderer as nr + from zope.interface import directlyProvides + + def view1(context, request): + return 'view1' + + def view2(context, request): + return 'view2' + + def view3(context, request): + return 'view3' + + def view4(context, request): + return 'view4' + + def view5(context, request): + return 'view5' + + def view6(context, request): + return 'view6' + + def view7(context, request): + return 'view7' + + def view8(context, request): + return 'view8' + + config = self._makeOne(autocommit=True) + config.add_view(view=view1, renderer=nr) + config.add_view(view=view2, request_method='POST', renderer=nr) + config.add_view(view=view3, request_param='param', renderer=nr) + config.add_view(view=view4, containment=IDummy, renderer=nr) + config.add_view( + view=view5, + request_method='POST', + request_param='param', + renderer=nr, + ) + config.add_view( + view=view6, request_method='POST', containment=IDummy, renderer=nr + ) + config.add_view( + view=view7, request_param='param', containment=IDummy, renderer=nr + ) + config.add_view( + view=view8, + request_method='POST', + request_param='param', + containment=IDummy, + renderer=nr, + ) + + wrapper = self._getViewCallable(config) + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view1') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view2') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {'param': '1'} + request.method = 'GET' + self.assertEqual(wrapper(ctx, request), 'view3') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view4') + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param': '1'} + self.assertEqual(wrapper(ctx, request), 'view5') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view6') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {'param': '1'} + self.assertEqual(wrapper(ctx, request), 'view7') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param': '1'} + self.assertEqual(wrapper(ctx, request), 'view8') + + def test_view_with_most_specific_predicate(self): + from pyramid.renderers import null_renderer as nr + from pyramid.router import Router + + class OtherBase(object): + pass + + class Int1(object): + pass + + class Int2(object): + pass + + class Resource(OtherBase, Int1, Int2): + def __init__(self, request): + pass + + def unknown(context, request): # pragma: no cover + return 'unknown' + + def view(context, request): + return 'hello' + + config = self._makeOne(autocommit=True) + config.add_route('root', '/', factory=Resource) + config.add_view(unknown, route_name='root', renderer=nr) + config.add_view( + view, + renderer=nr, + route_name='root', + context=Int1, + request_method='GET', + ) + config.add_view( + view=view, + renderer=nr, + route_name='root', + context=Int2, + request_method='POST', + ) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {} + router = Router(config.registry) + response = router.handle_request(request) + self.assertEqual(response, 'hello') + + def test_view_with_most_specific_predicate_with_mismatch(self): + from pyramid.renderers import null_renderer as nr + from pyramid.router import Router + + class OtherBase(object): + pass + + class Int1(object): + pass + + class Int2(object): + pass + + class Resource(OtherBase, Int1, Int2): + def __init__(self, request): + pass + + def unknown(context, request): # pragma: no cover + return 'unknown' + + def view(context, request): + return 'hello' + + config = self._makeOne(autocommit=True) + config.add_route('root', '/', factory=Resource) + + config.add_view( + unknown, + route_name='root', + renderer=nr, + request_method=('POST',), + xhr=True, + ) + + config.add_view( + view, + renderer=nr, + route_name='root', + context=Int1, + request_method='GET', + ) + config.add_view( + view=view, + renderer=nr, + route_name='root', + context=Int2, + request_method='POST', + ) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {} + router = Router(config.registry) + response = router.handle_request(request) + self.assertEqual(response, 'hello') + + def test_add_view_multiview___discriminator__(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + + class IFoo(Interface): + pass + + class IBar(Interface): + pass + + @implementer(IFoo) + class Foo(object): + pass + + @implementer(IBar) + class Bar(object): + pass + + foo = Foo() + bar = Bar() + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='' + ) + config.add_view(view=view, renderer=null_renderer, containment=IFoo) + config.add_view(view=view, renderer=null_renderer, containment=IBar) + wrapper = self._getViewCallable(config) + self.assertTrue(IMultiView.providedBy(wrapper)) + request = self._makeRequest(config) + self.assertNotEqual( + wrapper.__discriminator__(foo, request), + wrapper.__discriminator__(bar, request), + ) + + def test_add_view_with_template_renderer(self): + from tests import test_config + from pyramid.interfaces import ISettings + + class view(object): + def __init__(self, context, request): + self.request = request + self.context = context + + def __call__(self): + return {'a': '1'} + + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'tests.test_config:files/minimal.txt' + config.introspection = False + config.add_view(view=view, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, b'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, test_config) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_default_renderer(self): + class view(object): + def __init__(self, context, request): + self.request = request + self.context = context + + def __call__(self): + return {'a': '1'} + + config = self._makeOne(autocommit=True) + + class moo(object): + def __init__(self, *arg, **kw): + pass + + def __call__(self, *arg, **kw): + return b'moo' + + config.add_renderer(None, moo) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, b'moo') + + def test_add_view_with_template_renderer_no_callable(self): + from tests import test_config + from pyramid.interfaces import ISettings + + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'tests.test_config:files/minimal.txt' + config.introspection = False + config.add_view(view=None, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, b'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, test_config) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_request_type_as_iface(self): + from pyramid.renderers import null_renderer + from zope.interface import directlyProvides + + def view(context, request): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(request_type=IDummy, view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config, None) + request = self._makeRequest(config) + directlyProvides(request, IDummy) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_with_request_type_as_noniface(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises( + ConfigurationError, config.add_view, view, '', None, None, object + ) + + def test_add_view_with_route_name(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view(view=view, route_name='foo', renderer=null_renderer) + request_iface = self._getRouteRequestIface(config, 'foo') + self.assertNotEqual(request_iface, None) + wrapper = self._getViewCallable(config, request_iface=request_iface) + self.assertNotEqual(wrapper, None) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_nonexistant_route_name(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, route_name='foo', renderer=null_renderer) + self.assertRaises(ConfigurationExecutionError, config.commit) + + def test_add_view_with_route_name_exception(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + view=view, + route_name='foo', + context=RuntimeError, + renderer=null_renderer, + ) + request_iface = self._getRouteRequestIface(config, 'foo') + wrapper_exc_view = self._getViewCallable( + config, + exc_iface=implementedBy(RuntimeError), + request_iface=request_iface, + ) + self.assertNotEqual(wrapper_exc_view, None) + wrapper = self._getViewCallable( + config, + ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface, + ) + self.assertEqual(wrapper_exc_view, wrapper) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + + def test_add_view_with_request_method_true(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, request_method='POST', renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_method_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='POST') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_method_sequence_true(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, request_method=('POST', 'GET'), renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_method_sequence_conflict(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view( + view=view, request_method=('POST', 'GET'), renderer=null_renderer + ) + config.add_view( + view=view, request_method=('GET', 'POST'), renderer=null_renderer + ) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_view_with_request_method_sequence_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method=('POST', 'HEAD')) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_method_get_implies_head(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, request_method='GET', renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'HEAD' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_noval_true(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc': ''} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_noval_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_param_val_true(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, request_param='abc=123', renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc': '123'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_val_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc=123') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc': ''} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_xhr_true(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True, renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_xhr_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = False + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_badregex(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view, header='Host:a\\') + self.assertRaises(ConfigurationError, config.commit) + + def test_add_view_with_header_noval_match(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host': 'whatever'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_noval_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NotHost': 'whatever'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_match(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host': '1'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_val_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host': 'abc'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_missing(self): + from pyramid.httpexceptions import HTTPNotFound + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NoHost': '1'} + self.assertRaises(HTTPNotFound, wrapper, None, request) + + def test_add_view_with_accept_match(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = DummyAccept('text/xml') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_accept_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = DummyAccept('text/html') + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_wildcard_accept_raises(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + self.assertRaises( + ValueError, lambda: config.add_view(view=view, accept='text/*') + ) + + def test_add_view_with_containment_true(self): + from pyramid.renderers import null_renderer + from zope.interface import directlyProvides + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy, renderer=null_renderer) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_containment_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy) + wrapper = self._getViewCallable(config) + context = DummyContext() + self._assertNotFound(wrapper, context, None) + + def test_add_view_with_containment_dottedname(self): + from pyramid.renderers import null_renderer + from zope.interface import directlyProvides + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, + containment='tests.test_config.IDummy', + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_path_info_badregex(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view, path_info='\\') + self.assertRaises(ConfigurationError, config.commit) + + def test_add_view_with_path_info_match(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo', renderer=null_renderer) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.upath_info = text_(b'/foo') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_path_info_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.upath_info = text_('/') + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_check_csrf_predicates_match(self): + import warnings + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + config.add_view(view=view, check_csrf=True, renderer=null_renderer) + self.assertEqual(len(w), 1) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = "POST" + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'csrf_token': 'foo'} + request.headers = {} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_custom_predicates_match(self): + import warnings + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + + def pred1(context, request): + return True + + def pred2(context, request): + return True + + predicates = (pred1, pred2) + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + config.add_view( + view=view, custom_predicates=predicates, renderer=null_renderer + ) + self.assertEqual(len(w), 1) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_custom_predicates_nomatch(self): + import warnings + + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + + def pred1(context, request): + return True + + def pred2(context, request): + return False + + predicates = (pred1, pred2) + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + config.add_view(view=view, custom_predicates=predicates) + self.assertEqual(len(w), 1) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self._assertNotFound(wrapper, None, request) + + def test_add_view_custom_predicate_bests_standard_predicate(self): + import warnings + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + + def pred1(context, request): + return True + + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + config.add_view( + view=view, custom_predicates=(pred1,), renderer=null_renderer + ) + config.add_view( + view=view2, request_method='GET', renderer=null_renderer + ) + self.assertEqual(len(w), 1) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): + from pyramid.renderers import null_renderer + + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, request_method='GET', xhr=True, renderer=null_renderer + ) + config.add_view( + view=view2, request_method='GET', renderer=null_renderer + ) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_same_predicates(self): + view2 = lambda *arg: 'second' + view1 = lambda *arg: 'first' + config = self._makeOne() + config.add_view(view=view1) + config.add_view(view=view2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_view_with_csrf_param(self): + from pyramid.renderers import null_renderer + + def view(request): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view, require_csrf='st', renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + request.scheme = "http" + request.method = 'POST' + request.POST = {'st': 'foo'} + request.headers = {} + request.session = DummySession({'csrf_token': 'foo'}) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_csrf_header(self): + from pyramid.renderers import null_renderer + + def view(request): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view, require_csrf=True, renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + request.scheme = "http" + request.method = 'POST' + request.POST = {} + request.headers = {'X-CSRF-Token': 'foo'} + request.session = DummySession({'csrf_token': 'foo'}) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_missing_csrf_header(self): + from pyramid.exceptions import BadCSRFToken + from pyramid.renderers import null_renderer + + def view(request): # pragma: no cover + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view, require_csrf=True, renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + request.scheme = "http" + request.method = 'POST' + request.POST = {} + request.headers = {} + request.session = DummySession({'csrf_token': 'foo'}) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_add_view_with_permission(self): + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + outerself = self + + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + + policy = DummyPolicy() + config = self._makeOne( + authorization_policy=policy, + authentication_policy=policy, + autocommit=True, + ) + config.add_view(view=view1, permission='view', renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_default_permission_no_explicit_permission(self): + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + outerself = self + + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + + policy = DummyPolicy() + config = self._makeOne( + authorization_policy=policy, + authentication_policy=policy, + default_permission='view', + autocommit=True, + ) + config.add_view(view=view1, renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_no_default_permission_no_explicit_permission(self): + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + + class DummyPolicy(object): + pass # wont be called + + policy = DummyPolicy() + config = self._makeOne( + authorization_policy=policy, + authentication_policy=policy, + autocommit=True, + ) + config.add_view(view=view1, renderer=null_renderer) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_mapper(self): + from pyramid.renderers import null_renderer + + class Mapper(object): + def __init__(self, **kw): + self.__class__.kw = kw + + def __call__(self, view): + return view + + config = self._makeOne(autocommit=True) + + def view(context, request): + return 'OK' + + config.add_view(view=view, mapper=Mapper, renderer=null_renderer) + view = self._getViewCallable(config) + self.assertEqual(view(None, None), 'OK') + self.assertEqual(Mapper.kw['mapper'], Mapper) + + def test_add_view_with_view_defaults(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_view_with_view_defaults_viewname_is_dottedname_kwarg(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + + config = self._makeOne(autocommit=True) + config.add_view( + view='tests.test_config.test_views.DummyViewDefaultsClass', + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_view_with_view_defaults_viewname_is_dottedname_nonkwarg(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + + config = self._makeOne(autocommit=True) + config.add_view( + 'tests.test_config.test_views.DummyViewDefaultsClass', + renderer=null_renderer, + ) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_view_with_view_config_and_view_defaults_doesnt_conflict(self): + from pyramid.renderers import null_renderer + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + class view2(object): + __view_defaults__ = {'containment': 'tests.test_config.IFactory'} + + config = self._makeOne(autocommit=False) + config.add_view(view=view, renderer=null_renderer) + config.add_view(view=view2, renderer=null_renderer) + config.commit() # does not raise + + def test_add_view_with_view_config_and_view_defaults_conflicts(self): + from pyramid.renderers import null_renderer + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + class view2(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + config = self._makeOne(autocommit=False) + config.add_view(view=view, renderer=null_renderer) + config.add_view(view=view2, renderer=null_renderer) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_view_class_method_no_attr(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import ConfigurationError + + config = self._makeOne(autocommit=True) + + class DummyViewClass(object): + def run(self): # pragma: no cover + pass + + def configure_view(): + config.add_view(view=DummyViewClass.run, renderer=null_renderer) + + self.assertRaises(ConfigurationError, configure_view) + + def test_add_view_exception_only_no_regular_view(self): + from zope.interface import implementedBy + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view1, + context=Exception, + exception_only=True, + renderer=null_renderer, + ) + view = self._getViewCallable( + config, ctx_iface=implementedBy(Exception) + ) + self.assertTrue(view is None) + + def test_add_view_exception_only(self): + from zope.interface import implementedBy + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view1, + context=Exception, + exception_only=True, + renderer=null_renderer, + ) + view = self._getViewCallable( + config, exc_iface=implementedBy(Exception) + ) + self.assertEqual(view1, view) + + def test_add_view_exception_only_misconfiguration(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + + class NotAnException(object): + pass + + self.assertRaises( + ConfigurationError, + config.add_view, + view, + context=NotAnException, + exception_only=True, + ) + + def test_add_exception_view(self): + from zope.interface import implementedBy + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_exception_view(view=view1, renderer=null_renderer) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(Exception) + ) + context = Exception() + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + + def test_add_exception_view_with_subclass(self): + from zope.interface import implementedBy + from pyramid.renderers import null_renderer + + view1 = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_exception_view( + view=view1, context=ValueError, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(ValueError) + ) + context = ValueError() + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + + def test_add_exception_view_disallows_name(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, + config.add_exception_view, + context=Exception(), + name='foo', + ) + + def test_add_exception_view_disallows_permission(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, + config.add_exception_view, + context=Exception(), + permission='foo', + ) + + def test_add_exception_view_disallows_require_csrf(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, + config.add_exception_view, + context=Exception(), + require_csrf=True, + ) + + def test_add_exception_view_disallows_for_(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, + config.add_exception_view, + context=Exception(), + for_='foo', + ) + + def test_add_exception_view_disallows_exception_only(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, + config.add_exception_view, + context=Exception(), + exception_only=True, + ) + + def test_add_exception_view_with_view_defaults(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + from zope.interface import implementedBy + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_exception_view( + view=view, context=Exception, renderer=null_renderer + ) + wrapper = self._getViewCallable( + config, exc_iface=implementedBy(Exception) + ) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_derive_view_function(self): + from pyramid.renderers import null_renderer + + def view(request): + return 'OK' + + config = self._makeOne() + result = config.derive_view(view, renderer=null_renderer) + self.assertFalse(result is view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_dottedname(self): + from pyramid.renderers import null_renderer + + config = self._makeOne() + result = config.derive_view( + 'tests.test_config.dummy_view', renderer=null_renderer + ) + self.assertFalse(result is dummy_view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_with_default_renderer_no_explicit_renderer(self): + config = self._makeOne() + + class moo(object): + def __init__(self, view): + pass + + def __call__(self, *arg, **kw): + return 'moo' + + config.add_renderer(None, moo) + config.commit() + + def view(request): + return 'OK' + + result = config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(result(None, None).body, b'moo') + + def test_derive_view_with_default_renderer_with_explicit_renderer(self): + class moo(object): + pass + + class foo(object): + def __init__(self, view): + pass + + def __call__(self, *arg, **kw): + return b'foo' + + def view(request): + return 'OK' + + config = self._makeOne() + config.add_renderer(None, moo) + config.add_renderer('foo', foo) + config.commit() + result = config.derive_view(view, renderer='foo') + self.assertFalse(result is view) + request = self._makeRequest(config) + self.assertEqual(result(None, request).body, b'foo') + + def test_add_static_view_here_no_utility_registered(self): + from pyramid.renderers import null_renderer + from zope.interface import Interface + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + + config = self._makeOne(autocommit=True) + config.add_static_view('static', 'files', renderer=null_renderer) + request_type = self._getRouteRequestIface(config, '__static/') + self._assertRoute(config, '__static/', 'static/*subpath') + wrapped = config.registry.adapters.lookup( + (IViewClassifier, request_type, Interface), IView, name='' + ) + from pyramid.request import Request + + request = Request.blank('/static/minimal.txt') + request.subpath = ('minimal.txt',) + result = wrapped(None, request) + self.assertEqual(result.status, '200 OK') + self.assertTrue(result.body.startswith(b'<div')) + + def test_add_static_view_package_relative(self): + from pyramid.interfaces import IStaticURLInfo + + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'tests.test_config:files') + self.assertEqual( + info.added, [(config, 'static', 'tests.test_config:files', {})] + ) + + def test_add_static_view_package_here_relative(self): + from pyramid.interfaces import IStaticURLInfo + + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'files') + self.assertEqual( + info.added, [(config, 'static', 'tests.test_config:files', {})] + ) + + def test_add_static_view_absolute(self): + import os + from pyramid.interfaces import IStaticURLInfo + + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + here = os.path.dirname(__file__) + static_path = os.path.join(here, 'files') + config.add_static_view('static', static_path) + self.assertEqual(info.added, [(config, 'static', static_path, {})]) + + def test_add_forbidden_view(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPForbidden + + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_forbidden_view(view, renderer=null_renderer) + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPForbidden), + request_iface=IRequest, + ) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_add_forbidden_view_no_view_argument(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPForbidden + + config = self._makeOne(autocommit=True) + config.setup_registry() + config.add_forbidden_view() + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPForbidden), + request_iface=IRequest, + ) + context = HTTPForbidden() + result = view(context, request) + self.assertEqual(result, context) + + def test_add_forbidden_view_allows_other_predicates(self): + from pyramid.renderers import null_renderer + + config = self._makeOne(autocommit=True) + # doesnt blow up + config.add_view_predicate('dummy', DummyPredicate) + config.add_forbidden_view(renderer=null_renderer, dummy='abc') + + def test_add_forbidden_view_disallows_name(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_forbidden_view, name='foo' + ) + + def test_add_forbidden_view_disallows_permission(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_forbidden_view, permission='foo' + ) + + def test_add_forbidden_view_disallows_require_csrf(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_forbidden_view, require_csrf=True + ) + + def test_add_forbidden_view_disallows_context(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_forbidden_view, context='foo' + ) + + def test_add_forbidden_view_disallows_for_(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_forbidden_view, for_='foo' + ) + + def test_add_forbidden_view_with_view_defaults(self): + from pyramid.interfaces import IRequest + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from pyramid.httpexceptions import HTTPForbidden + from zope.interface import directlyProvides + from zope.interface import implementedBy + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_forbidden_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPForbidden), + request_iface=IRequest, + ) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_notfound_view(self): + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPNotFound + + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.add_notfound_view(view, renderer=null_renderer) + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + result = view(None, request) + self.assertEqual(result, (None, request)) + + def test_add_notfound_view_no_view_argument(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPNotFound + + config = self._makeOne(autocommit=True) + config.setup_registry() + config.add_notfound_view() + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + context = HTTPNotFound() + result = view(context, request) + self.assertEqual(result, context) + + def test_add_notfound_view_allows_other_predicates(self): + from pyramid.renderers import null_renderer + + config = self._makeOne(autocommit=True) + # doesnt blow up + config.add_view_predicate('dummy', DummyPredicate) + config.add_notfound_view(renderer=null_renderer, dummy='abc') + + def test_add_notfound_view_disallows_name(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_notfound_view, name='foo' + ) + + def test_add_notfound_view_disallows_permission(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_notfound_view, permission='foo' + ) + + def test_add_notfound_view_disallows_require_csrf(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_notfound_view, require_csrf=True + ) + + def test_add_notfound_view_disallows_context(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_notfound_view, context='foo' + ) + + def test_add_notfound_view_disallows_for_(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_notfound_view, for_='foo' + ) + + def test_add_notfound_view_append_slash(self): + from pyramid.response import Response + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPTemporaryRedirect, HTTPNotFound + + config = self._makeOne(autocommit=True) + config.add_route('foo', '/foo/') + + def view(request): # pragma: no cover + return Response('OK') + + config.add_notfound_view( + view, renderer=null_renderer, append_slash=True + ) + request = self._makeRequest(config) + request.environ['PATH_INFO'] = '/foo' + request.query_string = 'a=1&b=2' + request.path = '/scriptname/foo' + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + result = view(None, request) + self.assertTrue(isinstance(result, HTTPTemporaryRedirect)) + self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') + + def test_add_notfound_view_append_slash_custom_response(self): + from pyramid.response import Response + from pyramid.renderers import null_renderer + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPMovedPermanently, HTTPNotFound + + config = self._makeOne(autocommit=True) + config.add_route('foo', '/foo/') + + def view(request): # pragma: no cover + return Response('OK') + + config.add_notfound_view( + view, renderer=null_renderer, append_slash=HTTPMovedPermanently + ) + request = self._makeRequest(config) + request.environ['PATH_INFO'] = '/foo' + request.query_string = 'a=1&b=2' + request.path = '/scriptname/foo' + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + result = view(None, request) + self.assertTrue(isinstance(result, HTTPMovedPermanently)) + self.assertEqual(result.location, '/scriptname/foo/?a=1&b=2') + + def test_add_notfound_view_with_view_defaults(self): + from pyramid.interfaces import IRequest + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from pyramid.httpexceptions import HTTPNotFound + from zope.interface import directlyProvides + from zope.interface import implementedBy + + class view(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + config = self._makeOne(autocommit=True) + config.add_notfound_view(view=view, renderer=null_renderer) + wrapper = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + # Since Python 3 has to be all cool and fancy and different... + def _assertBody(self, response, value): + from pyramid.compat import text_type + + if isinstance(value, text_type): # pragma: no cover + self.assertEqual(response.text, value) + else: # pragma: no cover + self.assertEqual(response.body, value) + + def test_add_notfound_view_with_renderer(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPNotFound + + config = self._makeOne(autocommit=True) + view = lambda *arg: {} + config.introspection = False + config.add_notfound_view(view, renderer='json') + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPNotFound), + request_iface=IRequest, + ) + result = view(None, request) + self._assertBody(result, '{}') + + def test_add_forbidden_view_with_renderer(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.httpexceptions import HTTPForbidden + + config = self._makeOne(autocommit=True) + view = lambda *arg: {} + config.introspection = False + config.add_forbidden_view(view, renderer='json') + request = self._makeRequest(config) + view = self._getViewCallable( + config, + exc_iface=implementedBy(HTTPForbidden), + request_iface=IRequest, + ) + result = view(None, request) + self._assertBody(result, '{}') + + def test_set_view_mapper(self): + from pyramid.interfaces import IViewMapperFactory + + config = self._makeOne(autocommit=True) + mapper = object() + config.set_view_mapper(mapper) + result = config.registry.getUtility(IViewMapperFactory) + self.assertEqual(result, mapper) + + def test_set_view_mapper_dottedname(self): + from pyramid.interfaces import IViewMapperFactory + + config = self._makeOne(autocommit=True) + config.set_view_mapper('tests.test_config') + result = config.registry.getUtility(IViewMapperFactory) + from tests import test_config + + self.assertEqual(result, test_config) + + def test_add_normal_and_exception_view_intr_derived_callable(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import BadCSRFToken + + config = self._makeOne(autocommit=True) + introspector = DummyIntrospector() + config.introspector = introspector + view = lambda r: 'OK' + config.set_default_csrf_options(require_csrf=True) + config.add_view(view, context=Exception, renderer=null_renderer) + view_intr = introspector.introspectables[-1] + self.assertTrue(view_intr.type_name, 'view') + self.assertEqual(view_intr['callable'], view) + derived_view = view_intr['derived_callable'] + + request = self._makeRequest(config) + request.method = 'POST' + request.scheme = 'http' + request.POST = {} + request.headers = {} + request.session = DummySession({'csrf_token': 'foo'}) + self.assertRaises(BadCSRFToken, lambda: derived_view(None, request)) + request.exception = Exception() + self.assertEqual(derived_view(None, request), 'OK') + + def test_add_view_does_not_accept_iterable_accept(self): + from pyramid.exceptions import ConfigurationError + + config = self._makeOne(autocommit=True) + self.assertRaises( + ConfigurationError, config.add_view, accept=['image/*', 'text/*'] + ) + + def test_default_accept_view_order(self): + from pyramid.interfaces import IAcceptOrder + + config = self._makeOne(autocommit=True) + order = config.registry.getUtility(IAcceptOrder) + result = [v for _, v in order.sorted()] + self.assertEqual( + result, + [ + 'text/html', + 'application/xhtml+xml', + 'application/xml', + 'text/xml', + 'text/plain', + 'application/json', + ], + ) + + def test_add_accept_view_order_override(self): + from pyramid.interfaces import IAcceptOrder + + config = self._makeOne(autocommit=False) + config.add_accept_view_order( + 'text/html', + weighs_more_than='text/xml', + weighs_less_than='application/xml', + ) + config.commit() + order = config.registry.getUtility(IAcceptOrder) + result = [v for _, v in order.sorted()] + self.assertEqual( + result, + [ + 'application/xhtml+xml', + 'application/xml', + 'text/html', + 'text/xml', + 'text/plain', + 'application/json', + ], + ) + + def test_add_accept_view_order_throws_on_wildcard(self): + config = self._makeOne(autocommit=True) + self.assertRaises(ValueError, config.add_accept_view_order, '*/*') + + def test_add_accept_view_order_throws_on_type_mismatch(self): + config = self._makeOne(autocommit=True) + self.assertRaises( + ValueError, + config.add_accept_view_order, + 'text/*', + weighs_more_than='text/html', + ) + self.assertRaises( + ValueError, + config.add_accept_view_order, + 'text/html', + weighs_less_than='application/*', + ) + self.assertRaises( + ConfigurationError, + config.add_accept_view_order, + 'text/html', + weighs_more_than='text/html;charset=utf8', + ) + self.assertRaises( + ConfigurationError, + config.add_accept_view_order, + 'text/html;charset=utf8', + weighs_more_than='text/plain;charset=utf8', + ) + + +class Test_runtime_exc_view(unittest.TestCase): + def _makeOne(self, view1, view2): + from pyramid.config.views import runtime_exc_view + + return runtime_exc_view(view1, view2) + + def test_call(self): + def view1(context, request): + return 'OK' + + def view2(context, request): # pragma: no cover + raise AssertionError + + result_view = self._makeOne(view1, view2) + request = DummyRequest() + result = result_view(None, request) + self.assertEqual(result, 'OK') + + def test_call_dispatches_on_exception(self): + def view1(context, request): # pragma: no cover + raise AssertionError + + def view2(context, request): + return 'OK' + + result_view = self._makeOne(view1, view2) + request = DummyRequest() + request.exception = Exception() + result = result_view(None, request) + self.assertEqual(result, 'OK') + + def test_permitted(self): + def errfn(context, request): # pragma: no cover + raise AssertionError + + def view1(context, request): # pragma: no cover + raise AssertionError + + view1.__permitted__ = lambda c, r: 'OK' + + def view2(context, request): # pragma: no cover + raise AssertionError + + view2.__permitted__ = errfn + result_view = self._makeOne(view1, view2) + request = DummyRequest() + result = result_view.__permitted__(None, request) + self.assertEqual(result, 'OK') + + def test_permitted_dispatches_on_exception(self): + def errfn(context, request): # pragma: no cover + raise AssertionError + + def view1(context, request): # pragma: no cover + raise AssertionError + + view1.__permitted__ = errfn + + def view2(context, request): # pragma: no cover + raise AssertionError + + view2.__permitted__ = lambda c, r: 'OK' + result_view = self._makeOne(view1, view2) + request = DummyRequest() + request.exception = Exception() + result = result_view.__permitted__(None, request) + self.assertEqual(result, 'OK') + + +class Test_requestonly(unittest.TestCase): + def _callFUT(self, view, attr=None): + from pyramid.config.views import requestonly + + return requestonly(view, attr=attr) + + def test_defaults(self): + def aview(request, a=1, b=2): # pragma: no cover + pass + + self.assertTrue(self._callFUT(aview)) + + def test_otherattr(self): + class AView(object): + def __init__(self, request, a=1, b=2): # pragma: no cover + pass + + def bleh(self): # pragma: no cover + pass + + self.assertTrue(self._callFUT(AView, 'bleh')) + + +class Test_isexception(unittest.TestCase): + def _callFUT(self, ob): + from pyramid.config.views import isexception + + return isexception(ob) + + def test_is_exception_instance(self): + class E(Exception): + pass + + e = E() + self.assertEqual(self._callFUT(e), True) + + def test_is_exception_class(self): + class E(Exception): + pass + + self.assertEqual(self._callFUT(E), True) + + def test_is_IException(self): + from pyramid.interfaces import IException + + self.assertEqual(self._callFUT(IException), True) + + def test_is_IException_subinterface(self): + from pyramid.interfaces import IException + + class ISubException(IException): + pass + + self.assertEqual(self._callFUT(ISubException), True) + + +class TestMultiView(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.views import MultiView + + return MultiView + + def _makeOne(self, name='name'): + return self._getTargetClass()(name) + + def test_class_implements_ISecuredView(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import ISecuredView + + verifyClass(ISecuredView, self._getTargetClass()) + + def test_instance_implements_ISecuredView(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ISecuredView + + verifyObject(ISecuredView, self._makeOne()) + + def test_add(self): + mv = self._makeOne() + mv.add('view', 100) + self.assertEqual(mv.views, [(100, 'view', None)]) + mv.add('view2', 99) + self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) + mv.add('view3', 100, accept='text/html') + self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)]) + mv.add('view4', 99, 'abc', accept='text/html') + self.assertEqual( + mv.media_views['text/html'], + [(99, 'view4', 'abc'), (100, 'view3', None)], + ) + mv.add('view5', 100, accept='text/xml') + self.assertEqual(mv.media_views['text/xml'], [(100, 'view5', None)]) + self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html'])) + self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) + + def test_add_with_phash(self): + mv = self._makeOne() + mv.add('view', 100, phash='abc') + self.assertEqual(mv.views, [(100, 'view', 'abc')]) + mv.add('view', 100, phash='abc') + self.assertEqual(mv.views, [(100, 'view', 'abc')]) + mv.add('view', 100, phash='def') + self.assertEqual( + mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')] + ) + mv.add('view', 100, phash='abc') + self.assertEqual( + mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')] + ) + + def test_add_with_phash_override_accept(self): + mv = self._makeOne() + + def view1(): # pragma: no cover + pass + + def view2(): # pragma: no cover + pass + + def view3(): # pragma: no cover + pass + + mv.add(view1, 100, accept='text/html', phash='abc') + mv.add(view2, 100, accept='text/html', phash='abc') + mv.add(view3, 99, accept='text/html', phash='def') + self.assertEqual( + mv.media_views['text/html'], + [(99, view3, 'def'), (100, view2, 'abc')], + ) + + def test_add_with_phash_override_accept2(self): + mv = self._makeOne() + + def view1(): # pragma: no cover + pass + + def view2(): # pragma: no cover + pass + + def view3(): # pragma: no cover + pass + + mv.add(view1, 100, accept='text/html', phash='abc') + mv.add(view2, 100, accept='text/html', phash='def') + mv.add(view3, 99, accept='text/html', phash='ghi') + self.assertEqual( + mv.media_views['text/html'], + [(99, view3, 'ghi'), (100, view1, 'abc'), (100, view2, 'def')], + ) + + def test_multiple_with_functions_as_views(self): + # this failed on py3 at one point, because functions aren't orderable + # and we were sorting the views via a plain sort() rather than + # sort(key=itemgetter(0)). + def view1(request): # pragma: no cover + pass + + def view2(request): # pragma: no cover + pass + + mv = self._makeOne() + mv.add(view1, 100, None) + self.assertEqual(mv.views, [(100, view1, None)]) + mv.add(view2, 100, None) + self.assertEqual(mv.views, [(100, view1, None), (100, view2, None)]) + + def test_get_views_request_has_no_accept(self): + request = DummyRequest() + mv = self._makeOne() + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_get_views_no_self_accepts(self): + request = DummyRequest() + request.accept = True + mv = self._makeOne() + mv.accepts = [] + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_get_views(self): + request = DummyRequest() + request.accept = DummyAccept('text/html') + mv = self._makeOne() + mv.accepts = ['text/html'] + mv.views = [(99, lambda *arg: None)] + html_views = [(98, lambda *arg: None)] + mv.media_views['text/html'] = html_views + self.assertEqual(mv.get_views(request), html_views + mv.views) + + def test_get_views_best_match_returns_None(self): + request = DummyRequest() + request.accept = DummyAccept(None) + mv = self._makeOne() + mv.accepts = ['text/html'] + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_match_not_found(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(HTTPNotFound, mv.match, context, request) + + def test_match_predicate_fails(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + + def view(context, request): + """ """ + + view.__predicated__ = lambda *arg: False + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + self.assertRaises(HTTPNotFound, mv.match, context, request) + + def test_match_predicate_succeeds(self): + mv = self._makeOne() + + def view(context, request): + """ """ + + view.__predicated__ = lambda *arg: True + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + result = mv.match(context, request) + self.assertEqual(result, view) + + def test_permitted_no_views(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(HTTPNotFound, mv.__permitted__, context, request) + + def test_permitted_no_match_with__permitted__(self): + mv = self._makeOne() + + def view(context, request): + """ """ + + mv.views = [(100, view, None)] + self.assertEqual(mv.__permitted__(None, None), True) + + def test_permitted(self): + mv = self._makeOne() + + def view(context, request): + """ """ + + def permitted(context, request): + return False + + view.__permitted__ = permitted + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + result = mv.__permitted__(context, request) + self.assertEqual(result, False) + + def test__call__not_found(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(HTTPNotFound, mv, context, request) + + def test___call__intermediate_not_found(self): + from pyramid.exceptions import PredicateMismatch + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + + def view1(context, request): + raise PredicateMismatch + + def view2(context, request): + return expected_response + + mv.views = [(100, view1, None), (99, view2, None)] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test___call__raise_not_found_isnt_interpreted_as_pred_mismatch(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + + def view1(context, request): + raise HTTPNotFound + + def view2(context, request): + """ """ + + mv.views = [(100, view1, None), (99, view2, None)] + self.assertRaises(HTTPNotFound, mv, context, request) + + def test___call__(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + + def view(context, request): + return expected_response + + mv.views = [(100, view, None)] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test__call_permissive__not_found(self): + from pyramid.httpexceptions import HTTPNotFound + + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(HTTPNotFound, mv, context, request) + + def test___call_permissive_has_call_permissive(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + + def view(context, request): + """ """ + + def permissive(context, request): + return expected_response + + view.__call_permissive__ = permissive + mv.views = [(100, view, None)] + response = mv.__call_permissive__(context, request) + self.assertEqual(response, expected_response) + + def test___call_permissive_has_no_call_permissive(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + + def view(context, request): + return expected_response + + mv.views = [(100, view, None)] + response = mv.__call_permissive__(context, request) + self.assertEqual(response, expected_response) + + def test__call__with_accept_match(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/xml') + expected_response = DummyResponse() + + def view(context, request): + return expected_response + + mv.views = [(100, None)] + mv.media_views['text/xml'] = [(100, view, None)] + mv.accepts = ['text/xml'] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test__call__with_accept_miss(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.accept = DummyAccept('text/plain', 'text/html') + expected_response = DummyResponse() + + def view(context, request): + return expected_response + + mv.views = [(100, view, None)] + mv.media_views['text/xml'] = [(100, None, None)] + mv.accepts = ['text/xml'] + response = mv(context, request) + self.assertEqual(response, expected_response) + + +class TestDefaultViewMapper(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + self.registry = self.config.registry + + def tearDown(self): + del self.registry + testing.tearDown() + + def _makeOne(self, **kw): + from pyramid.config.views import DefaultViewMapper + + kw['registry'] = self.registry + return DefaultViewMapper(**kw) + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.registry + return request + + def test_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertTrue(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test__view_as_function_with_attr(self): + def view(context, request): + """ """ + + mapper = self._makeOne(attr='__name__') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertRaises(TypeError, result, None, request) + + def test_view_as_function_requestonly(self): + def view(request): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_function_requestonly_with_attr(self): + def view(request): + """ """ + + mapper = self._makeOne(attr='__name__') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertRaises(TypeError, result, None, request) + + def test_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + + def __call__(self): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_newstyle_class_context_and_request_with_attr(self): + class view(object): + def __init__(self, context, request): + pass + + def index(self): + return 'OK' + + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_newstyle_class_requestonly_with_attr(self): + class view(object): + def __init__(self, request): + pass + + def index(self): + return 'OK' + + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + + def __call__(self): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_oldstyle_class_context_and_request_with_attr(self): + class view: + def __init__(self, context, request): + pass + + def index(self): + return 'OK' + + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_oldstyle_class_requestonly_with_attr(self): + class view: + def __init__(self, request): + pass + + def index(self): + return 'OK' + + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + + view = View() + mapper = self._makeOne() + result = mapper(view) + self.assertTrue(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_instance_context_and_request_and_attr(self): + class View: + def index(self, context, request): + return 'OK' + + view = View() + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + + view = View() + mapper = self._makeOne() + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_instance_requestonly_with_attr(self): + class View: + def index(self, request): + return 'OK' + + view = View() + mapper = self._makeOne(attr='index') + result = mapper(view) + self.assertFalse(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + +class Test_preserve_view_attrs(unittest.TestCase): + def _callFUT(self, view, wrapped_view): + from pyramid.config.views import preserve_view_attrs + + return preserve_view_attrs(view, wrapped_view) + + def test_it_same(self): + def view(context, request): + """ """ + + result = self._callFUT(view, view) + self.assertTrue(result is view) + + def test_it_view_is_None(self): + def view(context, request): + """ """ + + result = self._callFUT(None, view) + self.assertTrue(result is view) + + def test_it_different_with_existing_original_view(self): + def view1(context, request): # pragma: no cover + pass + + view1.__original_view__ = 'abc' + + def view2(context, request): # pragma: no cover + pass + + result = self._callFUT(view1, view2) + self.assertEqual(result.__original_view__, 'abc') + self.assertFalse(result is view1) + + def test_it_different(self): + class DummyView1: + """ 1 """ + + __name__ = '1' + __module__ = '1' + + def __call__(self, context, request): + """ """ + + def __call_permissive__(self, context, request): + """ """ + + def __predicated__(self, context, request): + """ """ + + def __permitted__(self, context, request): + """ """ + + class DummyView2: + """ 2 """ + + __name__ = '2' + __module__ = '2' + + def __call__(self, context, request): + """ """ + + def __call_permissive__(self, context, request): + """ """ + + def __predicated__(self, context, request): + """ """ + + def __permitted__(self, context, request): + """ """ + + view1 = DummyView1() + view2 = DummyView2() + result = self._callFUT(view2, view1) + self.assertEqual(result, view1) + self.assertTrue(view1.__original_view__ is view2) + self.assertTrue(view1.__doc__ is view2.__doc__) + self.assertTrue(view1.__module__ is view2.__module__) + self.assertTrue(view1.__name__ is view2.__name__) + self.assertTrue( + getattr(view1.__call_permissive__, im_func) + is getattr(view2.__call_permissive__, im_func) + ) + self.assertTrue( + getattr(view1.__permitted__, im_func) + is getattr(view2.__permitted__, im_func) + ) + self.assertTrue( + getattr(view1.__predicated__, im_func) + is getattr(view2.__predicated__, im_func) + ) + + +class TestStaticURLInfo(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.views import StaticURLInfo + + return StaticURLInfo + + def _makeOne(self): + return self._getTargetClass()() + + def _makeRequest(self): + request = DummyRequest() + request.registry = DummyRegistry() + return request + + def test_verifyClass(self): + from pyramid.interfaces import IStaticURLInfo + from zope.interface.verify import verifyClass + + verifyClass(IStaticURLInfo, self._getTargetClass()) + + def test_verifyObject(self): + from pyramid.interfaces import IStaticURLInfo + from zope.interface.verify import verifyObject + + verifyObject(IStaticURLInfo, self._makeOne()) + + def test_generate_missing(self): + inst = self._makeOne() + request = self._makeRequest() + self.assertRaises(ValueError, inst.generate, 'path', request) + + def test_generate_registration_miss(self): + inst = self._makeOne() + inst.registrations = [ + (None, 'spec', 'route_name'), + ('http://example.com/foo/', 'package:path/', None), + ] + request = self._makeRequest() + result = inst.generate('package:path/abc', request) + self.assertEqual(result, 'http://example.com/foo/abc') + + def test_generate_slash_in_name1(self): + inst = self._makeOne() + inst.registrations = [ + ('http://example.com/foo/', 'package:path/', None) + ] + request = self._makeRequest() + result = inst.generate('package:path/abc', request) + self.assertEqual(result, 'http://example.com/foo/abc') + + def test_generate_slash_in_name2(self): + inst = self._makeOne() + inst.registrations = [ + ('http://example.com/foo/', 'package:path/', None) + ] + request = self._makeRequest() + result = inst.generate('package:path/', request) + self.assertEqual(result, 'http://example.com/foo/') + + def test_generate_quoting(self): + from pyramid.interfaces import IStaticURLInfo + + config = testing.setUp() + try: + config.add_static_view('images', path='mypkg:templates') + request = testing.DummyRequest() + request.registry = config.registry + inst = config.registry.getUtility(IStaticURLInfo) + result = inst.generate('mypkg:templates/foo%2Fbar', request) + self.assertEqual(result, 'http://example.com/images/foo%252Fbar') + finally: + testing.tearDown() + + def test_generate_route_url(self): + inst = self._makeOne() + inst.registrations = [(None, 'package:path/', '__viewname/')] + + def route_url(n, **kw): + self.assertEqual(n, '__viewname/') + self.assertEqual(kw, {'subpath': 'abc', 'a': 1}) + return 'url' + + request = self._makeRequest() + request.route_url = route_url + result = inst.generate('package:path/abc', request, a=1) + self.assertEqual(result, 'url') + + def test_generate_url_unquoted_local(self): + inst = self._makeOne() + inst.registrations = [(None, 'package:path/', '__viewname/')] + + def route_url(n, **kw): + self.assertEqual(n, '__viewname/') + self.assertEqual(kw, {'subpath': 'abc def', 'a': 1}) + return 'url' + + request = self._makeRequest() + request.route_url = route_url + result = inst.generate('package:path/abc def', request, a=1) + self.assertEqual(result, 'url') + + def test_generate_url_quoted_remote(self): + inst = self._makeOne() + inst.registrations = [('http://example.com/', 'package:path/', None)] + request = self._makeRequest() + result = inst.generate('package:path/abc def', request, a=1) + self.assertEqual(result, 'http://example.com/abc%20def') + + def test_generate_url_with_custom_query(self): + inst = self._makeOne() + registrations = [('http://example.com/', 'package:path/', None)] + inst.registrations = registrations + request = self._makeRequest() + result = inst.generate( + 'package:path/abc def', request, a=1, _query='(openlayers)' + ) + self.assertEqual(result, 'http://example.com/abc%20def?(openlayers)') + + def test_generate_url_with_custom_anchor(self): + inst = self._makeOne() + inst.registrations = [('http://example.com/', 'package:path/', None)] + request = self._makeRequest() + uc = text_(b'La Pe\xc3\xb1a', 'utf-8') + result = inst.generate( + 'package:path/abc def', request, a=1, _anchor=uc + ) + self.assertEqual(result, 'http://example.com/abc%20def#La%20Pe%C3%B1a') + + def test_generate_url_cachebust(self): + def cachebust(request, subpath, kw): + kw['foo'] = 'bar' + return 'foo' + '/' + subpath, kw + + inst = self._makeOne() + inst.registrations = [(None, 'package:path/', '__viewname')] + inst.cache_busters = [('package:path/', cachebust, False)] + request = self._makeRequest() + called = [False] + + def route_url(n, **kw): + called[0] = True + self.assertEqual(n, '__viewname') + self.assertEqual( + kw, + { + 'subpath': 'foo/abc', + 'foo': 'bar', + 'pathspec': 'package:path/abc', + 'rawspec': 'package:path/abc', + }, + ) + + request.route_url = route_url + inst.generate('package:path/abc', request) + self.assertTrue(called[0]) + + def test_generate_url_cachebust_abspath(self): + here = os.path.dirname(__file__) + os.sep + + def cachebust(pathspec, subpath, kw): + kw['foo'] = 'bar' + return 'foo' + '/' + subpath, kw + + inst = self._makeOne() + inst.registrations = [(None, here, '__viewname')] + inst.cache_busters = [(here, cachebust, False)] + request = self._makeRequest() + called = [False] + + def route_url(n, **kw): + called[0] = True + self.assertEqual(n, '__viewname') + self.assertEqual( + kw, + { + 'subpath': 'foo/abc', + 'foo': 'bar', + 'pathspec': here + 'abc', + 'rawspec': here + 'abc', + }, + ) + + request.route_url = route_url + inst.generate(here + 'abc', request) + self.assertTrue(called[0]) + + def test_generate_url_cachebust_nomatch(self): + def fake_cb(*a, **kw): # pragma: no cover + raise AssertionError + + inst = self._makeOne() + inst.registrations = [(None, 'package:path/', '__viewname')] + inst.cache_busters = [('package:path2/', fake_cb, False)] + request = self._makeRequest() + called = [False] + + def route_url(n, **kw): + called[0] = True + self.assertEqual(n, '__viewname') + self.assertEqual( + kw, + { + 'subpath': 'abc', + 'pathspec': 'package:path/abc', + 'rawspec': 'package:path/abc', + }, + ) + + request.route_url = route_url + inst.generate('package:path/abc', request) + self.assertTrue(called[0]) + + def test_generate_url_cachebust_with_overrides(self): + config = testing.setUp() + try: + request = testing.DummyRequest() + config.add_static_view('static', 'path') + config.override_asset( + 'tests.test_config:path/', 'tests.test_config:other_path/' + ) + + def cb(val): + def cb_(request, subpath, kw): + kw['_query'] = {'x': val} + return subpath, kw + + return cb_ + + config.add_cache_buster('path', cb('foo')) + result = request.static_url('path/foo.png') + self.assertEqual(result, 'http://example.com/static/foo.png?x=foo') + config.add_cache_buster('other_path', cb('bar'), explicit=True) + result = request.static_url('path/foo.png') + self.assertEqual(result, 'http://example.com/static/foo.png?x=bar') + finally: + testing.tearDown() + + def test_add_already_exists(self): + config = DummyConfig() + inst = self._makeOne() + inst.registrations = [('http://example.com/', 'package:path/', None)] + inst.add(config, 'http://example.com', 'anotherpackage:path') + expected = [('http://example.com/', 'anotherpackage:path/', None)] + self.assertEqual(inst.registrations, expected) + + def test_add_package_root(self): + config = DummyConfig() + inst = self._makeOne() + inst.add(config, 'http://example.com', 'package:') + expected = [('http://example.com/', 'package:', None)] + self.assertEqual(inst.registrations, expected) + + def test_add_url_withendslash(self): + config = DummyConfig() + inst = self._makeOne() + inst.add(config, 'http://example.com/', 'anotherpackage:path') + expected = [('http://example.com/', 'anotherpackage:path/', None)] + self.assertEqual(inst.registrations, expected) + + def test_add_url_noendslash(self): + config = DummyConfig() + inst = self._makeOne() + inst.add(config, 'http://example.com', 'anotherpackage:path') + expected = [('http://example.com/', 'anotherpackage:path/', None)] + self.assertEqual(inst.registrations, expected) + + def test_add_url_noscheme(self): + config = DummyConfig() + inst = self._makeOne() + inst.add(config, '//example.com', 'anotherpackage:path') + expected = [('//example.com/', 'anotherpackage:path/', None)] + self.assertEqual(inst.registrations, expected) + + def test_add_viewname(self): + from pyramid.security import NO_PERMISSION_REQUIRED + from pyramid.static import static_view + + config = DummyConfig() + inst = self._makeOne() + inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1) + expected = [(None, 'anotherpackage:path/', '__view/')] + self.assertEqual(inst.registrations, expected) + self.assertEqual(config.route_args, ('__view/', 'view/*subpath')) + self.assertEqual(config.view_kw['permission'], NO_PERMISSION_REQUIRED) + self.assertEqual(config.view_kw['view'].__class__, static_view) + + def test_add_viewname_with_route_prefix(self): + config = DummyConfig() + config.route_prefix = '/abc' + inst = self._makeOne() + inst.add(config, 'view', 'anotherpackage:path') + expected = [(None, 'anotherpackage:path/', '__/abc/view/')] + self.assertEqual(inst.registrations, expected) + self.assertEqual(config.route_args, ('__/abc/view/', 'view/*subpath')) + + def test_add_viewname_with_permission(self): + config = DummyConfig() + inst = self._makeOne() + inst.add( + config, + 'view', + 'anotherpackage:path', + cache_max_age=1, + permission='abc', + ) + self.assertEqual(config.view_kw['permission'], 'abc') + + def test_add_viewname_with_context(self): + config = DummyConfig() + inst = self._makeOne() + inst.add( + config, + 'view', + 'anotherpackage:path', + cache_max_age=1, + context=DummyContext, + ) + self.assertEqual(config.view_kw['context'], DummyContext) + + def test_add_viewname_with_for_(self): + config = DummyConfig() + inst = self._makeOne() + inst.add( + config, + 'view', + 'anotherpackage:path', + cache_max_age=1, + for_=DummyContext, + ) + self.assertEqual(config.view_kw['context'], DummyContext) + + def test_add_viewname_with_renderer(self): + config = DummyConfig() + inst = self._makeOne() + inst.add( + config, + 'view', + 'anotherpackage:path', + cache_max_age=1, + renderer='mypackage:templates/index.pt', + ) + self.assertEqual( + config.view_kw['renderer'], 'mypackage:templates/index.pt' + ) + + def test_add_cachebust_prevented(self): + config = DummyConfig() + config.registry.settings['pyramid.prevent_cachebust'] = True + inst = self._makeOne() + cachebust = DummyCacheBuster('foo') + inst.add_cache_buster(config, 'mypackage:path', cachebust) + self.assertEqual(inst.cache_busters, []) + + def test_add_cachebuster(self): + config = DummyConfig() + inst = self._makeOne() + inst.add_cache_buster( + config, 'mypackage:path', DummyCacheBuster('foo') + ) + cachebust = inst.cache_busters[-1][1] + subpath, kw = cachebust(None, 'some/path', {}) + self.assertEqual(subpath, 'some/path') + self.assertEqual(kw['x'], 'foo') + + def test_add_cachebuster_abspath(self): + here = os.path.dirname(__file__) + config = DummyConfig() + inst = self._makeOne() + cb = DummyCacheBuster('foo') + inst.add_cache_buster(config, here, cb) + self.assertEqual(inst.cache_busters, [(here + os.sep, cb, False)]) + + def test_add_cachebuster_overwrite(self): + config = DummyConfig() + inst = self._makeOne() + cb1 = DummyCacheBuster('foo') + cb2 = DummyCacheBuster('bar') + inst.add_cache_buster(config, 'mypackage:path/', cb1) + inst.add_cache_buster(config, 'mypackage:path', cb2) + self.assertEqual(inst.cache_busters, [('mypackage:path/', cb2, False)]) + + def test_add_cachebuster_overwrite_explicit(self): + config = DummyConfig() + inst = self._makeOne() + cb1 = DummyCacheBuster('foo') + cb2 = DummyCacheBuster('bar') + inst.add_cache_buster(config, 'mypackage:path/', cb1) + inst.add_cache_buster(config, 'mypackage:path', cb2, True) + self.assertEqual( + inst.cache_busters, + [('mypackage:path/', cb1, False), ('mypackage:path/', cb2, True)], + ) + + def test_add_cachebuster_for_more_specific_path(self): + config = DummyConfig() + inst = self._makeOne() + cb1 = DummyCacheBuster('foo') + cb2 = DummyCacheBuster('bar') + cb3 = DummyCacheBuster('baz') + cb4 = DummyCacheBuster('xyz') + cb5 = DummyCacheBuster('w') + inst.add_cache_buster(config, 'mypackage:path', cb1) + inst.add_cache_buster(config, 'mypackage:path/sub', cb2, True) + inst.add_cache_buster(config, 'mypackage:path/sub/other', cb3) + inst.add_cache_buster(config, 'mypackage:path/sub/other', cb4, True) + inst.add_cache_buster(config, 'mypackage:path/sub/less', cb5, True) + self.assertEqual( + inst.cache_busters, + [ + ('mypackage:path/', cb1, False), + ('mypackage:path/sub/other/', cb3, False), + ('mypackage:path/sub/', cb2, True), + ('mypackage:path/sub/less/', cb5, True), + ('mypackage:path/sub/other/', cb4, True), + ], + ) + + +class Test_view_description(unittest.TestCase): + def _callFUT(self, view): + from pyramid.config.views import view_description + + return view_description(view) + + def test_with_text(self): + def view(): # pragma: no cover + pass + + view.__text__ = 'some text' + result = self._callFUT(view) + self.assertEqual(result, 'some text') + + def test_without_text(self): + def view(): # pragma: no cover + pass + + result = self._callFUT(view) + self.assertEqual(result, 'function tests.test_config.test_views.view') + + +class Test_viewdefaults(unittest.TestCase): + def _makeOne(self, wrapped): + from pyramid.decorator import reify + + return reify(wrapped) + + def test_dunder_attrs_copied(self): + from pyramid.config.views import viewdefaults + + decorator = self._makeOne(viewdefaults) + self.assertEqual(decorator.__doc__, viewdefaults.__doc__) + self.assertEqual(decorator.__name__, viewdefaults.__name__) + self.assertEqual(decorator.__module__, viewdefaults.__module__) + + +class DummyRegistry: + utility = None + + def __init__(self): + self.settings = {} + + def queryUtility(self, type_or_iface, name=None, default=None): + return self.utility or default + + +@implementer(IResponse) +class DummyResponse(object): + content_type = None + default_content_type = None + body = None + + +class DummyRequest: + subpath = () + matchdict = None + request_iface = IRequest + application_url = 'http://example.com/foo' + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + self.params = {} + self.cookies = {} + self.response = DummyResponse() + + +class DummyContext: + pass + + +class DummyAccept(object): + def __init__(self, *matches, **kw): + self.matches = list(matches) + self.contains = kw.pop('contains', False) + + def acceptable_offers(self, offers): + results = [] + for match in self.matches: + if match in offers: + results.append((match, 1.0)) + return results + + +class DummyConfig: + def __init__(self): + self.registry = DummyRegistry() + + route_prefix = '' + + def add_route(self, *args, **kw): + self.route_args = args + self.route_kw = kw + + def add_view(self, *args, **kw): + self.view_args = args + self.view_kw = kw + + def action(self, discriminator, callable, introspectables=()): + callable() + + def introspectable(self, *arg): + return {} + + +@implementer(IMultiView) +class DummyMultiView: + def __init__(self): + self.views = [] + self.name = 'name' + + def add(self, view, order, phash=None, accept=None, accept_order=None): + self.views.append((view, phash, accept, accept_order)) + + def __call__(self, context, request): + return 'OK1' + + def __permitted__(self, context, request): + """ """ + + +class DummyCacheBuster(object): + def __init__(self, token): + self.token = token + + def __call__(self, request, subpath, kw): + kw['x'] = self.token + return subpath, kw + + +def parse_httpdate(s): + import datetime + + # cannot use %Z, must use literal GMT; Jython honors timezone + # but CPython does not + return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT") + + +def assert_similar_datetime(one, two): + for attr in ('year', 'month', 'day', 'hour', 'minute'): + one_attr = getattr(one, attr) + two_attr = getattr(two, attr) + if not one_attr == two_attr: # pragma: no cover + raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr)) + + +class DummyStaticURLInfo: + def __init__(self): + self.added = [] + + def add(self, config, name, spec, **kw): + self.added.append((config, name, spec, kw)) + + +class DummyViewDefaultsClass(object): + __view_defaults__ = {'containment': 'tests.test_config.IDummy'} + + def __init__(self, request): + pass + + def __call__(self): + return 'OK' + + +class DummyPredicate(object): + def __init__(self, val, config): + self.val = val + + def text(self): + return 'dummy' + + phash = text + + +class DummyIntrospector(object): + def __init__(self, getval=None): + self.related = [] + self.introspectables = [] + self.getval = getval + + def add(self, introspectable): + self.introspectables.append(introspectable) + + def get(self, name, discrim): + return self.getval + + def relate(self, a, b): + self.related.append((a, b)) + + +class DummySession(dict): + def get_csrf_token(self): + return self['csrf_token'] diff --git a/tests/test_csrf.py b/tests/test_csrf.py new file mode 100644 index 000000000..d1b569c32 --- /dev/null +++ b/tests/test_csrf.py @@ -0,0 +1,436 @@ +import unittest + +from pyramid import testing +from pyramid.config import Configurator + + +class TestLegacySessionCSRFStoragePolicy(unittest.TestCase): + class MockSession(object): + def __init__(self, current_token='02821185e4c94269bdc38e6eeae0a2f8'): + self.current_token = current_token + + def new_csrf_token(self): + self.current_token = 'e5e9e30a08b34ff9842ff7d2b958c14b' + return self.current_token + + def get_csrf_token(self): + return self.current_token + + def _makeOne(self): + from pyramid.csrf import LegacySessionCSRFStoragePolicy + + return LegacySessionCSRFStoragePolicy() + + def test_register_session_csrf_policy(self): + from pyramid.csrf import LegacySessionCSRFStoragePolicy + from pyramid.interfaces import ICSRFStoragePolicy + + config = Configurator() + config.set_csrf_storage_policy(self._makeOne()) + config.commit() + + policy = config.registry.queryUtility(ICSRFStoragePolicy) + + self.assertTrue(isinstance(policy, LegacySessionCSRFStoragePolicy)) + + def test_session_csrf_implementation_delegates_to_session(self): + policy = self._makeOne() + request = DummyRequest(session=self.MockSession()) + + self.assertEqual( + policy.get_csrf_token(request), '02821185e4c94269bdc38e6eeae0a2f8' + ) + self.assertEqual( + policy.new_csrf_token(request), 'e5e9e30a08b34ff9842ff7d2b958c14b' + ) + + def test_check_csrf_token(self): + request = DummyRequest(session=self.MockSession('foo')) + + policy = self._makeOne() + self.assertTrue(policy.check_csrf_token(request, 'foo')) + self.assertFalse(policy.check_csrf_token(request, 'bar')) + + +class TestSessionCSRFStoragePolicy(unittest.TestCase): + def _makeOne(self, **kw): + from pyramid.csrf import SessionCSRFStoragePolicy + + return SessionCSRFStoragePolicy(**kw) + + def test_register_session_csrf_policy(self): + from pyramid.csrf import SessionCSRFStoragePolicy + from pyramid.interfaces import ICSRFStoragePolicy + + config = Configurator() + config.set_csrf_storage_policy(self._makeOne()) + config.commit() + + policy = config.registry.queryUtility(ICSRFStoragePolicy) + + self.assertTrue(isinstance(policy, SessionCSRFStoragePolicy)) + + def test_it_creates_a_new_token(self): + request = DummyRequest(session={}) + + policy = self._makeOne() + policy._token_factory = lambda: 'foo' + self.assertEqual(policy.get_csrf_token(request), 'foo') + + def test_get_csrf_token_returns_the_new_token(self): + request = DummyRequest(session={'_csrft_': 'foo'}) + + policy = self._makeOne() + self.assertEqual(policy.get_csrf_token(request), 'foo') + + token = policy.new_csrf_token(request) + self.assertNotEqual(token, 'foo') + self.assertEqual(token, policy.get_csrf_token(request)) + + def test_check_csrf_token(self): + request = DummyRequest(session={}) + + policy = self._makeOne() + self.assertFalse(policy.check_csrf_token(request, 'foo')) + + request.session = {'_csrft_': 'foo'} + self.assertTrue(policy.check_csrf_token(request, 'foo')) + self.assertFalse(policy.check_csrf_token(request, 'bar')) + + +class TestCookieCSRFStoragePolicy(unittest.TestCase): + def _makeOne(self, **kw): + from pyramid.csrf import CookieCSRFStoragePolicy + + return CookieCSRFStoragePolicy(**kw) + + def test_register_cookie_csrf_policy(self): + from pyramid.csrf import CookieCSRFStoragePolicy + from pyramid.interfaces import ICSRFStoragePolicy + + config = Configurator() + config.set_csrf_storage_policy(self._makeOne()) + config.commit() + + policy = config.registry.queryUtility(ICSRFStoragePolicy) + + self.assertTrue(isinstance(policy, CookieCSRFStoragePolicy)) + + def test_get_cookie_csrf_with_no_existing_cookie_sets_cookies(self): + response = MockResponse() + request = DummyRequest() + + policy = self._makeOne() + token = policy.get_csrf_token(request) + request.response_callback(request, response) + self.assertEqual( + response.headerlist, + [ + ( + 'Set-Cookie', + 'csrf_token={}; Path=/; SameSite=Lax'.format(token), + ) + ], + ) + + def test_get_cookie_csrf_nondefault_samesite(self): + response = MockResponse() + request = DummyRequest() + + policy = self._makeOne(samesite=None) + token = policy.get_csrf_token(request) + request.response_callback(request, response) + self.assertEqual( + response.headerlist, + [('Set-Cookie', 'csrf_token={}; Path=/'.format(token))], + ) + + def test_existing_cookie_csrf_does_not_set_cookie(self): + request = DummyRequest() + request.cookies = {'csrf_token': 'e6f325fee5974f3da4315a8ccf4513d2'} + + policy = self._makeOne() + token = policy.get_csrf_token(request) + + self.assertEqual(token, 'e6f325fee5974f3da4315a8ccf4513d2') + self.assertIsNone(request.response_callback) + + def test_new_cookie_csrf_with_existing_cookie_sets_cookies(self): + request = DummyRequest() + request.cookies = {'csrf_token': 'e6f325fee5974f3da4315a8ccf4513d2'} + + policy = self._makeOne() + token = policy.new_csrf_token(request) + + response = MockResponse() + request.response_callback(request, response) + self.assertEqual( + response.headerlist, + [ + ( + 'Set-Cookie', + 'csrf_token={}; Path=/; SameSite=Lax'.format(token), + ) + ], + ) + + def test_get_csrf_token_returns_the_new_token(self): + request = DummyRequest() + request.cookies = {'csrf_token': 'foo'} + + policy = self._makeOne() + self.assertEqual(policy.get_csrf_token(request), 'foo') + + token = policy.new_csrf_token(request) + self.assertNotEqual(token, 'foo') + self.assertEqual(token, policy.get_csrf_token(request)) + + def test_check_csrf_token(self): + request = DummyRequest() + + policy = self._makeOne() + self.assertFalse(policy.check_csrf_token(request, 'foo')) + + request.cookies = {'csrf_token': 'foo'} + self.assertTrue(policy.check_csrf_token(request, 'foo')) + self.assertFalse(policy.check_csrf_token(request, 'bar')) + + +class Test_get_csrf_token(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def _callFUT(self, *args, **kwargs): + from pyramid.csrf import get_csrf_token + + return get_csrf_token(*args, **kwargs) + + def test_no_override_csrf_utility_registered(self): + request = testing.DummyRequest() + self._callFUT(request) + + def test_success(self): + self.config.set_csrf_storage_policy(DummyCSRF()) + request = testing.DummyRequest() + + csrf_token = self._callFUT(request) + + self.assertEquals(csrf_token, '02821185e4c94269bdc38e6eeae0a2f8') + + +class Test_new_csrf_token(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def _callFUT(self, *args, **kwargs): + from pyramid.csrf import new_csrf_token + + return new_csrf_token(*args, **kwargs) + + def test_no_override_csrf_utility_registered(self): + request = testing.DummyRequest() + self._callFUT(request) + + def test_success(self): + self.config.set_csrf_storage_policy(DummyCSRF()) + request = testing.DummyRequest() + + csrf_token = self._callFUT(request) + + self.assertEquals(csrf_token, 'e5e9e30a08b34ff9842ff7d2b958c14b') + + +class Test_check_csrf_token(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + # set up CSRF + self.config.set_default_csrf_options(require_csrf=False) + + def _callFUT(self, *args, **kwargs): + from pyramid.csrf import check_csrf_token + + return check_csrf_token(*args, **kwargs) + + def test_success_token(self): + request = testing.DummyRequest() + request.method = "POST" + request.POST = {'csrf_token': request.session.get_csrf_token()} + self.assertEqual(self._callFUT(request, token='csrf_token'), True) + + def test_success_header(self): + request = testing.DummyRequest() + request.headers['X-CSRF-Token'] = request.session.get_csrf_token() + self.assertEqual(self._callFUT(request, header='X-CSRF-Token'), True) + + def test_success_default_token(self): + request = testing.DummyRequest() + request.method = "POST" + request.POST = {'csrf_token': request.session.get_csrf_token()} + self.assertEqual(self._callFUT(request), True) + + def test_success_default_header(self): + request = testing.DummyRequest() + request.headers['X-CSRF-Token'] = request.session.get_csrf_token() + self.assertEqual(self._callFUT(request), True) + + def test_failure_raises(self): + from pyramid.exceptions import BadCSRFToken + + request = testing.DummyRequest() + self.assertRaises(BadCSRFToken, self._callFUT, request, 'csrf_token') + + def test_failure_no_raises(self): + request = testing.DummyRequest() + result = self._callFUT(request, 'csrf_token', raises=False) + self.assertEqual(result, False) + + +class Test_check_csrf_token_without_defaults_configured(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def _callFUT(self, *args, **kwargs): + from pyramid.csrf import check_csrf_token + + return check_csrf_token(*args, **kwargs) + + def test_success_token(self): + request = testing.DummyRequest() + request.method = "POST" + request.POST = {'csrf_token': request.session.get_csrf_token()} + self.assertEqual(self._callFUT(request, token='csrf_token'), True) + + def test_failure_raises(self): + from pyramid.exceptions import BadCSRFToken + + request = testing.DummyRequest() + self.assertRaises(BadCSRFToken, self._callFUT, request, 'csrf_token') + + def test_failure_no_raises(self): + request = testing.DummyRequest() + result = self._callFUT(request, 'csrf_token', raises=False) + self.assertEqual(result, False) + + +class Test_check_csrf_origin(unittest.TestCase): + def _callFUT(self, *args, **kwargs): + from pyramid.csrf import check_csrf_origin + + return check_csrf_origin(*args, **kwargs) + + def test_success_with_http(self): + request = testing.DummyRequest() + request.scheme = "http" + self.assertTrue(self._callFUT(request)) + + def test_success_with_https_and_referrer(self): + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com" + request.host_port = "443" + request.referrer = "https://example.com/login/" + request.registry.settings = {} + self.assertTrue(self._callFUT(request)) + + def test_success_with_https_and_origin(self): + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com" + request.host_port = "443" + request.headers = {"Origin": "https://example.com/"} + request.referrer = "https://not-example.com/" + request.registry.settings = {} + self.assertTrue(self._callFUT(request)) + + def test_success_with_additional_trusted_host(self): + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com" + request.host_port = "443" + request.referrer = "https://not-example.com/login/" + request.registry.settings = { + "pyramid.csrf_trusted_origins": ["not-example.com"] + } + self.assertTrue(self._callFUT(request)) + + def test_success_with_nonstandard_port(self): + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com:8080" + request.host_port = "8080" + request.referrer = "https://example.com:8080/login/" + request.registry.settings = {} + self.assertTrue(self._callFUT(request)) + + def test_fails_with_wrong_host(self): + from pyramid.exceptions import BadCSRFOrigin + + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com" + request.host_port = "443" + request.referrer = "https://not-example.com/login/" + request.registry.settings = {} + self.assertRaises(BadCSRFOrigin, self._callFUT, request) + self.assertFalse(self._callFUT(request, raises=False)) + + def test_fails_with_no_origin(self): + from pyramid.exceptions import BadCSRFOrigin + + request = testing.DummyRequest() + request.scheme = "https" + request.referrer = None + self.assertRaises(BadCSRFOrigin, self._callFUT, request) + self.assertFalse(self._callFUT(request, raises=False)) + + def test_fails_when_http_to_https(self): + from pyramid.exceptions import BadCSRFOrigin + + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com" + request.host_port = "443" + request.referrer = "http://example.com/evil/" + request.registry.settings = {} + self.assertRaises(BadCSRFOrigin, self._callFUT, request) + self.assertFalse(self._callFUT(request, raises=False)) + + def test_fails_with_nonstandard_port(self): + from pyramid.exceptions import BadCSRFOrigin + + request = testing.DummyRequest() + request.scheme = "https" + request.host = "example.com:8080" + request.host_port = "8080" + request.referrer = "https://example.com/login/" + request.registry.settings = {} + self.assertRaises(BadCSRFOrigin, self._callFUT, request) + self.assertFalse(self._callFUT(request, raises=False)) + + +class DummyRequest(object): + registry = None + session = None + response_callback = None + + def __init__(self, registry=None, session=None): + self.registry = registry + self.session = session + self.cookies = {} + + def add_response_callback(self, callback): + self.response_callback = callback + + +class MockResponse(object): + def __init__(self): + self.headerlist = [] + + +class DummyCSRF(object): + def new_csrf_token(self, request): + return 'e5e9e30a08b34ff9842ff7d2b958c14b' + + def get_csrf_token(self, request): + return '02821185e4c94269bdc38e6eeae0a2f8' diff --git a/tests/test_decorator.py b/tests/test_decorator.py new file mode 100644 index 000000000..2dcc9b4d3 --- /dev/null +++ b/tests/test_decorator.py @@ -0,0 +1,30 @@ +import unittest + + +class TestReify(unittest.TestCase): + def _makeOne(self, wrapped): + from pyramid.decorator import reify + + return reify(wrapped) + + def test___get__withinst(self): + def wrapped(inst): + return 'a' + + decorator = self._makeOne(wrapped) + inst = Dummy() + result = decorator.__get__(inst) + self.assertEqual(result, 'a') + self.assertEqual(inst.__dict__['wrapped'], 'a') + + def test___get__noinst(self): + def wrapped(inst): + return 'a' # pragma: no cover + + decorator = self._makeOne(wrapped) + result = decorator.__get__(None) + self.assertEqual(result, decorator) + + +class Dummy(object): + pass diff --git a/tests/test_docs.py b/tests/test_docs.py new file mode 100644 index 000000000..46c9ed765 --- /dev/null +++ b/tests/test_docs.py @@ -0,0 +1,36 @@ +import unittest + +if 0: + # no released version of manuel actually works with :lineno: + # settings yet + class ManuelDocsCase(unittest.TestCase): + def __new__(self, test): + return getattr(self, test)() + + @classmethod + def test_docs(cls): + import os + import pkg_resources + import manuel.testing + import manuel.codeblock + import manuel.capture + import manuel.ignore + + m = manuel.ignore.Manuel() + m += manuel.codeblock.Manuel() + m += manuel.capture.Manuel() + docs = [] + + egg_path = pkg_resources.get_distribution('pyramid').location + path = os.path.join(egg_path, 'docs') + for root, dirs, files in os.walk(path): + for ignore in ('.svn', '.build', '.hg', '.git', 'CVS'): + if ignore in dirs: + dirs.remove(ignore) + + for filename in files: + if filename.endswith('.rst'): + docs.append(os.path.join(root, filename)) + + print(path) + return manuel.testing.TestSuite(m, *docs) diff --git a/tests/test_encode.py b/tests/test_encode.py new file mode 100644 index 000000000..f70050cac --- /dev/null +++ b/tests/test_encode.py @@ -0,0 +1,89 @@ +import unittest +from pyramid.compat import text_, native_ + + +class UrlEncodeTests(unittest.TestCase): + def _callFUT(self, query, doseq=False, **kw): + from pyramid.encode import urlencode + + return urlencode(query, doseq, **kw) + + def test_ascii_only(self): + result = self._callFUT([('a', 1), ('b', 2)]) + self.assertEqual(result, 'a=1&b=2') + + def test_unicode_key(self): + la = text_(b'LaPe\xc3\xb1a', 'utf-8') + result = self._callFUT([(la, 1), ('b', 2)]) + self.assertEqual(result, 'LaPe%C3%B1a=1&b=2') + + def test_unicode_val_single(self): + la = text_(b'LaPe\xc3\xb1a', 'utf-8') + result = self._callFUT([('a', la), ('b', 2)]) + self.assertEqual(result, 'a=LaPe%C3%B1a&b=2') + + def test_unicode_val_multiple(self): + la = [text_(b'LaPe\xc3\xb1a', 'utf-8')] * 2 + result = self._callFUT([('a', la), ('b', 2)], doseq=True) + self.assertEqual(result, 'a=LaPe%C3%B1a&a=LaPe%C3%B1a&b=2') + + def test_int_val_multiple(self): + s = [1, 2] + result = self._callFUT([('a', s)], doseq=True) + self.assertEqual(result, 'a=1&a=2') + + def test_with_spaces(self): + result = self._callFUT([('a', '123 456')], doseq=True) + self.assertEqual(result, 'a=123+456') + + def test_dict(self): + result = self._callFUT({'a': 1}) + self.assertEqual(result, 'a=1') + + def test_None_value(self): + result = self._callFUT([('a', None)]) + self.assertEqual(result, 'a=') + + def test_None_value_with_prefix(self): + result = self._callFUT([('a', '1'), ('b', None)]) + self.assertEqual(result, 'a=1&b=') + + def test_None_value_with_prefix_values(self): + result = self._callFUT([('a', '1'), ('b', None), ('c', None)]) + self.assertEqual(result, 'a=1&b=&c=') + + def test_quote_via(self): + def my_quoter(value): + return 'xxx' + value + + result = self._callFUT( + [('a', '1'), ('b', None), ('c', None)], quote_via=my_quoter + ) + self.assertEqual(result, 'xxxa=xxx1&xxxb=&xxxc=') + + +class URLQuoteTests(unittest.TestCase): + def _callFUT(self, val, safe=''): + from pyramid.encode import url_quote + + return url_quote(val, safe) + + def test_it_bytes(self): + la = b'La/Pe\xc3\xb1a' + result = self._callFUT(la) + self.assertEqual(result, 'La%2FPe%C3%B1a') + + def test_it_native(self): + la = native_(b'La/Pe\xc3\xb1a', 'utf-8') + result = self._callFUT(la) + self.assertEqual(result, 'La%2FPe%C3%B1a') + + def test_it_with_safe(self): + la = b'La/Pe\xc3\xb1a' + result = self._callFUT(la, '/') + self.assertEqual(result, 'La/Pe%C3%B1a') + + def test_it_with_nonstr_nonbinary(self): + la = None + result = self._callFUT(la, '/') + self.assertEqual(result, 'None') diff --git a/tests/test_events.py b/tests/test_events.py new file mode 100644 index 000000000..25ed5fc0d --- /dev/null +++ b/tests/test_events.py @@ -0,0 +1,411 @@ +import unittest +from pyramid import testing + + +class NewRequestEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import NewRequest + + return NewRequest + + def _makeOne(self, request): + return self._getTargetClass()(request) + + def test_class_conforms_to_INewRequest(self): + from pyramid.interfaces import INewRequest + from zope.interface.verify import verifyClass + + klass = self._getTargetClass() + verifyClass(INewRequest, klass) + + def test_instance_conforms_to_INewRequest(self): + from pyramid.interfaces import INewRequest + from zope.interface.verify import verifyObject + + request = DummyRequest() + inst = self._makeOne(request) + verifyObject(INewRequest, inst) + + def test_ctor(self): + request = DummyRequest() + inst = self._makeOne(request) + self.assertEqual(inst.request, request) + + +class NewResponseEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import NewResponse + + return NewResponse + + def _makeOne(self, request, response): + return self._getTargetClass()(request, response) + + def test_class_conforms_to_INewResponse(self): + from pyramid.interfaces import INewResponse + from zope.interface.verify import verifyClass + + klass = self._getTargetClass() + verifyClass(INewResponse, klass) + + def test_instance_conforms_to_INewResponse(self): + from pyramid.interfaces import INewResponse + from zope.interface.verify import verifyObject + + request = DummyRequest() + response = DummyResponse() + inst = self._makeOne(request, response) + verifyObject(INewResponse, inst) + + def test_ctor(self): + request = DummyRequest() + response = DummyResponse() + inst = self._makeOne(request, response) + self.assertEqual(inst.request, request) + self.assertEqual(inst.response, response) + + +class ApplicationCreatedEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import ApplicationCreated + + return ApplicationCreated + + def _makeOne(self, context=object()): + return self._getTargetClass()(context) + + def test_class_conforms_to_IApplicationCreated(self): + from pyramid.interfaces import IApplicationCreated + from zope.interface.verify import verifyClass + + verifyClass(IApplicationCreated, self._getTargetClass()) + + def test_object_conforms_to_IApplicationCreated(self): + from pyramid.interfaces import IApplicationCreated + from zope.interface.verify import verifyObject + + verifyObject(IApplicationCreated, self._makeOne()) + + +class WSGIApplicationCreatedEventTests(ApplicationCreatedEventTests): + def _getTargetClass(self): + from pyramid.events import WSGIApplicationCreatedEvent + + return WSGIApplicationCreatedEvent + + def test_class_conforms_to_IWSGIApplicationCreatedEvent(self): + from pyramid.interfaces import IWSGIApplicationCreatedEvent + from zope.interface.verify import verifyClass + + verifyClass(IWSGIApplicationCreatedEvent, self._getTargetClass()) + + def test_object_conforms_to_IWSGIApplicationCreatedEvent(self): + from pyramid.interfaces import IWSGIApplicationCreatedEvent + from zope.interface.verify import verifyObject + + verifyObject(IWSGIApplicationCreatedEvent, self._makeOne()) + + +class ContextFoundEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import ContextFound + + return ContextFound + + def _makeOne(self, request=None): + if request is None: + request = DummyRequest() + return self._getTargetClass()(request) + + def test_class_conforms_to_IContextFound(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IContextFound + + verifyClass(IContextFound, self._getTargetClass()) + + def test_instance_conforms_to_IContextFound(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IContextFound + + verifyObject(IContextFound, self._makeOne()) + + +class AfterTraversalEventTests(ContextFoundEventTests): + def _getTargetClass(self): + from pyramid.events import AfterTraversal + + return AfterTraversal + + def test_class_conforms_to_IAfterTraversal(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IAfterTraversal + + verifyClass(IAfterTraversal, self._getTargetClass()) + + def test_instance_conforms_to_IAfterTraversal(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IAfterTraversal + + verifyObject(IAfterTraversal, self._makeOne()) + + +class BeforeTraversalEventTests(unittest.TestCase): + def _getTargetClass(self): + from pyramid.events import BeforeTraversal + + return BeforeTraversal + + def _makeOne(self, request=None): + if request is None: + request = DummyRequest() + return self._getTargetClass()(request) + + def test_class_conforms_to_IBeforeTraversal(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IBeforeTraversal + + verifyClass(IBeforeTraversal, self._getTargetClass()) + + def test_instance_conforms_to_IBeforeTraversal(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IBeforeTraversal + + verifyObject(IBeforeTraversal, self._makeOne()) + + +class TestSubscriber(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, *ifaces, **predicates): + from pyramid.events import subscriber + + return subscriber(*ifaces, **predicates) + + def test_register_single(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + class IBar(Interface): + pass + + dec = self._makeOne(IFoo) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, IFoo)]) + + def test_register_multi(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + class IBar(Interface): + pass + + dec = self._makeOne(IFoo, IBar) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, IFoo), (foo, IBar)]) + + def test_register_none_means_all(self): + from zope.interface import Interface + + dec = self._makeOne() + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, Interface)]) + + def test_register_objectevent(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + class IBar(Interface): + pass + + dec = self._makeOne([IFoo, IBar]) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, [IFoo, IBar])]) + + def test___call__(self): + dec = self._makeOne() + dummy_venusian = DummyVenusian() + dec.venusian = dummy_venusian + + def foo(): # pragma: no cover + pass + + dec(foo) + self.assertEqual( + dummy_venusian.attached, [(foo, dec.register, 'pyramid', 1)] + ) + + def test___call___with_venusian_args(self): + dec = self._makeOne(_category='foo', _depth=1) + dummy_venusian = DummyVenusian() + dec.venusian = dummy_venusian + + def foo(): # pragma: no cover + pass + + dec(foo) + self.assertEqual( + dummy_venusian.attached, [(foo, dec.register, 'foo', 2)] + ) + + def test_regsister_with_predicates(self): + from zope.interface import Interface + + dec = self._makeOne(a=1) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, Interface, {'a': 1})]) + + +class TestBeforeRender(unittest.TestCase): + def _makeOne(self, system, val=None): + from pyramid.events import BeforeRender + + return BeforeRender(system, val) + + @testing.skip_on( + 'pypy' + ) # see https://github.com/Pylons/pyramid/issues/3237 + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IBeforeRender + + event = self._makeOne({}) + verifyObject(IBeforeRender, event) + + def test_setitem_success(self): + event = self._makeOne({}) + event['a'] = 1 + self.assertEqual(event, {'a': 1}) + + def test_setdefault_fail(self): + event = self._makeOne({}) + result = event.setdefault('a', 1) + self.assertEqual(result, 1) + self.assertEqual(event, {'a': 1}) + + def test_setdefault_success(self): + event = self._makeOne({}) + event['a'] = 1 + result = event.setdefault('a', 2) + self.assertEqual(result, 1) + self.assertEqual(event, {'a': 1}) + + def test_update_success(self): + event = self._makeOne({'a': 1}) + event.update({'b': 2}) + self.assertEqual(event, {'a': 1, 'b': 2}) + + def test__contains__True(self): + system = {'a': 1} + event = self._makeOne(system) + self.assertTrue('a' in event) + + def test__contains__False(self): + system = {} + event = self._makeOne(system) + self.assertFalse('a' in event) + + def test__getitem__success(self): + system = {'a': 1} + event = self._makeOne(system) + self.assertEqual(event['a'], 1) + + def test__getitem__fail(self): + system = {} + event = self._makeOne(system) + self.assertRaises(KeyError, event.__getitem__, 'a') + + def test_get_success(self): + system = {'a': 1} + event = self._makeOne(system) + self.assertEqual(event.get('a'), 1) + + def test_get_fail(self): + system = {} + event = self._makeOne(system) + self.assertEqual(event.get('a'), None) + + def test_rendering_val(self): + system = {} + val = {} + event = self._makeOne(system, val) + self.assertTrue(event.rendering_val is val) + + +class DummyConfigurator(object): + def __init__(self): + self.subscribed = [] + + def add_subscriber(self, wrapped, ifaces, **predicates): + if not predicates: + self.subscribed.append((wrapped, ifaces)) + else: + self.subscribed.append((wrapped, ifaces, predicates)) + + +class DummyRegistry(object): + pass + + +class DummyVenusian(object): + def __init__(self): + self.attached = [] + + def attach(self, wrapped, fn, category=None, depth=None): + self.attached.append((wrapped, fn, category, depth)) + + +class Dummy: + pass + + +class DummyRequest: + pass + + +class DummyResponse: + pass diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 000000000..399940c05 --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,113 @@ +import unittest + + +class TestBWCompat(unittest.TestCase): + def test_bwcompat_notfound(self): + from pyramid.exceptions import NotFound as one + from pyramid.httpexceptions import HTTPNotFound as two + + self.assertTrue(one is two) + + def test_bwcompat_forbidden(self): + from pyramid.exceptions import Forbidden as one + from pyramid.httpexceptions import HTTPForbidden as two + + self.assertTrue(one is two) + + +class TestBadCSRFToken(unittest.TestCase): + def test_response_equivalence(self): + from pyramid.exceptions import BadCSRFToken + from pyramid.httpexceptions import HTTPBadRequest + + self.assertTrue(isinstance(BadCSRFToken(), HTTPBadRequest)) + + +class TestNotFound(unittest.TestCase): + def _makeOne(self, message): + from pyramid.exceptions import NotFound + + return NotFound(message) + + def test_it(self): + from pyramid.interfaces import IExceptionResponse + + e = self._makeOne('notfound') + self.assertTrue(IExceptionResponse.providedBy(e)) + self.assertEqual(e.status, '404 Not Found') + self.assertEqual(e.message, 'notfound') + + def test_response_equivalence(self): + from pyramid.exceptions import NotFound + from pyramid.httpexceptions import HTTPNotFound + + self.assertTrue(NotFound is HTTPNotFound) + + +class TestForbidden(unittest.TestCase): + def _makeOne(self, message): + from pyramid.exceptions import Forbidden + + return Forbidden(message) + + def test_it(self): + from pyramid.interfaces import IExceptionResponse + + e = self._makeOne('forbidden') + self.assertTrue(IExceptionResponse.providedBy(e)) + self.assertEqual(e.status, '403 Forbidden') + self.assertEqual(e.message, 'forbidden') + + def test_response_equivalence(self): + from pyramid.exceptions import Forbidden + from pyramid.httpexceptions import HTTPForbidden + + self.assertTrue(Forbidden is HTTPForbidden) + + +class TestConfigurationConflictError(unittest.TestCase): + def _makeOne(self, conflicts): + from pyramid.exceptions import ConfigurationConflictError + + return ConfigurationConflictError(conflicts) + + def test_str(self): + conflicts = {'a': ('1', '2', '3'), 'b': ('4', '5', '6')} + exc = self._makeOne(conflicts) + self.assertEqual( + str(exc), + """\ +Conflicting configuration actions + For: a + 1 + 2 + 3 + For: b + 4 + 5 + 6""", + ) + + +class TestConfigurationExecutionError(unittest.TestCase): + def _makeOne(self, etype, evalue, info): + from pyramid.exceptions import ConfigurationExecutionError + + return ConfigurationExecutionError(etype, evalue, info) + + def test_str(self): + exc = self._makeOne('etype', 'evalue', 'info') + self.assertEqual(str(exc), 'etype: evalue\n in:\n info') + + +class TestCyclicDependencyError(unittest.TestCase): + def _makeOne(self, cycles): + from pyramid.exceptions import CyclicDependencyError + + return CyclicDependencyError(cycles) + + def test___str__(self): + exc = self._makeOne({'a': ['c', 'd'], 'c': ['a']}) + result = str(exc) + self.assertTrue("'a' sorts before ['c', 'd']" in result) + self.assertTrue("'c' sorts before ['a']" in result) diff --git a/tests/test_httpexceptions.py b/tests/test_httpexceptions.py new file mode 100644 index 000000000..4c13e096d --- /dev/null +++ b/tests/test_httpexceptions.py @@ -0,0 +1,529 @@ +import unittest + +from pyramid.compat import bytes_, string_types, text_ + + +class Test_exception_response(unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.httpexceptions import exception_response + + return exception_response(*arg, **kw) + + def test_status_400(self): + from pyramid.httpexceptions import HTTPBadRequest + + self.assertTrue(isinstance(self._callFUT(400), HTTPBadRequest)) + + def test_status_404(self): + from pyramid.httpexceptions import HTTPNotFound + + self.assertTrue(isinstance(self._callFUT(404), HTTPNotFound)) + + def test_status_500(self): + from pyramid.httpexceptions import HTTPInternalServerError + + self.assertTrue( + isinstance(self._callFUT(500), HTTPInternalServerError) + ) + + def test_status_201(self): + from pyramid.httpexceptions import HTTPCreated + + self.assertTrue(isinstance(self._callFUT(201), HTTPCreated)) + + def test_extra_kw(self): + resp = self._callFUT(404, headers=[('abc', 'def')]) + self.assertEqual(resp.headers['abc'], 'def') + + +class Test_default_exceptionresponse_view(unittest.TestCase): + def _callFUT(self, context, request): + from pyramid.httpexceptions import default_exceptionresponse_view + + return default_exceptionresponse_view(context, request) + + def test_call_with_exception(self): + context = Exception() + result = self._callFUT(context, None) + self.assertEqual(result, context) + + def test_call_with_nonexception(self): + request = DummyRequest() + context = Exception() + request.exception = context + result = self._callFUT(None, request) + self.assertEqual(result, context) + + +class Test__no_escape(unittest.TestCase): + def _callFUT(self, val): + from pyramid.httpexceptions import _no_escape + + return _no_escape(val) + + def test_null(self): + self.assertEqual(self._callFUT(None), '') + + def test_not_basestring(self): + self.assertEqual(self._callFUT(42), '42') + + def test_unicode(self): + class DummyUnicodeObject(object): + def __unicode__(self): + return text_('42') + + duo = DummyUnicodeObject() + self.assertEqual(self._callFUT(duo), text_('42')) + + +class TestHTTPException(unittest.TestCase): + def _getTargetClass(self): + from pyramid.httpexceptions import HTTPException + + return HTTPException + + def _getTargetSubclass( + self, + code='200', + title='OK', + explanation='explanation', + empty_body=False, + ): + cls = self._getTargetClass() + + class Subclass(cls): + pass + + Subclass.empty_body = empty_body + Subclass.code = code + Subclass.title = title + Subclass.explanation = explanation + return Subclass + + def _makeOne(self, *arg, **kw): + cls = self._getTargetClass() + return cls(*arg, **kw) + + def test_implements_IResponse(self): + from pyramid.interfaces import IResponse + + cls = self._getTargetClass() + self.assertTrue(IResponse.implementedBy(cls)) + + def test_provides_IResponse(self): + from pyramid.interfaces import IResponse + + inst = self._getTargetClass()() + self.assertTrue(IResponse.providedBy(inst)) + + def test_implements_IExceptionResponse(self): + from pyramid.interfaces import IExceptionResponse + + cls = self._getTargetClass() + self.assertTrue(IExceptionResponse.implementedBy(cls)) + + def test_provides_IExceptionResponse(self): + from pyramid.interfaces import IExceptionResponse + + inst = self._getTargetClass()() + self.assertTrue(IExceptionResponse.providedBy(inst)) + + def test_ctor_sets_detail(self): + exc = self._makeOne('message') + self.assertEqual(exc.detail, 'message') + + def test_ctor_sets_comment(self): + exc = self._makeOne(comment='comment') + self.assertEqual(exc.comment, 'comment') + + def test_ctor_calls_Exception_ctor(self): + exc = self._makeOne('message') + self.assertEqual(exc.message, 'message') + + def test_ctor_calls_Response_ctor(self): + exc = self._makeOne('message') + self.assertEqual(exc.status, '520 Unknown Error') + + def test_ctor_extends_headers(self): + exc = self._makeOne(headers=[('X-Foo', 'foo')]) + self.assertEqual(exc.headers.get('X-Foo'), 'foo') + + def test_ctor_sets_body_template_obj(self): + exc = self._makeOne(body_template='${foo}') + self.assertEqual( + exc.body_template_obj.substitute({'foo': 'foo'}), 'foo' + ) + + def test_ctor_with_empty_body(self): + cls = self._getTargetSubclass(empty_body=True) + exc = cls() + self.assertEqual(exc.content_type, None) + self.assertEqual(exc.content_length, None) + + def test_ctor_with_body_doesnt_set_default_app_iter(self): + exc = self._makeOne(body=b'123') + self.assertEqual(exc.app_iter, [b'123']) + + def test_ctor_with_unicode_body_doesnt_set_default_app_iter(self): + exc = self._makeOne(unicode_body=text_('123')) + self.assertEqual(exc.app_iter, [b'123']) + + def test_ctor_with_app_iter_doesnt_set_default_app_iter(self): + exc = self._makeOne(app_iter=[b'123']) + self.assertEqual(exc.app_iter, [b'123']) + + def test_ctor_with_body_sets_default_app_iter_html(self): + cls = self._getTargetSubclass() + exc = cls('detail') + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'text/html' + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertTrue(body.startswith(b'<html')) + self.assertTrue(b'200 OK' in body) + self.assertTrue(b'explanation' in body) + self.assertTrue(b'detail' in body) + + def test_ctor_with_body_sets_default_app_iter_text(self): + cls = self._getTargetSubclass() + exc = cls('detail') + environ = _makeEnviron() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertEqual(body, b'200 OK\n\nexplanation\n\n\ndetail\n\n') + + def test__str__detail(self): + exc = self._makeOne() + exc.detail = 'abc' + self.assertEqual(str(exc), 'abc') + + def test__str__explanation(self): + exc = self._makeOne() + exc.explanation = 'def' + self.assertEqual(str(exc), 'def') + + def test_wsgi_response(self): + exc = self._makeOne() + self.assertTrue(exc is exc.wsgi_response) + + def test_exception(self): + exc = self._makeOne() + self.assertTrue(exc is exc.exception) + + def test__calls_start_response(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + start_response = DummyStartResponse() + exc(environ, start_response) + self.assertTrue(start_response.headerlist) + self.assertEqual(start_response.status, '200 OK') + + def test_call_returns_same_body_called_twice(self): + # optimization + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = '*/*' + start_response = DummyStartResponse() + app_iter = exc(environ, start_response) + self.assertEqual(app_iter[0], exc.body) + + def test__default_app_iter_no_comment_plain(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/plain; charset=UTF-8') + self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\n\n') + + def test__default_app_iter_with_comment_plain(self): + cls = self._getTargetSubclass() + exc = cls(comment='comment') + environ = _makeEnviron() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/plain; charset=UTF-8') + self.assertEqual(body, b'200 OK\n\nexplanation\n\n\n\ncomment\n') + + def test__default_app_iter_no_comment_html(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/plain; charset=UTF-8') + self.assertFalse(b'<!-- ' in body) + + def test__content_type(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + start_response = DummyStartResponse() + exc(environ, start_response) + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/plain; charset=UTF-8') + + def test__content_type_default_is_html(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = '*/*' + start_response = DummyStartResponse() + exc(environ, start_response) + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/html; charset=UTF-8') + + def test__content_type_text_html(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'text/html' + start_response = DummyStartResponse() + exc(environ, start_response) + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/html; charset=UTF-8') + + def test__content_type_application_json(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'application/json' + start_response = DummyStartResponse() + exc(environ, start_response) + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'application/json') + + def test__content_type_invalid(self): + cls = self._getTargetSubclass() + exc = cls() + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'invalid' + start_response = DummyStartResponse() + exc(environ, start_response) + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/html; charset=UTF-8') + + def test__default_app_iter_with_comment_ampersand(self): + cls = self._getTargetSubclass() + exc = cls(comment='comment & comment') + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'text/html' + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + for header in start_response.headerlist: + if header[0] == 'Content-Type': + self.assertEqual(header[1], 'text/html; charset=UTF-8') + self.assertTrue(b'<!-- comment & comment -->' in body) + + def test__default_app_iter_with_comment_html(self): + cls = self._getTargetSubclass() + exc = cls(comment='comment & comment') + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'text/html' + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertTrue(b'<!-- comment & comment -->' in body) + + def test__default_app_iter_with_comment_json(self): + cls = self._getTargetSubclass() + exc = cls(comment='comment & comment') + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'application/json' + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + import json + + retval = json.loads(body.decode('UTF-8')) + self.assertEqual(retval['code'], '200 OK') + self.assertEqual(retval['title'], 'OK') + + def test__default_app_iter_with_custom_json(self): + def json_formatter(status, body, title, environ): + return { + 'message': body, + 'code': status, + 'title': title, + 'custom': environ['CUSTOM_VARIABLE'], + } + + cls = self._getTargetSubclass() + exc = cls(comment='comment', json_formatter=json_formatter) + environ = _makeEnviron() + environ['HTTP_ACCEPT'] = 'application/json' + environ['CUSTOM_VARIABLE'] = 'custom!' + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + import json + + retval = json.loads(body.decode('UTF-8')) + self.assertEqual(retval['code'], '200 OK') + self.assertEqual(retval['title'], 'OK') + self.assertEqual(retval['custom'], 'custom!') + + def test_custom_body_template(self): + cls = self._getTargetSubclass() + exc = cls(body_template='${REQUEST_METHOD}') + environ = _makeEnviron() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertEqual(body, b'200 OK\n\nGET') + + def test_custom_body_template_with_custom_variable_doesnt_choke(self): + cls = self._getTargetSubclass() + exc = cls(body_template='${REQUEST_METHOD}') + environ = _makeEnviron() + + class Choke(object): + def __str__(self): # pragma no cover + raise ValueError + + environ['gardentheory.user'] = Choke() + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertEqual(body, b'200 OK\n\nGET') + + def test_body_template_unicode(self): + cls = self._getTargetSubclass() + la = text_(b'/La Pe\xc3\xb1a', 'utf-8') + environ = _makeEnviron(unicodeval=la) + exc = cls(body_template='${unicodeval}') + start_response = DummyStartResponse() + body = list(exc(environ, start_response))[0] + self.assertEqual(body, b'200 OK\n\n/La Pe\xc3\xb1a') + + def test_allow_detail_non_str(self): + exc = self._makeOne(detail={'error': 'This is a test'}) + self.assertIsInstance(exc.__str__(), string_types) + + +class TestRenderAllExceptionsWithoutArguments(unittest.TestCase): + def _doit(self, content_type): + from pyramid.httpexceptions import status_map + + L = [] + self.assertTrue(status_map) + for v in status_map.values(): + environ = _makeEnviron() + start_response = DummyStartResponse() + exc = v() + exc.content_type = content_type + result = list(exc(environ, start_response))[0] + if exc.empty_body: + self.assertEqual(result, b'') + else: + self.assertTrue(bytes_(exc.status) in result) + L.append(result) + self.assertEqual(len(L), len(status_map)) + + def test_it_plain(self): + self._doit('text/plain') + + def test_it_html(self): + self._doit('text/html') + + +class Test_HTTPMove(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.httpexceptions import _HTTPMove + + return _HTTPMove(*arg, **kw) + + def test_it_location_none_valueerrors(self): + # Constructing a HTTPMove instance with location=None should + # throw a ValueError from __init__ so that a more-confusing + # exception won't be thrown later from .prepare(environ) + self.assertRaises(ValueError, self._makeOne, location=None) + + def test_it_location_not_passed(self): + exc = self._makeOne() + self.assertEqual(exc.location, '') + + def test_it_location_passed(self): + exc = self._makeOne(location='foo') + self.assertEqual(exc.location, 'foo') + + def test_it_location_firstarg(self): + exc = self._makeOne('foo') + self.assertEqual(exc.location, 'foo') + + def test_it_call_with_default_body_tmpl(self): + exc = self._makeOne(location='foo') + environ = _makeEnviron() + start_response = DummyStartResponse() + app_iter = exc(environ, start_response) + self.assertEqual( + app_iter[0], + ( + b'520 Unknown Error\n\nThe resource has been moved to foo; ' + b'you should be redirected automatically.\n\n' + ), + ) + + +class TestHTTPForbidden(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.httpexceptions import HTTPForbidden + + return HTTPForbidden(*arg, **kw) + + def test_it_result_not_passed(self): + exc = self._makeOne() + self.assertEqual(exc.result, None) + + def test_it_result_passed(self): + exc = self._makeOne(result='foo') + self.assertEqual(exc.result, 'foo') + + +class TestHTTPMethodNotAllowed(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.httpexceptions import HTTPMethodNotAllowed + + return HTTPMethodNotAllowed(*arg, **kw) + + def test_it_with_default_body_tmpl(self): + exc = self._makeOne() + environ = _makeEnviron() + start_response = DummyStartResponse() + app_iter = exc(environ, start_response) + self.assertEqual( + app_iter[0], + ( + b'405 Method Not Allowed\n\nThe method GET is not ' + b'allowed for this resource. \n\n\n' + ), + ) + + +class DummyRequest(object): + exception = None + + +class DummyStartResponse(object): + def __call__(self, status, headerlist): + self.status = status + self.headerlist = headerlist + + +def _makeEnviron(**kw): + environ = { + 'REQUEST_METHOD': 'GET', + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'localhost', + 'SERVER_PORT': '80', + } + environ.update(kw) + return environ diff --git a/tests/test_i18n.py b/tests/test_i18n.py new file mode 100644 index 000000000..78891200d --- /dev/null +++ b/tests/test_i18n.py @@ -0,0 +1,553 @@ +# -*- coding: utf-8 -*- +import os +import unittest +from pyramid import testing + +here = os.path.dirname(__file__) +localedir = os.path.join(here, 'pkgs', 'localeapp', 'locale') + + +class TestTranslationString(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.i18n import TranslationString + + return TranslationString(*arg, **kw) + + def test_it(self): + # this is part of the API, we don't actually need to test much more + # than that it's importable + ts = self._makeOne('a') + self.assertEqual(ts, 'a') + + +class TestTranslationStringFactory(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.i18n import TranslationStringFactory + + return TranslationStringFactory(*arg, **kw) + + def test_it(self): + # this is part of the API, we don't actually need to test much more + # than that it's importable + factory = self._makeOne('a') + self.assertEqual(factory('').domain, 'a') + + +class TestLocalizer(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.i18n import Localizer + + return Localizer(*arg, **kw) + + def test_ctor(self): + localizer = self._makeOne('en_US', None) + self.assertEqual(localizer.locale_name, 'en_US') + self.assertEqual(localizer.translations, None) + + def test_translate(self): + translations = DummyTranslations() + localizer = self._makeOne(None, translations) + self.assertEqual( + localizer.translate('123', domain='1', mapping={}), '123' + ) + self.assertTrue(localizer.translator) + + def test_pluralize(self): + translations = DummyTranslations() + localizer = self._makeOne(None, translations) + result = localizer.pluralize( + 'singular', 'plural', 1, domain='1', mapping={} + ) + self.assertEqual(result, 'singular') + self.assertTrue(localizer.pluralizer) + + def test_pluralize_pluralizer_already_added(self): + translations = DummyTranslations() + localizer = self._makeOne(None, translations) + + def pluralizer(*arg, **kw): + return arg, kw + + localizer.pluralizer = pluralizer + result = localizer.pluralize( + 'singular', 'plural', 1, domain='1', mapping={} + ) + self.assertEqual( + result, (('singular', 'plural', 1), {'domain': '1', 'mapping': {}}) + ) + self.assertTrue(localizer.pluralizer is pluralizer) + + def test_pluralize_default_translations(self): + # test that even without message ids loaded that + # "localizer.pluralize" "works" instead of raising an inscrutable + # "translations object has no attr 'plural' error; see + # see https://github.com/Pylons/pyramid/issues/235 + from pyramid.i18n import Translations + + translations = Translations() + translations._catalog = {} + localizer = self._makeOne(None, translations) + result = localizer.pluralize( + 'singular', 'plural', 2, domain='1', mapping={} + ) + self.assertEqual(result, 'plural') + + +class Test_negotiate_locale_name(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request): + from pyramid.i18n import negotiate_locale_name + + return negotiate_locale_name(request) + + def _registerImpl(self, impl): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + from pyramid.interfaces import ILocaleNegotiator + + registry.registerUtility(impl, ILocaleNegotiator) + + def test_no_registry_on_request(self): + self._registerImpl(dummy_negotiator) + request = DummyRequest() + result = self._callFUT(request) + self.assertEqual(result, 'bogus') + + def test_with_registry_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + self._registerImpl(dummy_negotiator) + request = DummyRequest() + request.registry = registry + result = self._callFUT(request) + self.assertEqual(result, 'bogus') + + def test_default_from_settings(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + settings = {'default_locale_name': 'settings'} + registry.settings = settings + request = DummyRequest() + request.registry = registry + result = self._callFUT(request) + self.assertEqual(result, 'settings') + + def test_use_default_locale_negotiator(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = DummyRequest() + request.registry = registry + request._LOCALE_ = 'locale' + result = self._callFUT(request) + self.assertEqual(result, 'locale') + + def test_default_default(self): + request = DummyRequest() + result = self._callFUT(request) + self.assertEqual(result, 'en') + + +class Test_get_locale_name(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request): + from pyramid.i18n import get_locale_name + + return get_locale_name(request) + + def test_name_on_request(self): + request = DummyRequest() + request.locale_name = 'ie' + result = self._callFUT(request) + self.assertEqual(result, 'ie') + + +class Test_make_localizer(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, locale, tdirs): + from pyramid.i18n import make_localizer + + return make_localizer(locale, tdirs) + + def test_locale_from_mo(self): + from pyramid.i18n import Localizer + + localedirs = [localedir] + locale_name = 'de' + result = self._callFUT(locale_name, localedirs) + self.assertEqual(result.__class__, Localizer) + self.assertEqual( + result.translate('Approve', 'deformsite'), 'Genehmigen' + ) + self.assertEqual(result.translate('Approve'), 'Approve') + self.assertTrue(hasattr(result, 'pluralize')) + + def test_locale_from_mo_bad_mo(self): + from pyramid.i18n import Localizer + + localedirs = [localedir] + locale_name = 'be' + result = self._callFUT(locale_name, localedirs) + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') + + def test_locale_from_mo_mo_isdir(self): + from pyramid.i18n import Localizer + + localedirs = [localedir] + locale_name = 'gb' + result = self._callFUT(locale_name, localedirs) + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') + + def test_territory_fallback(self): + from pyramid.i18n import Localizer + + localedirs = [localedir] + locale_name = 'de_DE' + result = self._callFUT(locale_name, localedirs) + self.assertEqual(result.__class__, Localizer) + self.assertEqual( + result.translate('Submit', 'deformsite'), 'different' + ) # prefer translations from de_DE locale + self.assertEqual( + result.translate('Approve', 'deformsite'), 'Genehmigen' + ) # missing from de_DE locale, but in de + + +class Test_get_localizer(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request): + from pyramid.i18n import get_localizer + + return get_localizer(request) + + def test_it(self): + request = DummyRequest() + request.localizer = 'localizer' + self.assertEqual(self._callFUT(request), 'localizer') + + +class Test_default_locale_negotiator(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request): + from pyramid.i18n import default_locale_negotiator + + return default_locale_negotiator(request) + + def test_from_none(self): + request = DummyRequest() + result = self._callFUT(request) + self.assertEqual(result, None) + + def test_from_request_attr(self): + request = DummyRequest() + request._LOCALE_ = 'foo' + result = self._callFUT(request) + self.assertEqual(result, 'foo') + + def test_from_params(self): + request = DummyRequest() + request.params['_LOCALE_'] = 'foo' + result = self._callFUT(request) + self.assertEqual(result, 'foo') + + def test_from_cookies(self): + request = DummyRequest() + request.cookies['_LOCALE_'] = 'foo' + result = self._callFUT(request) + self.assertEqual(result, 'foo') + + +class TestTranslations(unittest.TestCase): + def _getTargetClass(self): + from pyramid.i18n import Translations + + return Translations + + def _makeOne(self): + messages1 = [('foo', 'Voh'), (('foo1', 1), 'Voh1')] + messages2 = [('foo', 'VohD'), (('foo1', 1), 'VohD1')] + + klass = self._getTargetClass() + + translations1 = klass(None, domain='messages') + translations1._catalog = dict(messages1) + translations1.plural = lambda *arg: 1 + translations2 = klass(None, domain='messages1') + translations2._catalog = dict(messages2) + translations2.plural = lambda *arg: 1 + translations = translations1.add(translations2, merge=False) + return translations + + def test_load_locales_None(self): + import gettext + + klass = self._getTargetClass() + result = klass.load(localedir, None, domain=None) + self.assertEqual(result.__class__, gettext.NullTranslations) + + def test_load_domain_None(self): + import gettext + + locales = ['de', 'en'] + klass = self._getTargetClass() + result = klass.load(localedir, locales, domain=None) + self.assertEqual(result.__class__, gettext.NullTranslations) + + def test_load_found_locale_and_domain(self): + locales = ['de', 'en'] + klass = self._getTargetClass() + result = klass.load(localedir, locales, domain='deformsite') + self.assertEqual(result.__class__, klass) + + def test_load_found_locale_and_domain_locale_is_string(self): + locales = 'de' + klass = self._getTargetClass() + result = klass.load(localedir, locales, domain='deformsite') + self.assertEqual(result.__class__, klass) + + def test___repr__(self): + inst = self._makeOne() + result = repr(inst) + self.assertEqual(result, '<Translations: "None">') + + def test_merge_not_gnutranslations(self): + inst = self._makeOne() + self.assertEqual(inst.merge(None), inst) + + def test_merge_gnutranslations(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2._catalog['a'] = 'b' + inst.merge(inst2) + self.assertEqual(inst._catalog['a'], 'b') + + def test_merge_gnutranslations_not_translations(self): + import gettext + + t = gettext.GNUTranslations() + t._catalog = {'a': 'b'} + inst = self._makeOne() + inst.merge(t) + self.assertEqual(inst._catalog['a'], 'b') + + def test_add_different_domain_merge_true_notexisting(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2.domain = 'domain2' + inst.add(inst2) + self.assertEqual(inst._domains['domain2'], inst2) + + def test_add_different_domain_merge_true_existing(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst3 = self._makeOne() + inst2.domain = 'domain2' + inst2._catalog['a'] = 'b' + inst3.domain = 'domain2' + inst._domains['domain2'] = inst3 + inst.add(inst2) + self.assertEqual(inst._domains['domain2'], inst3) + self.assertEqual(inst3._catalog['a'], 'b') + + def test_add_same_domain_merge_true(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2._catalog['a'] = 'b' + inst.add(inst2) + self.assertEqual(inst._catalog['a'], 'b') + + def test_add_default_domain_replaces_plural_first_time(self): + # Create three empty message catalogs in the default domain + inst = self._getTargetClass()(None, domain='messages') + inst2 = self._getTargetClass()(None, domain='messages') + inst3 = self._getTargetClass()(None, domain='messages') + inst._catalog = {} + inst2._catalog = {} + inst3._catalog = {} + + # The default plural scheme is the germanic one + self.assertEqual(inst.plural(0), 1) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + + # inst2 represents a message file that declares french plurals + inst2.plural = lambda n: n > 1 + inst.add(inst2) + # that plural rule should now apply to inst + self.assertEqual(inst.plural(0), 0) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + + # We load a second message file with different plural rules + inst3.plural = lambda n: n > 0 + inst.add(inst3) + # It doesn't override the previously loaded rule + self.assertEqual(inst.plural(0), 0) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + + def test_dgettext(self): + t = self._makeOne() + self.assertEqual(t.dgettext('messages', 'foo'), 'Voh') + self.assertEqual(t.dgettext('messages1', 'foo'), 'VohD') + + def test_ldgettext(self): + t = self._makeOne() + self.assertEqual(t.ldgettext('messages', 'foo'), b'Voh') + self.assertEqual(t.ldgettext('messages1', 'foo'), b'VohD') + + def test_dugettext(self): + t = self._makeOne() + self.assertEqual(t.dugettext('messages', 'foo'), 'Voh') + self.assertEqual(t.dugettext('messages1', 'foo'), 'VohD') + + def test_dngettext(self): + t = self._makeOne() + self.assertEqual(t.dngettext('messages', 'foo1', 'foos1', 1), 'Voh1') + self.assertEqual(t.dngettext('messages1', 'foo1', 'foos1', 1), 'VohD1') + + def test_ldngettext(self): + t = self._makeOne() + self.assertEqual(t.ldngettext('messages', 'foo1', 'foos1', 1), b'Voh1') + self.assertEqual( + t.ldngettext('messages1', 'foo1', 'foos1', 1), b'VohD1' + ) + + def test_dungettext(self): + t = self._makeOne() + self.assertEqual(t.dungettext('messages', 'foo1', 'foos1', 1), 'Voh1') + self.assertEqual( + t.dungettext('messages1', 'foo1', 'foos1', 1), 'VohD1' + ) + + def test_default_germanic_pluralization(self): + t = self._getTargetClass()() + t._catalog = {} + result = t.dungettext('messages', 'foo1', 'foos1', 2) + self.assertEqual(result, 'foos1') + + +class TestLocalizerRequestMixin(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self): + from pyramid.i18n import LocalizerRequestMixin + + request = LocalizerRequestMixin() + request.registry = self.config.registry + request.cookies = {} + request.params = {} + return request + + def test_default_localizer(self): + # `localizer` returns a default localizer for `en` + from pyramid.i18n import Localizer + + request = self._makeOne() + self.assertEqual(request.localizer.__class__, Localizer) + self.assertEqual(request.locale_name, 'en') + + def test_custom_localizer_for_default_locale(self): + from pyramid.interfaces import ILocalizer + + dummy = object() + self.config.registry.registerUtility(dummy, ILocalizer, name='en') + request = self._makeOne() + self.assertEqual(request.localizer, dummy) + + def test_custom_localizer_for_custom_locale(self): + from pyramid.interfaces import ILocalizer + + dummy = object() + self.config.registry.registerUtility(dummy, ILocalizer, name='ie') + request = self._makeOne() + request._LOCALE_ = 'ie' + self.assertEqual(request.localizer, dummy) + + def test_localizer_from_mo(self): + from pyramid.interfaces import ITranslationDirectories + from pyramid.i18n import Localizer + + localedirs = [localedir] + self.config.registry.registerUtility( + localedirs, ITranslationDirectories + ) + request = self._makeOne() + request._LOCALE_ = 'de' + result = request.localizer + self.assertEqual(result.__class__, Localizer) + self.assertEqual( + result.translate('Approve', 'deformsite'), 'Genehmigen' + ) + self.assertEqual(result.translate('Approve'), 'Approve') + self.assertTrue(hasattr(result, 'pluralize')) + + def test_localizer_from_mo_bad_mo(self): + from pyramid.interfaces import ITranslationDirectories + from pyramid.i18n import Localizer + + localedirs = [localedir] + self.config.registry.registerUtility( + localedirs, ITranslationDirectories + ) + request = self._makeOne() + request._LOCALE_ = 'be' + result = request.localizer + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') + + +class DummyRequest(object): + def __init__(self): + self.params = {} + self.cookies = {} + + +def dummy_negotiator(request): + return 'bogus' + + +class DummyTranslations(object): + def ugettext(self, text): + return text + + gettext = ugettext + + def ungettext(self, singular, plural, n): + return singular + + ngettext = ungettext diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 000000000..d57a7cf6e --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,933 @@ +# -*- coding: utf-8 -*- +import datetime +import gc +import locale +import os +import unittest +from webtest import TestApp +from zope.interface import Interface + +from pyramid.wsgi import wsgiapp +from pyramid.view import view_config +from pyramid.static import static_view +from pyramid.testing import skip_on +from pyramid.compat import text_, url_quote + +from .pkgs.exceptionviewapp.models import AnException, NotAnException + +# 5 years from now (more or less) +fiveyrsfuture = datetime.datetime.utcnow() + datetime.timedelta(5 * 365) + +defaultlocale = locale.getdefaultlocale()[1] + + +class INothing(Interface): + pass + + +@view_config(for_=INothing) +@wsgiapp +def wsgiapptest(environ, start_response): + """ """ + return '123' + + +class WGSIAppPlusViewConfigTests(unittest.TestCase): + def test_it(self): + from venusian import ATTACH_ATTR + import types + + self.assertTrue(getattr(wsgiapptest, ATTACH_ATTR)) + self.assertIsInstance(wsgiapptest, types.FunctionType) + context = DummyContext() + request = DummyRequest() + result = wsgiapptest(context, request) + self.assertEqual(result, '123') + + def test_scanned(self): + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.config import Configurator + from . import test_integration + + config = Configurator() + config.scan(test_integration) + config.commit() + reg = config.registry + view = reg.adapters.lookup( + (IViewClassifier, IRequest, INothing), IView, name='' + ) + self.assertEqual(view.__original_view__, wsgiapptest) + + +class IntegrationBase(object): + root_factory = None + package = None + + def setUp(self): + from pyramid.config import Configurator + + config = Configurator( + root_factory=self.root_factory, package=self.package + ) + config.include(self.package) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + +here = os.path.dirname(__file__) + + +class StaticAppBase(IntegrationBase): + def test_basic(self): + res = self.testapp.get('/minimal.txt', status=200) + _assertBody(res.body, os.path.join(here, 'fixtures/minimal.txt')) + + def test_hidden(self): + res = self.testapp.get('/static/.hiddenfile', status=200) + _assertBody( + res.body, os.path.join(here, 'fixtures/static/.hiddenfile') + ) + + if defaultlocale is not None: # pragma: no cover + # These tests are expected to fail on LANG=C systems due to decode + # errors and on non-Linux systems due to git highchar handling + # vagaries + def test_highchars_in_pathelement(self): + path = os.path.join( + here, text_('fixtures/static/héhé/index.html', 'utf-8') + ) + pathdir = os.path.dirname(path) + body = b'<html>hehe</html>\n' + try: + os.makedirs(pathdir) + with open(path, 'wb') as fp: + fp.write(body) + url = url_quote('/static/héhé/index.html') + res = self.testapp.get(url, status=200) + self.assertEqual(res.body, body) + finally: + os.unlink(path) + os.rmdir(pathdir) + + def test_highchars_in_filename(self): + path = os.path.join( + here, text_('fixtures/static/héhé.html', 'utf-8') + ) + body = b'<html>hehe file</html>\n' + with open(path, 'wb') as fp: + fp.write(body) + try: + url = url_quote('/static/héhé.html') + res = self.testapp.get(url, status=200) + self.assertEqual(res.body, body) + finally: + os.unlink(path) + + def test_not_modified(self): + self.testapp.extra_environ = { + 'HTTP_IF_MODIFIED_SINCE': httpdate(fiveyrsfuture) + } + res = self.testapp.get('/minimal.txt', status=304) + self.assertEqual(res.body, b'') + + def test_file_in_subdir(self): + fn = os.path.join(here, 'fixtures/static/index.html') + res = self.testapp.get('/static/index.html', status=200) + _assertBody(res.body, fn) + + def test_directory_noslash_redir(self): + res = self.testapp.get('/static', status=301) + self.assertEqual(res.headers['Location'], 'http://localhost/static/') + + def test_directory_noslash_redir_preserves_qs(self): + res = self.testapp.get('/static?a=1&b=2', status=301) + self.assertEqual( + res.headers['Location'], 'http://localhost/static/?a=1&b=2' + ) + + def test_directory_noslash_redir_with_scriptname(self): + self.testapp.extra_environ = {'SCRIPT_NAME': '/script_name'} + res = self.testapp.get('/static', status=301) + self.assertEqual( + res.headers['Location'], 'http://localhost/script_name/static/' + ) + + def test_directory_withslash(self): + fn = os.path.join(here, 'fixtures/static/index.html') + res = self.testapp.get('/static/', status=200) + _assertBody(res.body, fn) + + def test_range_inclusive(self): + self.testapp.extra_environ = {'HTTP_RANGE': 'bytes=1-2'} + res = self.testapp.get('/static/index.html', status=206) + self.assertEqual(res.body, b'ht') + + def test_range_tilend(self): + self.testapp.extra_environ = {'HTTP_RANGE': 'bytes=-5'} + res = self.testapp.get('/static/index.html', status=206) + self.assertEqual(res.body, b'html>') + + def test_range_notbytes(self): + self.testapp.extra_environ = {'HTTP_RANGE': 'kHz=-5'} + res = self.testapp.get('/static/index.html', status=200) + _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) + + def test_range_multiple(self): + res = self.testapp.get( + '/static/index.html', + [('HTTP_RANGE', 'bytes=10-11,11-12')], + status=200, + ) + _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) + + def test_range_oob(self): + self.testapp.extra_environ = {'HTTP_RANGE': 'bytes=1000-1002'} + self.testapp.get('/static/index.html', status=416) + + def test_notfound(self): + self.testapp.get('/static/wontbefound.html', status=404) + + def test_oob_dotdotslash(self): + self.testapp.get('/static/../../test_integration.py', status=404) + + def test_oob_dotdotslash_encoded(self): + self.testapp.get('/static/%2E%2E%2F/test_integration.py', status=404) + + def test_oob_slash(self): + self.testapp.get('/%2F/test_integration.py', status=404) + + +class TestEventOnlySubscribers(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.eventonly' + + def test_sendfoo(self): + res = self.testapp.get('/sendfoo', status=200) + self.assertEqual(sorted(res.body.split()), [b'foo', b'fooyup']) + + def test_sendfoobar(self): + res = self.testapp.get('/sendfoobar', status=200) + self.assertEqual( + sorted(res.body.split()), + [b'foobar', b'foobar2', b'foobaryup', b'foobaryup2'], + ) + + +class TestStaticAppUsingAbsPath(StaticAppBase, unittest.TestCase): + package = 'tests.pkgs.static_abspath' + + +class TestStaticAppUsingAssetSpec(StaticAppBase, unittest.TestCase): + package = 'tests.pkgs.static_assetspec' + + +class TestStaticAppNoSubpath(unittest.TestCase): + staticapp = static_view(os.path.join(here, 'fixtures'), use_subpath=False) + + def _makeRequest(self, extra): + from pyramid.request import Request + from io import BytesIO + + kw = { + 'PATH_INFO': '', + 'SCRIPT_NAME': '', + 'SERVER_NAME': 'localhost', + 'SERVER_PORT': '80', + 'REQUEST_METHOD': 'GET', + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': 'http', + 'wsgi.input': BytesIO(), + } + kw.update(extra) + request = Request(kw) + return request + + def test_basic(self): + request = self._makeRequest({'PATH_INFO': '/minimal.txt'}) + context = DummyContext() + result = self.staticapp(context, request) + self.assertEqual(result.status, '200 OK') + _assertBody(result.body, os.path.join(here, 'fixtures/minimal.txt')) + + +class TestStaticAppWithRoutePrefix(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.static_routeprefix' + + def test_includelevel1(self): + res = self.testapp.get('/static/minimal.txt', status=200) + _assertBody(res.body, os.path.join(here, 'fixtures/minimal.txt')) + + def test_includelevel2(self): + res = self.testapp.get('/prefix/static/index.html', status=200) + _assertBody(res.body, os.path.join(here, 'fixtures/static/index.html')) + + +class TestFixtureApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.fixtureapp' + + def test_another(self): + res = self.testapp.get('/another.html', status=200) + self.assertEqual(res.body, b'fixture') + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertEqual(res.body, b'fixture') + + def test_dummyskin(self): + self.testapp.get('/dummyskin.html', status=404) + + def test_error(self): + res = self.testapp.get('/error.html', status=200) + self.assertEqual(res.body, b'supressed') + + def test_protected(self): + self.testapp.get('/protected.html', status=403) + + +class TestStaticPermApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.staticpermapp' + root_factory = 'tests.pkgs.staticpermapp:RootFactory' + + def test_allowed(self): + result = self.testapp.get('/allowed/index.html', status=200) + _assertBody( + result.body, os.path.join(here, 'fixtures/static/index.html') + ) + + def test_denied_via_acl_global_root_factory(self): + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + self.testapp.get('/protected/index.html', status=403) + + def test_allowed_via_acl_global_root_factory(self): + self.testapp.extra_environ = {'REMOTE_USER': 'fred'} + result = self.testapp.get('/protected/index.html', status=200) + _assertBody( + result.body, os.path.join(here, 'fixtures/static/index.html') + ) + + def test_denied_via_acl_local_root_factory(self): + self.testapp.extra_environ = {'REMOTE_USER': 'fred'} + self.testapp.get('/factory_protected/index.html', status=403) + + def test_allowed_via_acl_local_root_factory(self): + self.testapp.extra_environ = {'REMOTE_USER': 'bob'} + result = self.testapp.get('/factory_protected/index.html', status=200) + _assertBody( + result.body, os.path.join(here, 'fixtures/static/index.html') + ) + + +class TestCCBug(IntegrationBase, unittest.TestCase): + # "unordered" as reported in IRC by author of + # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ + package = 'tests.pkgs.ccbugapp' + + def test_rdf(self): + res = self.testapp.get('/licenses/1/v1/rdf', status=200) + self.assertEqual(res.body, b'rdf') + + def test_juri(self): + res = self.testapp.get('/licenses/1/v1/juri', status=200) + self.assertEqual(res.body, b'juri') + + +class TestHybridApp(IntegrationBase, unittest.TestCase): + # make sure views registered for a route "win" over views registered + # without one, even though the context of the non-route view may + # be more specific than the route view. + package = 'tests.pkgs.hybridapp' + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertEqual(res.body, b'global') + + def test_abc(self): + res = self.testapp.get('/abc', status=200) + self.assertEqual(res.body, b'route') + + def test_def(self): + res = self.testapp.get('/def', status=200) + self.assertEqual(res.body, b'route2') + + def test_ghi(self): + res = self.testapp.get('/ghi', status=200) + self.assertEqual(res.body, b'global') + + def test_jkl(self): + self.testapp.get('/jkl', status=404) + + def test_mno(self): + self.testapp.get('/mno', status=404) + + def test_pqr_global2(self): + res = self.testapp.get('/pqr/global2', status=200) + self.assertEqual(res.body, b'global2') + + def test_error(self): + res = self.testapp.get('/error', status=200) + self.assertEqual(res.body, b'supressed') + + def test_error2(self): + res = self.testapp.get('/error2', status=200) + self.assertEqual(res.body, b'supressed2') + + def test_error_sub(self): + res = self.testapp.get('/error_sub', status=200) + self.assertEqual(res.body, b'supressed2') + + +class TestRestBugApp(IntegrationBase, unittest.TestCase): + # test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515) + package = 'tests.pkgs.restbugapp' + + def test_it(self): + res = self.testapp.get('/pet', status=200) + self.assertEqual(res.body, b'gotten') + + +class TestForbiddenAppHasResult(IntegrationBase, unittest.TestCase): + # test that forbidden exception has ACLDenied result attached + package = 'tests.pkgs.forbiddenapp' + + def test_it(self): + res = self.testapp.get('/x', status=403) + message, result = [x.strip() for x in res.body.split(b'\n')] + self.assertTrue(message.endswith(b'failed permission check')) + self.assertTrue( + result.startswith( + b"ACLDenied permission 'private' via ACE " + b"'<default deny>' in ACL " + b"'<No ACL found on any object in resource " + b"lineage>' on context" + ) + ) + self.assertTrue(result.endswith(b"for principals ['system.Everyone']")) + + +class TestViewDecoratorApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.viewdecoratorapp' + + def test_first(self): + res = self.testapp.get('/first', status=200) + self.assertTrue(b'OK' in res.body) + + def test_second(self): + res = self.testapp.get('/second', status=200) + self.assertTrue(b'OK2' in res.body) + + +class TestNotFoundView(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.notfoundview' + + def test_it(self): + res = self.testapp.get('/wontbefound', status=200) + self.assertTrue(b'generic_notfound' in res.body) + res = self.testapp.get('/bar', status=307) + self.assertEqual(res.location, 'http://localhost/bar/') + res = self.testapp.get('/bar/', status=200) + self.assertTrue(b'OK bar' in res.body) + res = self.testapp.get('/foo', status=307) + self.assertEqual(res.location, 'http://localhost/foo/') + res = self.testapp.get('/foo/', status=200) + self.assertTrue(b'OK foo2' in res.body) + res = self.testapp.get('/baz', status=200) + self.assertTrue(b'baz_notfound' in res.body) + + +class TestForbiddenView(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.forbiddenview' + + def test_it(self): + res = self.testapp.get('/foo', status=200) + self.assertTrue(b'foo_forbidden' in res.body) + res = self.testapp.get('/bar', status=200) + self.assertTrue(b'generic_forbidden' in res.body) + + +class TestViewPermissionBug(IntegrationBase, unittest.TestCase): + # view_execution_permitted bug as reported by Shane at + # http://lists.repoze.org/pipermail/repoze-dev/2010-October/003603.html + package = 'tests.pkgs.permbugapp' + + def test_test(self): + res = self.testapp.get('/test', status=200) + self.assertTrue(b'ACLDenied' in res.body) + + def test_x(self): + self.testapp.get('/x', status=403) + + +class TestDefaultViewPermissionBug(IntegrationBase, unittest.TestCase): + # default_view_permission bug as reported by Wiggy at + # http://lists.repoze.org/pipermail/repoze-dev/2010-October/003602.html + package = 'tests.pkgs.defpermbugapp' + + def test_x(self): + res = self.testapp.get('/x', status=403) + self.assertTrue(b'failed permission check' in res.body) + + def test_y(self): + res = self.testapp.get('/y', status=403) + self.assertTrue(b'failed permission check' in res.body) + + def test_z(self): + res = self.testapp.get('/z', status=200) + self.assertTrue(b'public' in res.body) + + +excroot = {'anexception': AnException(), 'notanexception': NotAnException()} + + +class TestExceptionViewsApp(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.exceptionviewapp' + root_factory = lambda *arg: excroot + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertTrue(b'maybe' in res.body) + + def test_notanexception(self): + res = self.testapp.get('/notanexception', status=200) + self.assertTrue(b'no' in res.body) + + def test_anexception(self): + res = self.testapp.get('/anexception', status=200) + self.assertTrue(b'yes' in res.body) + + def test_route_raise_exception(self): + res = self.testapp.get('/route_raise_exception', status=200) + self.assertTrue(b'yes' in res.body) + + def test_route_raise_exception2(self): + res = self.testapp.get('/route_raise_exception2', status=200) + self.assertTrue(b'yes' in res.body) + + def test_route_raise_exception3(self): + res = self.testapp.get('/route_raise_exception3', status=200) + self.assertTrue(b'whoa' in res.body) + + def test_route_raise_exception4(self): + res = self.testapp.get('/route_raise_exception4', status=200) + self.assertTrue(b'whoa' in res.body) + + def test_raise_httpexception(self): + res = self.testapp.get('/route_raise_httpexception', status=200) + self.assertTrue(b'caught' in res.body) + + +class TestConflictApp(unittest.TestCase): + package = 'tests.pkgs.conflictapp' + + def _makeConfig(self): + from pyramid.config import Configurator + + config = Configurator() + return config + + def test_autoresolved_view(self): + config = self._makeConfig() + config.include(self.package) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + res = self.testapp.get('/') + self.assertTrue(b'a view' in res.body) + res = self.testapp.get('/route') + self.assertTrue(b'route view' in res.body) + + def test_overridden_autoresolved_view(self): + from pyramid.response import Response + + config = self._makeConfig() + config.include(self.package) + + def thisview(request): + return Response('this view') + + config.add_view(thisview) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + res = self.testapp.get('/') + self.assertTrue(b'this view' in res.body) + + def test_overridden_route_view(self): + from pyramid.response import Response + + config = self._makeConfig() + config.include(self.package) + + def thisview(request): + return Response('this view') + + config.add_view(thisview, route_name='aroute') + app = config.make_wsgi_app() + self.testapp = TestApp(app) + res = self.testapp.get('/route') + self.assertTrue(b'this view' in res.body) + + def test_nonoverridden_authorization_policy(self): + config = self._makeConfig() + config.include(self.package) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + res = self.testapp.get('/protected', status=403) + self.assertTrue(b'403 Forbidden' in res.body) + + def test_overridden_authorization_policy(self): + config = self._makeConfig() + config.include(self.package) + from pyramid.testing import DummySecurityPolicy + + config.set_authorization_policy(DummySecurityPolicy('fred')) + config.set_authentication_policy(DummySecurityPolicy(permissive=True)) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + res = self.testapp.get('/protected', status=200) + self.assertTrue('protected view' in res) + + +class ImperativeIncludeConfigurationTest(unittest.TestCase): + def setUp(self): + from pyramid.config import Configurator + + config = Configurator() + from .pkgs.includeapp1.root import configure + + configure(config) + app = config.make_wsgi_app() + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertTrue(b'root' in res.body) + + def test_two(self): + res = self.testapp.get('/two', status=200) + self.assertTrue(b'two' in res.body) + + def test_three(self): + res = self.testapp.get('/three', status=200) + self.assertTrue(b'three' in res.body) + + +class SelfScanAppTest(unittest.TestCase): + def setUp(self): + from .test_config.pkgs.selfscan import main + + config = main() + app = config.make_wsgi_app() + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertTrue(b'root' in res.body) + + def test_two(self): + res = self.testapp.get('/two', status=200) + self.assertTrue(b'two' in res.body) + + +class WSGIApp2AppTest(unittest.TestCase): + def setUp(self): + from .pkgs.wsgiapp2app import main + + config = main() + app = config.make_wsgi_app() + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_hello(self): + res = self.testapp.get('/hello', status=200) + self.assertTrue(b'Hello' in res.body) + + +class SubrequestAppTest(unittest.TestCase): + def setUp(self): + from .pkgs.subrequestapp import main + + config = main() + app = config.make_wsgi_app() + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_one(self): + res = self.testapp.get('/view_one', status=200) + self.assertTrue(b'This came from view_two, foo=bar' in res.body) + + def test_three(self): + res = self.testapp.get('/view_three', status=500) + self.assertTrue(b'Bad stuff happened' in res.body) + + def test_five(self): + res = self.testapp.get('/view_five', status=200) + self.assertTrue(b'Value error raised' in res.body) + + +class RendererScanAppTest(IntegrationBase, unittest.TestCase): + package = 'tests.pkgs.rendererscanapp' + + def test_root(self): + res = self.testapp.get('/one', status=200) + self.assertTrue(b'One!' in res.body) + + def test_two(self): + res = self.testapp.get('/two', status=200) + self.assertTrue(b'Two!' in res.body) + + def test_rescan(self): + self.config.scan('tests.pkgs.rendererscanapp') + app = self.config.make_wsgi_app() + testapp = TestApp(app) + res = testapp.get('/one', status=200) + self.assertTrue(b'One!' in res.body) + res = testapp.get('/two', status=200) + self.assertTrue(b'Two!' in res.body) + + +class UnicodeInURLTest(unittest.TestCase): + def _makeConfig(self): + from pyramid.config import Configurator + + config = Configurator() + return config + + def _makeTestApp(self, config): + app = config.make_wsgi_app() + return TestApp(app) + + def test_unicode_in_url_404(self): + request_path = '/avalia%C3%A7%C3%A3o_participante' + request_path_unicode = b'/avalia\xc3\xa7\xc3\xa3o_participante'.decode( + 'utf-8' + ) + + config = self._makeConfig() + testapp = self._makeTestApp(config) + + res = testapp.get(request_path, status=404) + + # Pyramid default 404 handler outputs: + # u'404 Not Found\n\nThe resource could not be found.\n\n\n' + # u'/avalia\xe7\xe3o_participante\n\n' + self.assertTrue(request_path_unicode in res.text) + + def test_unicode_in_url_200(self): + request_path = '/avalia%C3%A7%C3%A3o_participante' + request_path_unicode = b'/avalia\xc3\xa7\xc3\xa3o_participante'.decode( + 'utf-8' + ) + + def myview(request): + return 'XXX' + + config = self._makeConfig() + config.add_route('myroute', request_path_unicode) + config.add_view(myview, route_name='myroute', renderer='json') + testapp = self._makeTestApp(config) + + res = testapp.get(request_path, status=200) + + self.assertEqual(res.text, '"XXX"') + + +class AcceptContentTypeTest(unittest.TestCase): + def _makeConfig(self): + def hello_view(request): + return {'message': 'Hello!'} + + from pyramid.config import Configurator + + config = Configurator() + config.add_route('hello', '/hello') + config.add_view( + hello_view, + route_name='hello', + accept='text/plain', + renderer='string', + ) + config.add_view( + hello_view, + route_name='hello', + accept='application/json', + renderer='json', + ) + + def hello_fallback_view(request): + request.response.content_type = 'text/x-fallback' + return 'hello fallback' + + config.add_view( + hello_fallback_view, route_name='hello', renderer='string' + ) + return config + + def _makeTestApp(self, config): + app = config.make_wsgi_app() + return TestApp(app) + + def tearDown(self): + import pyramid.config + + pyramid.config.global_registries.empty() + + def test_client_side_ordering(self): + config = self._makeConfig() + app = self._makeTestApp(config) + res = app.get( + '/hello', + headers={'Accept': 'application/json; q=1.0, text/plain; q=0.9'}, + status=200, + ) + self.assertEqual(res.content_type, 'application/json') + res = app.get( + '/hello', + headers={'Accept': 'text/plain; q=0.9, application/json; q=1.0'}, + status=200, + ) + self.assertEqual(res.content_type, 'application/json') + res = app.get( + '/hello', headers={'Accept': 'application/*'}, status=200 + ) + self.assertEqual(res.content_type, 'application/json') + res = app.get('/hello', headers={'Accept': 'text/*'}, status=200) + self.assertEqual(res.content_type, 'text/plain') + res = app.get( + '/hello', headers={'Accept': 'something/else'}, status=200 + ) + self.assertEqual(res.content_type, 'text/x-fallback') + + def test_default_server_side_ordering(self): + config = self._makeConfig() + app = self._makeTestApp(config) + res = app.get( + '/hello', + headers={'Accept': 'application/json, text/plain'}, + status=200, + ) + self.assertEqual(res.content_type, 'text/plain') + res = app.get( + '/hello', + headers={'Accept': 'text/plain, application/json'}, + status=200, + ) + self.assertEqual(res.content_type, 'text/plain') + res = app.get('/hello', headers={'Accept': '*/*'}, status=200) + self.assertEqual(res.content_type, 'text/plain') + res = app.get('/hello', status=200) + self.assertEqual(res.content_type, 'text/plain') + res = app.get('/hello', headers={'Accept': 'invalid'}, status=200) + self.assertEqual(res.content_type, 'text/plain') + res = app.get( + '/hello', headers={'Accept': 'something/else'}, status=200 + ) + self.assertEqual(res.content_type, 'text/x-fallback') + + def test_custom_server_side_ordering(self): + config = self._makeConfig() + config.add_accept_view_order( + 'application/json', weighs_more_than='text/plain' + ) + app = self._makeTestApp(config) + res = app.get( + '/hello', + headers={'Accept': 'application/json, text/plain'}, + status=200, + ) + self.assertEqual(res.content_type, 'application/json') + res = app.get( + '/hello', + headers={'Accept': 'text/plain, application/json'}, + status=200, + ) + self.assertEqual(res.content_type, 'application/json') + res = app.get('/hello', headers={'Accept': '*/*'}, status=200) + self.assertEqual(res.content_type, 'application/json') + res = app.get('/hello', status=200) + self.assertEqual(res.content_type, 'application/json') + res = app.get('/hello', headers={'Accept': 'invalid'}, status=200) + self.assertEqual(res.content_type, 'application/json') + res = app.get( + '/hello', headers={'Accept': 'something/else'}, status=200 + ) + self.assertEqual(res.content_type, 'text/x-fallback') + + +class DummyContext(object): + pass + + +class DummyRequest: + subpath = ('__init__.py',) + traversed = None + environ = {'REQUEST_METHOD': 'GET', 'wsgi.version': (1, 0)} + + def get_response(self, application): + return application(None, None) + + +def httpdate(ts): + return ts.strftime("%a, %d %b %Y %H:%M:%S GMT") + + +def read_(filename): + with open(filename, 'rb') as fp: + val = fp.read() + return val + + +def _assertBody(body, filename): + if defaultlocale is None: # pragma: no cover + # If system locale does not have an encoding then default to utf-8 + filename = filename.encode('utf-8') + # strip both \n and \r for windows + body = body.replace(b'\r', b'') + body = body.replace(b'\n', b'') + data = read_(filename) + data = data.replace(b'\r', b'') + data = data.replace(b'\n', b'') + assert body == data + + +class MemoryLeaksTest(unittest.TestCase): + def tearDown(self): + import pyramid.config + + pyramid.config.global_registries.empty() + + def get_gc_count(self): + last_collected = 0 + while True: + collected = gc.collect() + if collected == last_collected: + break + last_collected = collected + return len(gc.get_objects()) + + @skip_on('pypy') + def test_memory_leaks(self): + from pyramid.config import Configurator + + Configurator().make_wsgi_app() # Initialize all global objects + + initial_count = self.get_gc_count() + Configurator().make_wsgi_app() + current_count = self.get_gc_count() + self.assertEqual(current_count, initial_count) diff --git a/tests/test_location.py b/tests/test_location.py new file mode 100644 index 000000000..163bb85aa --- /dev/null +++ b/tests/test_location.py @@ -0,0 +1,51 @@ +import unittest +from zope.interface import implementer +from pyramid.interfaces import ILocation + + +class TestInside(unittest.TestCase): + def _callFUT(self, one, two): + from pyramid.location import inside + + return inside(one, two) + + def test_inside(self): + o1 = Location() + o2 = Location() + o2.__parent__ = o1 + o3 = Location() + o3.__parent__ = o2 + o4 = Location() + o4.__parent__ = o3 + + self.assertEqual(self._callFUT(o1, o1), True) + self.assertEqual(self._callFUT(o2, o1), True) + self.assertEqual(self._callFUT(o3, o1), True) + self.assertEqual(self._callFUT(o4, o1), True) + self.assertEqual(self._callFUT(o1, o4), False) + self.assertEqual(self._callFUT(o1, None), False) + + +class TestLineage(unittest.TestCase): + def _callFUT(self, context): + from pyramid.location import lineage + + return lineage(context) + + def test_lineage(self): + o1 = Location() + o2 = Location() + o2.__parent__ = o1 + o3 = Location() + o3.__parent__ = o2 + o4 = Location() + o4.__parent__ = o3 + result = list(self._callFUT(o3)) + self.assertEqual(result, [o3, o2, o1]) + result = list(self._callFUT(o1)) + self.assertEqual(result, [o1]) + + +@implementer(ILocation) +class Location(object): + __name__ = __parent__ = None diff --git a/tests/test_paster.py b/tests/test_paster.py new file mode 100644 index 000000000..dd53195c7 --- /dev/null +++ b/tests/test_paster.py @@ -0,0 +1,193 @@ +import os +import unittest +from .test_scripts.dummy import DummyLoader + +here = os.path.dirname(__file__) + + +class Test_get_app(unittest.TestCase): + def _callFUT(self, config_file, section_name, options=None, _loader=None): + import pyramid.paster + + old_loader = pyramid.paster.get_config_loader + try: + if _loader is not None: + pyramid.paster.get_config_loader = _loader + return pyramid.paster.get_app( + config_file, section_name, options=options + ) + finally: + pyramid.paster.get_config_loader = old_loader + + def test_it(self): + app = DummyApp() + loader = DummyLoader(app=app) + result = self._callFUT( + '/foo/bar/myapp.ini', 'myapp', options={'a': 'b'}, _loader=loader + ) + self.assertEqual(loader.uri.path, '/foo/bar/myapp.ini') + self.assertEqual(len(loader.calls), 1) + self.assertEqual(loader.calls[0]['op'], 'app') + self.assertEqual(loader.calls[0]['name'], 'myapp') + self.assertEqual(loader.calls[0]['defaults'], {'a': 'b'}) + self.assertEqual(result, app) + + def test_it_with_dummyapp_requiring_options(self): + options = {'bar': 'baz'} + app = self._callFUT( + os.path.join(here, 'fixtures', 'dummy.ini'), + 'myapp', + options=options, + ) + self.assertEqual(app.settings['foo'], 'baz') + + +class Test_get_appsettings(unittest.TestCase): + def _callFUT(self, config_file, section_name, options=None, _loader=None): + import pyramid.paster + + old_loader = pyramid.paster.get_config_loader + try: + if _loader is not None: + pyramid.paster.get_config_loader = _loader + return pyramid.paster.get_appsettings( + config_file, section_name, options=options + ) + finally: + pyramid.paster.get_config_loader = old_loader + + def test_it(self): + values = {'a': 1} + loader = DummyLoader(app_settings=values) + result = self._callFUT( + '/foo/bar/myapp.ini', 'myapp', options={'a': 'b'}, _loader=loader + ) + self.assertEqual(loader.uri.path, '/foo/bar/myapp.ini') + self.assertEqual(len(loader.calls), 1) + self.assertEqual(loader.calls[0]['op'], 'app_settings') + self.assertEqual(loader.calls[0]['name'], 'myapp') + self.assertEqual(loader.calls[0]['defaults'], {'a': 'b'}) + self.assertEqual(result, values) + + def test_it_with_dummyapp_requiring_options(self): + options = {'bar': 'baz'} + result = self._callFUT( + os.path.join(here, 'fixtures', 'dummy.ini'), + 'myapp', + options=options, + ) + self.assertEqual(result['foo'], 'baz') + + +class Test_setup_logging(unittest.TestCase): + def _callFUT(self, config_file, global_conf=None, _loader=None): + import pyramid.paster + + old_loader = pyramid.paster.get_config_loader + try: + if _loader is not None: + pyramid.paster.get_config_loader = _loader + return pyramid.paster.setup_logging(config_file, global_conf) + finally: + pyramid.paster.get_config_loader = old_loader + + def test_it_no_global_conf(self): + loader = DummyLoader() + self._callFUT('/abc.ini', _loader=loader) + self.assertEqual(loader.uri.path, '/abc.ini') + self.assertEqual(len(loader.calls), 1) + self.assertEqual(loader.calls[0]['op'], 'logging') + self.assertEqual(loader.calls[0]['defaults'], None) + + def test_it_global_conf_empty(self): + loader = DummyLoader() + self._callFUT('/abc.ini', global_conf={}, _loader=loader) + self.assertEqual(loader.uri.path, '/abc.ini') + self.assertEqual(len(loader.calls), 1) + self.assertEqual(loader.calls[0]['op'], 'logging') + self.assertEqual(loader.calls[0]['defaults'], {}) + + def test_it_global_conf_not_empty(self): + loader = DummyLoader() + self._callFUT('/abc.ini', global_conf={'key': 'val'}, _loader=loader) + self.assertEqual(loader.uri.path, '/abc.ini') + self.assertEqual(len(loader.calls), 1) + self.assertEqual(loader.calls[0]['op'], 'logging') + self.assertEqual(loader.calls[0]['defaults'], {'key': 'val'}) + + +class Test_bootstrap(unittest.TestCase): + def _callFUT(self, config_uri, request=None): + from pyramid.paster import bootstrap + + return bootstrap(config_uri, request) + + def setUp(self): + import pyramid.paster + + self.original_get_app = pyramid.paster.get_app + self.original_prepare = pyramid.paster.prepare + self.app = app = DummyApp() + self.root = root = Dummy() + + class DummyGetApp(object): + def __call__(self, *a, **kw): + self.a = a + self.kw = kw + return app + + self.get_app = pyramid.paster.get_app = DummyGetApp() + + class DummyPrepare(object): + def __call__(self, *a, **kw): + self.a = a + self.kw = kw + return {'root': root, 'closer': lambda: None} + + self.getroot = pyramid.paster.prepare = DummyPrepare() + + def tearDown(self): + import pyramid.paster + + pyramid.paster.get_app = self.original_get_app + pyramid.paster.prepare = self.original_prepare + + def test_it_request_with_registry(self): + request = DummyRequest({}) + request.registry = dummy_registry + result = self._callFUT('/foo/bar/myapp.ini', request) + self.assertEqual(result['app'], self.app) + self.assertEqual(result['root'], self.root) + self.assertTrue('closer' in result) + + +class Dummy: + pass + + +class DummyRegistry(object): + settings = {} + + +dummy_registry = DummyRegistry() + + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + + +def make_dummyapp(global_conf, **settings): + app = DummyApp() + app.settings = settings + app.global_conf = global_conf + return app + + +class DummyRequest: + application_url = 'http://example.com:5432' + script_name = '' + + def __init__(self, environ): + self.environ = environ + self.matchdict = {} diff --git a/tests/test_path.py b/tests/test_path.py new file mode 100644 index 000000000..626bb1139 --- /dev/null +++ b/tests/test_path.py @@ -0,0 +1,665 @@ +import unittest +import os +from pyramid.compat import PY2 + +here = os.path.abspath(os.path.dirname(__file__)) + + +class TestCallerPath(unittest.TestCase): + def tearDown(self): + from . import test_path + + if hasattr(test_path, '__abspath__'): + del test_path.__abspath__ + + def _callFUT(self, path, level=2): + from pyramid.path import caller_path + + return caller_path(path, level) + + def test_isabs(self): + result = self._callFUT('/a/b/c') + self.assertEqual(result, '/a/b/c') + + def test_pkgrelative(self): + import os + + result = self._callFUT('a/b/c') + self.assertEqual(result, os.path.join(here, 'a/b/c')) + + def test_memoization_has_abspath(self): + import os + from . import test_path + + test_path.__abspath__ = '/foo/bar' + result = self._callFUT('a/b/c') + self.assertEqual(result, os.path.join('/foo/bar', 'a/b/c')) + + def test_memoization_success(self): + import os + from . import test_path + + result = self._callFUT('a/b/c') + self.assertEqual(result, os.path.join(here, 'a/b/c')) + self.assertEqual(test_path.__abspath__, here) + + +class TestCallerModule(unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.path import caller_module + + return caller_module(*arg, **kw) + + def test_it_level_1(self): + from . import test_path + + result = self._callFUT(1) + self.assertEqual(result, test_path) + + def test_it_level_2(self): + from . import test_path + + result = self._callFUT(2) + self.assertEqual(result, test_path) + + def test_it_level_3(self): + from . import test_path + + result = self._callFUT(3) + self.assertNotEqual(result, test_path) + + def test_it_no___name__(self): + class DummyFrame(object): + f_globals = {} + + class DummySys(object): + def _getframe(self, level): + return DummyFrame() + + modules = {'__main__': 'main'} + + dummy_sys = DummySys() + result = self._callFUT(3, sys=dummy_sys) + self.assertEqual(result, 'main') + + +class TestCallerPackage(unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.path import caller_package + + return caller_package(*arg, **kw) + + def test_it_level_1(self): + import tests + + result = self._callFUT(1) + self.assertEqual(result, tests) + + def test_it_level_2(self): + import tests + + result = self._callFUT(2) + self.assertEqual(result, tests) + + def test_it_level_3(self): + import unittest + + result = self._callFUT(3) + self.assertEqual(result, unittest) + + def test_it_package(self): + import tests + + def dummy_caller_module(*arg): + return tests + + result = self._callFUT(1, caller_module=dummy_caller_module) + self.assertEqual(result, tests) + + +class TestPackagePath(unittest.TestCase): + def _callFUT(self, package): + from pyramid.path import package_path + + return package_path(package) + + def test_it_package(self): + import tests + + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, package.package_path) + + def test_it_module(self): + from . import test_path + + module = DummyPackageOrModule(test_path) + result = self._callFUT(module) + self.assertEqual(result, module.package_path) + + def test_memoization_success(self): + from . import test_path + + module = DummyPackageOrModule(test_path) + self._callFUT(module) + self.assertEqual(module.__abspath__, module.package_path) + + def test_memoization_fail(self): + from . import test_path + + module = DummyPackageOrModule(test_path, raise_exc=TypeError) + result = self._callFUT(module) + self.assertFalse(hasattr(module, '__abspath__')) + self.assertEqual(result, module.package_path) + + +class TestPackageOf(unittest.TestCase): + def _callFUT(self, package): + from pyramid.path import package_of + + return package_of(package) + + def test_it_package(self): + import tests + + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, tests) + + def test_it_module(self): + import tests.test_path + import tests + + package = DummyPackageOrModule(tests.test_path) + result = self._callFUT(package) + self.assertEqual(result, tests) + + +class TestPackageName(unittest.TestCase): + def _callFUT(self, package): + from pyramid.path import package_name + + return package_name(package) + + def test_it_package(self): + import tests + + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, 'tests') + + def test_it_namespace_package(self): + import tests + + package = DummyNamespacePackage(tests) + result = self._callFUT(package) + self.assertEqual(result, 'tests') + + def test_it_module(self): + from . import test_path + + module = DummyPackageOrModule(test_path) + result = self._callFUT(module) + self.assertEqual(result, 'tests') + + def test_it_None(self): + result = self._callFUT(None) + self.assertEqual(result, '__main__') + + def test_it_main(self): + import __main__ + + result = self._callFUT(__main__) + self.assertEqual(result, '__main__') + + +class TestResolver(unittest.TestCase): + def _getTargetClass(self): + from pyramid.path import Resolver + + return Resolver + + def _makeOne(self, package): + return self._getTargetClass()(package) + + def test_get_package_caller_package(self): + import tests + from pyramid.path import CALLER_PACKAGE + + self.assertEqual(self._makeOne(CALLER_PACKAGE).get_package(), tests) + + def test_get_package_name_caller_package(self): + from pyramid.path import CALLER_PACKAGE + + self.assertEqual( + self._makeOne(CALLER_PACKAGE).get_package_name(), 'tests' + ) + + def test_get_package_string(self): + import tests + + self.assertEqual(self._makeOne('tests').get_package(), tests) + + def test_get_package_name_string(self): + self.assertEqual(self._makeOne('tests').get_package_name(), 'tests') + + +class TestAssetResolver(unittest.TestCase): + def _getTargetClass(self): + from pyramid.path import AssetResolver + + return AssetResolver + + def _makeOne(self, package='tests'): + return self._getTargetClass()(package) + + def test_ctor_as_package(self): + import sys + + tests = sys.modules['tests'] + inst = self._makeOne(tests) + self.assertEqual(inst.package, tests) + + def test_ctor_as_str(self): + import sys + + tests = sys.modules['tests'] + inst = self._makeOne('tests') + self.assertEqual(inst.package, tests) + + def test_resolve_abspath(self): + from pyramid.path import FSAssetDescriptor + + inst = self._makeOne(None) + r = inst.resolve(os.path.join(here, 'test_asset.py')) + self.assertEqual(r.__class__, FSAssetDescriptor) + self.assertTrue(r.exists()) + + def test_resolve_absspec(self): + from pyramid.path import PkgResourcesAssetDescriptor + + inst = self._makeOne(None) + r = inst.resolve('tests:test_asset.py') + self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) + self.assertTrue(r.exists()) + + def test_resolve_relspec_with_pkg(self): + from pyramid.path import PkgResourcesAssetDescriptor + + inst = self._makeOne('tests') + r = inst.resolve('test_asset.py') + self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) + self.assertTrue(r.exists()) + + def test_resolve_relspec_no_package(self): + inst = self._makeOne(None) + self.assertRaises(ValueError, inst.resolve, 'test_asset.py') + + def test_resolve_relspec_caller_package(self): + from pyramid.path import PkgResourcesAssetDescriptor + from pyramid.path import CALLER_PACKAGE + + inst = self._makeOne(CALLER_PACKAGE) + r = inst.resolve('test_asset.py') + self.assertEqual(r.__class__, PkgResourcesAssetDescriptor) + self.assertTrue(r.exists()) + + +class TestPkgResourcesAssetDescriptor(unittest.TestCase): + def _getTargetClass(self): + from pyramid.path import PkgResourcesAssetDescriptor + + return PkgResourcesAssetDescriptor + + def _makeOne(self, pkg='tests', path='test_asset.py'): + return self._getTargetClass()(pkg, path) + + def test_class_conforms_to_IAssetDescriptor(self): + from pyramid.interfaces import IAssetDescriptor + from zope.interface.verify import verifyClass + + verifyClass(IAssetDescriptor, self._getTargetClass()) + + def test_instance_conforms_to_IAssetDescriptor(self): + from pyramid.interfaces import IAssetDescriptor + from zope.interface.verify import verifyObject + + verifyObject(IAssetDescriptor, self._makeOne()) + + def test_absspec(self): + inst = self._makeOne() + self.assertEqual(inst.absspec(), 'tests:test_asset.py') + + def test_abspath(self): + inst = self._makeOne() + self.assertEqual(inst.abspath(), os.path.join(here, 'test_asset.py')) + + def test_stream(self): + inst = self._makeOne() + inst.pkg_resources = DummyPkgResource() + inst.pkg_resources.resource_stream = lambda x, y: '%s:%s' % (x, y) + s = inst.stream() + self.assertEqual(s, '%s:%s' % ('tests', 'test_asset.py')) + + def test_isdir(self): + inst = self._makeOne() + inst.pkg_resources = DummyPkgResource() + inst.pkg_resources.resource_isdir = lambda x, y: '%s:%s' % (x, y) + self.assertEqual(inst.isdir(), '%s:%s' % ('tests', 'test_asset.py')) + + def test_listdir(self): + inst = self._makeOne() + inst.pkg_resources = DummyPkgResource() + inst.pkg_resources.resource_listdir = lambda x, y: '%s:%s' % (x, y) + self.assertEqual(inst.listdir(), '%s:%s' % ('tests', 'test_asset.py')) + + def test_exists(self): + inst = self._makeOne() + inst.pkg_resources = DummyPkgResource() + inst.pkg_resources.resource_exists = lambda x, y: '%s:%s' % (x, y) + self.assertEqual(inst.exists(), '%s:%s' % ('tests', 'test_asset.py')) + + +class TestFSAssetDescriptor(unittest.TestCase): + def _getTargetClass(self): + from pyramid.path import FSAssetDescriptor + + return FSAssetDescriptor + + def _makeOne(self, path=os.path.join(here, 'test_asset.py')): + return self._getTargetClass()(path) + + def test_class_conforms_to_IAssetDescriptor(self): + from pyramid.interfaces import IAssetDescriptor + from zope.interface.verify import verifyClass + + verifyClass(IAssetDescriptor, self._getTargetClass()) + + def test_instance_conforms_to_IAssetDescriptor(self): + from pyramid.interfaces import IAssetDescriptor + from zope.interface.verify import verifyObject + + verifyObject(IAssetDescriptor, self._makeOne()) + + def test_absspec(self): + inst = self._makeOne() + self.assertRaises(NotImplementedError, inst.absspec) + + def test_abspath(self): + inst = self._makeOne() + self.assertEqual(inst.abspath(), os.path.join(here, 'test_asset.py')) + + def test_stream(self): + inst = self._makeOne() + s = inst.stream() + val = s.read() + s.close() + self.assertTrue(b'asset' in val) + + def test_isdir_False(self): + inst = self._makeOne() + self.assertFalse(inst.isdir()) + + def test_isdir_True(self): + inst = self._makeOne(here) + self.assertTrue(inst.isdir()) + + def test_listdir(self): + inst = self._makeOne(here) + self.assertTrue(inst.listdir()) + + def test_exists(self): + inst = self._makeOne() + self.assertTrue(inst.exists()) + + +class TestDottedNameResolver(unittest.TestCase): + def _makeOne(self, package=None): + from pyramid.path import DottedNameResolver + + return DottedNameResolver(package) + + def config_exc(self, func, *arg, **kw): + try: + func(*arg, **kw) + except ValueError as e: + return e + else: + raise AssertionError('Invalid not raised') # pragma: no cover + + def test_zope_dottedname_style_resolve_builtin(self): + typ = self._makeOne() + if PY2: + result = typ._zope_dottedname_style('__builtin__.str', None) + else: + result = typ._zope_dottedname_style('builtins.str', None) + self.assertEqual(result, str) + + def test_zope_dottedname_style_resolve_absolute(self): + typ = self._makeOne() + result = typ._zope_dottedname_style( + 'tests.test_path.TestDottedNameResolver', None + ) + self.assertEqual(result, self.__class__) + + def test_zope_dottedname_style_irrresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises( + ImportError, + typ._zope_dottedname_style, + 'pyramid.test_path.nonexisting_name', + None, + ) + + def test__zope_dottedname_style_resolve_relative(self): + import tests + + typ = self._makeOne() + result = typ._zope_dottedname_style( + '.test_path.TestDottedNameResolver', tests + ) + self.assertEqual(result, self.__class__) + + def test__zope_dottedname_style_resolve_relative_leading_dots(self): + import tests.test_path + + typ = self._makeOne() + result = typ._zope_dottedname_style( + '..tests.test_path.TestDottedNameResolver', tests + ) + self.assertEqual(result, self.__class__) + + def test__zope_dottedname_style_resolve_relative_is_dot(self): + import tests + + typ = self._makeOne() + result = typ._zope_dottedname_style('.', tests) + self.assertEqual(result, tests) + + def test__zope_dottedname_style_irresolveable_relative_is_dot(self): + typ = self._makeOne() + e = self.config_exc(typ._zope_dottedname_style, '.', None) + self.assertEqual( + e.args[0], "relative name '.' irresolveable without package" + ) + + def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self): + typ = self._makeOne() + e = self.config_exc(typ._zope_dottedname_style, '.whatever', None) + self.assertEqual( + e.args[0], + "relative name '.whatever' irresolveable without package", + ) + + def test_zope_dottedname_style_irrresolveable_relative(self): + import tests + + typ = self._makeOne() + self.assertRaises( + ImportError, typ._zope_dottedname_style, '.notexisting', tests + ) + + def test__zope_dottedname_style_resolveable_relative(self): + import tests + + typ = self._makeOne() + result = typ._zope_dottedname_style('.', tests) + self.assertEqual(result, tests) + + def test__zope_dottedname_style_irresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises( + ImportError, typ._zope_dottedname_style, 'pyramid.fudge.bar', None + ) + + def test__zope_dottedname_style_resolveable_absolute(self): + typ = self._makeOne() + result = typ._zope_dottedname_style( + 'tests.test_path.TestDottedNameResolver', None + ) + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_resolve_absolute(self): + typ = self._makeOne() + result = typ._pkg_resources_style( + 'tests.test_path:TestDottedNameResolver', None + ) + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_irrresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises( + ImportError, typ._pkg_resources_style, 'tests:nonexisting', None + ) + + def test__pkg_resources_style_resolve_relative(self): + import tests + + typ = self._makeOne() + result = typ._pkg_resources_style( + '.test_path:TestDottedNameResolver', tests + ) + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_resolve_relative_is_dot(self): + import tests + + typ = self._makeOne() + result = typ._pkg_resources_style('.', tests) + self.assertEqual(result, tests) + + def test__pkg_resources_style_resolve_relative_nocurrentpackage(self): + typ = self._makeOne() + self.assertRaises( + ValueError, typ._pkg_resources_style, '.whatever', None + ) + + def test__pkg_resources_style_irrresolveable_relative(self): + import pyramid + + typ = self._makeOne() + self.assertRaises( + ImportError, typ._pkg_resources_style, ':notexisting', pyramid + ) + + def test_resolve_not_a_string(self): + typ = self._makeOne() + e = self.config_exc(typ.resolve, None) + self.assertEqual(e.args[0], 'None is not a string') + + def test_resolve_using_pkgresources_style(self): + typ = self._makeOne() + result = typ.resolve('tests.test_path:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test_resolve_using_zope_dottedname_style(self): + typ = self._makeOne() + result = typ.resolve('tests.test_path:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test_resolve_missing_raises(self): + typ = self._makeOne() + self.assertRaises(ImportError, typ.resolve, 'cant.be.found') + + def test_resolve_caller_package(self): + from pyramid.path import CALLER_PACKAGE + + typ = self._makeOne(CALLER_PACKAGE) + self.assertEqual( + typ.resolve('.test_path.TestDottedNameResolver'), self.__class__ + ) + + def test_maybe_resolve_caller_package(self): + from pyramid.path import CALLER_PACKAGE + + typ = self._makeOne(CALLER_PACKAGE) + self.assertEqual( + typ.maybe_resolve('.test_path.TestDottedNameResolver'), + self.__class__, + ) + + def test_ctor_string_module_resolveable(self): + import tests + + typ = self._makeOne('tests.test_path') + self.assertEqual(typ.package, tests) + + def test_ctor_string_package_resolveable(self): + import tests + + typ = self._makeOne('tests') + self.assertEqual(typ.package, tests) + + def test_ctor_string_irresolveable(self): + self.assertRaises(ValueError, self._makeOne, 'cant.be.found') + + def test_ctor_module(self): + import tests + from . import test_path + + typ = self._makeOne(test_path) + self.assertEqual(typ.package, tests) + + def test_ctor_package(self): + import tests + + typ = self._makeOne(tests) + self.assertEqual(typ.package, tests) + + def test_ctor_None(self): + typ = self._makeOne(None) + self.assertEqual(typ.package, None) + + +class DummyPkgResource(object): + pass + + +class DummyPackageOrModule: + def __init__(self, real_package_or_module, raise_exc=None): + self.__dict__['raise_exc'] = raise_exc + self.__dict__['__name__'] = real_package_or_module.__name__ + import os + + self.__dict__['package_path'] = os.path.dirname( + os.path.abspath(real_package_or_module.__file__) + ) + self.__dict__['__file__'] = real_package_or_module.__file__ + + def __setattr__(self, key, val): + if self.raise_exc is not None: + raise self.raise_exc + self.__dict__[key] = val + + +class DummyNamespacePackage: + """Has no __file__ attribute. + """ + + def __init__(self, real_package_or_module): + self.__name__ = real_package_or_module.__name__ + import os + + self.package_path = os.path.dirname( + os.path.abspath(real_package_or_module.__file__) + ) diff --git a/tests/test_predicates.py b/tests/test_predicates.py new file mode 100644 index 000000000..c072b4229 --- /dev/null +++ b/tests/test_predicates.py @@ -0,0 +1,594 @@ +import unittest + +from pyramid import testing + +from pyramid.compat import text_ + + +class TestXHRPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import XHRPredicate + + return XHRPredicate(val, None) + + def test___call___true(self): + inst = self._makeOne(True) + request = Dummy() + request.is_xhr = True + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(True) + request = Dummy() + request.is_xhr = False + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(True) + self.assertEqual(inst.text(), 'xhr = True') + + def test_phash(self): + inst = self._makeOne(True) + self.assertEqual(inst.phash(), 'xhr = True') + + +class TestRequestMethodPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import RequestMethodPredicate + + return RequestMethodPredicate(val, None) + + def test_ctor_get_but_no_head(self): + inst = self._makeOne('GET') + self.assertEqual(inst.val, ('GET', 'HEAD')) + + def test___call___true_single(self): + inst = self._makeOne('GET') + request = Dummy() + request.method = 'GET' + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_multi(self): + inst = self._makeOne(('GET', 'HEAD')) + request = Dummy() + request.method = 'GET' + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(('GET', 'HEAD')) + request = Dummy() + request.method = 'POST' + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(('HEAD', 'GET')) + self.assertEqual(inst.text(), 'request_method = GET,HEAD') + + def test_phash(self): + inst = self._makeOne(('HEAD', 'GET')) + self.assertEqual(inst.phash(), 'request_method = GET,HEAD') + + +class TestPathInfoPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import PathInfoPredicate + + return PathInfoPredicate(val, None) + + def test_ctor_compilefail(self): + from pyramid.exceptions import ConfigurationError + + self.assertRaises(ConfigurationError, self._makeOne, '\\') + + def test___call___true(self): + inst = self._makeOne(r'/\d{2}') + request = Dummy() + request.upath_info = text_('/12') + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(r'/\d{2}') + request = Dummy() + request.upath_info = text_('/n12') + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne('/') + self.assertEqual(inst.text(), 'path_info = /') + + def test_phash(self): + inst = self._makeOne('/') + self.assertEqual(inst.phash(), 'path_info = /') + + +class TestRequestParamPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import RequestParamPredicate + + return RequestParamPredicate(val, None) + + def test___call___true_exists(self): + inst = self._makeOne('abc') + request = Dummy() + request.params = {'abc': 1} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withval(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.params = {'abc': '1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_multi(self): + inst = self._makeOne(('abc', '=def =2= ')) + request = Dummy() + request.params = {'abc': '1', '=def': '2='} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false_multi(self): + inst = self._makeOne(('abc=3', 'def =2 ')) + request = Dummy() + request.params = {'abc': '3', 'def': '1'} + result = inst(None, request) + self.assertFalse(result) + + def test___call___false(self): + inst = self._makeOne('abc') + request = Dummy() + request.params = {} + result = inst(None, request) + self.assertFalse(result) + + def test_text_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.text(), 'request_param abc') + + def test_text_exists_equal_sign(self): + inst = self._makeOne('=abc') + self.assertEqual(inst.text(), 'request_param =abc') + + def test_text_withval(self): + inst = self._makeOne('abc= 1') + self.assertEqual(inst.text(), 'request_param abc=1') + + def test_text_multi(self): + inst = self._makeOne(('abc= 1', 'def')) + self.assertEqual(inst.text(), 'request_param abc=1,def') + + def test_text_multi_equal_sign(self): + inst = self._makeOne(('abc= 1', '=def= 2')) + self.assertEqual(inst.text(), 'request_param =def=2,abc=1') + + def test_phash_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.phash(), 'request_param abc') + + def test_phash_exists_equal_sign(self): + inst = self._makeOne('=abc') + self.assertEqual(inst.phash(), 'request_param =abc') + + def test_phash_withval(self): + inst = self._makeOne('abc= 1') + self.assertEqual(inst.phash(), "request_param abc=1") + + +class TestMatchParamPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import MatchParamPredicate + + return MatchParamPredicate(val, None) + + def test___call___true_single(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = {'abc': '1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_multi(self): + inst = self._makeOne(('abc=1', 'def=2')) + request = Dummy() + request.matchdict = {'abc': '1', 'def': '2'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = {} + result = inst(None, request) + self.assertFalse(result) + + def test___call___matchdict_is_None(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = None + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(('def= 1', 'abc =2')) + self.assertEqual(inst.text(), 'match_param abc=2,def=1') + + def test_phash(self): + inst = self._makeOne(('def= 1', 'abc =2')) + self.assertEqual(inst.phash(), 'match_param abc=2,def=1') + + +class TestCustomPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import CustomPredicate + + return CustomPredicate(val, None) + + def test___call___true(self): + def func(context, request): + self.assertEqual(context, None) + self.assertEqual(request, None) + return True + + inst = self._makeOne(func) + result = inst(None, None) + self.assertTrue(result) + + def test___call___false(self): + def func(context, request): + self.assertEqual(context, None) + self.assertEqual(request, None) + return False + + inst = self._makeOne(func) + result = inst(None, None) + self.assertFalse(result) + + def test_text_func_has___text__(self): + pred = predicate() + pred.__text__ = 'text' + inst = self._makeOne(pred) + self.assertEqual(inst.text(), 'text') + + def test_text_func_repr(self): + pred = predicate() + inst = self._makeOne(pred) + self.assertEqual(inst.text(), 'custom predicate: object predicate') + + def test_phash(self): + pred = predicate() + inst = self._makeOne(pred) + self.assertEqual(inst.phash(), 'custom:1') + + +class TestTraversePredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import TraversePredicate + + return TraversePredicate(val, None) + + def test___call__traverse_has_remainder_already(self): + inst = self._makeOne('/1/:a/:b') + info = {'traverse': 'abc'} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'traverse': 'abc'}) + + def test___call__traverse_matches(self): + inst = self._makeOne('/1/:a/:b') + info = {'match': {'a': 'a', 'b': 'b'}} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual( + info, {'match': {'a': 'a', 'b': 'b', 'traverse': ('1', 'a', 'b')}} + ) + + def test___call__traverse_matches_with_highorder_chars(self): + inst = self._makeOne(text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8')) + info = {'match': {'x': text_(b'Qu\xc3\xa9bec', 'utf-8')}} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual( + info['match']['traverse'], + ( + text_(b'La Pe\xc3\xb1a', 'utf-8'), + text_(b'Qu\xc3\xa9bec', 'utf-8'), + ), + ) + + def test_text(self): + inst = self._makeOne('/abc') + self.assertEqual(inst.text(), 'traverse matchdict pseudo-predicate') + + def test_phash(self): + inst = self._makeOne('/abc') + self.assertEqual(inst.phash(), '') + + +class Test_CheckCSRFTokenPredicate(unittest.TestCase): + def _makeOne(self, val, config): + from pyramid.predicates import CheckCSRFTokenPredicate + + return CheckCSRFTokenPredicate(val, config) + + def test_text(self): + inst = self._makeOne(True, None) + self.assertEqual(inst.text(), 'check_csrf = True') + + def test_phash(self): + inst = self._makeOne(True, None) + self.assertEqual(inst.phash(), 'check_csrf = True') + + def test_it_call_val_True(self): + inst = self._makeOne(True, None) + request = Dummy() + + def check_csrf_token(req, val, raises=True): + self.assertEqual(req, request) + self.assertEqual(val, 'csrf_token') + self.assertEqual(raises, False) + return True + + inst.check_csrf_token = check_csrf_token + result = inst(None, request) + self.assertEqual(result, True) + + def test_it_call_val_str(self): + inst = self._makeOne('abc', None) + request = Dummy() + + def check_csrf_token(req, val, raises=True): + self.assertEqual(req, request) + self.assertEqual(val, 'abc') + self.assertEqual(raises, False) + return True + + inst.check_csrf_token = check_csrf_token + result = inst(None, request) + self.assertEqual(result, True) + + def test_it_call_val_False(self): + inst = self._makeOne(False, None) + request = Dummy() + result = inst(None, request) + self.assertEqual(result, True) + + +class TestHeaderPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import HeaderPredicate + + return HeaderPredicate(val, None) + + def test___call___true_exists(self): + inst = self._makeOne('abc') + request = Dummy() + request.headers = {'abc': 1} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withval(self): + inst = self._makeOne('abc:1') + request = Dummy() + request.headers = {'abc': '1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withregex(self): + inst = self._makeOne(r'abc:\d+') + request = Dummy() + request.headers = {'abc': '1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false_withregex(self): + inst = self._makeOne(r'abc:\d+') + request = Dummy() + request.headers = {'abc': 'a'} + result = inst(None, request) + self.assertFalse(result) + + def test___call___false(self): + inst = self._makeOne('abc') + request = Dummy() + request.headers = {} + result = inst(None, request) + self.assertFalse(result) + + def test_text_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.text(), 'header abc') + + def test_text_withval(self): + inst = self._makeOne('abc:1') + self.assertEqual(inst.text(), 'header abc=1') + + def test_text_withregex(self): + inst = self._makeOne(r'abc:\d+') + self.assertEqual(inst.text(), r'header abc=\d+') + + def test_phash_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.phash(), 'header abc') + + def test_phash_withval(self): + inst = self._makeOne('abc:1') + self.assertEqual(inst.phash(), "header abc=1") + + def test_phash_withregex(self): + inst = self._makeOne(r'abc:\d+') + self.assertEqual(inst.phash(), r'header abc=\d+') + + +class Test_PhysicalPathPredicate(unittest.TestCase): + def _makeOne(self, val, config): + from pyramid.predicates import PhysicalPathPredicate + + return PhysicalPathPredicate(val, config) + + def test_text(self): + inst = self._makeOne('/', None) + self.assertEqual(inst.text(), "physical_path = ('',)") + + def test_phash(self): + inst = self._makeOne('/', None) + self.assertEqual(inst.phash(), "physical_path = ('',)") + + def test_it_call_val_tuple_True(self): + inst = self._makeOne(('', 'abc'), None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_val_list_True(self): + inst = self._makeOne(['', 'abc'], None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_val_str_True(self): + inst = self._makeOne('/abc', None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_False(self): + inst = self._makeOne('/', None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertFalse(inst(context, None)) + + def test_it_call_context_has_no_name(self): + inst = self._makeOne('/', None) + context = Dummy() + self.assertFalse(inst(context, None)) + + +class Test_EffectivePrincipalsPredicate(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, val, config): + from pyramid.predicates import EffectivePrincipalsPredicate + + return EffectivePrincipalsPredicate(val, config) + + def test_text(self): + inst = self._makeOne(('verna', 'fred'), None) + self.assertEqual( + inst.text(), "effective_principals = ['fred', 'verna']" + ) + + def test_text_noniter(self): + inst = self._makeOne('verna', None) + self.assertEqual(inst.text(), "effective_principals = ['verna']") + + def test_phash(self): + inst = self._makeOne(('verna', 'fred'), None) + self.assertEqual( + inst.phash(), "effective_principals = ['fred', 'verna']" + ) + + def test_it_call_no_authentication_policy(self): + request = testing.DummyRequest() + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertFalse(inst(context, request)) + + def test_it_call_authentication_policy_provides_superset(self): + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertTrue(inst(context, request)) + + def test_it_call_authentication_policy_provides_superset_implicit(self): + from pyramid.security import Authenticated + + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + inst = self._makeOne(Authenticated, None) + context = Dummy() + self.assertTrue(inst(context, request)) + + def test_it_call_authentication_policy_doesnt_provide_superset(self): + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred') + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertFalse(inst(context, request)) + + +class TestNotted(unittest.TestCase): + def _makeOne(self, predicate): + from pyramid.predicates import Notted + + return Notted(predicate) + + def test_it_with_phash_val(self): + pred = DummyPredicate('val') + inst = self._makeOne(pred) + self.assertEqual(inst.text(), '!val') + self.assertEqual(inst.phash(), '!val') + self.assertEqual(inst(None, None), False) + + def test_it_without_phash_val(self): + pred = DummyPredicate('') + inst = self._makeOne(pred) + self.assertEqual(inst.text(), '') + self.assertEqual(inst.phash(), '') + self.assertEqual(inst(None, None), True) + + +class predicate(object): + def __repr__(self): + return 'predicate' + + def __hash__(self): + return 1 + + +class Dummy(object): + pass + + +class DummyPredicate(object): + def __init__(self, result): + self.result = result + + def text(self): + return self.result + + phash = text + + def __call__(self, context, request): + return True diff --git a/tests/test_registry.py b/tests/test_registry.py new file mode 100644 index 000000000..aee4f0e15 --- /dev/null +++ b/tests/test_registry.py @@ -0,0 +1,452 @@ +import unittest +from zope.interface import Interface +from zope.interface import implementer + + +class TestRegistry(unittest.TestCase): + def _getTargetClass(self): + from pyramid.registry import Registry + + return Registry + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test___nonzero__(self): + registry = self._makeOne() + self.assertEqual(registry.__nonzero__(), True) + + def test__lock(self): + registry = self._makeOne() + self.assertTrue(registry._lock) + + def test_clear_view_cache_lookup(self): + registry = self._makeOne() + registry._view_lookup_cache[1] = 2 + registry._clear_view_lookup_cache() + self.assertEqual(registry._view_lookup_cache, {}) + + def test_package_name(self): + package_name = 'testing' + registry = self._makeOne(package_name) + self.assertEqual(registry.package_name, package_name) + + def test_default_package_name(self): + registry = self._makeOne() + self.assertEqual(registry.package_name, 'tests') + + def test_registerHandler_and_notify(self): + registry = self._makeOne() + self.assertEqual(registry.has_listeners, False) + L = [] + + def f(event): + L.append(event) + + registry.registerHandler(f, [IDummyEvent]) + self.assertEqual(registry.has_listeners, True) + event = DummyEvent() + registry.notify(event) + self.assertEqual(L, [event]) + + def test_registerSubscriptionAdapter(self): + registry = self._makeOne() + self.assertEqual(registry.has_listeners, False) + from zope.interface import Interface + + registry.registerSubscriptionAdapter( + DummyEvent, [IDummyEvent], Interface + ) + self.assertEqual(registry.has_listeners, True) + + def test__get_settings(self): + registry = self._makeOne() + registry._settings = 'foo' + self.assertEqual(registry.settings, 'foo') + + def test__set_settings(self): + registry = self._makeOne() + registry.settings = 'foo' + self.assertEqual(registry._settings, 'foo') + + def test_init_forwards_args(self): + from zope.interface import Interface + from zope.interface.registry import Components + + dummy = object() + c = Components() + c.registerUtility(dummy, Interface) + registry = self._makeOne('foo', (c,)) + self.assertEqual(registry.__name__, 'foo') + self.assertEqual(registry.getUtility(Interface), dummy) + + def test_init_forwards_kw(self): + from zope.interface import Interface + from zope.interface.registry import Components + + dummy = object() + c = Components() + c.registerUtility(dummy, Interface) + registry = self._makeOne(bases=(c,)) + self.assertEqual(registry.getUtility(Interface), dummy) + + +class TestIntrospector(unittest.TestCase): + def _getTargetClass(slf): + from pyramid.registry import Introspector + + return Introspector + + def _makeOne(self): + return self._getTargetClass()() + + def test_conformance(self): + from zope.interface.verify import verifyClass + from zope.interface.verify import verifyObject + from pyramid.interfaces import IIntrospector + + verifyClass(IIntrospector, self._getTargetClass()) + verifyObject(IIntrospector, self._makeOne()) + + def test_add(self): + inst = self._makeOne() + intr = DummyIntrospectable() + inst.add(intr) + self.assertEqual(intr.order, 0) + category = {'discriminator': intr, 'discriminator_hash': intr} + self.assertEqual(inst._categories, {'category': category}) + + def test_get_success(self): + inst = self._makeOne() + intr = DummyIntrospectable() + inst.add(intr) + self.assertEqual(inst.get('category', 'discriminator'), intr) + + def test_get_success_byhash(self): + inst = self._makeOne() + intr = DummyIntrospectable() + inst.add(intr) + self.assertEqual(inst.get('category', 'discriminator_hash'), intr) + + def test_get_fail(self): + inst = self._makeOne() + intr = DummyIntrospectable() + inst.add(intr) + self.assertEqual(inst.get('category', 'wontexist', 'foo'), 'foo') + + def test_get_category(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr2) + inst.add(intr) + expected = [ + {'introspectable': intr2, 'related': []}, + {'introspectable': intr, 'related': []}, + ] + self.assertEqual(inst.get_category('category'), expected) + + def test_get_category_returns_default_on_miss(self): + inst = self._makeOne() + self.assertEqual(inst.get_category('category', '123'), '123') + + def test_get_category_with_sortkey(self): + import operator + + inst = self._makeOne() + intr = DummyIntrospectable() + intr.foo = 2 + intr2 = DummyIntrospectable() + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + intr2.foo = 1 + inst.add(intr) + inst.add(intr2) + expected = [ + {'introspectable': intr2, 'related': []}, + {'introspectable': intr, 'related': []}, + ] + self.assertEqual( + inst.get_category('category', sort_key=operator.attrgetter('foo')), + expected, + ) + + def test_categorized(self): + import operator + + inst = self._makeOne() + intr = DummyIntrospectable() + intr.foo = 2 + intr2 = DummyIntrospectable() + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + intr2.foo = 1 + inst.add(intr) + inst.add(intr2) + expected = [ + ( + 'category', + [ + {'introspectable': intr2, 'related': []}, + {'introspectable': intr, 'related': []}, + ], + ) + ] + self.assertEqual( + inst.categorized(sort_key=operator.attrgetter('foo')), expected + ) + + def test_categories(self): + inst = self._makeOne() + inst._categories['a'] = 1 + inst._categories['b'] = 2 + self.assertEqual(list(inst.categories()), ['a', 'b']) + + def test_remove(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.category_name = 'category2' + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr) + inst.add(intr2) + inst.relate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + inst.remove('category', 'discriminator') + self.assertEqual( + inst._categories, + { + 'category': {}, + 'category2': { + 'discriminator2': intr2, + 'discriminator2_hash': intr2, + }, + }, + ) + self.assertEqual(inst._refs.get(intr), None) + self.assertEqual(inst._refs[intr2], []) + + def test_remove_fail(self): + inst = self._makeOne() + self.assertEqual(inst.remove('a', 'b'), None) + + def test_relate(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.category_name = 'category2' + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr) + inst.add(intr2) + inst.relate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + self.assertEqual( + inst._categories, + { + 'category': { + 'discriminator': intr, + 'discriminator_hash': intr, + }, + 'category2': { + 'discriminator2': intr2, + 'discriminator2_hash': intr2, + }, + }, + ) + self.assertEqual(inst._refs[intr], [intr2]) + self.assertEqual(inst._refs[intr2], [intr]) + + def test_relate_fail(self): + inst = self._makeOne() + intr = DummyIntrospectable() + inst.add(intr) + self.assertRaises( + KeyError, + inst.relate, + ('category', 'discriminator'), + ('category2', 'discriminator2'), + ) + + def test_unrelate(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.category_name = 'category2' + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr) + inst.add(intr2) + inst.relate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + inst.unrelate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + self.assertEqual( + inst._categories, + { + 'category': { + 'discriminator': intr, + 'discriminator_hash': intr, + }, + 'category2': { + 'discriminator2': intr2, + 'discriminator2_hash': intr2, + }, + }, + ) + self.assertEqual(inst._refs[intr], []) + self.assertEqual(inst._refs[intr2], []) + + def test_related(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.category_name = 'category2' + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr) + inst.add(intr2) + inst.relate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + self.assertEqual(inst.related(intr), [intr2]) + + def test_related_fail(self): + inst = self._makeOne() + intr = DummyIntrospectable() + intr2 = DummyIntrospectable() + intr2.category_name = 'category2' + intr2.discriminator = 'discriminator2' + intr2.discriminator_hash = 'discriminator2_hash' + inst.add(intr) + inst.add(intr2) + inst.relate( + ('category', 'discriminator'), ('category2', 'discriminator2') + ) + del inst._categories['category'] + self.assertRaises(KeyError, inst.related, intr) + + +class TestIntrospectable(unittest.TestCase): + def _getTargetClass(slf): + from pyramid.registry import Introspectable + + return Introspectable + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + def _makeOnePopulated(self): + return self._makeOne('category', 'discrim', 'title', 'type') + + def test_conformance(self): + from zope.interface.verify import verifyClass + from zope.interface.verify import verifyObject + from pyramid.interfaces import IIntrospectable + + verifyClass(IIntrospectable, self._getTargetClass()) + verifyObject(IIntrospectable, self._makeOnePopulated()) + + def test_relate(self): + inst = self._makeOnePopulated() + inst.relate('a', 'b') + self.assertEqual(inst._relations, [(True, 'a', 'b')]) + + def test_unrelate(self): + inst = self._makeOnePopulated() + inst.unrelate('a', 'b') + self.assertEqual(inst._relations, [(False, 'a', 'b')]) + + def test_discriminator_hash(self): + inst = self._makeOnePopulated() + self.assertEqual(inst.discriminator_hash, hash(inst.discriminator)) + + def test___hash__(self): + inst = self._makeOnePopulated() + self.assertEqual( + hash(inst), hash((inst.category_name,) + (inst.discriminator,)) + ) + + def test___repr__(self): + inst = self._makeOnePopulated() + self.assertEqual( + repr(inst), + "<Introspectable category 'category', discriminator 'discrim'>", + ) + + def test___nonzero__(self): + inst = self._makeOnePopulated() + self.assertEqual(inst.__nonzero__(), True) + + def test___bool__(self): + inst = self._makeOnePopulated() + self.assertEqual(inst.__bool__(), True) + + def test_register(self): + introspector = DummyIntrospector() + action_info = object() + inst = self._makeOnePopulated() + inst._relations.append((True, 'category1', 'discrim1')) + inst._relations.append((False, 'category2', 'discrim2')) + inst.register(introspector, action_info) + self.assertEqual(inst.action_info, action_info) + self.assertEqual(introspector.intrs, [inst]) + self.assertEqual( + introspector.relations, + [(('category', 'discrim'), ('category1', 'discrim1'))], + ) + self.assertEqual( + introspector.unrelations, + [(('category', 'discrim'), ('category2', 'discrim2'))], + ) + + +class DummyIntrospector(object): + def __init__(self): + self.intrs = [] + self.relations = [] + self.unrelations = [] + + def add(self, intr): + self.intrs.append(intr) + + def relate(self, *pairs): + self.relations.append(pairs) + + def unrelate(self, *pairs): + self.unrelations.append(pairs) + + +class DummyModule: + __path__ = "foo" + __name__ = "dummy" + __file__ = '' + + +class DummyIntrospectable(object): + category_name = 'category' + discriminator = 'discriminator' + title = 'title' + type_name = 'type' + order = None + action_info = None + discriminator_hash = 'discriminator_hash' + + def __hash__(self): + return hash((self.category_name,) + (self.discriminator,)) + + +class IDummyEvent(Interface): + pass + + +@implementer(IDummyEvent) +class DummyEvent(object): + pass diff --git a/tests/test_renderers.py b/tests/test_renderers.py new file mode 100644 index 000000000..0eacfa996 --- /dev/null +++ b/tests/test_renderers.py @@ -0,0 +1,780 @@ +import unittest + +from pyramid.testing import cleanUp +from pyramid import testing +from pyramid.compat import text_ + + +class TestJSON(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, **kw): + from pyramid.renderers import JSON + + return JSON(**kw) + + def test_it(self): + renderer = self._makeOne()(None) + result = renderer({'a': 1}, {}) + self.assertEqual(result, '{"a": 1}') + + def test_with_request_content_type_notset(self): + request = testing.DummyRequest() + renderer = self._makeOne()(None) + renderer({'a': 1}, {'request': request}) + self.assertEqual(request.response.content_type, 'application/json') + + def test_with_request_content_type_set(self): + request = testing.DummyRequest() + request.response.content_type = 'text/mishmash' + renderer = self._makeOne()(None) + renderer({'a': 1}, {'request': request}) + self.assertEqual(request.response.content_type, 'text/mishmash') + + def test_with_custom_adapter(self): + request = testing.DummyRequest() + from datetime import datetime + + def adapter(obj, req): + self.assertEqual(req, request) + return obj.isoformat() + + now = datetime.utcnow() + renderer = self._makeOne() + renderer.add_adapter(datetime, adapter) + result = renderer(None)({'a': now}, {'request': request}) + self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) + + def test_with_custom_adapter2(self): + request = testing.DummyRequest() + from datetime import datetime + + def adapter(obj, req): + self.assertEqual(req, request) + return obj.isoformat() + + now = datetime.utcnow() + renderer = self._makeOne(adapters=((datetime, adapter),)) + result = renderer(None)({'a': now}, {'request': request}) + self.assertEqual(result, '{"a": "%s"}' % now.isoformat()) + + def test_with_custom_serializer(self): + class Serializer(object): + def __call__(self, obj, **kw): + self.obj = obj + self.kw = kw + return 'foo' + + serializer = Serializer() + renderer = self._makeOne(serializer=serializer, baz=5) + obj = {'a': 'b'} + result = renderer(None)(obj, {}) + self.assertEqual(result, 'foo') + self.assertEqual(serializer.obj, obj) + self.assertEqual(serializer.kw['baz'], 5) + self.assertTrue('default' in serializer.kw) + + def test_with_object_adapter(self): + request = testing.DummyRequest() + outerself = self + + class MyObject(object): + def __init__(self, x): + self.x = x + + def __json__(self, req): + outerself.assertEqual(req, request) + return {'x': self.x} + + objects = [MyObject(1), MyObject(2)] + renderer = self._makeOne()(None) + result = renderer(objects, {'request': request}) + self.assertEqual(result, '[{"x": 1}, {"x": 2}]') + + def test_with_object_adapter_no___json__(self): + class MyObject(object): + def __init__(self, x): + self.x = x + + objects = [MyObject(1), MyObject(2)] + renderer = self._makeOne()(None) + self.assertRaises(TypeError, renderer, objects, {}) + + +class Test_string_renderer_factory(unittest.TestCase): + def _callFUT(self, name): + from pyramid.renderers import string_renderer_factory + + return string_renderer_factory(name) + + def test_it_unicode(self): + renderer = self._callFUT(None) + value = text_('La Pe\xc3\xb1a', 'utf-8') + result = renderer(value, {}) + self.assertEqual(result, value) + + def test_it_str(self): + renderer = self._callFUT(None) + value = 'La Pe\xc3\xb1a' + result = renderer(value, {}) + self.assertEqual(result, value) + + def test_it_other(self): + renderer = self._callFUT(None) + value = None + result = renderer(value, {}) + self.assertEqual(result, 'None') + + def test_with_request_content_type_notset(self): + request = testing.DummyRequest() + renderer = self._callFUT(None) + renderer('', {'request': request}) + self.assertEqual(request.response.content_type, 'text/plain') + + def test_with_request_content_type_set(self): + request = testing.DummyRequest() + request.response.content_type = 'text/mishmash' + renderer = self._callFUT(None) + renderer('', {'request': request}) + self.assertEqual(request.response.content_type, 'text/mishmash') + + +class TestRendererHelper(unittest.TestCase): + def setUp(self): + self.config = cleanUp() + + def tearDown(self): + cleanUp() + + def _makeOne(self, *arg, **kw): + from pyramid.renderers import RendererHelper + + return RendererHelper(*arg, **kw) + + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IRendererInfo + + helper = self._makeOne() + verifyObject(IRendererInfo, helper) + + def test_settings_registry_settings_is_None(self): + class Dummy(object): + settings = None + + helper = self._makeOne(registry=Dummy) + self.assertEqual(helper.settings, {}) + + def test_settings_registry_name_is_None(self): + class Dummy(object): + settings = None + + helper = self._makeOne(registry=Dummy) + self.assertEqual(helper.name, None) + self.assertEqual(helper.type, '') + + def test_settings_registry_settings_is_not_None(self): + class Dummy(object): + settings = {'a': 1} + + helper = self._makeOne(registry=Dummy) + self.assertEqual(helper.settings, {'a': 1}) + + def _registerRendererFactory(self): + from pyramid.interfaces import IRendererFactory + + def renderer(*arg): + def respond(*arg): + return arg + + renderer.respond = respond + return respond + + self.config.registry.registerUtility( + renderer, IRendererFactory, name='.foo' + ) + return renderer + + def _registerResponseFactory(self): + from pyramid.interfaces import IResponseFactory + + class ResponseFactory(object): + pass + + self.config.registry.registerUtility( + lambda r: ResponseFactory(), IResponseFactory + ) + + def test_render_to_response(self): + self._registerRendererFactory() + self._registerResponseFactory() + request = Dummy() + helper = self._makeOne('loo.foo') + response = helper.render_to_response('values', {}, request=request) + self.assertEqual(response.app_iter[0], 'values') + self.assertEqual(response.app_iter[1], {}) + + def test_get_renderer(self): + factory = self._registerRendererFactory() + helper = self._makeOne('loo.foo') + self.assertEqual(helper.get_renderer(), factory.respond) + + def test_render_view(self): + import pyramid.csrf + + self._registerRendererFactory() + self._registerResponseFactory() + request = Dummy() + helper = self._makeOne('loo.foo') + view = 'view' + context = 'context' + request = testing.DummyRequest() + response = 'response' + response = helper.render_view(request, response, view, context) + get_csrf = response.app_iter[1].pop('get_csrf_token') + self.assertEqual(get_csrf.args, (request,)) + self.assertEqual(get_csrf.func, pyramid.csrf.get_csrf_token) + self.assertEqual(response.app_iter[0], 'response') + self.assertEqual( + response.app_iter[1], + { + 'renderer_info': helper, + 'renderer_name': 'loo.foo', + 'request': request, + 'context': 'context', + 'view': 'view', + 'req': request, + }, + ) + + def test_render_explicit_registry(self): + factory = self._registerRendererFactory() + + class DummyRegistry(object): + def __init__(self): + self.responses = [factory, lambda *arg: {}, None] + + def queryUtility(self, iface, name=None): + self.queried = True + return self.responses.pop(0) + + def notify(self, event): + self.event = event + + reg = DummyRegistry() + helper = self._makeOne('loo.foo', registry=reg) + result = helper.render('value', {}) + self.assertEqual(result[0], 'value') + self.assertEqual(result[1], {}) + self.assertTrue(reg.queried) + self.assertEqual(reg.event, {}) + self.assertEqual(reg.event.__class__.__name__, 'BeforeRender') + + def test_render_system_values_is_None(self): + import pyramid.csrf + + self._registerRendererFactory() + request = Dummy() + context = Dummy() + request.context = context + helper = self._makeOne('loo.foo') + result = helper.render('values', None, request=request) + get_csrf = result[1].pop('get_csrf_token') + self.assertEqual(get_csrf.args, (request,)) + self.assertEqual(get_csrf.func, pyramid.csrf.get_csrf_token) + system = { + 'request': request, + 'context': context, + 'renderer_name': 'loo.foo', + 'view': None, + 'renderer_info': helper, + 'req': request, + } + self.assertEqual(result[0], 'values') + self.assertEqual(result[1], system) + + def test__make_response_request_is_None(self): + request = None + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.body, b'abc') + + def test__make_response_request_is_None_response_factory_exists(self): + self._registerResponseFactory() + request = None + helper = self._makeOne('loo.foo') + response = helper._make_response(b'abc', request) + self.assertEqual(response.__class__.__name__, 'ResponseFactory') + self.assertEqual(response.body, b'abc') + + def test__make_response_result_is_unicode(self): + from pyramid.response import Response + + request = testing.DummyRequest() + request.response = Response() + helper = self._makeOne('loo.foo') + la = text_('/La Pe\xc3\xb1a', 'utf-8') + response = helper._make_response(la, request) + self.assertEqual(response.body, la.encode('utf-8')) + + def test__make_response_result_is_str(self): + from pyramid.response import Response + + request = testing.DummyRequest() + request.response = Response() + helper = self._makeOne('loo.foo') + la = text_('/La Pe\xc3\xb1a', 'utf-8') + response = helper._make_response(la.encode('utf-8'), request) + self.assertEqual(response.body, la.encode('utf-8')) + + def test__make_response_result_is_iterable(self): + from pyramid.response import Response + + request = testing.DummyRequest() + request.response = Response() + helper = self._makeOne('loo.foo') + la = text_('/La Pe\xc3\xb1a', 'utf-8') + response = helper._make_response([la.encode('utf-8')], request) + self.assertEqual(response.body, la.encode('utf-8')) + + def test__make_response_result_is_other(self): + self._registerResponseFactory() + request = None + helper = self._makeOne('loo.foo') + result = object() + response = helper._make_response(result, request) + self.assertEqual(response.body, result) + + def test__make_response_result_is_None_no_body(self): + from pyramid.response import Response + + request = testing.DummyRequest() + request.response = Response() + helper = self._makeOne('loo.foo') + response = helper._make_response(None, request) + self.assertEqual(response.body, b'') + + def test__make_response_result_is_None_existing_body_not_molested(self): + from pyramid.response import Response + + request = testing.DummyRequest() + response = Response() + response.body = b'abc' + request.response = response + helper = self._makeOne('loo.foo') + response = helper._make_response(None, request) + self.assertEqual(response.body, b'abc') + + def test_with_alternate_response_factory(self): + from pyramid.interfaces import IResponseFactory + + class ResponseFactory(object): + def __init__(self): + pass + + self.config.registry.registerUtility( + lambda r: ResponseFactory(), IResponseFactory + ) + request = testing.DummyRequest() + helper = self._makeOne('loo.foo') + response = helper._make_response(b'abc', request) + self.assertEqual(response.__class__, ResponseFactory) + self.assertEqual(response.body, b'abc') + + def test__make_response_with_real_request(self): + # functional + from pyramid.request import Request + + request = Request({}) + request.registry = self.config.registry + request.response.status = '406 You Lose' + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.status, '406 You Lose') + self.assertEqual(response.body, b'abc') + + def test_clone_noargs(self): + helper = self._makeOne('name', 'package', 'registry') + cloned_helper = helper.clone() + self.assertEqual(cloned_helper.name, 'name') + self.assertEqual(cloned_helper.package, 'package') + self.assertEqual(cloned_helper.registry, 'registry') + self.assertFalse(helper is cloned_helper) + + def test_clone_allargs(self): + helper = self._makeOne('name', 'package', 'registry') + cloned_helper = helper.clone( + name='name2', package='package2', registry='registry2' + ) + self.assertEqual(cloned_helper.name, 'name2') + self.assertEqual(cloned_helper.package, 'package2') + self.assertEqual(cloned_helper.registry, 'registry2') + self.assertFalse(helper is cloned_helper) + + def test_renderer_absolute_file(self): + registry = self.config.registry + settings = {} + registry.settings = settings + from pyramid.interfaces import IRendererFactory + import os + + here = os.path.dirname(os.path.abspath(__file__)) + fixture = os.path.join(here, 'fixtures/minimal.pt') + + def factory(info, **kw): + return info + + self.config.registry.registerUtility( + factory, IRendererFactory, name='.pt' + ) + result = self._makeOne(fixture).renderer + self.assertEqual(result.registry, registry) + self.assertEqual(result.type, '.pt') + self.assertEqual(result.package, None) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_renderer_with_package(self): + import pyramid + + registry = self.config.registry + settings = {} + registry.settings = settings + from pyramid.interfaces import IRendererFactory + import os + + here = os.path.dirname(os.path.abspath(__file__)) + fixture = os.path.join(here, 'fixtures/minimal.pt') + + def factory(info, **kw): + return info + + self.config.registry.registerUtility( + factory, IRendererFactory, name='.pt' + ) + result = self._makeOne(fixture, pyramid).renderer + self.assertEqual(result.registry, registry) + self.assertEqual(result.type, '.pt') + self.assertEqual(result.package, pyramid) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_renderer_missing(self): + inst = self._makeOne('foo') + self.assertRaises(ValueError, getattr, inst, 'renderer') + + +class TestNullRendererHelper(unittest.TestCase): + def setUp(self): + self.config = cleanUp() + + def tearDown(self): + cleanUp() + + def _makeOne(self, *arg, **kw): + from pyramid.renderers import NullRendererHelper + + return NullRendererHelper(*arg, **kw) + + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IRendererInfo + + helper = self._makeOne() + verifyObject(IRendererInfo, helper) + + def test_render_view(self): + helper = self._makeOne() + self.assertEqual(helper.render_view(None, True, None, None), True) + + def test_render(self): + helper = self._makeOne() + self.assertEqual(helper.render(True, None, None), True) + + def test_render_to_response(self): + helper = self._makeOne() + self.assertEqual(helper.render_to_response(True, None, None), True) + + def test_clone(self): + helper = self._makeOne() + self.assertTrue(helper.clone() is helper) + + +class Test_render(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, renderer_name, value, request=None, package=None): + from pyramid.renderers import render + + return render(renderer_name, value, request=request, package=package) + + def _registerRenderer(self): + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + renderer.string_response = 'abc' + return renderer + + def test_it_no_request(self): + renderer = self._registerRenderer() + result = self._callFUT('abc/def.pt', dict(a=1)) + self.assertEqual(result, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=None) + + def test_it_with_request(self): + renderer = self._registerRenderer() + request = testing.DummyRequest() + result = self._callFUT('abc/def.pt', dict(a=1), request=request) + self.assertEqual(result, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + + def test_it_with_package(self): + import tests + + renderer = self._registerRenderer() + request = testing.DummyRequest() + result = self._callFUT( + 'abc/def.pt', dict(a=1), request=request, package=tests + ) + self.assertEqual(result, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + + def test_response_preserved(self): + request = testing.DummyRequest() + response = object() # should error if mutated + request.response = response + # use a json renderer, which will mutate the response + result = self._callFUT('json', dict(a=1), request=request) + self.assertEqual(result, '{"a": 1}') + self.assertEqual(request.response, response) + + def test_no_response_to_preserve(self): + from pyramid.decorator import reify + + class DummyRequestWithClassResponse(object): + _response = DummyResponse() + _response.content_type = None + _response.default_content_type = None + + @reify + def response(self): + return self._response + + request = DummyRequestWithClassResponse() + # use a json renderer, which will mutate the response + result = self._callFUT('json', dict(a=1), request=request) + self.assertEqual(result, '{"a": 1}') + self.assertFalse('response' in request.__dict__) + + +class Test_render_to_response(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT( + self, renderer_name, value, request=None, package=None, response=None + ): + from pyramid.renderers import render_to_response + + return render_to_response( + renderer_name, + value, + request=request, + package=package, + response=response, + ) + + def test_it_no_request(self): + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + renderer.string_response = 'abc' + response = self._callFUT('abc/def.pt', dict(a=1)) + self.assertEqual(response.body, b'abc') + renderer.assert_(a=1) + renderer.assert_(request=None) + + def test_it_with_request(self): + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + renderer.string_response = 'abc' + request = testing.DummyRequest() + response = self._callFUT('abc/def.pt', dict(a=1), request=request) + self.assertEqual(response.body, b'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + + def test_it_with_package(self): + import tests + + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + renderer.string_response = 'abc' + request = testing.DummyRequest() + response = self._callFUT( + 'abc/def.pt', dict(a=1), request=request, package=tests + ) + self.assertEqual(response.body, b'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + + def test_response_preserved(self): + request = testing.DummyRequest() + response = object() # should error if mutated + request.response = response + # use a json renderer, which will mutate the response + result = self._callFUT('json', dict(a=1), request=request) + self.assertEqual(result.body, b'{"a": 1}') + self.assertNotEqual(request.response, result) + self.assertEqual(request.response, response) + + def test_no_response_to_preserve(self): + from pyramid.decorator import reify + + class DummyRequestWithClassResponse(object): + _response = DummyResponse() + _response.content_type = None + _response.default_content_type = None + + @reify + def response(self): + return self._response + + request = DummyRequestWithClassResponse() + # use a json renderer, which will mutate the response + result = self._callFUT('json', dict(a=1), request=request) + self.assertEqual(result.body, b'{"a": 1}') + self.assertFalse('response' in request.__dict__) + + def test_custom_response_object(self): + class DummyRequestWithClassResponse(object): + pass + + request = DummyRequestWithClassResponse() + response = DummyResponse() + # use a json renderer, which will mutate the response + result = self._callFUT( + 'json', dict(a=1), request=request, response=response + ) + self.assertTrue(result is response) + self.assertEqual(result.body, b'{"a": 1}') + self.assertFalse('response' in request.__dict__) + + +class Test_get_renderer(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, renderer_name, **kw): + from pyramid.renderers import get_renderer + + return get_renderer(renderer_name, **kw) + + def test_it_no_package(self): + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + result = self._callFUT('abc/def.pt') + self.assertEqual(result, renderer) + + def test_it_with_package(self): + import tests + + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + result = self._callFUT('abc/def.pt', package=tests) + self.assertEqual(result, renderer) + + def test_it_with_registry(self): + renderer = self.config.testing_add_renderer('tests:abc/def.pt') + result = self._callFUT('abc/def.pt', registry=self.config.registry) + self.assertEqual(result, renderer) + + def test_it_with_isolated_registry(self): + from pyramid.config import Configurator + + isolated_config = Configurator() + renderer = isolated_config.testing_add_renderer('tests:abc/def.pt') + result = self._callFUT('abc/def.pt', registry=isolated_config.registry) + self.assertEqual(result, renderer) + + +class TestJSONP(unittest.TestCase): + def _makeOne(self, param_name='callback'): + from pyramid.renderers import JSONP + + return JSONP(param_name) + + def test_render_to_jsonp(self): + renderer_factory = self._makeOne() + renderer = renderer_factory(None) + request = testing.DummyRequest() + request.GET['callback'] = 'callback' + result = renderer({'a': '1'}, {'request': request}) + self.assertEqual(result, '/**/callback({"a": "1"});') + self.assertEqual( + request.response.content_type, 'application/javascript' + ) + + def test_render_to_jsonp_with_dot(self): + renderer_factory = self._makeOne() + renderer = renderer_factory(None) + request = testing.DummyRequest() + request.GET['callback'] = 'angular.callbacks._0' + result = renderer({'a': '1'}, {'request': request}) + self.assertEqual(result, '/**/angular.callbacks._0({"a": "1"});') + self.assertEqual( + request.response.content_type, 'application/javascript' + ) + + def test_render_to_json(self): + renderer_factory = self._makeOne() + renderer = renderer_factory(None) + request = testing.DummyRequest() + result = renderer({'a': '1'}, {'request': request}) + self.assertEqual(result, '{"a": "1"}') + self.assertEqual(request.response.content_type, 'application/json') + + def test_render_without_request(self): + renderer_factory = self._makeOne() + renderer = renderer_factory(None) + result = renderer({'a': '1'}, {}) + self.assertEqual(result, '{"a": "1"}') + + def test_render_to_jsonp_invalid_callback(self): + from pyramid.httpexceptions import HTTPBadRequest + + renderer_factory = self._makeOne() + renderer = renderer_factory(None) + request = testing.DummyRequest() + request.GET['callback'] = '78mycallback' + self.assertRaises( + HTTPBadRequest, renderer, {'a': '1'}, {'request': request} + ) + + +class Dummy: + pass + + +class DummyResponse: + status = '200 OK' + default_content_type = 'text/html' + content_type = default_content_type + headerlist = () + app_iter = () + body = b'' + + # compat for renderer that will set unicode on py3 + def _set_text(self, val): # pragma: no cover + self.body = val.encode('utf8') + + text = property(fset=_set_text) diff --git a/tests/test_request.py b/tests/test_request.py new file mode 100644 index 000000000..dcac501aa --- /dev/null +++ b/tests/test_request.py @@ -0,0 +1,657 @@ +import unittest +from pyramid import testing + +from pyramid.compat import PY2, text_, bytes_, native_ +from pyramid.security import AuthenticationAPIMixin, AuthorizationAPIMixin + + +class TestRequest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getTargetClass(self): + from pyramid.request import Request + + return Request + + def _makeOne(self, environ=None): + if environ is None: + environ = {} + return self._getTargetClass()(environ) + + def _registerResourceURL(self): + from pyramid.interfaces import IResourceURL + from zope.interface import Interface + + class DummyResourceURL(object): + def __init__(self, context, request): + self.physical_path = '/context/' + self.virtual_path = '/context/' + + self.config.registry.registerAdapter( + DummyResourceURL, (Interface, Interface), IResourceURL + ) + + def test_class_conforms_to_IRequest(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import IRequest + + verifyClass(IRequest, self._getTargetClass()) + + def test_instance_conforms_to_IRequest(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import IRequest + + verifyObject(IRequest, self._makeOne()) + + def test_ResponseClass_is_pyramid_Response(self): + from pyramid.response import Response + + cls = self._getTargetClass() + self.assertEqual(cls.ResponseClass, Response) + + def test_implements_security_apis(self): + apis = (AuthenticationAPIMixin, AuthorizationAPIMixin) + r = self._makeOne() + self.assertTrue(isinstance(r, apis)) + + def test_charset_defaults_to_utf8(self): + r = self._makeOne({'PATH_INFO': '/'}) + self.assertEqual(r.charset, 'UTF-8') + + def test_exception_defaults_to_None(self): + r = self._makeOne({'PATH_INFO': '/'}) + self.assertEqual(r.exception, None) + + def test_matchdict_defaults_to_None(self): + r = self._makeOne({'PATH_INFO': '/'}) + self.assertEqual(r.matchdict, None) + + def test_matched_route_defaults_to_None(self): + r = self._makeOne({'PATH_INFO': '/'}) + self.assertEqual(r.matched_route, None) + + def test_params_decoded_from_utf_8_by_default(self): + environ = {'PATH_INFO': '/', 'QUERY_STRING': 'la=La%20Pe%C3%B1a'} + request = self._makeOne(environ) + request.charset = None + self.assertEqual(request.GET['la'], text_(b'La Pe\xf1a')) + + def test_tmpl_context(self): + from pyramid.request import TemplateContext + + inst = self._makeOne() + result = inst.tmpl_context + self.assertEqual(result.__class__, TemplateContext) + + def test_session_configured(self): + from pyramid.interfaces import ISessionFactory + + inst = self._makeOne() + + def factory(request): + return 'orangejuice' + + self.config.registry.registerUtility(factory, ISessionFactory) + inst.registry = self.config.registry + self.assertEqual(inst.session, 'orangejuice') + self.assertEqual(inst.__dict__['session'], 'orangejuice') + + def test_session_not_configured(self): + inst = self._makeOne() + inst.registry = self.config.registry + self.assertRaises(AttributeError, getattr, inst, 'session') + + def test_setattr_and_getattr_dotnotation(self): + inst = self._makeOne() + inst.foo = 1 + self.assertEqual(inst.foo, 1) + + def test_setattr_and_getattr(self): + environ = {} + inst = self._makeOne(environ) + setattr(inst, 'bar', 1) + self.assertEqual(getattr(inst, 'bar'), 1) + self.assertEqual(environ, {}) # make sure we're not using adhoc attrs + + def test_add_response_callback(self): + inst = self._makeOne() + self.assertEqual(len(inst.response_callbacks), 0) + + def callback(request, response): + """ """ + + inst.add_response_callback(callback) + self.assertEqual(list(inst.response_callbacks), [callback]) + inst.add_response_callback(callback) + self.assertEqual(list(inst.response_callbacks), [callback, callback]) + + def test__process_response_callbacks(self): + inst = self._makeOne() + + def callback1(request, response): + request.called1 = True + response.called1 = True + + def callback2(request, response): + request.called2 = True + response.called2 = True + + inst.add_response_callback(callback1) + inst.add_response_callback(callback2) + response = DummyResponse() + inst._process_response_callbacks(response) + self.assertEqual(inst.called1, True) + self.assertEqual(inst.called2, True) + self.assertEqual(response.called1, True) + self.assertEqual(response.called2, True) + self.assertEqual(len(inst.response_callbacks), 0) + + def test__process_response_callback_adding_response_callback(self): + """ + When a response callback adds another callback, that new callback + should still be called. + + See https://github.com/Pylons/pyramid/pull/1373 + """ + inst = self._makeOne() + + def callback1(request, response): + request.called1 = True + response.called1 = True + request.add_response_callback(callback2) + + def callback2(request, response): + request.called2 = True + response.called2 = True + + inst.add_response_callback(callback1) + response = DummyResponse() + inst._process_response_callbacks(response) + self.assertEqual(inst.called1, True) + self.assertEqual(inst.called2, True) + self.assertEqual(response.called1, True) + self.assertEqual(response.called2, True) + self.assertEqual(len(inst.response_callbacks), 0) + + def test_add_finished_callback(self): + inst = self._makeOne() + self.assertEqual(len(inst.finished_callbacks), 0) + + def callback(request): + """ """ + + inst.add_finished_callback(callback) + self.assertEqual(list(inst.finished_callbacks), [callback]) + inst.add_finished_callback(callback) + self.assertEqual(list(inst.finished_callbacks), [callback, callback]) + + def test__process_finished_callbacks(self): + inst = self._makeOne() + + def callback1(request): + request.called1 = True + + def callback2(request): + request.called2 = True + + inst.add_finished_callback(callback1) + inst.add_finished_callback(callback2) + inst._process_finished_callbacks() + self.assertEqual(inst.called1, True) + self.assertEqual(inst.called2, True) + self.assertEqual(len(inst.finished_callbacks), 0) + + def test_resource_url(self): + self._registerResourceURL() + environ = { + 'PATH_INFO': '/', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + 'wsgi.url_scheme': 'http', + } + inst = self._makeOne(environ) + root = DummyContext() + result = inst.resource_url(root) + self.assertEqual(result, 'http://example.com/context/') + + def test_route_url(self): + environ = { + 'PATH_INFO': '/', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '5432', + 'QUERY_STRING': 'la=La%20Pe%C3%B1a', + 'wsgi.url_scheme': 'http', + } + from pyramid.interfaces import IRoutesMapper + + inst = self._makeOne(environ) + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + self.config.registry.registerUtility(mapper, IRoutesMapper) + result = inst.route_url( + 'flub', + 'extra1', + 'extra2', + a=1, + b=2, + c=3, + _query={'a': 1}, + _anchor=text_("foo"), + ) + self.assertEqual( + result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo' + ) + + def test_route_path(self): + environ = { + 'PATH_INFO': '/', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '5432', + 'QUERY_STRING': 'la=La%20Pe%C3%B1a', + 'wsgi.url_scheme': 'http', + } + from pyramid.interfaces import IRoutesMapper + + inst = self._makeOne(environ) + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + self.config.registry.registerUtility(mapper, IRoutesMapper) + result = inst.route_path( + 'flub', + 'extra1', + 'extra2', + a=1, + b=2, + c=3, + _query={'a': 1}, + _anchor=text_("foo"), + ) + self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo') + + def test_static_url(self): + from pyramid.interfaces import IStaticURLInfo + + environ = { + 'PATH_INFO': '/', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '5432', + 'QUERY_STRING': '', + 'wsgi.url_scheme': 'http', + } + request = self._makeOne(environ) + info = DummyStaticURLInfo('abc') + self.config.registry.registerUtility(info, IStaticURLInfo) + result = request.static_url('pyramid.tests:static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, ('pyramid.tests:static/foo.css', request, {}) + ) + + def test_is_response_false(self): + request = self._makeOne() + request.registry = self.config.registry + self.assertEqual(request.is_response('abc'), False) + + def test_is_response_true_ob_is_pyramid_response(self): + from pyramid.response import Response + + r = Response('hello') + request = self._makeOne() + request.registry = self.config.registry + self.assertEqual(request.is_response(r), True) + + def test_is_response_false_adapter_is_not_self(self): + from pyramid.interfaces import IResponse + + request = self._makeOne() + request.registry = self.config.registry + + def adapter(ob): + return object() + + class Foo(object): + pass + + foo = Foo() + request.registry.registerAdapter(adapter, (Foo,), IResponse) + self.assertEqual(request.is_response(foo), False) + + def test_is_response_adapter_true(self): + from pyramid.interfaces import IResponse + + request = self._makeOne() + request.registry = self.config.registry + + class Foo(object): + pass + + foo = Foo() + + def adapter(ob): + return ob + + request.registry.registerAdapter(adapter, (Foo,), IResponse) + self.assertEqual(request.is_response(foo), True) + + def test_json_body_invalid_json(self): + request = self._makeOne({'REQUEST_METHOD': 'POST'}) + request.body = b'{' + self.assertRaises(ValueError, getattr, request, 'json_body') + + def test_json_body_valid_json(self): + request = self._makeOne({'REQUEST_METHOD': 'POST'}) + request.body = b'{"a":1}' + self.assertEqual(request.json_body, {'a': 1}) + + def test_json_body_alternate_charset(self): + import json + + request = self._makeOne({'REQUEST_METHOD': 'POST'}) + inp = text_( + b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8' + ) + if PY2: + body = json.dumps({'a': inp}).decode('utf-8').encode('utf-16') + else: + body = bytes(json.dumps({'a': inp}), 'utf-16') + request.body = body + request.content_type = 'application/json; charset=utf-16' + self.assertEqual(request.json_body, {'a': inp}) + + def test_json_body_GET_request(self): + request = self._makeOne({'REQUEST_METHOD': 'GET'}) + self.assertRaises(ValueError, getattr, request, 'json_body') + + def test_set_property(self): + request = self._makeOne() + opts = [2, 1] + + def connect(obj): + return opts.pop() + + request.set_property(connect, name='db') + self.assertEqual(1, request.db) + self.assertEqual(2, request.db) + + def test_set_property_reify(self): + request = self._makeOne() + opts = [2, 1] + + def connect(obj): + return opts.pop() + + request.set_property(connect, name='db', reify=True) + self.assertEqual(1, request.db) + self.assertEqual(1, request.db) + + +class Test_route_request_iface(unittest.TestCase): + def _callFUT(self, name): + from pyramid.request import route_request_iface + + return route_request_iface(name) + + def test_it(self): + iface = self._callFUT('routename') + self.assertEqual(iface.__name__, 'routename_IRequest') + self.assertTrue(hasattr(iface, 'combined')) + self.assertEqual( + iface.combined.__name__, 'routename_combined_IRequest' + ) + + def test_it_routename_with_spaces(self): + # see https://github.com/Pylons/pyramid/issues/232 + iface = self._callFUT('routename with spaces') + self.assertEqual(iface.__name__, 'routename with spaces_IRequest') + self.assertTrue(hasattr(iface, 'combined')) + self.assertEqual( + iface.combined.__name__, 'routename with spaces_combined_IRequest' + ) + + +class Test_add_global_response_headers(unittest.TestCase): + def _callFUT(self, request, headerlist): + from pyramid.request import add_global_response_headers + + return add_global_response_headers(request, headerlist) + + def test_it(self): + request = DummyRequest() + response = DummyResponse() + self._callFUT(request, [('c', 1)]) + self.assertEqual(len(request.response_callbacks), 1) + request.response_callbacks[0](None, response) + self.assertEqual(response.headerlist, [('c', 1)]) + + +class Test_call_app_with_subpath_as_path_info(unittest.TestCase): + def _callFUT(self, request, app): + from pyramid.request import call_app_with_subpath_as_path_info + + return call_app_with_subpath_as_path_info(request, app) + + def test_it_all_request_and_environment_data_missing(self): + request = DummyRequest({}) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/') + + def test_it_with_subpath_and_path_info(self): + request = DummyRequest({'PATH_INFO': '/hello'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/hello') + + def test_it_with_subpath_and_path_info_path_info_endswith_slash(self): + request = DummyRequest({'PATH_INFO': '/hello/'}) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + self.assertEqual(request.environ['PATH_INFO'], '/hello/') + + def test_it_with_subpath_and_path_info_extra_script_name(self): + request = DummyRequest( + {'PATH_INFO': '/hello', 'SCRIPT_NAME': '/script'} + ) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/script') + self.assertEqual(request.environ['PATH_INFO'], '/hello') + + def test_it_with_extra_slashes_in_path_info(self): + request = DummyRequest( + {'PATH_INFO': '//hello/', 'SCRIPT_NAME': '/script'} + ) + request.subpath = ('hello',) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/script') + self.assertEqual(request.environ['PATH_INFO'], '/hello/') + + def test_subpath_path_info_and_script_name_have_utf8(self): + encoded = native_(text_(b'La Pe\xc3\xb1a')) + decoded = text_(bytes_(encoded), 'utf-8') + request = DummyRequest( + {'PATH_INFO': '/' + encoded, 'SCRIPT_NAME': '/' + encoded} + ) + request.subpath = (decoded,) + response = self._callFUT(request, 'app') + self.assertTrue(request.copied) + self.assertEqual(response, 'app') + self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded) + self.assertEqual(request.environ['PATH_INFO'], '/' + encoded) + + +class Test_apply_request_extensions(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request, extensions=None): + from pyramid.request import apply_request_extensions + + return apply_request_extensions(request, extensions=extensions) + + def test_it_with_registry(self): + from pyramid.interfaces import IRequestExtensions + + extensions = Dummy() + extensions.methods = {'foo': lambda x, y: y} + extensions.descriptors = {'bar': property(lambda x: 'bar')} + self.config.registry.registerUtility(extensions, IRequestExtensions) + request = DummyRequest() + request.registry = self.config.registry + self._callFUT(request) + self.assertEqual(request.bar, 'bar') + self.assertEqual(request.foo('abc'), 'abc') + + def test_it_override_extensions(self): + from pyramid.interfaces import IRequestExtensions + + ignore = Dummy() + ignore.methods = {'x': lambda x, y, z: 'asdf'} + ignore.descriptors = {'bar': property(lambda x: 'asdf')} + self.config.registry.registerUtility(ignore, IRequestExtensions) + request = DummyRequest() + request.registry = self.config.registry + + extensions = Dummy() + extensions.methods = {'foo': lambda x, y: y} + extensions.descriptors = {'bar': property(lambda x: 'bar')} + self._callFUT(request, extensions=extensions) + self.assertRaises(AttributeError, lambda: request.x) + self.assertEqual(request.bar, 'bar') + self.assertEqual(request.foo('abc'), 'abc') + + +class Dummy(object): + pass + + +class Test_subclassing_Request(unittest.TestCase): + def test_subclass(self): + from pyramid.interfaces import IRequest + from pyramid.request import Request + + class RequestSub(Request): + pass + + self.assertTrue(hasattr(Request, '__provides__')) + self.assertTrue(hasattr(Request, '__implemented__')) + self.assertTrue(hasattr(Request, '__providedBy__')) + self.assertFalse(hasattr(RequestSub, '__provides__')) + self.assertTrue(hasattr(RequestSub, '__providedBy__')) + self.assertTrue(hasattr(RequestSub, '__implemented__')) + + self.assertTrue(IRequest.implementedBy(RequestSub)) + # The call to implementedBy will add __provides__ to the class + self.assertTrue(hasattr(RequestSub, '__provides__')) + + def test_subclass_with_implementer(self): + from pyramid.interfaces import IRequest + from pyramid.request import Request + from pyramid.util import InstancePropertyHelper + from zope.interface import implementer + + @implementer(IRequest) + class RequestSub(Request): + pass + + self.assertTrue(hasattr(Request, '__provides__')) + self.assertTrue(hasattr(Request, '__implemented__')) + self.assertTrue(hasattr(Request, '__providedBy__')) + self.assertTrue(hasattr(RequestSub, '__provides__')) + self.assertTrue(hasattr(RequestSub, '__providedBy__')) + self.assertTrue(hasattr(RequestSub, '__implemented__')) + + req = RequestSub({}) + helper = InstancePropertyHelper() + helper.apply_properties(req, {'b': 'b'}) + + self.assertTrue(IRequest.providedBy(req)) + self.assertTrue(IRequest.implementedBy(RequestSub)) + + def test_subclass_mutate_before_providedBy(self): + from pyramid.interfaces import IRequest + from pyramid.request import Request + from pyramid.util import InstancePropertyHelper + + class RequestSub(Request): + pass + + req = RequestSub({}) + helper = InstancePropertyHelper() + helper.apply_properties(req, {'b': 'b'}) + + self.assertTrue(IRequest.providedBy(req)) + self.assertTrue(IRequest.implementedBy(RequestSub)) + + +class DummyRequest(object): + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + + def add_response_callback(self, callback): + self.response_callbacks = [callback] + + def get_response(self, app): + return app + + def copy(self): + self.copied = True + return self + + +class DummyResponse: + def __init__(self): + self.headerlist = [] + + +class DummyContext: + pass + + +class DummyRoutesMapper: + raise_exc = None + + def __init__(self, route=None, raise_exc=False): + self.route = route + + def get_route(self, route_name): + return self.route + + +class DummyRoute: + pregenerator = None + + def __init__(self, result='/1/2/3'): + self.result = result + + def generate(self, kw): + self.kw = kw + return self.result + + +class DummyStaticURLInfo: + def __init__(self, result): + self.result = result + + def generate(self, path, request, **kw): + self.args = path, request, kw + return self.result diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 000000000..5231e47f0 --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,262 @@ +import io +import mimetypes +import os +import unittest +from pyramid import testing + + +class TestResponse(unittest.TestCase): + def _getTargetClass(self): + from pyramid.response import Response + + return Response + + def test_implements_IResponse(self): + from pyramid.interfaces import IResponse + + cls = self._getTargetClass() + self.assertTrue(IResponse.implementedBy(cls)) + + def test_provides_IResponse(self): + from pyramid.interfaces import IResponse + + inst = self._getTargetClass()() + self.assertTrue(IResponse.providedBy(inst)) + + +class TestFileResponse(unittest.TestCase): + def _makeOne(self, file, **kw): + from pyramid.response import FileResponse + + return FileResponse(file, **kw) + + def _getPath(self, suffix='txt'): + here = os.path.dirname(__file__) + return os.path.join(here, 'fixtures', 'minimal.%s' % (suffix,)) + + def test_with_image_content_type(self): + path = self._getPath('jpg') + r = self._makeOne(path, content_type='image/jpeg') + self.assertEqual(r.content_type, 'image/jpeg') + self.assertEqual(r.headers['content-type'], 'image/jpeg') + path = self._getPath() + r.app_iter.close() + + def test_with_xml_content_type(self): + path = self._getPath('xml') + r = self._makeOne(path, content_type='application/xml') + self.assertEqual(r.content_type, 'application/xml') + self.assertEqual( + r.headers['content-type'], 'application/xml; charset=UTF-8' + ) + r.app_iter.close() + + def test_with_pdf_content_type(self): + path = self._getPath('xml') + r = self._makeOne(path, content_type='application/pdf') + self.assertEqual(r.content_type, 'application/pdf') + self.assertEqual(r.headers['content-type'], 'application/pdf') + r.app_iter.close() + + def test_without_content_type(self): + for suffix in ('txt', 'xml', 'pdf'): + path = self._getPath(suffix) + r = self._makeOne(path) + self.assertEqual( + r.headers['content-type'].split(';')[0], + mimetypes.guess_type(path, strict=False)[0], + ) + r.app_iter.close() + + def test_python_277_bug_15207(self): + # python 2.7.7 on windows has a bug where its mimetypes.guess_type + # function returns Unicode for the content_type, unlike any previous + # version of Python. See https://github.com/Pylons/pyramid/issues/1360 + # for more information. + from pyramid.compat import text_ + import mimetypes as old_mimetypes + from pyramid import response + + class FakeMimetypesModule(object): + def guess_type(self, *arg, **kw): + return text_('foo/bar'), None + + fake_mimetypes = FakeMimetypesModule() + try: + response.mimetypes = fake_mimetypes + path = self._getPath('xml') + r = self._makeOne(path) + self.assertEqual(r.content_type, 'foo/bar') + self.assertEqual(type(r.content_type), str) + finally: + response.mimetypes = old_mimetypes + + +class TestFileIter(unittest.TestCase): + def _makeOne(self, file, block_size): + from pyramid.response import FileIter + + return FileIter(file, block_size) + + def test___iter__(self): + f = io.BytesIO(b'abc') + inst = self._makeOne(f, 1) + self.assertEqual(inst.__iter__(), inst) + + def test_iteration(self): + data = b'abcdef' + f = io.BytesIO(b'abcdef') + inst = self._makeOne(f, 1) + r = b'' + for x in inst: + self.assertEqual(len(x), 1) + r += x + self.assertEqual(r, data) + + def test_close(self): + f = io.BytesIO(b'abc') + inst = self._makeOne(f, 1) + inst.close() + self.assertTrue(f.closed) + + +class Test_patch_mimetypes(unittest.TestCase): + def _callFUT(self, module): + from pyramid.response import init_mimetypes + + return init_mimetypes(module) + + def test_has_init(self): + class DummyMimetypes(object): + def init(self): + self.initted = True + + module = DummyMimetypes() + result = self._callFUT(module) + self.assertEqual(result, True) + self.assertEqual(module.initted, True) + + def test_missing_init(self): + class DummyMimetypes(object): + pass + + module = DummyMimetypes() + result = self._callFUT(module) + self.assertEqual(result, False) + + +class TestResponseAdapter(unittest.TestCase): + def setUp(self): + registry = Dummy() + self.config = testing.setUp(registry=registry) + + def tearDown(self): + self.config.end() + + def _makeOne(self, *types_or_ifaces, **kw): + from pyramid.response import response_adapter + + return response_adapter(*types_or_ifaces, **kw) + + def test_register_single(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + dec = self._makeOne(IFoo) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.adapters, [(foo, IFoo)]) + + def test_register_multi(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + class IBar(Interface): + pass + + dec = self._makeOne(IFoo, IBar) + + def foo(): # pragma: no cover + pass + + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.adapters, [(foo, IFoo), (foo, IBar)]) + + def test___call__(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + dec = self._makeOne(IFoo) + dummy_venusian = DummyVenusian() + dec.venusian = dummy_venusian + + def foo(): # pragma: no cover + pass + + dec(foo) + self.assertEqual( + dummy_venusian.attached, [(foo, dec.register, 'pyramid', 1)] + ) + + def test___call___with_venusian_args(self): + from zope.interface import Interface + + class IFoo(Interface): + pass + + dec = self._makeOne(IFoo, _category='foo', _depth=1) + dummy_venusian = DummyVenusian() + dec.venusian = dummy_venusian + + def foo(): # pragma: no cover + pass + + dec(foo) + self.assertEqual( + dummy_venusian.attached, [(foo, dec.register, 'foo', 2)] + ) + + +class TestGetResponseFactory(unittest.TestCase): + def test_get_factory(self): + from pyramid.registry import Registry + from pyramid.response import Response, _get_response_factory + + registry = Registry() + response = _get_response_factory(registry)(None) + self.assertTrue(isinstance(response, Response)) + + +class Dummy(object): + pass + + +class DummyConfigurator(object): + def __init__(self): + self.adapters = [] + + def add_response_adapter(self, wrapped, type_or_iface): + self.adapters.append((wrapped, type_or_iface)) + + +class DummyVenusian(object): + def __init__(self): + self.attached = [] + + def attach(self, wrapped, fn, category=None, depth=None): + self.attached.append((wrapped, fn, category, depth)) diff --git a/tests/test_router.py b/tests/test_router.py new file mode 100644 index 000000000..3e66757f6 --- /dev/null +++ b/tests/test_router.py @@ -0,0 +1,1724 @@ +import unittest +from zope.interface import implementer + +from pyramid import testing +from pyramid.interfaces import IResponse + + +class TestRouter(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + self.registry = self.config.registry + + def tearDown(self): + testing.tearDown() + + def _registerRouteRequest(self, name): + from pyramid.interfaces import IRouteRequest + from pyramid.request import route_request_iface + + iface = route_request_iface(name) + self.registry.registerUtility(iface, IRouteRequest, name=name) + return iface + + def _connectRoute(self, name, path, factory=None): + from pyramid.interfaces import IRoutesMapper + from pyramid.urldispatch import RoutesMapper + + mapper = self.registry.queryUtility(IRoutesMapper) + if mapper is None: + mapper = RoutesMapper() + self.registry.registerUtility(mapper, IRoutesMapper) + return mapper.connect(name, path, factory) + + def _registerLogger(self): + from pyramid.interfaces import IDebugLogger + + logger = DummyLogger() + self.registry.registerUtility(logger, IDebugLogger) + return logger + + def _registerSettings(self, **kw): + settings = { + 'debug_authorization': False, + 'debug_notfound': False, + 'debug_routematch': False, + } + settings.update(kw) + self.registry.settings = settings + + def _registerTraverserFactory( + self, + context, + view_name='', + subpath=None, + traversed=None, + virtual_root=None, + virtual_root_path=None, + raise_error=None, + **kw + ): + from pyramid.interfaces import ITraverser + + if virtual_root is None: + virtual_root = context + if subpath is None: + subpath = [] + if traversed is None: + traversed = [] + if virtual_root_path is None: + virtual_root_path = [] + + class DummyTraverserFactory: + def __init__(self, root): + self.root = root + + def __call__(self, request): + if raise_error: + raise raise_error + values = { + 'root': self.root, + 'context': context, + 'view_name': view_name, + 'subpath': subpath, + 'traversed': traversed, + 'virtual_root': virtual_root, + 'virtual_root_path': virtual_root_path, + } + kw.update(values) + return kw + + self.registry.registerAdapter( + DummyTraverserFactory, (None,), ITraverser, name='' + ) + + def _registerView(self, app, name, classifier, req_iface, ctx_iface): + from pyramid.interfaces import IView + + self.registry.registerAdapter( + app, (classifier, req_iface, ctx_iface), IView, name + ) + + def _registerEventListener(self, iface): + L = [] + + def listener(event): + L.append(event) + + self.registry.registerHandler(listener, (iface,)) + return L + + def _registerRootFactory(self, val): + rootfactory = DummyRootFactory(val) + from pyramid.interfaces import IRootFactory + + self.registry.registerUtility(rootfactory, IRootFactory) + return rootfactory + + def _getTargetClass(self): + from pyramid.router import Router + + return Router + + def _makeOne(self): + klass = self._getTargetClass() + return klass(self.registry) + + def _makeEnviron(self, **extras): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'localhost', + 'SERVER_PORT': '8080', + 'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/', + } + environ.update(extras) + return environ + + def test_ctor_registry_has_no_settings(self): + self.registry.settings = None + router = self._makeOne() + self.assertEqual(router.debug_notfound, False) + self.assertEqual(router.debug_routematch, False) + self.assertFalse('debug_notfound' in router.__dict__) + self.assertFalse('debug_routematch' in router.__dict__) + + def test_root_policy(self): + context = DummyContext() + self._registerTraverserFactory(context) + rootfactory = self._registerRootFactory('abc') + router = self._makeOne() + self.assertEqual(router.root_policy, rootfactory) + + def test_request_factory(self): + from pyramid.interfaces import IRequestFactory + + class DummyRequestFactory(object): + pass + + self.registry.registerUtility(DummyRequestFactory, IRequestFactory) + router = self._makeOne() + self.assertEqual(router.request_factory, DummyRequestFactory) + + def test_tween_factories(self): + from pyramid.interfaces import ITweens + from pyramid.config.tweens import Tweens + from pyramid.response import Response + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IResponse + + tweens = Tweens() + self.registry.registerUtility(tweens, ITweens) + L = [] + + def tween_factory1(handler, registry): + L.append((handler, registry)) + + def wrapper(request): + request.environ['handled'].append('one') + return handler(request) + + wrapper.name = 'one' + wrapper.child = handler + return wrapper + + def tween_factory2(handler, registry): + L.append((handler, registry)) + + def wrapper(request): + request.environ['handled'] = ['two'] + return handler(request) + + wrapper.name = 'two' + wrapper.child = handler + return wrapper + + tweens.add_implicit('one', tween_factory1) + tweens.add_implicit('two', tween_factory2) + router = self._makeOne() + self.assertEqual(router.handle_request.name, 'two') + self.assertEqual(router.handle_request.child.name, 'one') + self.assertEqual( + router.handle_request.child.child.__name__, 'handle_request' + ) + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron() + view = DummyView('abc') + self._registerView( + self.config.derive_view(view), '', IViewClassifier, None, None + ) + start_response = DummyStartResponse() + + def make_response(s): + return Response(s) + + router.registry.registerAdapter(make_response, (str,), IResponse) + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + self.assertEqual(start_response.status, '200 OK') + self.assertEqual(environ['handled'], ['two', 'one']) + + def test_call_traverser_default(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + logger = self._registerLogger() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue('/' in why.args[0], why) + self.assertFalse('debug_notfound' in why.args[0]) + self.assertEqual(len(logger.messages), 0) + + def test_traverser_raises_notfound_class(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=HTTPNotFound) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(HTTPNotFound, router, environ, start_response) + + def test_traverser_raises_notfound_instance(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory( + context, raise_error=HTTPNotFound('foo') + ) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue('foo' in why.args[0], why) + + def test_traverser_raises_forbidden_class(self): + from pyramid.httpexceptions import HTTPForbidden + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=HTTPForbidden) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(HTTPForbidden, router, environ, start_response) + + def test_traverser_raises_forbidden_instance(self): + from pyramid.httpexceptions import HTTPForbidden + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory( + context, raise_error=HTTPForbidden('foo') + ) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPForbidden, router, environ, start_response) + self.assertTrue('foo' in why.args[0], why) + + def test_call_no_view_registered_no_isettings(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context) + logger = self._registerLogger() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue('/' in why.args[0], why) + self.assertFalse('debug_notfound' in why.args[0]) + self.assertEqual(len(logger.messages), 0) + + def test_call_no_view_registered_debug_notfound_false(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context) + logger = self._registerLogger() + self._registerSettings(debug_notfound=False) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue('/' in why.args[0], why) + self.assertFalse('debug_notfound' in why.args[0]) + self.assertEqual(len(logger.messages), 0) + + def test_call_no_view_registered_debug_notfound_true(self): + from pyramid.httpexceptions import HTTPNotFound + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context) + self._registerSettings(debug_notfound=True) + logger = self._registerLogger() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue( + "debug_notfound of url http://localhost:8080/; " in why.args[0] + ) + self.assertTrue("view_name: '', subpath: []" in why.args[0]) + self.assertTrue('http://localhost:8080' in why.args[0], why) + + self.assertEqual(len(logger.messages), 1) + message = logger.messages[0] + self.assertTrue('of url http://localhost:8080' in message) + self.assertTrue("path_info: " in message) + self.assertTrue('DummyContext' in message) + self.assertTrue("view_name: ''" in message) + self.assertTrue("subpath: []" in message) + + def test_call_view_returns_non_iresponse(self): + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron() + view = DummyView('abc') + self._registerView( + self.config.derive_view(view), '', IViewClassifier, None, None + ) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(ValueError, router, environ, start_response) + + def test_call_view_returns_adapted_response(self): + from pyramid.response import Response + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IResponse + + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron() + view = DummyView('abc') + self._registerView( + self.config.derive_view(view), '', IViewClassifier, None, None + ) + router = self._makeOne() + start_response = DummyStartResponse() + + def make_response(s): + return Response(s) + + router.registry.registerAdapter(make_response, (str,), IResponse) + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + self.assertEqual(start_response.status, '200 OK') + + def test_call_with_request_extensions(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequestExtensions + from pyramid.interfaces import IRequest + from pyramid.request import Request + from pyramid.util import InstancePropertyHelper + + context = DummyContext() + self._registerTraverserFactory(context) + + class Extensions(object): + def __init__(self): + self.methods = {} + self.descriptors = {} + + extensions = Extensions() + ext_method = lambda r: 'bar' + name, fn = InstancePropertyHelper.make_property(ext_method, name='foo') + extensions.descriptors[name] = fn + request = Request.blank('/') + request.request_iface = IRequest + request.registry = self.registry + + def request_factory(environ): + return request + + self.registry.registerUtility(extensions, IRequestExtensions) + environ = self._makeEnviron() + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + self._registerView( + self.config.derive_view(view), '', IViewClassifier, None, None + ) + router = self._makeOne() + router.request_factory = request_factory + start_response = DummyStartResponse() + router(environ, start_response) + self.assertEqual(view.request.foo, 'bar') + + def test_call_view_registered_nonspecific_default_path(self): + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView( + self.config.derive_view(view), '', IViewClassifier, None, None + ) + self._registerRootFactory(context) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['Hello world']) + self.assertEqual(start_response.headers, ()) + self.assertEqual(start_response.status, '200 OK') + request = view.request + self.assertEqual(request.view_name, '') + self.assertEqual(request.subpath, []) + self.assertEqual(request.context, context) + self.assertEqual(request.root, context) + + def test_call_view_registered_nonspecific_nondefault_path_and_subpath( + self + ): + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + self._registerTraverserFactory( + context, view_name='foo', subpath=['bar'], traversed=['context'] + ) + self._registerRootFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, 'foo', IViewClassifier, None, None) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['Hello world']) + self.assertEqual(start_response.headers, ()) + self.assertEqual(start_response.status, '200 OK') + request = view.request + self.assertEqual(request.view_name, 'foo') + self.assertEqual(request.subpath, ['bar']) + self.assertEqual(request.context, context) + self.assertEqual(request.root, context) + + def test_call_view_registered_specific_success(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context) + self._registerRootFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['Hello world']) + self.assertEqual(start_response.headers, ()) + self.assertEqual(start_response.status, '200 OK') + request = view.request + self.assertEqual(request.view_name, '') + self.assertEqual(request.subpath, []) + self.assertEqual(request.context, context) + self.assertEqual(request.root, context) + + def test_call_view_registered_specific_fail(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from pyramid.httpexceptions import HTTPNotFound + from pyramid.interfaces import IViewClassifier + + class IContext(Interface): + pass + + class INotContext(Interface): + pass + + from pyramid.interfaces import IRequest + + context = DummyContext() + directlyProvides(context, INotContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(HTTPNotFound, router, environ, start_response) + + def test_call_view_raises_forbidden(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from pyramid.httpexceptions import HTTPForbidden + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView( + response, raise_exception=HTTPForbidden("unauthorized") + ) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPForbidden, router, environ, start_response) + self.assertEqual(why.args[0], 'unauthorized') + + def test_call_view_raises_notfound(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.httpexceptions import HTTPNotFound + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView(response, raise_exception=HTTPNotFound("notfound")) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertEqual(why.args[0], 'notfound') + + def test_call_view_raises_response_cleared(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from pyramid.interfaces import IExceptionViewClassifier + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + + def view(context, request): + request.response.a = 1 + raise KeyError + + def exc_view(context, request): + self.assertFalse(hasattr(request.response, 'a')) + request.response.body = b'OK' + return request.response + + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + self._registerView( + exc_view, '', IExceptionViewClassifier, IRequest, KeyError + ) + router = self._makeOne() + start_response = DummyStartResponse() + itera = router(environ, start_response) + self.assertEqual(itera, [b'OK']) + + def test_call_request_has_response_callbacks(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse('200 OK') + + def view(context, request): + def callback(request, response): + response.called_back = True + + request.add_response_callback(callback) + return response + + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + router(environ, start_response) + self.assertEqual(response.called_back, True) + + def test_call_request_has_finished_callbacks_when_view_succeeds(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse('200 OK') + + def view(context, request): + def callback(request): + request.environ['called_back'] = True + + request.add_finished_callback(callback) + return response + + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + router(environ, start_response) + self.assertEqual(environ['called_back'], True) + + def test_call_request_has_finished_callbacks_when_view_raises(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + + def view(context, request): + def callback(request): + request.environ['called_back'] = True + + request.add_finished_callback(callback) + raise NotImplementedError + + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + exc_raised(NotImplementedError, router, environ, start_response) + self.assertEqual(environ['called_back'], True) + + def test_call_request_factory_raises(self): + # making sure finally doesnt barf when a request cannot be created + environ = self._makeEnviron() + router = self._makeOne() + + def dummy_request_factory(environ): + raise NotImplementedError + + router.request_factory = dummy_request_factory + start_response = DummyStartResponse() + exc_raised(NotImplementedError, router, environ, start_response) + + def test_call_eventsends(self): + from pyramid.interfaces import INewRequest + from pyramid.interfaces import INewResponse + from pyramid.interfaces import IBeforeTraversal + from pyramid.interfaces import IContextFound + from pyramid.interfaces import IViewClassifier + + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, None, None) + request_events = self._registerEventListener(INewRequest) + beforetraversal_events = self._registerEventListener(IBeforeTraversal) + context_found_events = self._registerEventListener(IContextFound) + response_events = self._registerEventListener(INewResponse) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(len(request_events), 1) + self.assertEqual(request_events[0].request.environ, environ) + self.assertEqual(len(beforetraversal_events), 1) + self.assertEqual(beforetraversal_events[0].request.environ, environ) + self.assertEqual(len(context_found_events), 1) + self.assertEqual(context_found_events[0].request.environ, environ) + self.assertEqual(context_found_events[0].request.context, context) + self.assertEqual(len(response_events), 1) + self.assertEqual(response_events[0].response, response) + self.assertEqual(response_events[0].request.context, context) + self.assertEqual(result, response.app_iter) + + def test_call_newrequest_evllist_exc_can_be_caught_by_exceptionview(self): + from pyramid.interfaces import INewRequest + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron() + + def listener(event): + raise KeyError + + self.registry.registerHandler(listener, (INewRequest,)) + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView( + exception_view, '', IExceptionViewClassifier, IRequest, KeyError + ) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, exception_response.app_iter) + + def test_call_route_matches_and_has_factory(self): + from pyramid.interfaces import IViewClassifier + + logger = self._registerLogger() + self._registerSettings(debug_routematch=True) + self._registerRouteRequest('foo') + root = object() + + def factory(request): + return root + + route = self._connectRoute('foo', 'archives/:action/:article', factory) + route.predicates = [DummyPredicate()] + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + self._registerView(view, '', IViewClassifier, None, None) + self._registerRootFactory(context) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['Hello world']) + self.assertEqual(start_response.headers, ()) + self.assertEqual(start_response.status, '200 OK') + request = view.request + self.assertEqual(request.view_name, '') + self.assertEqual(request.subpath, []) + self.assertEqual(request.context, context) + self.assertEqual(request.root, root) + matchdict = {'action': 'action1', 'article': 'article1'} + self.assertEqual(request.matchdict, matchdict) + self.assertEqual(request.matched_route.name, 'foo') + self.assertEqual(len(logger.messages), 1) + self.assertTrue( + logger.messages[0].startswith( + "route matched for url http://localhost:8080" + "/archives/action1/article1; " + "route_name: 'foo', " + "path_info: " + ) + ) + self.assertTrue("predicates: 'predicate'" in logger.messages[0]) + + def test_call_route_match_miss_debug_routematch(self): + from pyramid.httpexceptions import HTTPNotFound + + logger = self._registerLogger() + self._registerSettings(debug_routematch=True) + self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article') + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron(PATH_INFO='/wontmatch') + self._registerRootFactory(context) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(HTTPNotFound, router, environ, start_response) + + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + 'no route matched for url http://localhost:8080/wontmatch', + ) + + def test_call_route_matches_doesnt_overwrite_subscriber_iface(self): + from pyramid.interfaces import INewRequest + from pyramid.interfaces import IViewClassifier + from zope.interface import alsoProvides + from zope.interface import Interface + + self._registerRouteRequest('foo') + + class IFoo(Interface): + pass + + def listener(event): + alsoProvides(event.request, IFoo) + + self.registry.registerHandler(listener, (INewRequest,)) + root = object() + + def factory(request): + return root + + self._connectRoute('foo', 'archives/:action/:article', factory) + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + self._registerView(view, '', IViewClassifier, None, None) + self._registerRootFactory(context) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['Hello world']) + self.assertEqual(start_response.headers, ()) + self.assertEqual(start_response.status, '200 OK') + request = view.request + self.assertEqual(request.view_name, '') + self.assertEqual(request.subpath, []) + self.assertEqual(request.context, context) + self.assertEqual(request.root, root) + matchdict = {'action': 'action1', 'article': 'article1'} + self.assertEqual(request.matchdict, matchdict) + self.assertEqual(request.matched_route.name, 'foo') + self.assertTrue(IFoo.providedBy(request)) + + def test_root_factory_raises_notfound(self): + from pyramid.interfaces import IRootFactory + from pyramid.httpexceptions import HTTPNotFound + from zope.interface import Interface + from zope.interface import directlyProvides + + def rootfactory(request): + raise HTTPNotFound('from root factory') + + self.registry.registerUtility(rootfactory, IRootFactory) + + class IContext(Interface): + pass + + context = DummyContext() + directlyProvides(context, IContext) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPNotFound, router, environ, start_response) + self.assertTrue('from root factory' in why.args[0]) + + def test_root_factory_raises_forbidden(self): + from pyramid.interfaces import IRootFactory + from pyramid.httpexceptions import HTTPForbidden + from zope.interface import Interface + from zope.interface import directlyProvides + + def rootfactory(request): + raise HTTPForbidden('from root factory') + + self.registry.registerUtility(rootfactory, IRootFactory) + + class IContext(Interface): + pass + + context = DummyContext() + directlyProvides(context, IContext) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(HTTPForbidden, router, environ, start_response) + self.assertTrue('from root factory' in why.args[0]) + + def test_root_factory_exception_propagating(self): + from pyramid.interfaces import IRootFactory + from zope.interface import Interface + from zope.interface import directlyProvides + + def rootfactory(request): + raise RuntimeError() + + self.registry.registerUtility(rootfactory, IRootFactory) + + class IContext(Interface): + pass + + context = DummyContext() + directlyProvides(context, IContext) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(RuntimeError, router, environ, start_response) + + def test_traverser_exception_propagating(self): + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=RuntimeError()) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(RuntimeError, router, environ, start_response) + + def test_call_view_exception_propagating(self): + from zope.interface import Interface + from zope.interface import directlyProvides + + class IContext(Interface): + pass + + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequestFactory + from pyramid.interfaces import IExceptionViewClassifier + + def rfactory(environ): + return request + + self.registry.registerUtility(rfactory, IRequestFactory) + from pyramid.request import Request + + request = Request.blank('/') + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + response.app_iter = ['OK'] + error = RuntimeError() + view = DummyView(response, raise_exception=error) + environ = self._makeEnviron() + + def exception_view(context, request): + self.assertEqual(request.exc_info[0], RuntimeError) + return response + + self._registerView(view, '', IViewClassifier, IRequest, IContext) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ['OK']) + # exc_info and exception should still be around on the request after + # the excview tween has run (see + # https://github.com/Pylons/pyramid/issues/1223) + self.assertEqual(request.exception, error) + self.assertEqual(request.exc_info[:2], (RuntimeError, error)) + + def test_call_view_raises_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=RuntimeError) + + def exception_view(context, request): + self.assertEqual(request.exception.__class__, RuntimeError) + return exception_response + + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_call_view_raises_super_exception_sub_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class SuperException(Exception): + pass + + class SubException(SuperException): + pass + + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=SuperException) + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + SubException, + ) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(SuperException, router, environ, start_response) + + def test_call_view_raises_sub_exception_super_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class SuperException(Exception): + pass + + class SubException(SuperException): + pass + + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=SubException) + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + SuperException, + ) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_call_view_raises_exception_another_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class MyException(Exception): + pass + + class AnotherException(Exception): + pass + + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=MyException) + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + AnotherException, + ) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(MyException, router, environ, start_response) + + def test_root_factory_raises_exception_view(self): + from pyramid.interfaces import IRootFactory + from pyramid.interfaces import IRequest + from pyramid.interfaces import IExceptionViewClassifier + + def rootfactory(request): + raise RuntimeError() + + self.registry.registerUtility(rootfactory, IRootFactory) + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + exception_view = DummyView(exception_response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(app_iter, ["Hello, world"]) + + def test_traverser_raises_exception_view(self): + from pyramid.interfaces import IRequest + from pyramid.interfaces import IExceptionViewClassifier + + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=RuntimeError()) + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + exception_view = DummyView(exception_response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_exception_view_returns_non_iresponse(self): + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + environ = self._makeEnviron() + response = DummyResponse() + view = DummyView(response, raise_exception=RuntimeError) + + self._registerView( + self.config.derive_view(view), '', IViewClassifier, IRequest, None + ) + exception_view = DummyView(None) + self._registerView( + self.config.derive_view(exception_view), + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(ValueError, router, environ, start_response) + + def test_call_route_raises_route_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + req_iface, + RuntimeError, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_call_view_raises_exception_route_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, IRequest, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + req_iface, + RuntimeError, + ) + environ = self._makeEnviron() + start_response = DummyStartResponse() + router = self._makeOne() + self.assertRaises(RuntimeError, router, environ, start_response) + + def test_call_route_raises_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_call_route_raises_super_exception_sub_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class SuperException(Exception): + pass + + class SubException(SuperException): + pass + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=SuperException) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + SubException, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + self.assertRaises(SuperException, router, environ, start_response) + + def test_call_route_raises_sub_exception_super_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class SuperException(Exception): + pass + + class SubException(SuperException): + pass + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=SubException) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + SuperException, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_call_route_raises_exception_another_exception_view(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class MyException(Exception): + pass + + class AnotherException(Exception): + pass + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=MyException) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + AnotherException, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + self.assertRaises(MyException, router, environ, start_response) + + def test_call_route_raises_exception_view_specializing(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + IRequest, + RuntimeError, + ) + response_spec = DummyResponse() + response_spec.app_iter = ["Hello, special world"] + exception_view_spec = DummyView(response_spec) + self._registerView( + exception_view_spec, + '', + IExceptionViewClassifier, + req_iface, + RuntimeError, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, special world"]) + + def test_call_route_raises_exception_view_another_route(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + req_iface = self._registerRouteRequest('foo') + another_req_iface = self._registerRouteRequest('bar') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, req_iface, None) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view = DummyView(response) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + another_req_iface, + RuntimeError, + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + self.assertRaises(RuntimeError, router, environ, start_response) + + def test_call_view_raises_exception_view_route(self): + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + + req_iface = self._registerRouteRequest('foo') + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=RuntimeError) + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView( + exception_view, + '', + IExceptionViewClassifier, + req_iface, + RuntimeError, + ) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(RuntimeError, router, environ, start_response) + + def test_call_view_raises_predicate_mismatch(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest + + view = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view, '', IViewClassifier, IRequest, None) + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(PredicateMismatch, router, environ, start_response) + + def test_call_view_predicate_mismatch_doesnt_hide_views(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest, IResponse + from pyramid.response import Response + + class BaseContext: + pass + + class DummyContext(BaseContext): + pass + + context = DummyContext() + self._registerTraverserFactory(context) + view = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view, '', IViewClassifier, IRequest, DummyContext) + good_view = DummyView('abc') + self._registerView( + self.config.derive_view(good_view), + '', + IViewClassifier, + IRequest, + BaseContext, + ) + router = self._makeOne() + + def make_response(s): + return Response(s) + + router.registry.registerAdapter(make_response, (str,), IResponse) + environ = self._makeEnviron() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + + def test_call_view_multiple_predicate_mismatches_dont_hide_views(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest, IResponse + from pyramid.response import Response + from zope.interface import Interface, implementer + + class IBaseContext(Interface): + pass + + class IContext(IBaseContext): + pass + + @implementer(IContext) + class DummyContext: + pass + + context = DummyContext() + self._registerTraverserFactory(context) + view1 = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view1, '', IViewClassifier, IRequest, DummyContext) + view2 = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view2, '', IViewClassifier, IRequest, IContext) + good_view = DummyView('abc') + self._registerView( + self.config.derive_view(good_view), + '', + IViewClassifier, + IRequest, + IBaseContext, + ) + router = self._makeOne() + + def make_response(s): + return Response(s) + + router.registry.registerAdapter(make_response, (str,), IResponse) + environ = self._makeEnviron() + start_response = DummyStartResponse() + app_iter = router(environ, start_response) + self.assertEqual(app_iter, [b'abc']) + + def test_call_view_predicate_mismatch_doesnt_find_unrelated_views(self): + from pyramid.exceptions import PredicateMismatch + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRequest + from zope.interface import Interface, implementer + + class IContext(Interface): + pass + + class IOtherContext(Interface): + pass + + @implementer(IContext) + class DummyContext: + pass + + context = DummyContext() + self._registerTraverserFactory(context) + view = DummyView(DummyResponse(), raise_exception=PredicateMismatch) + self._registerView(view, '', IViewClassifier, IRequest, DummyContext) + please_dont_call_me_view = DummyView('abc') + self._registerView( + self.config.derive_view(please_dont_call_me_view), + '', + IViewClassifier, + IRequest, + IOtherContext, + ) + router = self._makeOne() + environ = self._makeEnviron() + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(PredicateMismatch, router, environ, start_response) + + def test_custom_execution_policy(self): + from pyramid.interfaces import IExecutionPolicy + from pyramid.request import Request + from pyramid.response import Response + + registry = self.config.registry + + def dummy_policy(environ, router): + return Response(status=200, body=b'foo') + + registry.registerUtility(dummy_policy, IExecutionPolicy) + router = self._makeOne() + resp = Request.blank('/').get_response(router) + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.body, b'foo') + + def test_execution_policy_handles_exception(self): + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IRequest + + class Exception1(Exception): + pass + + class Exception2(Exception): + pass + + req_iface = self._registerRouteRequest('foo') + self._connectRoute('foo', 'archives/:action/:article', None) + view = DummyView(DummyResponse(), raise_exception=Exception1) + self._registerView(view, '', IViewClassifier, req_iface, None) + exception_view1 = DummyView( + DummyResponse(), raise_exception=Exception2 + ) + self._registerView( + exception_view1, '', IExceptionViewClassifier, IRequest, Exception1 + ) + response = DummyResponse() + response.app_iter = ["Hello, world"] + exception_view2 = DummyView(response) + self._registerView( + exception_view2, '', IExceptionViewClassifier, IRequest, Exception2 + ) + environ = self._makeEnviron(PATH_INFO='/archives/action1/article1') + start_response = DummyStartResponse() + router = self._makeOne() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + + def test_request_context_with_statement(self): + from pyramid.threadlocal import get_current_request + from pyramid.interfaces import IExecutionPolicy + from pyramid.request import Request + from pyramid.response import Response + + registry = self.config.registry + result = [] + + def dummy_policy(environ, router): + with router.request_context(environ): + result.append(get_current_request()) + result.append(get_current_request()) + return Response(status=200, body=b'foo') + + registry.registerUtility(dummy_policy, IExecutionPolicy) + router = self._makeOne() + resp = Request.blank('/test_path').get_response(router) + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.body, b'foo') + self.assertEqual(result[0].path_info, '/test_path') + self.assertEqual(result[1], None) + + def test_request_context_manually(self): + from pyramid.threadlocal import get_current_request + from pyramid.interfaces import IExecutionPolicy + from pyramid.request import Request + from pyramid.response import Response + + registry = self.config.registry + result = [] + + def dummy_policy(environ, router): + ctx = router.request_context(environ) + ctx.begin() + result.append(get_current_request()) + ctx.end() + result.append(get_current_request()) + return Response(status=200, body=b'foo') + + registry.registerUtility(dummy_policy, IExecutionPolicy) + router = self._makeOne() + resp = Request.blank('/test_path').get_response(router) + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.body, b'foo') + self.assertEqual(result[0].path_info, '/test_path') + self.assertEqual(result[1], None) + + +class DummyPredicate(object): + def __call__(self, info, request): + return True + + def text(self): + return 'predicate' + + +class DummyContext: + pass + + +class DummyView: + def __init__(self, response, raise_exception=None): + self.response = response + self.raise_exception = raise_exception + + def __call__(self, context, request): + self.context = context + self.request = request + if self.raise_exception is not None: + raise self.raise_exception + return self.response + + +class DummyRootFactory: + def __init__(self, root): + self.root = root + + def __call__(self, environ): + return self.root + + +class DummyStartResponse: + status = () + headers = () + + def __call__(self, status, headers): + self.status = status + self.headers = headers + + +@implementer(IResponse) +class DummyResponse(object): + headerlist = () + app_iter = () + environ = None + + def __init__(self, status='200 OK'): + self.status = status + + def __call__(self, environ, start_response): + self.environ = environ + start_response(self.status, self.headerlist) + return self.app_iter + + +class DummyAuthenticationPolicy: + pass + + +class DummyLogger: + def __init__(self): + self.messages = [] + + def info(self, msg): + self.messages.append(msg) + + warn = info + debug = info + + +def exc_raised(exc, func, *arg, **kw): + try: + func(*arg, **kw) + except exc as e: + return e + else: + raise AssertionError('%s not raised' % exc) # pragma: no cover diff --git a/tests/test_scripting.py b/tests/test_scripting.py new file mode 100644 index 000000000..8f74f35f8 --- /dev/null +++ b/tests/test_scripting.py @@ -0,0 +1,242 @@ +import unittest + + +class Test_get_root(unittest.TestCase): + def _callFUT(self, app, request=None): + from pyramid.scripting import get_root + + return get_root(app, request) + + def _makeRegistry(self): + return DummyRegistry([DummyFactory]) + + def setUp(self): + from pyramid.threadlocal import manager + + self.manager = manager + self.default = manager.get() + + def test_it_norequest(self): + registry = self._makeRegistry() + app = DummyApp(registry=registry) + root, closer = self._callFUT(app) + self.assertEqual(dummy_root, root) + pushed = self.manager.get() + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'].registry, registry) + self.assertEqual(pushed['request'].environ['path'], '/') + closer() + self.assertEqual(self.default, self.manager.get()) + + def test_it_withrequest(self): + registry = self._makeRegistry() + app = DummyApp(registry=registry) + request = DummyRequest({}) + root, closer = self._callFUT(app, request) + self.assertEqual(dummy_root, root) + pushed = self.manager.get() + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'], request) + self.assertEqual(pushed['request'].registry, registry) + closer() + self.assertEqual(self.default, self.manager.get()) + + +class Test_prepare(unittest.TestCase): + def _callFUT(self, request=None, registry=None): + from pyramid.scripting import prepare + + return prepare(request, registry) + + def _makeRegistry(self, L=None): + if L is None: + L = [None, DummyFactory] + return DummyRegistry(L) + + def setUp(self): + from pyramid.threadlocal import manager + + self.manager = manager + self.default = manager.get() + + def test_it_no_valid_apps(self): + from pyramid.exceptions import ConfigurationError + + self.assertRaises(ConfigurationError, self._callFUT) + + def test_it_norequest(self): + registry = self._makeRegistry([DummyFactory, None, DummyFactory]) + info = self._callFUT(registry=registry) + root, closer, request = info['root'], info['closer'], info['request'] + pushed = self.manager.get() + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'].registry, registry) + self.assertEqual(root.a, (pushed['request'],)) + closer() + self.assertEqual(self.default, self.manager.get()) + self.assertEqual(request.context, root) + + def test_it_withrequest_hasregistry(self): + request = DummyRequest({}) + registry = request.registry = self._makeRegistry() + info = self._callFUT(request=request) + root, closer, request = info['root'], info['closer'], info['request'] + pushed = self.manager.get() + self.assertEqual(pushed['request'], request) + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'].registry, registry) + self.assertEqual(root.a, (request,)) + closer() + self.assertEqual(self.default, self.manager.get()) + self.assertEqual(request.context, root) + self.assertEqual(request.registry, registry) + + def test_it_withrequest_noregistry(self): + request = DummyRequest({}) + registry = self._makeRegistry() + info = self._callFUT(request=request, registry=registry) + root, closer, request = info['root'], info['closer'], info['request'] + closer() + self.assertEqual(request.context, root) + # should be set by prepare + self.assertEqual(request.registry, registry) + + def test_it_with_request_and_registry(self): + request = DummyRequest({}) + registry = request.registry = self._makeRegistry() + info = self._callFUT(request=request, registry=registry) + root, closer, root = info['root'], info['closer'], info['root'] + pushed = self.manager.get() + self.assertEqual(pushed['request'], request) + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'].registry, registry) + self.assertEqual(root.a, (request,)) + closer() + self.assertEqual(self.default, self.manager.get()) + self.assertEqual(request.context, root) + + def test_it_with_request_context_already_set(self): + request = DummyRequest({}) + context = Dummy() + request.context = context + registry = request.registry = self._makeRegistry() + info = self._callFUT(request=request, registry=registry) + closer = info['closer'] + closer() + self.assertEqual(request.context, context) + + def test_it_with_extensions(self): + from pyramid.util import InstancePropertyHelper + + exts = DummyExtensions() + ext_method = lambda r: 'bar' + name, fn = InstancePropertyHelper.make_property(ext_method, 'foo') + exts.descriptors[name] = fn + request = DummyRequest({}) + registry = request.registry = self._makeRegistry([exts, DummyFactory]) + info = self._callFUT(request=request, registry=registry) + self.assertEqual(request.foo, 'bar') + closer = info['closer'] + closer() + + def test_it_is_a_context_manager(self): + request = DummyRequest({}) + registry = request.registry = self._makeRegistry() + closer_called = [False] + with self._callFUT(request=request) as info: + root, request = info['root'], info['request'] + pushed = self.manager.get() + self.assertEqual(pushed['request'], request) + self.assertEqual(pushed['registry'], registry) + self.assertEqual(pushed['request'].registry, registry) + self.assertEqual(root.a, (request,)) + orig_closer = info['closer'] + + def closer(): + orig_closer() + closer_called[0] = True + + info['closer'] = closer + self.assertTrue(closer_called[0]) + self.assertEqual(self.default, self.manager.get()) + self.assertEqual(request.context, root) + self.assertEqual(request.registry, registry) + + +class Test__make_request(unittest.TestCase): + def _callFUT(self, path='/', registry=None): + from pyramid.scripting import _make_request + + return _make_request(path, registry) + + def _makeRegistry(self): + return DummyRegistry([DummyFactory]) + + def test_it_with_registry(self): + registry = self._makeRegistry() + request = self._callFUT('/', registry) + self.assertEqual(request.environ['path'], '/') + self.assertEqual(request.registry, registry) + + def test_it_with_no_registry(self): + from pyramid.config import global_registries + + registry = self._makeRegistry() + global_registries.add(registry) + try: + request = self._callFUT('/hello') + self.assertEqual(request.environ['path'], '/hello') + self.assertEqual(request.registry, registry) + finally: + global_registries.empty() + + +class Dummy: + pass + + +dummy_root = Dummy() + + +class DummyFactory(object): + @classmethod + def blank(cls, path): + req = DummyRequest({'path': path}) + return req + + def __init__(self, *a, **kw): + self.a = a + self.kw = kw + + +class DummyRegistry(object): + def __init__(self, utilities): + self.utilities = utilities + + def queryUtility(self, iface, default=None): # pragma: no cover + if self.utilities: + return self.utilities.pop(0) + return default + + +class DummyApp: + def __init__(self, registry=None): + if registry: + self.registry = registry + + def root_factory(self, environ): + return dummy_root + + +class DummyRequest(object): + matchdict = None + matched_route = None + + def __init__(self, environ): + self.environ = environ + + +class DummyExtensions: + def __init__(self): + self.descriptors = {} + self.methods = {} diff --git a/tests/test_scripts/__init__.py b/tests/test_scripts/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/tests/test_scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/tests/test_scripts/dummy.py b/tests/test_scripts/dummy.py new file mode 100644 index 000000000..8e340f645 --- /dev/null +++ b/tests/test_scripts/dummy.py @@ -0,0 +1,220 @@ +from zope.interface import implementer +from pyramid.interfaces import IMultiView + + +class DummyTweens(object): + def __init__(self, implicit, explicit): + self._implicit = implicit + self.explicit = explicit + self.name_to_alias = {} + + def implicit(self): + return self._implicit + + +class Dummy: + pass + + +dummy_root = Dummy() + + +class DummyRegistry(object): + settings = {} + + def queryUtility(self, iface, default=None, name=''): + return default + + +dummy_registry = DummyRegistry() + + +class DummyShell(object): + env = {} + help = '' + called = False + dummy_attr = 1 + + def __call__(self, env, help): + self.env = env + self.help = help + self.called = True + self.env['request'].dummy_attr = self.dummy_attr + + +class DummyInteractor: + def __call__(self, banner, local): + self.banner = banner + self.local = local + + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + + +class DummyMapper(object): + def __init__(self, *routes): + self.routes = routes + + def get_routes(self, include_static=False): + return self.routes + + +class DummyRoute(object): + def __init__( + self, name, pattern, factory=None, matchdict=None, predicate=None + ): + self.name = name + self.path = pattern + self.pattern = pattern + self.factory = factory + self.matchdict = matchdict + self.predicates = [] + if predicate is not None: + self.predicates = [predicate] + + def match(self, route): + return self.matchdict + + +class DummyRequest: + application_url = 'http://example.com:5432' + script_name = '' + + def __init__(self, environ): + self.environ = environ + self.matchdict = {} + + +class DummyView(object): + def __init__(self, **attrs): + self.__request_attrs__ = attrs + + def view(context, request): # pragma: no cover + pass + + +@implementer(IMultiView) +class DummyMultiView(object): + def __init__(self, *views, **attrs): + self.views = [(None, view, None) for view in views] + self.__request_attrs__ = attrs + + +class DummyCloser(object): + def __call__(self): + self.called = True + + +class DummyBootstrap(object): + def __init__( + self, + app=None, + registry=None, + request=None, + root=None, + root_factory=None, + closer=None, + ): + self.app = app or DummyApp() + if registry is None: + registry = DummyRegistry() + self.registry = registry + if request is None: + request = DummyRequest({}) + self.request = request + if root is None: + root = Dummy() + self.root = root + if root_factory is None: + root_factory = Dummy() + self.root_factory = root_factory + if closer is None: + closer = DummyCloser() + self.closer = closer + + def __call__(self, *a, **kw): + self.a = a + self.kw = kw + registry = kw.get('registry', self.registry) + request = kw.get('request', self.request) + request.registry = registry + return { + 'app': self.app, + 'registry': registry, + 'request': request, + 'root': self.root, + 'root_factory': self.root_factory, + 'closer': self.closer, + } + + +class DummyEntryPoint(object): + def __init__(self, name, module): + self.name = name + self.module = module + + def load(self): + return self.module + + +class DummyPkgResources(object): + def __init__(self, entry_point_values): + self.entry_points = [] + + for name, module in entry_point_values.items(): + self.entry_points.append(DummyEntryPoint(name, module)) + + def iter_entry_points(self, name): + return self.entry_points + + +class dummy_setup_logging(object): + def __call__(self, config_uri, global_conf): + self.config_uri = config_uri + self.defaults = global_conf + + +class DummyLoader(object): + def __init__( + self, settings=None, app_settings=None, app=None, server=None + ): + if not settings: + settings = {} + if not app_settings: + app_settings = {} + self.settings = settings + self.app_settings = app_settings + self.app = app + self.server = server + self.calls = [] + + def __call__(self, uri): + import plaster + + self.uri = plaster.parse_uri(uri) + return self + + def add_call(self, op, name, defaults): + self.calls.append({'op': op, 'name': name, 'defaults': defaults}) + + def get_settings(self, name=None, defaults=None): + self.add_call('settings', name, defaults) + return self.settings.get(name, {}) + + def get_wsgi_app(self, name=None, defaults=None): + self.add_call('app', name, defaults) + return self.app + + def get_wsgi_app_settings(self, name=None, defaults=None): + self.add_call('app_settings', name, defaults) + return self.app_settings + + def get_wsgi_server(self, name=None, defaults=None): + self.add_call('server', name, defaults) + return self.server + + def setup_logging(self, defaults): + self.add_call('logging', None, defaults) + self.defaults = defaults diff --git a/tests/test_scripts/pystartup.txt b/tests/test_scripts/pystartup.txt new file mode 100644 index 000000000..c62c4ca74 --- /dev/null +++ b/tests/test_scripts/pystartup.txt @@ -0,0 +1,3 @@ +# this file has a .txt extension to avoid coverage reports +# since it is not imported but rather the contents are read and exec'd +foo = 1 diff --git a/tests/test_scripts/test_common.py b/tests/test_scripts/test_common.py new file mode 100644 index 000000000..e286d3c3e --- /dev/null +++ b/tests/test_scripts/test_common.py @@ -0,0 +1,16 @@ +import unittest + + +class TestParseVars(unittest.TestCase): + def test_parse_vars_good(self): + from pyramid.scripts.common import parse_vars + + vars = ['a=1', 'b=2'] + result = parse_vars(vars) + self.assertEqual(result, {'a': '1', 'b': '2'}) + + def test_parse_vars_bad(self): + from pyramid.scripts.common import parse_vars + + vars = ['a'] + self.assertRaises(ValueError, parse_vars, vars) diff --git a/tests/test_scripts/test_pdistreport.py b/tests/test_scripts/test_pdistreport.py new file mode 100644 index 000000000..031a6ff2b --- /dev/null +++ b/tests/test_scripts/test_pdistreport.py @@ -0,0 +1,84 @@ +import unittest + + +class TestPDistReportCommand(unittest.TestCase): + def _callFUT(self, **kw): + argv = [] + from pyramid.scripts.pdistreport import main + + return main(argv, **kw) + + def test_no_dists(self): + def platform(): + return 'myplatform' + + pkg_resources = DummyPkgResources() + L = [] + + def out(*args): + L.extend(args) + + result = self._callFUT( + pkg_resources=pkg_resources, platform=platform, out=out + ) + self.assertEqual(result, None) + self.assertEqual( + L, + ['Pyramid version:', '1', 'Platform:', 'myplatform', 'Packages:'], + ) + + def test_with_dists(self): + def platform(): + return 'myplatform' + + working_set = (DummyDistribution('abc'), DummyDistribution('def')) + pkg_resources = DummyPkgResources(working_set) + L = [] + + def out(*args): + L.extend(args) + + result = self._callFUT( + pkg_resources=pkg_resources, platform=platform, out=out + ) + self.assertEqual(result, None) + self.assertEqual( + L, + [ + 'Pyramid version:', + '1', + 'Platform:', + 'myplatform', + 'Packages:', + ' ', + 'abc', + '1', + ' ', + '/projects/abc', + ' ', + 'def', + '1', + ' ', + '/projects/def', + ], + ) + + +class DummyPkgResources(object): + def __init__(self, working_set=()): + self.working_set = working_set + + def get_distribution(self, name): + return Version('1') + + +class Version(object): + def __init__(self, version): + self.version = version + + +class DummyDistribution(object): + def __init__(self, name): + self.project_name = name + self.version = '1' + self.location = '/projects/%s' % name diff --git a/tests/test_scripts/test_prequest.py b/tests/test_scripts/test_prequest.py new file mode 100644 index 000000000..1521172bc --- /dev/null +++ b/tests/test_scripts/test_prequest.py @@ -0,0 +1,258 @@ +import unittest +from . import dummy + + +class TestPRequestCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.prequest import PRequestCommand + + return PRequestCommand + + def _makeOne(self, argv, headers=None): + cmd = self._getTargetClass()(argv) + + def helloworld(environ, start_request): + self._environ = environ + self._path_info = environ['PATH_INFO'] + start_request('200 OK', headers or []) + return [b'abc'] + + self.loader = dummy.DummyLoader(app=helloworld) + self._out = [] + cmd._get_config_loader = self.loader + cmd.out = self.out + return cmd + + def out(self, msg): + self._out.append(msg) + + def test_command_not_enough_args(self): + command = self._makeOne([]) + command.run() + self.assertEqual( + self._out, ['You must provide at least two arguments'] + ) + + def test_command_two_args(self): + command = self._makeOne( + ['', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._path_info, '/') + self.assertEqual(self.loader.uri.path, 'development.ini') + self.assertEqual(self.loader.calls[0]['op'], 'logging') + self.assertEqual(self.loader.calls[1]['op'], 'app') + self.assertEqual(self.loader.calls[1]['name'], None) + self.assertEqual(self._out, ['abc']) + + def test_command_path_doesnt_start_with_slash(self): + command = self._makeOne( + ['', 'development.ini', 'abc'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._path_info, '/abc') + self.assertEqual(self.loader.uri.path, 'development.ini') + self.assertEqual(self._out, ['abc']) + + def test_command_has_bad_config_header(self): + command = self._makeOne(['', '--header=name', 'development.ini', '/']) + command.run() + self.assertEqual( + self._out[0], + ( + "Bad --header=name option, value must be in the form " + "'name:value'" + ), + ) + + def test_command_has_good_header_var(self): + command = self._makeOne( + ['', '--header=name:value', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['HTTP_NAME'], 'value') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_w_basic_auth(self): + command = self._makeOne( + [ + '', + '--login=user:password', + '--header=name:value', + 'development.ini', + '/', + ], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['HTTP_NAME'], 'value') + self.assertEqual( + self._environ['HTTP_AUTHORIZATION'], 'Basic dXNlcjpwYXNzd29yZA==' + ) + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_has_content_type_header_var(self): + command = self._makeOne( + ['', '--header=content-type:app/foo', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['CONTENT_TYPE'], 'app/foo') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_has_multiple_header_vars(self): + command = self._makeOne( + [ + '', + '--header=name:value', + '--header=name2:value2', + 'development.ini', + '/', + ], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['HTTP_NAME'], 'value') + self.assertEqual(self._environ['HTTP_NAME2'], 'value2') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_get(self): + command = self._makeOne( + ['', '--method=GET', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'GET') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_post(self): + from pyramid.compat import NativeIO + + command = self._makeOne( + ['', '--method=POST', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + stdin = NativeIO() + command.stdin = stdin + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'POST') + self.assertEqual(self._environ['CONTENT_LENGTH'], '-1') + self.assertEqual(self._environ['wsgi.input'], stdin) + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_put(self): + from pyramid.compat import NativeIO + + command = self._makeOne( + ['', '--method=PUT', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + stdin = NativeIO() + command.stdin = stdin + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'PUT') + self.assertEqual(self._environ['CONTENT_LENGTH'], '-1') + self.assertEqual(self._environ['wsgi.input'], stdin) + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_patch(self): + from pyramid.compat import NativeIO + + command = self._makeOne( + ['', '--method=PATCH', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + stdin = NativeIO() + command.stdin = stdin + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'PATCH') + self.assertEqual(self._environ['CONTENT_LENGTH'], '-1') + self.assertEqual(self._environ['wsgi.input'], stdin) + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_propfind(self): + from pyramid.compat import NativeIO + + command = self._makeOne( + ['', '--method=PROPFIND', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + stdin = NativeIO() + command.stdin = stdin + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'PROPFIND') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_method_options(self): + from pyramid.compat import NativeIO + + command = self._makeOne( + ['', '--method=OPTIONS', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + stdin = NativeIO() + command.stdin = stdin + command.run() + self.assertEqual(self._environ['REQUEST_METHOD'], 'OPTIONS') + self.assertEqual(self._path_info, '/') + self.assertEqual(self._out, ['abc']) + + def test_command_with_query_string(self): + command = self._makeOne( + ['', 'development.ini', '/abc?a=1&b=2&c'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._environ['QUERY_STRING'], 'a=1&b=2&c') + self.assertEqual(self._path_info, '/abc') + self.assertEqual(self._out, ['abc']) + + def test_command_display_headers(self): + command = self._makeOne( + ['', '--display-headers', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')], + ) + command.run() + self.assertEqual(self._path_info, '/') + self.assertEqual( + self._out, + ['200 OK', 'Content-Type: text/html; charset=UTF-8', 'abc'], + ) + + def test_command_response_has_no_charset(self): + command = self._makeOne( + ['', '--method=GET', 'development.ini', '/'], + headers=[('Content-Type', 'image/jpeg')], + ) + command.run() + self.assertEqual(self._path_info, '/') + + self.assertEqual(self._out, [b'abc']) + + def test_command_method_configures_logging(self): + command = self._makeOne(['', 'development.ini', '/']) + command.run() + self.assertEqual(self.loader.calls[0]['op'], 'logging') + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.prequest import main + + return main(argv, True) + + def test_it(self): + result = self._callFUT(['prequest']) + self.assertEqual(result, 2) diff --git a/tests/test_scripts/test_proutes.py b/tests/test_scripts/test_proutes.py new file mode 100644 index 000000000..5e3f359f6 --- /dev/null +++ b/tests/test_scripts/test_proutes.py @@ -0,0 +1,823 @@ +import os +import unittest +from . import dummy + + +class DummyIntrospector(object): + def __init__(self): + self.relations = {} + self.introspectables = {} + + def get(self, name, discrim): + pass + + +class TestPRoutesCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.proutes import PRoutesCommand + + return PRoutesCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = dummy.DummyBootstrap() + cmd.get_config_loader = dummy.DummyLoader() + cmd.args.config_uri = '/foo/bar/myapp.ini#myapp' + + return cmd + + def _makeRegistry(self): + from pyramid.registry import Registry + + registry = Registry() + registry.introspector = DummyIntrospector() + return registry + + def _makeConfig(self, *arg, **kw): + from pyramid.config import Configurator + + config = Configurator(*arg, **kw) + return config + + def test_good_args(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = dummy.DummyBootstrap() + cmd.get_config_loader = dummy.DummyLoader() + cmd.args.config_uri = '/foo/bar/myapp.ini#myapp' + cmd.args.config_args = ('a=1',) + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + cmd._get_mapper = lambda *arg: mapper + registry = self._makeRegistry() + cmd.bootstrap = dummy.DummyBootstrap(registry=registry) + L = [] + cmd.out = lambda msg: L.append(msg) + cmd.run() + self.assertTrue('<unknown>' in ''.join(L)) + + def test_bad_args(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = dummy.DummyBootstrap() + cmd.get_config_loader = dummy.DummyLoader() + cmd.args.config_uri = '/foo/bar/myapp.ini#myapp' + cmd.args.config_vars = ('a',) + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + cmd._get_mapper = lambda *arg: mapper + + self.assertRaises(ValueError, cmd.run) + + def test_no_routes(self): + command = self._makeOne() + mapper = dummy.DummyMapper() + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L, []) + + def test_no_mapper(self): + command = self._makeOne() + command._get_mapper = lambda *arg: None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L, []) + + def test_single_route_no_route_registered(self): + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + registry = self._makeRegistry() + command.bootstrap = dummy.DummyBootstrap(registry=registry) + + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>', '*']) + + def test_route_with_no_slash_prefix(self): + command = self._makeOne() + route = dummy.DummyRoute('a', 'a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + registry = self._makeRegistry() + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split(), ['a', '/a', '<unknown>', '*']) + + def test_single_route_no_views_registered(self): + from zope.interface import Interface + from pyramid.interfaces import IRouteRequest + + registry = self._makeRegistry() + + def view(): # pragma: no cover + pass + + class IMyRoute(Interface): + pass + + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>']) + + def test_single_route_one_view_registered(self): + from zope.interface import Interface + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + + registry = self._makeRegistry() + + def view(): # pragma: no cover + pass + + class IMyRoute(Interface): + pass + + registry.registerAdapter( + view, (IViewClassifier, IMyRoute, Interface), IView, '' + ) + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split()[:3] + self.assertEqual( + compare_to, ['a', '/a', 'tests.test_scripts.test_proutes.view'] + ) + + def test_one_route_with_long_name_one_view_registered(self): + from zope.interface import Interface + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + + registry = self._makeRegistry() + + def view(): # pragma: no cover + pass + + class IMyRoute(Interface): + pass + + registry.registerAdapter( + view, (IViewClassifier, IMyRoute, Interface), IView, '' + ) + + registry.registerUtility( + IMyRoute, IRouteRequest, name='very_long_name_123' + ) + + command = self._makeOne() + route = dummy.DummyRoute( + 'very_long_name_123', '/and_very_long_pattern_as_well' + ) + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split()[:3] + self.assertEqual( + compare_to, + [ + 'very_long_name_123', + '/and_very_long_pattern_as_well', + 'tests.test_scripts.test_proutes.view', + ], + ) + + def test_class_view(self): + from pyramid.renderers import null_renderer as nr + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=dummy.DummyView, + attr='view', + renderer=nr, + request_method='POST', + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.dummy.DummyView.view', + 'POST', + ] + self.assertEqual(compare_to, expected) + + def test_single_route_one_view_registered_with_factory(self): + from zope.interface import Interface + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + + registry = self._makeRegistry() + + def view(): # pragma: no cover + pass + + class IMyRoot(Interface): + pass + + class IMyRoute(Interface): + pass + + registry.registerAdapter( + view, (IViewClassifier, IMyRoute, IMyRoot), IView, '' + ) + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + + def factory(request): # pragma: no cover + pass + + route = dummy.DummyRoute('a', '/a', factory=factory) + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>']) + + def test_single_route_multiview_registered(self): + from zope.interface import Interface + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + registry = self._makeRegistry() + + def view(): # pragma: no cover + pass + + class IMyRoute(Interface): + pass + + multiview1 = dummy.DummyMultiView( + view, context='context', view_name='a1' + ) + + registry.registerAdapter( + multiview1, (IViewClassifier, IMyRoute, Interface), IMultiView, '' + ) + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split()[:3] + view_module = 'tests.test_scripts.dummy' + view_str = '<tests.test_scripts.dummy.DummyMultiView' + final = '%s.%s' % (view_module, view_str) + + self.assertEqual(compare_to, ['a', '/a', final]) + + def test__get_mapper(self): + from pyramid.urldispatch import RoutesMapper + + command = self._makeOne() + registry = self._makeRegistry() + + result = command._get_mapper(registry) + self.assertEqual(result.__class__, RoutesMapper) + + def test_one_route_all_methods_view_only_post(self): + from pyramid.renderers import null_renderer as nr + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', view=view1, renderer=nr, request_method='POST' + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + 'POST', + ] + self.assertEqual(compare_to, expected) + + def test_one_route_only_post_view_all_methods(self): + from pyramid.renderers import null_renderer as nr + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b', request_method='POST') + config.add_view(route_name='foo', view=view1, renderer=nr) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + 'POST', + ] + self.assertEqual(compare_to, expected) + + def test_one_route_only_post_view_post_and_get(self): + from pyramid.renderers import null_renderer as nr + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b', request_method='POST') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=('POST', 'GET'), + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + 'POST', + ] + self.assertEqual(compare_to, expected) + + def test_route_request_method_mismatch(self): + from pyramid.renderers import null_renderer as nr + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b', request_method='POST') + config.add_view( + route_name='foo', view=view1, renderer=nr, request_method='GET' + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + '<route', + 'mismatch>', + ] + self.assertEqual(compare_to, expected) + + def test_route_static_views(self): + config = self._makeConfig(autocommit=True) + config.add_static_view('static', 'static', cache_max_age=3600) + path2 = os.path.normpath('/var/www/static') + config.add_static_view(name='static2', path=path2) + config.add_static_view( + name='pyramid_scaffold', + path='pyramid:scaffolds/starter/+package+/static', + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 5) + + expected = [ + [ + '__static/', + '/static/*subpath', + 'tests.test_scripts:static/', + '*', + ], + ['__static2/', '/static2/*subpath', path2 + os.sep, '*'], + [ + '__pyramid_scaffold/', + '/pyramid_scaffold/*subpath', + 'pyramid:scaffolds/starter/+package+/static/', + '*', + ], + ] + + for index, line in enumerate(L[2:]): + data = line.split() + self.assertEqual(data, expected[index]) + + def test_route_no_view(self): + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b', request_method='POST') + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['foo', '/a/b', '<unknown>', 'POST'] + self.assertEqual(compare_to, expected) + + def test_route_as_wsgiapp(self): + from pyramid.wsgi import wsgiapp2 + + config1 = self._makeConfig(autocommit=True) + + def view1(context, request): # pragma: no cover + return 'view1' + + config1.add_route('foo', '/a/b', request_method='POST') + config1.add_view(view=view1, route_name='foo') + + config2 = self._makeConfig(autocommit=True) + config2.add_route('foo', '/a/b', request_method='POST') + config2.add_view(wsgiapp2(config1.make_wsgi_app()), route_name='foo') + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config2.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['foo', '/a/b', '<wsgiapp>', 'POST'] + self.assertEqual(compare_to, expected) + + def test_route_is_get_view_request_method_not_post(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b', request_method='GET') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + 'GET', + ] + self.assertEqual(compare_to, expected) + + def test_view_request_method_not_post(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + '!POST,*', + ] + self.assertEqual(compare_to, expected) + + def test_view_glob(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + def view2(context, request): # pragma: no cover + return 'view2' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + config.add_route('bar', '/b/a') + config.add_view( + route_name='bar', + view=view2, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + command.args.glob = '*foo*' + + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = [ + 'foo', + '/a/b', + 'tests.test_scripts.test_proutes.view1', + '!POST,*', + ] + self.assertEqual(compare_to, expected) + + def test_good_format(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + command.args.glob = '*foo*' + command.args.format = 'method,name' + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['!POST,*', 'foo'] + + self.assertEqual(compare_to, expected) + self.assertEqual(L[0].split(), ['Method', 'Name']) + + def test_bad_format(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + command.args.glob = '*foo*' + command.args.format = 'predicates,name,pattern' + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + expected = ( + "You provided invalid formats ['predicates'], " + "Available formats are ['name', 'pattern', 'view', 'method']" + ) + result = command.run() + self.assertEqual(result, 2) + self.assertEqual(L[0], expected) + + def test_config_format_ini_newlines(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + command.get_config_loader = dummy.DummyLoader( + {'proutes': {'format': 'method\nname'}} + ) + + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['!POST,*', 'foo'] + + self.assertEqual(compare_to, expected) + self.assertEqual(L[0].split(), ['Method', 'Name']) + + def test_config_format_ini_spaces(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + command.get_config_loader = dummy.DummyLoader( + {'proutes': {'format': 'method name'}} + ) + + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['!POST,*', 'foo'] + + self.assertEqual(compare_to, expected) + self.assertEqual(L[0].split(), ['Method', 'Name']) + + def test_config_format_ini_commas(self): + from pyramid.renderers import null_renderer as nr + from pyramid.config import not_ + + def view1(context, request): # pragma: no cover + return 'view1' + + config = self._makeConfig(autocommit=True) + config.add_route('foo', '/a/b') + config.add_view( + route_name='foo', + view=view1, + renderer=nr, + request_method=not_('POST'), + ) + + command = self._makeOne() + + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + command.get_config_loader = dummy.DummyLoader( + {'proutes': {'format': 'method,name'}} + ) + + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['!POST,*', 'foo'] + + self.assertEqual(compare_to, expected) + self.assertEqual(L[0].split(), ['Method', 'Name']) + + def test_static_routes_included_in_list(self): + config = self._makeConfig(autocommit=True) + config.add_route('foo', 'http://example.com/bar.aspx', static=True) + + command = self._makeOne() + L = [] + command.out = L.append + command.bootstrap = dummy.DummyBootstrap(registry=config.registry) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split() + expected = ['foo', 'http://example.com/bar.aspx', '<unknown>', '*'] + self.assertEqual(compare_to, expected) + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.proutes import main + + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['proutes']) + self.assertEqual(result, 2) diff --git a/tests/test_scripts/test_pserve.py b/tests/test_scripts/test_pserve.py new file mode 100644 index 000000000..b85f4ddb7 --- /dev/null +++ b/tests/test_scripts/test_pserve.py @@ -0,0 +1,139 @@ +import os +import unittest +from . import dummy + + +here = os.path.abspath(os.path.dirname(__file__)) + + +class TestPServeCommand(unittest.TestCase): + def setUp(self): + from pyramid.compat import NativeIO + + self.out_ = NativeIO() + + def out(self, msg): + self.out_.write(msg) + + def _getTargetClass(self): + from pyramid.scripts.pserve import PServeCommand + + return PServeCommand + + def _makeOne(self, *args): + effargs = ['pserve'] + effargs.extend(args) + cmd = self._getTargetClass()(effargs) + cmd.out = self.out + self.loader = dummy.DummyLoader() + cmd._get_config_loader = self.loader + return cmd + + def test_run_no_args(self): + inst = self._makeOne() + result = inst.run() + self.assertEqual(result, 2) + self.assertEqual(self.out_.getvalue(), 'You must give a config file') + + def test_parse_vars_good(self): + inst = self._makeOne('development.ini', 'a=1', 'b=2') + app = dummy.DummyApp() + + def get_app(name, global_conf): + app.name = name + app.global_conf = global_conf + return app + + self.loader.get_wsgi_app = get_app + self.loader.server = lambda x: x + + inst.run() + self.assertEqual(app.global_conf, {'a': '1', 'b': '2'}) + + def test_parse_vars_bad(self): + inst = self._makeOne('development.ini', 'a') + self.assertRaises(ValueError, inst.run) + + def test_config_file_finds_watch_files(self): + inst = self._makeOne('development.ini') + loader = self.loader('/base/path.ini') + loader.settings = { + 'pserve': {'watch_files': 'foo\n/baz\ntests.test_scripts:*.py'} + } + inst.pserve_file_config(loader, global_conf={'a': '1'}) + self.assertEqual(loader.calls[0]['defaults'], {'a': '1'}) + self.assertEqual( + inst.watch_files, + set( + [ + os.path.abspath('/base/foo'), + os.path.abspath('/baz'), + os.path.abspath(os.path.join(here, '*.py')), + ] + ), + ) + + def test_config_file_finds_open_url(self): + inst = self._makeOne('development.ini') + loader = self.loader('/base/path.ini') + loader.settings = {'pserve': {'open_url': 'http://127.0.0.1:8080/'}} + inst.pserve_file_config(loader, global_conf={'a': '1'}) + self.assertEqual(loader.calls[0]['defaults'], {'a': '1'}) + self.assertEqual(inst.open_url, 'http://127.0.0.1:8080/') + + def test_guess_server_url(self): + inst = self._makeOne('development.ini') + loader = self.loader('/base/path.ini') + loader.settings = {'server:foo': {'port': '8080'}} + url = inst.guess_server_url(loader, 'foo', global_conf={'a': '1'}) + self.assertEqual(loader.calls[0]['defaults'], {'a': '1'}) + self.assertEqual(url, 'http://127.0.0.1:8080') + + def test_reload_call_hupper_with_correct_args(self): + from pyramid.scripts import pserve + + class AttrDict(dict): + def __init__(self, *args, **kwargs): + super(AttrDict, self).__init__(*args, **kwargs) + self.__dict__ = self + + def dummy_start_reloader(*args, **kwargs): + dummy_start_reloader.args = args + dummy_start_reloader.kwargs = kwargs + + orig_hupper = pserve.hupper + try: + pserve.hupper = AttrDict( + is_active=lambda: False, start_reloader=dummy_start_reloader + ) + + inst = self._makeOne('--reload', 'development.ini') + inst.run() + finally: + pserve.hupper = orig_hupper + + self.assertEquals( + dummy_start_reloader.args, ('pyramid.scripts.pserve.main',) + ) + self.assertEquals( + dummy_start_reloader.kwargs, + { + 'reload_interval': 1, + 'verbose': 1, + 'worker_kwargs': { + 'argv': ['pserve', '--reload', 'development.ini'], + 'quiet': False, + }, + }, + ) + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pserve import main + + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['pserve']) + self.assertEqual(result, 2) diff --git a/tests/test_scripts/test_pshell.py b/tests/test_scripts/test_pshell.py new file mode 100644 index 000000000..6beaacda6 --- /dev/null +++ b/tests/test_scripts/test_pshell.py @@ -0,0 +1,431 @@ +import os +import unittest +from . import dummy + + +class TestPShellCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pshell import PShellCommand + + return PShellCommand + + def _makeOne( + self, + patch_bootstrap=True, + patch_loader=True, + patch_args=True, + patch_options=True, + ): + cmd = self._getTargetClass()([]) + + if patch_bootstrap: + self.bootstrap = dummy.DummyBootstrap() + cmd.bootstrap = self.bootstrap + if patch_loader: + self.loader = dummy.DummyLoader() + cmd.get_config_loader = self.loader + if patch_args: + + class Args(object): + pass + + self.args = Args() + self.args.config_uri = '/foo/bar/myapp.ini#myapp' + cmd.args.config_uri = self.args.config_uri + if patch_options: + + class Options(object): + pass + + self.options = Options() + self.options.python_shell = '' + self.options.setup = None + self.options.list = None + cmd.options = self.options + + # default to None to prevent side-effects from running tests in + # unknown environments + cmd.pystartup = None + return cmd + + def _makeEntryPoints(self, command, shells): + command.pkg_resources = dummy.DummyPkgResources(shells) + + def test_command_loads_default_shell(self): + command = self._makeOne() + shell = dummy.DummyShell() + self._makeEntryPoints(command, {}) + + command.default_runner = shell + command.run() + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': self.bootstrap.root, + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_errors_with_unknown_shell(self): + command = self._makeOne() + out_calls = [] + + def out(msg): + out_calls.append(msg) + + command.out = out + + shell = dummy.DummyShell() + + self._makeEntryPoints(command, {}) + + command.default_runner = shell + command.args.python_shell = 'unknown_python_shell' + result = command.run() + self.assertEqual(result, 1) + self.assertEqual( + out_calls, ['could not find a shell named "unknown_python_shell"'] + ) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertTrue(self.bootstrap.closer.called) + + def test_command_loads_ipython(self): + command = self._makeOne() + shell = dummy.DummyShell() + bad_shell = dummy.DummyShell() + self._makeEntryPoints( + command, {'ipython': shell, 'bpython': bad_shell} + ) + + command.args.python_shell = 'ipython' + + command.run() + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': self.bootstrap.root, + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_shell_entry_points(self): + command = self._makeOne() + dshell = dummy.DummyShell() + + self._makeEntryPoints(command, {'ipython': dshell, 'bpython': dshell}) + + command.default_runner = None + shell = command.make_shell() + self.assertEqual(shell, dshell) + + def test_shell_override(self): + command = self._makeOne() + ipshell = dummy.DummyShell() + bpshell = dummy.DummyShell() + dshell = dummy.DummyShell() + + self._makeEntryPoints(command, {}) + + command.default_runner = dshell + + shell = command.make_shell() + self.assertEqual(shell, dshell) + + command.args.python_shell = 'ipython' + self.assertRaises(ValueError, command.make_shell) + + self._makeEntryPoints( + command, {'ipython': ipshell, 'bpython': bpshell, 'python': dshell} + ) + + command.args.python_shell = 'ipython' + shell = command.make_shell() + self.assertEqual(shell, ipshell) + + command.args.python_shell = 'bpython' + shell = command.make_shell() + self.assertEqual(shell, bpshell) + + command.args.python_shell = 'python' + shell = command.make_shell() + self.assertEqual(shell, dshell) + + def test_shell_ordering(self): + command = self._makeOne() + ipshell = dummy.DummyShell() + bpshell = dummy.DummyShell() + dshell = dummy.DummyShell() + + self._makeEntryPoints( + command, {'ipython': ipshell, 'bpython': bpshell, 'python': dshell} + ) + + command.default_runner = dshell + + command.preferred_shells = ['ipython', 'bpython'] + shell = command.make_shell() + self.assertEqual(shell, ipshell) + + command.preferred_shells = ['bpython', 'python'] + shell = command.make_shell() + self.assertEqual(shell, bpshell) + + command.preferred_shells = ['python', 'ipython'] + shell = command.make_shell() + self.assertEqual(shell, dshell) + + def test_command_loads_custom_items(self): + command = self._makeOne() + model = dummy.Dummy() + user = dummy.Dummy() + self.loader.settings = {'pshell': {'m': model, 'User': user}} + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': self.bootstrap.root, + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'm': model, + 'User': user, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_setup(self): + command = self._makeOne() + + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + env['none'] = None + + self.loader.settings = {'pshell': {'setup': setup}} + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': 'root override', + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'a': 1, + 'none': None, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_setup_generator(self): + command = self._makeOne() + did_resume_after_yield = {} + + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + env['none'] = None + request = env['request'] + yield + did_resume_after_yield['result'] = True + self.assertEqual(request.dummy_attr, 1) + + self.loader.settings = {'pshell': {'setup': setup}} + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': 'root override', + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'a': 1, + 'none': None, + }, + ) + self.assertTrue(did_resume_after_yield['result']) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_default_shell_option(self): + command = self._makeOne() + ipshell = dummy.DummyShell() + dshell = dummy.DummyShell() + self._makeEntryPoints(command, {'ipython': ipshell, 'python': dshell}) + self.loader.settings = { + 'pshell': {'default_shell': 'bpython python\nipython'} + } + command.run() + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertTrue(dshell.called) + + def test_command_loads_check_variable_override_order(self): + command = self._makeOne() + model = dummy.Dummy() + + def setup(env): + env['a'] = 1 + env['m'] = 'model override' + env['root'] = 'root override' + + self.loader.settings = {'pshell': {'setup': setup, 'm': model}} + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': 'root override', + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'a': 1, + 'm': 'model override', + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_setup_from_options(self): + command = self._makeOne() + + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + + model = dummy.Dummy() + self.loader.settings = {'pshell': {'setup': 'abc', 'm': model}} + command.args.setup = setup + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': 'root override', + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'a': 1, + 'm': model, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_custom_section_override(self): + command = self._makeOne() + dummy_ = dummy.Dummy() + self.loader.settings = { + 'pshell': { + 'app': dummy_, + 'root': dummy_, + 'registry': dummy_, + 'request': dummy_, + } + } + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': dummy_, + 'root': dummy_, + 'registry': dummy_, + 'request': dummy_, + 'root_factory': self.bootstrap.root_factory, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_pythonstartup(self): + command = self._makeOne() + command.pystartup = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'pystartup.txt') + ) + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual( + shell.env, + { + 'app': self.bootstrap.app, + 'root': self.bootstrap.root, + 'registry': self.bootstrap.registry, + 'request': self.bootstrap.request, + 'root_factory': self.bootstrap.root_factory, + 'foo': 1, + }, + ) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_list_shells(self): + command = self._makeOne() + + dshell = dummy.DummyShell() + out_calls = [] + + def out(msg): + out_calls.append(msg) + + command.out = out + + self._makeEntryPoints(command, {'ipython': dshell, 'python': dshell}) + + command.args.list = True + result = command.run() + self.assertEqual(result, 0) + self.assertEqual( + out_calls, ['Available shells:', ' ipython', ' python'] + ) + + +class Test_python_shell_runner(unittest.TestCase): + def _callFUT(self, env, help, interact): + from pyramid.scripts.pshell import python_shell_runner + + return python_shell_runner(env, help, interact=interact) + + def test_it(self): + interact = dummy.DummyInteractor() + self._callFUT({'foo': 'bar'}, 'a help message', interact) + self.assertEqual(interact.local, {'foo': 'bar'}) + self.assertTrue('a help message' in interact.banner) + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pshell import main + + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['pshell']) + self.assertEqual(result, 2) diff --git a/tests/test_scripts/test_ptweens.py b/tests/test_scripts/test_ptweens.py new file mode 100644 index 000000000..ee50887f6 --- /dev/null +++ b/tests/test_scripts/test_ptweens.py @@ -0,0 +1,69 @@ +import unittest +from . import dummy + + +class TestPTweensCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.ptweens import PTweensCommand + + return PTweensCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = dummy.DummyBootstrap() + cmd.setup_logging = dummy.dummy_setup_logging() + cmd.args.config_uri = '/foo/bar/myapp.ini#myapp' + return cmd + + def test_command_no_tweens(self): + command = self._makeOne() + command._get_tweens = lambda *arg: None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L, []) + + def test_command_implicit_tweens_only(self): + command = self._makeOne() + tweens = dummy.DummyTweens([('name', 'item')], None) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual( + L[0], + '"pyramid.tweens" config value NOT set (implicitly ordered tweens ' + 'used)', + ) + + def test_command_implicit_and_explicit_tweens(self): + command = self._makeOne() + tweens = dummy.DummyTweens([('name', 'item')], [('name2', 'item2')]) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, 0) + self.assertEqual( + L[0], + '"pyramid.tweens" config value set (explicitly ordered tweens ' + 'used)', + ) + + def test__get_tweens(self): + command = self._makeOne() + registry = dummy.DummyRegistry() + self.assertEqual(command._get_tweens(registry), None) + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.ptweens import main + + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['ptweens']) + self.assertEqual(result, 2) diff --git a/tests/test_scripts/test_pviews.py b/tests/test_scripts/test_pviews.py new file mode 100644 index 000000000..0b26a9cf3 --- /dev/null +++ b/tests/test_scripts/test_pviews.py @@ -0,0 +1,597 @@ +import unittest +from . import dummy + + +class TestPViewsCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pviews import PViewsCommand + + return PViewsCommand + + def _makeOne(self, registry=None): + cmd = self._getTargetClass()([]) + cmd.bootstrap = dummy.DummyBootstrap(registry=registry) + cmd.setup_logging = dummy.dummy_setup_logging() + cmd.args.config_uri = '/foo/bar/myapp.ini#myapp' + return cmd + + def _makeRequest(self, url, registry): + from pyramid.request import Request + + request = Request.blank('/a') + request.registry = registry + return request + + def _register_mapper(self, registry, routes): + from pyramid.interfaces import IRoutesMapper + + mapper = dummy.DummyMapper(*routes) + registry.registerUtility(mapper, IRoutesMapper) + + def test__find_view_no_match(self): + from pyramid.registry import Registry + + registry = Registry() + self._register_mapper(registry, []) + command = self._makeOne(registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertEqual(result, None) + + def test__find_view_no_match_multiview_registered(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + + registry = Registry() + + @implementer(IMultiView) + class View1(object): + pass + + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter( + View1(), (IViewClassifier, IRequest, root_iface), IMultiView + ) + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + request = self._makeRequest('/x', registry) + result = command._find_view(request) + self.assertEqual(result, None) + + def test__find_view_traversal(self): + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + + registry = Registry() + + def view1(): # pragma: no cover + pass + + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter( + view1, (IViewClassifier, IRequest, root_iface), IView, name='a' + ) + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertEqual(result, view1) + + def test__find_view_traversal_multiview(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + + registry = Registry() + + @implementer(IMultiView) + class View1(object): + pass + + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + view = View1() + registry.registerAdapter( + view, (IViewClassifier, IRequest, root_iface), IMultiView, name='a' + ) + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertEqual(result, view) + + def test__find_view_route_no_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.registry import Registry + + registry = Registry() + + def view(): # pragma: no cover + pass + + class IMyRoot(Interface): + pass + + class IMyRoute(Interface): + pass + + registry.registerAdapter( + view, (IViewClassifier, IMyRoute, IMyRoot), IView, '' + ) + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + + routes = [ + dummy.DummyRoute('a', '/a', factory=Factory, matchdict={}), + dummy.DummyRoute('b', '/b', factory=Factory), + ] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertEqual(result, view) + + def test__find_view_route_multiview_no_view_registered(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + + registry = Registry() + + def view1(): # pragma: no cover + pass + + def view2(): # pragma: no cover + pass + + class IMyRoot(Interface): + pass + + class IMyRoute1(Interface): + pass + + class IMyRoute2(Interface): + pass + + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + + registry.registerUtility(Factory, IRootFactory) + routes = [ + dummy.DummyRoute('a', '/a', matchdict={}), + dummy.DummyRoute('b', '/a', matchdict={}), + ] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertTrue(IMultiView.providedBy(result)) + + def test__find_view_route_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + + registry = Registry() + + def view1(): # pragma: no cover + pass + + def view2(): # pragma: no cover + pass + + class IMyRoot(Interface): + pass + + class IMyRoute1(Interface): + pass + + class IMyRoute2(Interface): + pass + + registry.registerAdapter( + view1, (IViewClassifier, IMyRoute1, IMyRoot), IView, '' + ) + registry.registerAdapter( + view2, (IViewClassifier, IMyRoute2, IMyRoot), IView, '' + ) + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + + registry.registerUtility(Factory, IRootFactory) + routes = [ + dummy.DummyRoute('a', '/a', matchdict={}), + dummy.DummyRoute('b', '/a', matchdict={}), + ] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + request = self._makeRequest('/a', registry) + result = command._find_view(request) + self.assertTrue(IMultiView.providedBy(result)) + self.assertEqual(len(result.views), 2) + self.assertTrue((None, view1, None) in result.views) + self.assertTrue((None, view2, None) in result.views) + + def test__find_multi_routes_all_match(self): + command = self._makeOne() + + def factory(request): # pragma: no cover + pass + + routes = [ + dummy.DummyRoute('a', '/a', factory=factory, matchdict={}), + dummy.DummyRoute('b', '/a', factory=factory, matchdict={}), + ] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual( + result, + [ + {'match': {}, 'route': routes[0]}, + {'match': {}, 'route': routes[1]}, + ], + ) + + def test__find_multi_routes_some_match(self): + command = self._makeOne() + + def factory(request): # pragma: no cover + pass + + routes = [ + dummy.DummyRoute('a', '/a', factory=factory), + dummy.DummyRoute('b', '/a', factory=factory, matchdict={}), + ] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match': {}, 'route': routes[1]}]) + + def test__find_multi_routes_none_match(self): + command = self._makeOne() + + def factory(request): # pragma: no cover + pass + + routes = [ + dummy.DummyRoute('a', '/a', factory=factory), + dummy.DummyRoute('b', '/a', factory=factory), + ] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO': '/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, []) + + def test_views_command_not_found(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1: None + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_not_found_url_starts_without_slash(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1: None + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = 'a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_single_view_traversal(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context', view_name='a') + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.DummyView') + + def test_views_command_single_view_function_traversal(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + + def view(): # pragma: no cover + pass + + view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.test_pviews.view') + + def test_views_command_single_view_traversal_with_permission(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context', view_name='a') + view.__permission__ = 'test' + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.DummyView') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_single_view_traversal_with_predicates(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + + def predicate(): # pragma: no cover + pass + + predicate.text = lambda *arg: "predicate = x" + view = dummy.DummyView(context='context', view_name='a') + view.__predicates__ = [predicate] + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.DummyView') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + def test_views_command_single_view_route(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + route = dummy.DummyRoute('a', '/a', matchdict={}) + view = dummy.DummyView( + context='context', view_name='a', matched_route=route, subpath='' + ) + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[15], ' tests.test_scripts.dummy.DummyView') + + def test_views_command_multi_view_nested(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view1 = dummy.DummyView(context='context', view_name='a1') + view1.__name__ = 'view1' + view1.__view_attr__ = 'call' + multiview1 = dummy.DummyMultiView( + view1, context='context', view_name='a1' + ) + multiview2 = dummy.DummyMultiView( + multiview1, context='context', view_name='a' + ) + command._find_view = lambda arg1: multiview2 + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.DummyMultiView') + self.assertEqual(L[12], ' tests.test_scripts.dummy.view1.call') + + def test_views_command_single_view_route_with_route_predicates(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + + def predicate(): # pragma: no cover + pass + + predicate.text = lambda *arg: "predicate = x" + route = dummy.DummyRoute('a', '/a', matchdict={}, predicate=predicate) + view = dummy.DummyView( + context='context', view_name='a', matched_route=route, subpath='' + ) + command._find_view = lambda arg1: view + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[12], ' route predicates (predicate = x)') + self.assertEqual(L[16], ' tests.test_scripts.dummy.DummyView') + + def test_views_command_multiview(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + multiview = dummy.DummyMultiView( + view, context='context', view_name='a' + ) + command._find_view = lambda arg1: multiview + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.view.call') + + def test_views_command_multiview_with_permission(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__permission__ = 'test' + multiview = dummy.DummyMultiView( + view, context='context', view_name='a' + ) + command._find_view = lambda arg1: multiview + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.view.call') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_multiview_with_predicates(self): + from pyramid.registry import Registry + + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + + def predicate(): # pragma: no cover + pass + + predicate.text = lambda *arg: "predicate = x" + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__predicates__ = [predicate] + multiview = dummy.DummyMultiView( + view, context='context', view_name='a' + ) + command._find_view = lambda arg1: multiview + command.args.config_uri = '/foo/bar/myapp.ini#myapp' + command.args.url = '/a' + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' tests.test_scripts.dummy.view.call') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pviews import main + + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['pviews']) + self.assertEqual(result, 2) diff --git a/tests/test_security.py b/tests/test_security.py new file mode 100644 index 000000000..8b8028f61 --- /dev/null +++ b/tests/test_security.py @@ -0,0 +1,541 @@ +import unittest + +from pyramid import testing + + +class TestAllPermissionsList(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getTargetClass(self): + from pyramid.security import AllPermissionsList + + return AllPermissionsList + + def _makeOne(self): + return self._getTargetClass()() + + def test_equality_w_self(self): + thing = self._makeOne() + self.assertTrue(thing.__eq__(thing)) + + def test_equality_w_other_instances_of_class(self): + thing = self._makeOne() + other = self._makeOne() + self.assertTrue(thing.__eq__(other)) + + def test_equality_miss(self): + thing = self._makeOne() + other = object() + self.assertFalse(thing.__eq__(other)) + + def test_contains_w_string(self): + thing = self._makeOne() + self.assertTrue('anything' in thing) + + def test_contains_w_object(self): + thing = self._makeOne() + self.assertTrue(object() in thing) + + def test_iterable(self): + thing = self._makeOne() + self.assertEqual(list(thing), []) + + def test_singleton(self): + from pyramid.security import ALL_PERMISSIONS + + self.assertEqual(ALL_PERMISSIONS.__class__, self._getTargetClass()) + + +class TestAllowed(unittest.TestCase): + def _getTargetClass(self): + from pyramid.security import Allowed + + return Allowed + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + allowed = self._makeOne('hello') + self.assertEqual(allowed.msg, 'hello') + self.assertEqual(allowed, True) + self.assertTrue(allowed) + self.assertEqual(str(allowed), 'hello') + self.assertTrue('<Allowed instance at ' in repr(allowed)) + self.assertTrue("with msg 'hello'>" in repr(allowed)) + + +class TestDenied(unittest.TestCase): + def _getTargetClass(self): + from pyramid.security import Denied + + return Denied + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + denied = self._makeOne('hello') + self.assertEqual(denied.msg, 'hello') + self.assertEqual(denied, False) + self.assertFalse(denied) + self.assertEqual(str(denied), 'hello') + self.assertTrue('<Denied instance at ' in repr(denied)) + self.assertTrue("with msg 'hello'>" in repr(denied)) + + +class TestACLAllowed(unittest.TestCase): + def _getTargetClass(self): + from pyramid.security import ACLAllowed + + return ACLAllowed + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + from pyramid.security import Allowed + + msg = ( + "ACLAllowed permission 'permission' via ACE 'ace' in ACL 'acl' " + "on context 'ctx' for principals 'principals'" + ) + allowed = self._makeOne( + 'ace', 'acl', 'permission', 'principals', 'ctx' + ) + self.assertIsInstance(allowed, Allowed) + self.assertTrue(msg in allowed.msg) + self.assertEqual(allowed, True) + self.assertTrue(allowed) + self.assertEqual(str(allowed), msg) + self.assertTrue('<ACLAllowed instance at ' in repr(allowed)) + self.assertTrue("with msg %r>" % msg in repr(allowed)) + + +class TestACLDenied(unittest.TestCase): + def _getTargetClass(self): + from pyramid.security import ACLDenied + + return ACLDenied + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + from pyramid.security import Denied + + msg = ( + "ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' " + "on context 'ctx' for principals 'principals'" + ) + denied = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') + self.assertIsInstance(denied, Denied) + self.assertTrue(msg in denied.msg) + self.assertEqual(denied, False) + self.assertFalse(denied) + self.assertEqual(str(denied), msg) + self.assertTrue('<ACLDenied instance at ' in repr(denied)) + self.assertTrue("with msg %r>" % msg in repr(denied)) + + +class TestPrincipalsAllowedByPermission(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg): + from pyramid.security import principals_allowed_by_permission + + return principals_allowed_by_permission(*arg) + + def test_no_authorization_policy(self): + from pyramid.security import Everyone + + context = DummyContext() + result = self._callFUT(context, 'view') + self.assertEqual(result, [Everyone]) + + def test_with_authorization_policy(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + _registerAuthorizationPolicy(registry, 'yo') + context = DummyContext() + result = self._callFUT(context, 'view') + self.assertEqual(result, 'yo') + + +class TestRemember(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kwarg): + from pyramid.security import remember + + return remember(*arg, **kwarg) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request, 'me') + self.assertEqual(result, []) + + def test_with_authentication_policy(self): + request = _makeRequest() + registry = request.registry + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request, 'me') + self.assertEqual(result, [('X-Pyramid-Test', 'me')]) + + def test_with_authentication_policy_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = _makeRequest() + del request.registry + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request, 'me') + self.assertEqual(result, [('X-Pyramid-Test', 'me')]) + + def test_with_missing_arg(self): + request = _makeRequest() + registry = request.registry + _registerAuthenticationPolicy(registry, 'yo') + self.assertRaises(TypeError, lambda: self._callFUT(request)) + + +class TestForget(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg): + from pyramid.security import forget + + return forget(*arg) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request) + self.assertEqual(result, []) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, [('X-Pyramid-Test', 'logout')]) + + def test_with_authentication_policy_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = _makeRequest() + del request.registry + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, [('X-Pyramid-Test', 'logout')]) + + +class TestViewExecutionPermitted(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from pyramid.security import view_execution_permitted + + return view_execution_permitted(*arg, **kw) + + def _registerSecuredView(self, view_name, allow=True): + from pyramid.threadlocal import get_current_registry + from zope.interface import Interface + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IViewClassifier + + class Checker(object): + def __permitted__(self, context, request): + self.context = context + self.request = request + return allow + + checker = Checker() + reg = get_current_registry() + reg.registerAdapter( + checker, + (IViewClassifier, Interface, Interface), + ISecuredView, + view_name, + ) + return checker + + def test_no_permission(self): + from zope.interface import Interface + from pyramid.threadlocal import get_current_registry + from pyramid.interfaces import ISettings + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + + settings = dict(debug_authorization=True) + reg = get_current_registry() + reg.registerUtility(settings, ISettings) + context = DummyContext() + request = testing.DummyRequest({}) + + class DummyView(object): + pass + + view = DummyView() + reg.registerAdapter( + view, (IViewClassifier, Interface, Interface), IView, '' + ) + result = self._callFUT(context, request, '') + msg = result.msg + self.assertTrue("Allowed: view name '' in context" in msg) + self.assertTrue('(no permission defined)' in msg) + self.assertEqual(result, True) + + def test_no_view_registered(self): + from pyramid.threadlocal import get_current_registry + from pyramid.interfaces import ISettings + + settings = dict(debug_authorization=True) + reg = get_current_registry() + reg.registerUtility(settings, ISettings) + context = DummyContext() + request = testing.DummyRequest({}) + self.assertRaises(TypeError, self._callFUT, context, request, '') + + def test_with_permission(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from pyramid.interfaces import IRequest + + class IContext(Interface): + pass + + context = DummyContext() + directlyProvides(context, IContext) + self._registerSecuredView('', True) + request = testing.DummyRequest({}) + directlyProvides(request, IRequest) + result = self._callFUT(context, request, '') + self.assertTrue(result) + + +class TestAuthenticatedUserId(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_no_authentication_policy(self): + request = _makeRequest() + self.assertEqual(request.authenticated_userid, None) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + self.assertEqual(request.authenticated_userid, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = _makeRequest() + del request.registry + _registerAuthenticationPolicy(registry, 'yo') + self.assertEqual(request.authenticated_userid, 'yo') + + +class TestUnAuthenticatedUserId(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_no_authentication_policy(self): + request = _makeRequest() + self.assertEqual(request.unauthenticated_userid, None) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + self.assertEqual(request.unauthenticated_userid, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = _makeRequest() + del request.registry + _registerAuthenticationPolicy(registry, 'yo') + self.assertEqual(request.unauthenticated_userid, 'yo') + + +class TestEffectivePrincipals(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_no_authentication_policy(self): + from pyramid.security import Everyone + + request = _makeRequest() + self.assertEqual(request.effective_principals, [Everyone]) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + self.assertEqual(request.effective_principals, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = _makeRequest() + del request.registry + _registerAuthenticationPolicy(registry, 'yo') + self.assertEqual(request.effective_principals, 'yo') + + +class TestHasPermission(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self): + from pyramid.security import AuthorizationAPIMixin + from pyramid.registry import Registry + + mixin = AuthorizationAPIMixin() + mixin.registry = Registry() + mixin.context = object() + return mixin + + def test_no_authentication_policy(self): + request = self._makeOne() + result = request.has_permission('view') + self.assertTrue(result) + self.assertEqual(result.msg, 'No authentication policy in use.') + + def test_with_no_authorization_policy(self): + request = self._makeOne() + _registerAuthenticationPolicy(request.registry, None) + self.assertRaises( + ValueError, request.has_permission, 'view', context=None + ) + + def test_with_authn_and_authz_policies_registered(self): + request = self._makeOne() + _registerAuthenticationPolicy(request.registry, None) + _registerAuthorizationPolicy(request.registry, 'yo') + self.assertEqual(request.has_permission('view', context=None), 'yo') + + def test_with_no_reg_on_request(self): + from pyramid.threadlocal import get_current_registry + + registry = get_current_registry() + request = self._makeOne() + del request.registry + _registerAuthenticationPolicy(registry, None) + _registerAuthorizationPolicy(registry, 'yo') + self.assertEqual(request.has_permission('view'), 'yo') + + def test_with_no_context_passed(self): + request = self._makeOne() + self.assertTrue(request.has_permission('view')) + + def test_with_no_context_passed_or_on_request(self): + request = self._makeOne() + del request.context + self.assertRaises(AttributeError, request.has_permission, 'view') + + +_TEST_HEADER = 'X-Pyramid-Test' + + +class DummyContext: + def __init__(self, *arg, **kw): + self.__dict__.update(kw) + + +class DummyAuthenticationPolicy: + def __init__(self, result): + self.result = result + + def effective_principals(self, request): + return self.result + + def unauthenticated_userid(self, request): + return self.result + + def authenticated_userid(self, request): + return self.result + + def remember(self, request, userid, **kw): + headers = [(_TEST_HEADER, userid)] + self._header_remembered = headers[0] + return headers + + def forget(self, request): + headers = [(_TEST_HEADER, 'logout')] + self._header_forgotten = headers[0] + return headers + + +class DummyAuthorizationPolicy: + def __init__(self, result): + self.result = result + + def permits(self, context, principals, permission): + return self.result + + def principals_allowed_by_permission(self, context, permission): + return self.result + + +def _registerAuthenticationPolicy(reg, result): + from pyramid.interfaces import IAuthenticationPolicy + + policy = DummyAuthenticationPolicy(result) + reg.registerUtility(policy, IAuthenticationPolicy) + return policy + + +def _registerAuthorizationPolicy(reg, result): + from pyramid.interfaces import IAuthorizationPolicy + + policy = DummyAuthorizationPolicy(result) + reg.registerUtility(policy, IAuthorizationPolicy) + return policy + + +def _makeRequest(): + from pyramid.registry import Registry + + request = testing.DummyRequest(environ={}) + request.registry = Registry() + request.context = object() + return request diff --git a/tests/test_session.py b/tests/test_session.py new file mode 100644 index 000000000..5e2a1ff55 --- /dev/null +++ b/tests/test_session.py @@ -0,0 +1,640 @@ +import base64 +import json +import unittest +from pyramid import testing +from pyramid.compat import pickle + + +class SharedCookieSessionTests(object): + def test_ctor_no_cookie(self): + request = testing.DummyRequest() + session = self._makeOne(request) + self.assertEqual(dict(session), {}) + + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ISession + + request = testing.DummyRequest() + session = self._makeOne(request) + verifyObject(ISession, session) + + def test_ctor_with_cookie_still_valid(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request) + self.assertEqual(dict(session), {'state': 1}) + + def test_ctor_with_cookie_expired(self): + request = testing.DummyRequest() + cookieval = self._serialize((0, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request) + self.assertEqual(dict(session), {}) + + def test_ctor_with_bad_cookie_cannot_deserialize(self): + request = testing.DummyRequest() + request.cookies['session'] = 'abc' + session = self._makeOne(request) + self.assertEqual(dict(session), {}) + + def test_ctor_with_bad_cookie_not_tuple(self): + request = testing.DummyRequest() + cookieval = self._serialize('abc') + request.cookies['session'] = cookieval + session = self._makeOne(request) + self.assertEqual(dict(session), {}) + + def test_timeout(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time() - 5, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, timeout=1) + self.assertEqual(dict(session), {}) + + def test_timeout_never(self): + import time + + request = testing.DummyRequest() + LONG_TIME = 31536000 + cookieval = self._serialize((time.time() + LONG_TIME, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, timeout=None) + self.assertEqual(dict(session), {'state': 1}) + + def test_timeout_str(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time() - 5, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, timeout='1') + self.assertEqual(dict(session), {}) + + def test_timeout_invalid(self): + request = testing.DummyRequest() + self.assertRaises( + ValueError, self._makeOne, request, timeout='Invalid value' + ) + + def test_changed(self): + request = testing.DummyRequest() + session = self._makeOne(request) + self.assertEqual(session.changed(), None) + self.assertTrue(session._dirty) + + def test_invalidate(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session['a'] = 1 + self.assertEqual(session.invalidate(), None) + self.assertFalse('a' in session) + + def test_reissue_triggered(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time() - 2, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request) + self.assertEqual(session['state'], 1) + self.assertTrue(session._dirty) + + def test__set_cookie_on_exception(self): + request = testing.DummyRequest() + request.exception = True + session = self._makeOne(request) + session._cookie_on_exception = False + response = DummyResponse() + self.assertEqual(session._set_cookie(response), False) + + def test__set_cookie_on_exception_no_request_exception(self): + import webob + + request = testing.DummyRequest() + request.exception = None + session = self._makeOne(request) + session._cookie_on_exception = False + response = webob.Response() + self.assertEqual(session._set_cookie(response), True) + self.assertEqual(response.headerlist[-1][0], 'Set-Cookie') + + def test__set_cookie_cookieval_too_long(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session['abc'] = 'x' * 100000 + response = DummyResponse() + self.assertRaises(ValueError, session._set_cookie, response) + + def test__set_cookie_real_webob_response(self): + import webob + + request = testing.DummyRequest() + session = self._makeOne(request) + session['abc'] = 'x' + response = webob.Response() + self.assertEqual(session._set_cookie(response), True) + self.assertEqual(response.headerlist[-1][0], 'Set-Cookie') + + def test__set_cookie_options(self): + from pyramid.response import Response + + request = testing.DummyRequest() + request.exception = None + session = self._makeOne( + request, + cookie_name='abc', + path='/foo', + domain='localhost', + secure=True, + httponly=True, + ) + session['abc'] = 'x' + response = Response() + self.assertEqual(session._set_cookie(response), True) + cookieval = response.headerlist[-1][1] + val, domain, path, secure, httponly, samesite = [ + x.strip() for x in cookieval.split(';') + ] + self.assertTrue(val.startswith('abc=')) + self.assertEqual(domain, 'Domain=localhost') + self.assertEqual(path, 'Path=/foo') + self.assertEqual(secure, 'secure') + self.assertEqual(httponly, 'HttpOnly') + self.assertEqual(samesite, 'SameSite=Lax') + + def test_flash_default(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session.flash('msg1') + session.flash('msg2') + self.assertEqual(session['_f_'], ['msg1', 'msg2']) + + def test_flash_allow_duplicate_false(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session.flash('msg1') + session.flash('msg1', allow_duplicate=False) + self.assertEqual(session['_f_'], ['msg1']) + + def test_flash_allow_duplicate_true_and_msg_not_in_storage(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session.flash('msg1', allow_duplicate=True) + self.assertEqual(session['_f_'], ['msg1']) + + def test_flash_allow_duplicate_false_and_msg_not_in_storage(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session.flash('msg1', allow_duplicate=False) + self.assertEqual(session['_f_'], ['msg1']) + + def test_flash_mixed(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session.flash('warn1', 'warn') + session.flash('warn2', 'warn') + session.flash('err1', 'error') + session.flash('err2', 'error') + self.assertEqual(session['_f_warn'], ['warn1', 'warn2']) + + def test_pop_flash_default_queue(self): + request = testing.DummyRequest() + session = self._makeOne(request) + queue = ['one', 'two'] + session['_f_'] = queue + result = session.pop_flash() + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_'), None) + + def test_pop_flash_nodefault_queue(self): + request = testing.DummyRequest() + session = self._makeOne(request) + queue = ['one', 'two'] + session['_f_error'] = queue + result = session.pop_flash('error') + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_error'), None) + + def test_peek_flash_default_queue(self): + request = testing.DummyRequest() + session = self._makeOne(request) + queue = ['one', 'two'] + session['_f_'] = queue + result = session.peek_flash() + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_'), queue) + + def test_peek_flash_nodefault_queue(self): + request = testing.DummyRequest() + session = self._makeOne(request) + queue = ['one', 'two'] + session['_f_error'] = queue + result = session.peek_flash('error') + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_error'), queue) + + def test_new_csrf_token(self): + request = testing.DummyRequest() + session = self._makeOne(request) + token = session.new_csrf_token() + self.assertEqual(token, session['_csrft_']) + + def test_get_csrf_token(self): + request = testing.DummyRequest() + session = self._makeOne(request) + session['_csrft_'] = 'token' + token = session.get_csrf_token() + self.assertEqual(token, 'token') + self.assertTrue('_csrft_' in session) + + def test_get_csrf_token_new(self): + request = testing.DummyRequest() + session = self._makeOne(request) + token = session.get_csrf_token() + self.assertTrue(token) + self.assertTrue('_csrft_' in session) + + def test_no_set_cookie_with_exception(self): + import webob + + request = testing.DummyRequest() + request.exception = True + session = self._makeOne(request, set_on_exception=False) + session['a'] = 1 + callbacks = request.response_callbacks + self.assertEqual(len(callbacks), 1) + response = webob.Response() + result = callbacks[0](request, response) + self.assertEqual(result, None) + self.assertFalse('Set-Cookie' in dict(response.headerlist)) + + def test_set_cookie_with_exception(self): + import webob + + request = testing.DummyRequest() + request.exception = True + session = self._makeOne(request) + session['a'] = 1 + callbacks = request.response_callbacks + self.assertEqual(len(callbacks), 1) + response = webob.Response() + result = callbacks[0](request, response) + self.assertEqual(result, None) + self.assertTrue('Set-Cookie' in dict(response.headerlist)) + + def test_cookie_is_set(self): + import webob + + request = testing.DummyRequest() + session = self._makeOne(request) + session['a'] = 1 + callbacks = request.response_callbacks + self.assertEqual(len(callbacks), 1) + response = webob.Response() + result = callbacks[0](request, response) + self.assertEqual(result, None) + self.assertTrue('Set-Cookie' in dict(response.headerlist)) + + +class TestBaseCookieSession(SharedCookieSessionTests, unittest.TestCase): + def _makeOne(self, request, **kw): + from pyramid.session import BaseCookieSessionFactory + + serializer = DummySerializer() + return BaseCookieSessionFactory(serializer, **kw)(request) + + def _serialize(self, value): + return base64.b64encode(json.dumps(value).encode('utf-8')) + + def test_reissue_not_triggered(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=1) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + + def test_reissue_never(self): + request = testing.DummyRequest() + cookieval = self._serialize((0, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=None, timeout=None) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + + def test_reissue_str_triggered(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time() - 2, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time='0') + self.assertEqual(session['state'], 1) + self.assertTrue(session._dirty) + + def test_reissue_invalid(self): + request = testing.DummyRequest() + self.assertRaises( + ValueError, self._makeOne, request, reissue_time='invalid value' + ) + + def test_cookie_max_age_invalid(self): + request = testing.DummyRequest() + self.assertRaises( + ValueError, self._makeOne, request, max_age='invalid value' + ) + + +class TestSignedCookieSession(SharedCookieSessionTests, unittest.TestCase): + def _makeOne(self, request, **kw): + from pyramid.session import SignedCookieSessionFactory + + kw.setdefault('secret', 'secret') + return SignedCookieSessionFactory(**kw)(request) + + def _serialize(self, value, salt=b'pyramid.session.', hashalg='sha512'): + import base64 + import hashlib + import hmac + import json + + digestmod = lambda: hashlib.new(hashalg) + cstruct = json.dumps(value).encode('utf-8') + sig = hmac.new(salt + b'secret', cstruct, digestmod).digest() + return base64.urlsafe_b64encode(sig + cstruct).rstrip(b'=') + + def test_reissue_not_triggered(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=1) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + + def test_reissue_never(self): + request = testing.DummyRequest() + cookieval = self._serialize((0, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time=None, timeout=None) + self.assertEqual(session['state'], 1) + self.assertFalse(session._dirty) + + def test_reissue_str_triggered(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time() - 2, 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, reissue_time='0') + self.assertEqual(session['state'], 1) + self.assertTrue(session._dirty) + + def test_reissue_invalid(self): + request = testing.DummyRequest() + self.assertRaises( + ValueError, self._makeOne, request, reissue_time='invalid value' + ) + + def test_cookie_max_age_invalid(self): + request = testing.DummyRequest() + self.assertRaises( + ValueError, self._makeOne, request, max_age='invalid value' + ) + + def test_custom_salt(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1}), salt=b'f.') + request.cookies['session'] = cookieval + session = self._makeOne(request, salt=b'f.') + self.assertEqual(session['state'], 1) + + def test_salt_mismatch(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1}), salt=b'f.') + request.cookies['session'] = cookieval + session = self._makeOne(request, salt=b'g.') + self.assertEqual(session, {}) + + def test_custom_hashalg(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize( + (time.time(), 0, {'state': 1}), hashalg='sha1' + ) + request.cookies['session'] = cookieval + session = self._makeOne(request, hashalg='sha1') + self.assertEqual(session['state'], 1) + + def test_hashalg_mismatch(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize( + (time.time(), 0, {'state': 1}), hashalg='sha1' + ) + request.cookies['session'] = cookieval + session = self._makeOne(request, hashalg='sha256') + self.assertEqual(session, {}) + + def test_secret_mismatch(self): + import time + + request = testing.DummyRequest() + cookieval = self._serialize((time.time(), 0, {'state': 1})) + request.cookies['session'] = cookieval + session = self._makeOne(request, secret='evilsecret') + self.assertEqual(session, {}) + + def test_custom_serializer(self): + import base64 + from hashlib import sha512 + import hmac + import time + + request = testing.DummyRequest() + serializer = DummySerializer() + cstruct = serializer.dumps((time.time(), 0, {'state': 1})) + sig = hmac.new(b'pyramid.session.secret', cstruct, sha512).digest() + cookieval = base64.urlsafe_b64encode(sig + cstruct).rstrip(b'=') + request.cookies['session'] = cookieval + session = self._makeOne(request, serializer=serializer) + self.assertEqual(session['state'], 1) + + def test_invalid_data_size(self): + from hashlib import sha512 + import base64 + + request = testing.DummyRequest() + num_bytes = sha512().digest_size - 1 + cookieval = base64.b64encode(b' ' * num_bytes) + request.cookies['session'] = cookieval + session = self._makeOne(request) + self.assertEqual(session, {}) + + def test_very_long_key(self): + verylongkey = b'a' * 1024 + import webob + + request = testing.DummyRequest() + session = self._makeOne(request, secret=verylongkey) + session['a'] = 1 + callbacks = request.response_callbacks + self.assertEqual(len(callbacks), 1) + response = webob.Response() + + try: + result = callbacks[0](request, response) + except TypeError: # pragma: no cover + self.fail('HMAC failed to initialize due to key length.') + + self.assertEqual(result, None) + self.assertTrue('Set-Cookie' in dict(response.headerlist)) + + +class Test_manage_accessed(unittest.TestCase): + def _makeOne(self, wrapped): + from pyramid.session import manage_accessed + + return manage_accessed(wrapped) + + def test_accessed_set(self): + request = testing.DummyRequest() + session = DummySessionFactory(request) + session.renewed = 0 + wrapper = self._makeOne(session.__class__.get) + wrapper(session, 'a') + self.assertNotEqual(session.accessed, None) + self.assertTrue(session._dirty) + + def test_accessed_without_renew(self): + import time + + request = testing.DummyRequest() + session = DummySessionFactory(request) + session._reissue_time = 5 + session.renewed = time.time() + wrapper = self._makeOne(session.__class__.get) + wrapper(session, 'a') + self.assertNotEqual(session.accessed, None) + self.assertFalse(session._dirty) + + def test_already_dirty(self): + request = testing.DummyRequest() + session = DummySessionFactory(request) + session.renewed = 0 + session._dirty = True + session['a'] = 1 + wrapper = self._makeOne(session.__class__.get) + self.assertEqual(wrapper.__doc__, session.get.__doc__) + result = wrapper(session, 'a') + self.assertEqual(result, 1) + callbacks = request.response_callbacks + if callbacks is not None: + self.assertEqual(len(callbacks), 0) + + +class Test_manage_changed(unittest.TestCase): + def _makeOne(self, wrapped): + from pyramid.session import manage_changed + + return manage_changed(wrapped) + + def test_it(self): + request = testing.DummyRequest() + session = DummySessionFactory(request) + wrapper = self._makeOne(session.__class__.__setitem__) + wrapper(session, 'a', 1) + self.assertNotEqual(session.accessed, None) + self.assertTrue(session._dirty) + + +class TestPickleSerializer(unittest.TestCase): + def _makeOne(self): + from pyramid.session import PickleSerializer + + return PickleSerializer() + + def test_loads(self): + # generated from dumping Dummy() using protocol=2 + cstruct = b'\x80\x02ctests.test_session\nDummy\nq\x00)\x81q\x01.' + serializer = self._makeOne() + result = serializer.loads(cstruct) + self.assertIsInstance(result, Dummy) + + def test_loads_raises_ValueError_on_invalid_data(self): + cstruct = b'not pickled' + serializer = self._makeOne() + self.assertRaises(ValueError, serializer.loads, cstruct) + + def test_loads_raises_ValueError_on_bad_import(self): + # generated from dumping an object that cannot be found anymore, eg: + # class Foo: pass + # print(pickle.dumps(Foo())) + cstruct = b'(i__main__\nFoo\np0\n(dp1\nb.' + serializer = self._makeOne() + self.assertRaises(ValueError, serializer.loads, cstruct) + + def test_dumps(self): + obj = Dummy() + serializer = self._makeOne() + result = serializer.dumps(obj) + expected_result = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL) + self.assertEqual(result, expected_result) + self.assertIsInstance(result, bytes) + + +class Dummy(object): + pass + + +class DummySerializer(object): + def dumps(self, value): + return base64.b64encode(json.dumps(value).encode('utf-8')) + + def loads(self, value): + try: + return json.loads(base64.b64decode(value).decode('utf-8')) + + # base64.b64decode raises a TypeError on py2 instead of a ValueError + # and a ValueError is required for the session to handle it properly + except TypeError: + raise ValueError + + +class DummySessionFactory(dict): + _dirty = False + _cookie_name = 'session' + _cookie_max_age = None + _cookie_path = '/' + _cookie_domain = None + _cookie_secure = False + _cookie_httponly = False + _timeout = 1200 + _reissue_time = 0 + + def __init__(self, request): + self.request = request + dict.__init__(self, {}) + + def changed(self): + self._dirty = True + + +class DummyResponse(object): + def __init__(self): + self.headerlist = [] diff --git a/tests/test_settings.py b/tests/test_settings.py new file mode 100644 index 000000000..e8be490ee --- /dev/null +++ b/tests/test_settings.py @@ -0,0 +1,86 @@ +import unittest + + +class Test_asbool(unittest.TestCase): + def _callFUT(self, s): + from pyramid.settings import asbool + + return asbool(s) + + def test_s_is_None(self): + result = self._callFUT(None) + self.assertEqual(result, False) + + def test_s_is_True(self): + result = self._callFUT(True) + self.assertEqual(result, True) + + def test_s_is_False(self): + result = self._callFUT(False) + self.assertEqual(result, False) + + def test_s_is_true(self): + result = self._callFUT('True') + self.assertEqual(result, True) + + def test_s_is_false(self): + result = self._callFUT('False') + self.assertEqual(result, False) + + def test_s_is_yes(self): + result = self._callFUT('yes') + self.assertEqual(result, True) + + def test_s_is_on(self): + result = self._callFUT('on') + self.assertEqual(result, True) + + def test_s_is_1(self): + result = self._callFUT(1) + self.assertEqual(result, True) + + +class Test_aslist_cronly(unittest.TestCase): + def _callFUT(self, val): + from pyramid.settings import aslist_cronly + + return aslist_cronly(val) + + def test_with_list(self): + result = self._callFUT(['abc', 'def']) + self.assertEqual(result, ['abc', 'def']) + + def test_with_string(self): + result = self._callFUT('abc def') + self.assertEqual(result, ['abc def']) + + def test_with_string_crsep(self): + result = self._callFUT(' abc\n def') + self.assertEqual(result, ['abc', 'def']) + + +class Test_aslist(unittest.TestCase): + def _callFUT(self, val, **kw): + from pyramid.settings import aslist + + return aslist(val, **kw) + + def test_with_list(self): + result = self._callFUT(['abc', 'def']) + self.assertEqual(list(result), ['abc', 'def']) + + def test_with_string(self): + result = self._callFUT('abc def') + self.assertEqual(result, ['abc', 'def']) + + def test_with_string_crsep(self): + result = self._callFUT(' abc\n def') + self.assertEqual(result, ['abc', 'def']) + + def test_with_string_crsep_spacesep(self): + result = self._callFUT(' abc\n def ghi') + self.assertEqual(result, ['abc', 'def', 'ghi']) + + def test_with_string_crsep_spacesep_no_flatten(self): + result = self._callFUT(' abc\n def ghi ', flatten=False) + self.assertEqual(result, ['abc', 'def ghi']) diff --git a/tests/test_static.py b/tests/test_static.py new file mode 100644 index 000000000..a323b1d89 --- /dev/null +++ b/tests/test_static.py @@ -0,0 +1,521 @@ +import datetime +import os.path +import unittest + +here = os.path.dirname(__file__) + +# 5 years from now (more or less) +fiveyrsfuture = datetime.datetime.utcnow() + datetime.timedelta(5 * 365) + + +class Test_static_view_use_subpath_False(unittest.TestCase): + def _getTargetClass(self): + from pyramid.static import static_view + + return static_view + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + def _makeRequest(self, kw=None): + from pyramid.request import Request + + environ = { + 'wsgi.url_scheme': 'http', + 'wsgi.version': (1, 0), + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '6543', + 'PATH_INFO': '/', + 'SCRIPT_NAME': '', + 'REQUEST_METHOD': 'GET', + } + if kw is not None: + environ.update(kw) + return Request(environ=environ) + + def test_ctor_defaultargs(self): + inst = self._makeOne('package:resource_name') + self.assertEqual(inst.package_name, 'package') + self.assertEqual(inst.docroot, 'resource_name') + self.assertEqual(inst.cache_max_age, 3600) + self.assertEqual(inst.index, 'index.html') + + def test_call_adds_slash_path_info_empty(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': ''}) + context = DummyContext() + from pyramid.httpexceptions import HTTPMovedPermanently + + self.assertRaises(HTTPMovedPermanently, inst, context, request) + + def test_path_info_slash_means_index_html(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + + def test_oob_singledot(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/./index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertTrue(b'<html>static</html>' in response.body) + + def test_oob_emptyelement(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '//index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertTrue(b'<html>static</html>' in response.body) + + def test_oob_dotdotslash(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/subdir/../../minimal.pt'}) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_dotdotslash_encoded(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest( + {'PATH_INFO': '/subdir/%2E%2E%2F%2E%2E/minimal.pt'} + ) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_os_sep(self): + import os + + inst = self._makeOne('tests:fixtures/static') + dds = '..' + os.sep + request = self._makeRequest( + {'PATH_INFO': '/subdir/%s%sminimal.pt' % (dds, dds)} + ) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_resource_doesnt_exist(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/notthere'}) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_resource_isdir(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/subdir/'}) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>subdir</html>' in response.body) + + def test_resource_is_file(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + + def test_resource_is_file_with_wsgi_file_wrapper(self): + from pyramid.response import _BLOCK_SIZE + + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/index.html'}) + + class _Wrapper(object): + def __init__(self, file, block_size=None): + self.file = file + self.block_size = block_size + + request.environ['wsgi.file_wrapper'] = _Wrapper + context = DummyContext() + response = inst(context, request) + app_iter = response.app_iter + self.assertTrue(isinstance(app_iter, _Wrapper)) + self.assertTrue(b'<html>static</html>' in app_iter.file.read()) + self.assertEqual(app_iter.block_size, _BLOCK_SIZE) + app_iter.file.close() + + def test_resource_is_file_with_cache_max_age(self): + inst = self._makeOne('tests:fixtures/static', cache_max_age=600) + request = self._makeRequest({'PATH_INFO': '/index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + self.assertEqual(len(response.headerlist), 5) + header_names = [x[0] for x in response.headerlist] + header_names.sort() + self.assertEqual( + header_names, + [ + 'Cache-Control', + 'Content-Length', + 'Content-Type', + 'Expires', + 'Last-Modified', + ], + ) + + def test_resource_is_file_with_no_cache_max_age(self): + inst = self._makeOne('tests:fixtures/static', cache_max_age=None) + request = self._makeRequest({'PATH_INFO': '/index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + self.assertEqual(len(response.headerlist), 3) + header_names = [x[0] for x in response.headerlist] + header_names.sort() + self.assertEqual( + header_names, ['Content-Length', 'Content-Type', 'Last-Modified'] + ) + + def test_resource_notmodified(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/index.html'}) + request.if_modified_since = fiveyrsfuture + context = DummyContext() + response = inst(context, request) + start_response = DummyStartResponse() + app_iter = response(request.environ, start_response) + try: + self.assertEqual(start_response.status, '304 Not Modified') + self.assertEqual(list(app_iter), []) + finally: + app_iter.close() + + def test_not_found(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/notthere.html'}) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_gz_resource_no_content_encoding(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/arcs.svg.tgz'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/x-tar') + self.assertEqual(response.content_encoding, None) + response.app_iter.close() + + def test_resource_no_content_encoding(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': '/index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'text/html') + self.assertEqual(response.content_encoding, None) + response.app_iter.close() + + +class Test_static_view_use_subpath_True(unittest.TestCase): + def _getTargetClass(self): + from pyramid.static import static_view + + return static_view + + def _makeOne(self, *arg, **kw): + kw['use_subpath'] = True + return self._getTargetClass()(*arg, **kw) + + def _makeRequest(self, kw=None): + from pyramid.request import Request + + environ = { + 'wsgi.url_scheme': 'http', + 'wsgi.version': (1, 0), + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '6543', + 'PATH_INFO': '/', + 'SCRIPT_NAME': '', + 'REQUEST_METHOD': 'GET', + } + if kw is not None: + environ.update(kw) + return Request(environ=environ) + + def test_ctor_defaultargs(self): + inst = self._makeOne('package:resource_name') + self.assertEqual(inst.package_name, 'package') + self.assertEqual(inst.docroot, 'resource_name') + self.assertEqual(inst.cache_max_age, 3600) + self.assertEqual(inst.index, 'index.html') + + def test_call_adds_slash_path_info_empty(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest({'PATH_INFO': ''}) + request.subpath = () + context = DummyContext() + from pyramid.httpexceptions import HTTPMovedPermanently + + self.assertRaises(HTTPMovedPermanently, inst, context, request) + + def test_path_info_slash_means_index_html(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = () + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + + def test_oob_singledot(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('.', 'index.html') + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_emptyelement(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('', 'index.html') + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_dotdotslash(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('subdir', '..', '..', 'minimal.pt') + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_dotdotslash_encoded(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('subdir', '%2E%2E', '%2E%2E', 'minimal.pt') + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_oob_os_sep(self): + import os + + inst = self._makeOne('tests:fixtures/static') + dds = '..' + os.sep + request = self._makeRequest() + request.subpath = ('subdir', dds, dds, 'minimal.pt') + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_resource_doesnt_exist(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = 'notthere,' + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + def test_resource_isdir(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('subdir',) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>subdir</html>' in response.body) + + def test_resource_is_file(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('index.html',) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + + def test_resource_is_file_with_cache_max_age(self): + inst = self._makeOne('tests:fixtures/static', cache_max_age=600) + request = self._makeRequest() + request.subpath = ('index.html',) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + self.assertEqual(len(response.headerlist), 5) + header_names = [x[0] for x in response.headerlist] + header_names.sort() + self.assertEqual( + header_names, + [ + 'Cache-Control', + 'Content-Length', + 'Content-Type', + 'Expires', + 'Last-Modified', + ], + ) + + def test_resource_is_file_with_no_cache_max_age(self): + inst = self._makeOne('tests:fixtures/static', cache_max_age=None) + request = self._makeRequest() + request.subpath = ('index.html',) + context = DummyContext() + response = inst(context, request) + self.assertTrue(b'<html>static</html>' in response.body) + self.assertEqual(len(response.headerlist), 3) + header_names = [x[0] for x in response.headerlist] + header_names.sort() + self.assertEqual( + header_names, ['Content-Length', 'Content-Type', 'Last-Modified'] + ) + + def test_resource_notmodified(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.if_modified_since = fiveyrsfuture + request.subpath = ('index.html',) + context = DummyContext() + response = inst(context, request) + start_response = DummyStartResponse() + app_iter = response(request.environ, start_response) + try: + self.assertEqual(start_response.status, '304 Not Modified') + self.assertEqual(list(app_iter), []) + finally: + app_iter.close() + + def test_not_found(self): + inst = self._makeOne('tests:fixtures/static') + request = self._makeRequest() + request.subpath = ('notthere.html',) + context = DummyContext() + from pyramid.httpexceptions import HTTPNotFound + + self.assertRaises(HTTPNotFound, inst, context, request) + + +class TestQueryStringConstantCacheBuster(unittest.TestCase): + def _makeOne(self, param=None): + from pyramid.static import QueryStringConstantCacheBuster as cls + + if param: + inst = cls('foo', param) + else: + inst = cls('foo') + return inst + + def test_token(self): + fut = self._makeOne().tokenize + self.assertEqual(fut(None, 'whatever', None), 'foo') + + def test_it(self): + fut = self._makeOne() + self.assertEqual( + fut('foo', 'bar', {}), ('bar', {'_query': {'x': 'foo'}}) + ) + + def test_change_param(self): + fut = self._makeOne('y') + self.assertEqual( + fut('foo', 'bar', {}), ('bar', {'_query': {'y': 'foo'}}) + ) + + def test_query_is_already_tuples(self): + fut = self._makeOne() + self.assertEqual( + fut('foo', 'bar', {'_query': [('a', 'b')]}), + ('bar', {'_query': (('a', 'b'), ('x', 'foo'))}), + ) + + def test_query_is_tuple_of_tuples(self): + fut = self._makeOne() + self.assertEqual( + fut('foo', 'bar', {'_query': (('a', 'b'),)}), + ('bar', {'_query': (('a', 'b'), ('x', 'foo'))}), + ) + + +class TestManifestCacheBuster(unittest.TestCase): + def _makeOne(self, path, **kw): + from pyramid.static import ManifestCacheBuster as cls + + return cls(path, **kw) + + def test_it(self): + manifest_path = os.path.join(here, 'fixtures', 'manifest.json') + fut = self._makeOne(manifest_path) + self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-test.css', {}) + ) + + def test_it_with_relspec(self): + fut = self._makeOne('fixtures/manifest.json') + self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-test.css', {}) + ) + + def test_it_with_absspec(self): + fut = self._makeOne('tests:fixtures/manifest.json') + self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-test.css', {}) + ) + + def test_reload(self): + manifest_path = os.path.join(here, 'fixtures', 'manifest.json') + new_manifest_path = os.path.join(here, 'fixtures', 'manifest2.json') + inst = self._makeOne('foo', reload=True) + inst.getmtime = lambda *args, **kwargs: 0 + fut = inst + + # test without a valid manifest + self.assertEqual(fut('foo', 'css/main.css', {}), ('css/main.css', {})) + + # swap to a real manifest, setting mtime to 0 + inst.manifest_path = manifest_path + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-test.css', {}) + ) + + # ensure switching the path doesn't change the result + inst.manifest_path = new_manifest_path + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-test.css', {}) + ) + + # update mtime, should cause a reload + inst.getmtime = lambda *args, **kwargs: 1 + self.assertEqual( + fut('foo', 'css/main.css', {}), ('css/main-678b7c80.css', {}) + ) + + def test_invalid_manifest(self): + self.assertRaises(IOError, lambda: self._makeOne('foo')) + + def test_invalid_manifest_with_reload(self): + inst = self._makeOne('foo', reload=True) + self.assertEqual(inst.manifest, {}) + + +class DummyContext: + pass + + +class DummyStartResponse: + status = () + headers = () + + def __call__(self, status, headers): + self.status = status + self.headers = headers diff --git a/tests/test_testing.py b/tests/test_testing.py new file mode 100644 index 000000000..16c94ee19 --- /dev/null +++ b/tests/test_testing.py @@ -0,0 +1,789 @@ +import unittest +from zope.component import getSiteManager +from zope.interface import Interface +from zope.interface import implementer +from pyramid import testing + + +class TestDummyRootFactory(unittest.TestCase): + def _makeOne(self, environ): + from pyramid.testing import DummyRootFactory + + return DummyRootFactory(environ) + + def test_it(self): + environ = {'bfg.routes.matchdict': {'a': 1}} + factory = self._makeOne(environ) + self.assertEqual(factory.a, 1) + + +class TestDummySecurityPolicy(unittest.TestCase): + def _getTargetClass(self): + from pyramid.testing import DummySecurityPolicy + + return DummySecurityPolicy + + def _makeOne(self, userid=None, groupids=(), permissive=True): + klass = self._getTargetClass() + return klass(userid, groupids, permissive) + + def test_authenticated_userid(self): + policy = self._makeOne('user') + self.assertEqual(policy.authenticated_userid(None), 'user') + + def test_unauthenticated_userid(self): + policy = self._makeOne('user') + self.assertEqual(policy.unauthenticated_userid(None), 'user') + + def test_effective_principals_userid(self): + policy = self._makeOne('user', ('group1',)) + from pyramid.security import Everyone + from pyramid.security import Authenticated + + self.assertEqual( + policy.effective_principals(None), + [Everyone, Authenticated, 'user', 'group1'], + ) + + def test_effective_principals_nouserid(self): + policy = self._makeOne() + from pyramid.security import Everyone + + self.assertEqual(policy.effective_principals(None), [Everyone]) + + def test_permits(self): + policy = self._makeOne() + self.assertEqual(policy.permits(None, None, None), True) + + def test_principals_allowed_by_permission(self): + policy = self._makeOne('user', ('group1',)) + from pyramid.security import Everyone + from pyramid.security import Authenticated + + result = policy.principals_allowed_by_permission(None, None) + self.assertEqual(result, [Everyone, Authenticated, 'user', 'group1']) + + def test_forget(self): + policy = self._makeOne() + self.assertEqual(policy.forget(None), []) + + def test_remember(self): + policy = self._makeOne() + self.assertEqual(policy.remember(None, None), []) + + +class TestDummyResource(unittest.TestCase): + def _getTargetClass(self): + from pyramid.testing import DummyResource + + return DummyResource + + def _makeOne(self, name=None, parent=None, **kw): + klass = self._getTargetClass() + return klass(name, parent, **kw) + + def test__setitem__and__getitem__and__delitem__and__contains__and_get( + self + ): + class Dummy: + pass + + dummy = Dummy() + resource = self._makeOne() + resource['abc'] = dummy + self.assertEqual(dummy.__name__, 'abc') + self.assertEqual(dummy.__parent__, resource) + self.assertEqual(resource['abc'], dummy) + self.assertEqual(resource.get('abc'), dummy) + self.assertRaises(KeyError, resource.__getitem__, 'none') + self.assertTrue('abc' in resource) + del resource['abc'] + self.assertFalse('abc' in resource) + self.assertEqual(resource.get('abc', 'foo'), 'foo') + self.assertEqual(resource.get('abc'), None) + + def test_extra_params(self): + resource = self._makeOne(foo=1) + self.assertEqual(resource.foo, 1) + + def test_clone(self): + resource = self._makeOne('name', 'parent', foo=1, bar=2) + clone = resource.clone('name2', 'parent2', bar=1) + self.assertEqual(clone.bar, 1) + self.assertEqual(clone.__name__, 'name2') + self.assertEqual(clone.__parent__, 'parent2') + self.assertEqual(clone.foo, 1) + + def test_keys_items_values_len(self): + class Dummy: + pass + + resource = self._makeOne() + resource['abc'] = Dummy() + resource['def'] = Dummy() + L = list + self.assertEqual(L(resource.values()), L(resource.subs.values())) + self.assertEqual(L(resource.items()), L(resource.subs.items())) + self.assertEqual(L(resource.keys()), L(resource.subs.keys())) + self.assertEqual(len(resource), 2) + + def test_nonzero(self): + resource = self._makeOne() + self.assertEqual(resource.__nonzero__(), True) + + def test_bool(self): + resource = self._makeOne() + self.assertEqual(resource.__bool__(), True) + + def test_ctor_with__provides__(self): + resource = self._makeOne(__provides__=IDummy) + self.assertTrue(IDummy.providedBy(resource)) + + +class TestDummyRequest(unittest.TestCase): + def _getTargetClass(self): + from pyramid.testing import DummyRequest + + return DummyRequest + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + def test_params(self): + request = self._makeOne( + params={'say': 'Hello'}, + environ={'PATH_INFO': '/foo'}, + headers={'X-Foo': 'YUP'}, + ) + self.assertEqual(request.params['say'], 'Hello') + self.assertEqual(request.GET['say'], 'Hello') + self.assertEqual(request.POST['say'], 'Hello') + self.assertEqual(request.headers['X-Foo'], 'YUP') + self.assertEqual(request.environ['PATH_INFO'], '/foo') + + def test_defaults(self): + from pyramid.threadlocal import get_current_registry + from pyramid.testing import DummySession + + request = self._makeOne() + self.assertEqual(request.method, 'GET') + self.assertEqual(request.application_url, 'http://example.com') + self.assertEqual(request.host_url, 'http://example.com') + self.assertEqual(request.path_url, 'http://example.com') + self.assertEqual(request.url, 'http://example.com') + self.assertEqual(request.host, 'example.com:80') + self.assertEqual(request.content_length, 0) + self.assertEqual(request.environ.get('PATH_INFO'), None) + self.assertEqual(request.headers.get('X-Foo'), None) + self.assertEqual(request.params.get('foo'), None) + self.assertEqual(request.GET.get('foo'), None) + self.assertEqual(request.POST.get('foo'), None) + self.assertEqual(request.cookies.get('type'), None) + self.assertEqual(request.path, '/') + self.assertEqual(request.path_info, '/') + self.assertEqual(request.script_name, '') + self.assertEqual(request.path_qs, '') + self.assertEqual(request.view_name, '') + self.assertEqual(request.subpath, ()) + self.assertEqual(request.context, None) + self.assertEqual(request.root, None) + self.assertEqual(request.virtual_root, None) + self.assertEqual(request.virtual_root_path, ()) + self.assertEqual(request.registry, get_current_registry()) + self.assertEqual(request.session.__class__, DummySession) + + def test_params_explicit(self): + request = self._makeOne(params={'foo': 'bar'}) + self.assertEqual(request.params['foo'], 'bar') + self.assertEqual(request.GET['foo'], 'bar') + self.assertEqual(request.POST['foo'], 'bar') + + def test_environ_explicit(self): + request = self._makeOne(environ={'PATH_INFO': '/foo'}) + self.assertEqual(request.environ['PATH_INFO'], '/foo') + + def test_headers_explicit(self): + request = self._makeOne(headers={'X-Foo': 'YUP'}) + self.assertEqual(request.headers['X-Foo'], 'YUP') + + def test_path_explicit(self): + request = self._makeOne(path='/abc') + self.assertEqual(request.path, '/abc') + + def test_cookies_explicit(self): + request = self._makeOne(cookies={'type': 'gingersnap'}) + self.assertEqual(request.cookies['type'], 'gingersnap') + + def test_post_explicit(self): + POST = {'foo': 'bar', 'baz': 'qux'} + request = self._makeOne(post=POST) + self.assertEqual(request.method, 'POST') + self.assertEqual(request.POST, POST) + # N.B.: Unlike a normal request, passing 'post' should *not* put + # explict POST data into params: doing so masks a possible + # XSS bug in the app. Tests for apps which don't care about + # the distinction should just use 'params'. + self.assertEqual(request.params, {}) + + def test_post_empty_shadows_params(self): + request = self._makeOne(params={'foo': 'bar'}, post={}) + self.assertEqual(request.method, 'POST') + self.assertEqual(request.params.get('foo'), 'bar') + self.assertEqual(request.POST.get('foo'), None) + + def test_kwargs(self): + request = self._makeOne(water=1) + self.assertEqual(request.water, 1) + + def test_add_response_callback(self): + request = self._makeOne() + request.add_response_callback(1) + self.assertEqual(list(request.response_callbacks), [1]) + + def test_registry_is_config_registry_when_setup_is_called_after_ctor(self): + # see https://github.com/Pylons/pyramid/issues/165 + from pyramid.registry import Registry + from pyramid.config import Configurator + + request = self._makeOne() + try: + registry = Registry('this_test') + config = Configurator(registry=registry) + config.begin() + self.assertTrue(request.registry is registry) + finally: + config.end() + + def test_set_registry(self): + request = self._makeOne() + request.registry = 'abc' + self.assertEqual(request.registry, 'abc') + + def test_del_registry(self): + # see https://github.com/Pylons/pyramid/issues/165 + from pyramid.registry import Registry + from pyramid.config import Configurator + + request = self._makeOne() + request.registry = 'abc' + self.assertEqual(request.registry, 'abc') + del request.registry + try: + registry = Registry('this_test') + config = Configurator(registry=registry) + config.begin() + self.assertTrue(request.registry is registry) + finally: + config.end() + + def test_response_with_responsefactory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IResponseFactory + + registry = Registry('this_test') + + class ResponseFactory(object): + pass + + registry.registerUtility(lambda r: ResponseFactory(), IResponseFactory) + request = self._makeOne() + request.registry = registry + resp = request.response + self.assertEqual(resp.__class__, ResponseFactory) + self.assertTrue(request.response is resp) # reified + + def test_response_without_responsefactory(self): + from pyramid.registry import Registry + from pyramid.response import Response + + registry = Registry('this_test') + request = self._makeOne() + request.registry = registry + resp = request.response + self.assertEqual(resp.__class__, Response) + self.assertTrue(request.response is resp) # reified + + def test_default_accept(self): + request = self._makeOne() + self.assertEqual( + request.accept.acceptable_offers(['text/html']), + [('text/html', 1.0)], + ) + + request.accept = 'text/plain' + self.assertEqual(request.accept.acceptable_offers(['text/html']), []) + + del request.accept + self.assertEqual( + request.accept.acceptable_offers(['text/html']), + [('text/html', 1.0)], + ) + + def test_accept__init__(self): + request = self._makeOne(accept='text/plain') + self.assertEqual( + request.accept.acceptable_offers(['text/html', 'text/plain']), + [('text/plain', 1.0)], + ) + + +class TestDummyTemplateRenderer(unittest.TestCase): + def _getTargetClass(self,): + from pyramid.testing import DummyTemplateRenderer + + return DummyTemplateRenderer + + def _makeOne(self, string_response=''): + return self._getTargetClass()(string_response=string_response) + + def test_implementation(self): + renderer = self._makeOne() + impl = renderer.implementation() + impl(a=1, b=2) + self.assertEqual(renderer._implementation._received['a'], 1) + self.assertEqual(renderer._implementation._received['b'], 2) + + def test_getattr(self): + renderer = self._makeOne() + renderer({'a': 1}) + self.assertEqual(renderer.a, 1) + self.assertRaises(AttributeError, renderer.__getattr__, 'b') + + def test_assert_(self): + renderer = self._makeOne() + renderer({'a': 1, 'b': 2}) + self.assertRaises(AssertionError, renderer.assert_, c=1) + self.assertRaises(AssertionError, renderer.assert_, b=3) + self.assertTrue(renderer.assert_(a=1, b=2)) + + def test_nondefault_string_response(self): + renderer = self._makeOne('abc') + result = renderer({'a': 1, 'b': 2}) + self.assertEqual(result, 'abc') + + +class Test_setUp(unittest.TestCase): + def _callFUT(self, **kw): + from pyramid.testing import setUp + + return setUp(**kw) + + def tearDown(self): + from pyramid.threadlocal import manager + + manager.clear() + getSiteManager.reset() + + def _assertSMHook(self, hook): + result = getSiteManager.sethook(None) + self.assertEqual(result, hook) + + def test_it_defaults(self): + from pyramid.threadlocal import manager + from pyramid.threadlocal import get_current_registry + from pyramid.registry import Registry + + old = True + manager.push(old) + config = self._callFUT() + current = manager.get() + self.assertFalse(current is old) + self.assertEqual(config.registry, current['registry']) + self.assertEqual(current['registry'].__class__, Registry) + self.assertEqual(current['request'], None) + self.assertEqual(config.package.__name__, 'tests') + self._assertSMHook(get_current_registry) + + def test_it_with_registry(self): + from pyramid.registry import Registry + from pyramid.threadlocal import manager + + registry = Registry() + self._callFUT(registry=registry) + current = manager.get() + self.assertEqual(current['registry'], registry) + + def test_it_with_request(self): + from pyramid.threadlocal import manager + + request = object() + self._callFUT(request=request) + current = manager.get() + self.assertEqual(current['request'], request) + + def test_it_with_package(self): + config = self._callFUT(package='pyramid') + self.assertEqual(config.package.__name__, 'pyramid') + + def test_it_with_hook_zca_false(self): + from pyramid.registry import Registry + + registry = Registry() + self._callFUT(registry=registry, hook_zca=False) + sm = getSiteManager() + self.assertFalse(sm is registry) + + def test_it_with_settings_passed_explicit_registry(self): + from pyramid.registry import Registry + + registry = Registry() + self._callFUT(registry=registry, hook_zca=False, settings=dict(a=1)) + self.assertEqual(registry.settings['a'], 1) + + def test_it_with_settings_passed_implicit_registry(self): + config = self._callFUT(hook_zca=False, settings=dict(a=1)) + self.assertEqual(config.registry.settings['a'], 1) + + +class Test_cleanUp(Test_setUp): + def _callFUT(self, *arg, **kw): + from pyramid.testing import cleanUp + + return cleanUp(*arg, **kw) + + +class Test_tearDown(unittest.TestCase): + def _callFUT(self, **kw): + from pyramid.testing import tearDown + + return tearDown(**kw) + + def tearDown(self): + from pyramid.threadlocal import manager + + manager.clear() + getSiteManager.reset() + + def _assertSMHook(self, hook): + result = getSiteManager.sethook(None) + self.assertEqual(result, hook) + + def _setSMHook(self, hook): + getSiteManager.sethook(hook) + + def test_defaults(self): + from pyramid.threadlocal import manager + + registry = DummyRegistry() + old = {'registry': registry} + hook = lambda *arg: None + try: + self._setSMHook(hook) + manager.push(old) + self._callFUT() + current = manager.get() + self.assertNotEqual(current, old) + self.assertEqual(registry.inited, 2) + finally: + result = getSiteManager.sethook(None) + self.assertNotEqual(result, hook) + + def test_registry_cannot_be_inited(self): + from pyramid.threadlocal import manager + + registry = DummyRegistry() + + def raiseit(name): + raise TypeError + + registry.__init__ = raiseit + old = {'registry': registry} + try: + manager.push(old) + self._callFUT() # doesn't blow up + current = manager.get() + self.assertNotEqual(current, old) + self.assertEqual(registry.inited, 1) + finally: + manager.clear() + + def test_unhook_zc_false(self): + hook = lambda *arg: None + try: + self._setSMHook(hook) + self._callFUT(unhook_zca=False) + finally: + self._assertSMHook(hook) + + +class TestDummyRendererFactory(unittest.TestCase): + def _makeOne(self, name, factory): + from pyramid.testing import DummyRendererFactory + + return DummyRendererFactory(name, factory) + + def test_add_no_colon(self): + f = self._makeOne('name', None) + f.add('spec', 'renderer') + self.assertEqual(f.renderers['spec'], 'renderer') + + def test_add_with_colon(self): + f = self._makeOne('name', None) + f.add('spec:spec2', 'renderer') + self.assertEqual(f.renderers['spec:spec2'], 'renderer') + self.assertEqual(f.renderers['spec2'], 'renderer') + + def test_call(self): + f = self._makeOne('name', None) + f.renderers['spec'] = 'renderer' + info = DummyRendererInfo({'name': 'spec'}) + self.assertEqual(f(info), 'renderer') + + def test_call2(self): + f = self._makeOne('name', None) + f.renderers['spec'] = 'renderer' + info = DummyRendererInfo({'name': 'spec:spec'}) + self.assertEqual(f(info), 'renderer') + + def test_call3(self): + def factory(spec): + return 'renderer' + + f = self._makeOne('name', factory) + info = DummyRendererInfo({'name': 'spec'}) + self.assertEqual(f(info), 'renderer') + + def test_call_miss(self): + f = self._makeOne('name', None) + info = DummyRendererInfo({'name': 'spec'}) + self.assertRaises(KeyError, f, info) + + +class TestMockTemplate(unittest.TestCase): + def _makeOne(self, response): + from pyramid.testing import MockTemplate + + return MockTemplate(response) + + def test_getattr(self): + template = self._makeOne(None) + self.assertEqual(template.foo, template) + + def test_getitem(self): + template = self._makeOne(None) + self.assertEqual(template['foo'], template) + + def test_call(self): + template = self._makeOne('123') + self.assertEqual(template(), '123') + + +class Test_skip_on(unittest.TestCase): + def setUp(self): + from pyramid.testing import skip_on + + self.os_name = skip_on.os_name + skip_on.os_name = 'wrong' + + def tearDown(self): + from pyramid.testing import skip_on + + skip_on.os_name = self.os_name + + def _callFUT(self, *platforms): + from pyramid.testing import skip_on + + return skip_on(*platforms) + + def test_wrong_platform(self): + def foo(): # pragma: no cover + return True + + decorated = self._callFUT('wrong')(foo) + self.assertEqual(decorated(), None) + + def test_ok_platform(self): + def foo(): + return True + + decorated = self._callFUT('ok')(foo) + self.assertEqual(decorated(), True) + + +class TestDummySession(unittest.TestCase): + def _makeOne(self): + from pyramid.testing import DummySession + + return DummySession() + + @testing.skip_on( + 'pypy' + ) # see https://github.com/Pylons/pyramid/issues/3237 + def test_instance_conforms(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ISession + + session = self._makeOne() + verifyObject(ISession, session) + + def test_changed(self): + session = self._makeOne() + self.assertEqual(session.changed(), None) + + def test_invalidate(self): + session = self._makeOne() + session['a'] = 1 + self.assertEqual(session.invalidate(), None) + self.assertFalse('a' in session) + + def test_flash_default(self): + session = self._makeOne() + session.flash('msg1') + session.flash('msg2') + self.assertEqual(session['_f_'], ['msg1', 'msg2']) + + def test_flash_mixed(self): + session = self._makeOne() + session.flash('warn1', 'warn') + session.flash('warn2', 'warn') + session.flash('err1', 'error') + session.flash('err2', 'error') + self.assertEqual(session['_f_warn'], ['warn1', 'warn2']) + + def test_pop_flash_default_queue(self): + session = self._makeOne() + queue = ['one', 'two'] + session['_f_'] = queue + result = session.pop_flash() + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_'), None) + + def test_pop_flash_nodefault_queue(self): + session = self._makeOne() + queue = ['one', 'two'] + session['_f_error'] = queue + result = session.pop_flash('error') + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_error'), None) + + def test_peek_flash_default_queue(self): + session = self._makeOne() + queue = ['one', 'two'] + session['_f_'] = queue + result = session.peek_flash() + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_'), queue) + + def test_peek_flash_nodefault_queue(self): + session = self._makeOne() + queue = ['one', 'two'] + session['_f_error'] = queue + result = session.peek_flash('error') + self.assertEqual(result, queue) + self.assertEqual(session.get('_f_error'), queue) + + def test_new_csrf_token(self): + session = self._makeOne() + token = session.new_csrf_token() + self.assertEqual(token, session['_csrft_']) + + def test_get_csrf_token(self): + session = self._makeOne() + session['_csrft_'] = 'token' + token = session.get_csrf_token() + self.assertEqual(token, 'token') + self.assertTrue('_csrft_' in session) + + def test_get_csrf_token_generates_token(self): + session = self._makeOne() + token = session.get_csrf_token() + self.assertNotEqual(token, None) + self.assertTrue(len(token) >= 1) + + +class IDummy(Interface): + pass + + +@implementer(IDummy) +class DummyEvent: + pass + + +class DummyFactory: + def __init__(self, environ): + """ """ + + +class DummyRegistry(object): + inited = 0 + __name__ = 'name' + + def __init__(self, name=''): + self.inited = self.inited + 1 + + +class DummyRendererInfo(object): + def __init__(self, kw): + self.__dict__.update(kw) + + +class Test_testConfig(unittest.TestCase): + def _setUp(self, **kw): + self._log.append(('setUp', kw)) + return 'fake config' + + def _tearDown(self, **kw): + self._log.append(('tearDown', kw)) + + def setUp(self): + from pyramid import testing + + self._log = [] + self._orig_setUp = testing.setUp + testing.setUp = self._setUp + self._orig_tearDown = testing.tearDown + testing.tearDown = self._tearDown + + def tearDown(self): + from pyramid import testing + + testing.setUp = self._orig_setUp + testing.tearDown = self._orig_tearDown + + def _callFUT(self, inner, **kw): + from pyramid.testing import testConfig + + with testConfig(**kw) as config: + inner(config) + + def test_ok_calls(self): + self.assertEqual(self._log, []) + + def inner(config): + self.assertEqual( + self._log, + [ + ( + 'setUp', + { + 'autocommit': True, + 'hook_zca': True, + 'registry': None, + 'request': None, + 'settings': None, + }, + ) + ], + ) + self._log.pop() + + self._callFUT(inner) + self.assertEqual(self._log, [('tearDown', {'unhook_zca': True})]) + + def test_teardown_called_on_exception(self): + class TestException(Exception): + pass + + def inner(config): + self._log = [] + raise TestException('oops') + + self.assertRaises(TestException, self._callFUT, inner) + self.assertEqual(self._log[0][0], 'tearDown') + + def test_ok_get_config(self): + def inner(config): + self.assertEqual(config, 'fake config') + + self._callFUT(inner) diff --git a/tests/test_threadlocal.py b/tests/test_threadlocal.py new file mode 100644 index 000000000..487c7f4f2 --- /dev/null +++ b/tests/test_threadlocal.py @@ -0,0 +1,105 @@ +from pyramid import testing +import unittest + + +class TestThreadLocalManager(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getTargetClass(self): + from pyramid.threadlocal import ThreadLocalManager + + return ThreadLocalManager + + def _makeOne(self, default=lambda *x: 1): + return self._getTargetClass()(default) + + def test_init(self): + local = self._makeOne() + self.assertEqual(local.stack, []) + self.assertEqual(local.get(), 1) + + def test_default(self): + def thedefault(): + return '123' + + local = self._makeOne(thedefault) + self.assertEqual(local.stack, []) + self.assertEqual(local.get(), '123') + + def test_push_and_pop(self): + local = self._makeOne() + local.push(True) + self.assertEqual(local.get(), True) + self.assertEqual(local.pop(), True) + self.assertEqual(local.pop(), None) + self.assertEqual(local.get(), 1) + + def test_set_get_and_clear(self): + local = self._makeOne() + local.set(None) + self.assertEqual(local.stack, [None]) + self.assertEqual(local.get(), None) + local.clear() + self.assertEqual(local.get(), 1) + local.clear() + self.assertEqual(local.get(), 1) + + +class TestGetCurrentRequest(unittest.TestCase): + def _callFUT(self): + from pyramid.threadlocal import get_current_request + + return get_current_request() + + def test_it_None(self): + request = self._callFUT() + self.assertEqual(request, None) + + def test_it(self): + from pyramid.threadlocal import manager + + request = object() + try: + manager.push({'request': request}) + self.assertEqual(self._callFUT(), request) + finally: + manager.pop() + self.assertEqual(self._callFUT(), None) + + +class GetCurrentRegistryTests(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self): + from pyramid.threadlocal import get_current_registry + + return get_current_registry() + + def test_it(self): + from pyramid.threadlocal import manager + + try: + manager.push({'registry': 123}) + self.assertEqual(self._callFUT(), 123) + finally: + manager.pop() + + +class GetCurrentRegistryWithoutTestingRegistry(unittest.TestCase): + def _callFUT(self): + from pyramid.threadlocal import get_current_registry + + return get_current_registry() + + def test_it(self): + from pyramid.registry import global_registry + + self.assertEqual(self._callFUT(), global_registry) diff --git a/tests/test_traversal.py b/tests/test_traversal.py new file mode 100644 index 000000000..61e480cbc --- /dev/null +++ b/tests/test_traversal.py @@ -0,0 +1,1313 @@ +# -*- coding: utf-8 -*- +import unittest + +from pyramid.testing import cleanUp + +from pyramid.compat import text_, native_, text_type, url_quote, PY2 + + +class TraversalPathTests(unittest.TestCase): + def _callFUT(self, path): + from pyramid.traversal import traversal_path + + return traversal_path(path) + + def test_utf8(self): + la = b'La Pe\xc3\xb1a' + encoded = url_quote(la) + decoded = text_(la, 'utf-8') + path = '/'.join([encoded, encoded]) + result = self._callFUT(path) + self.assertEqual(result, (decoded, decoded)) + + def test_utf16(self): + from pyramid.exceptions import URLDecodeError + + la = text_(b'La Pe\xc3\xb1a', 'utf-8').encode('utf-16') + encoded = url_quote(la) + path = '/'.join([encoded, encoded]) + self.assertRaises(URLDecodeError, self._callFUT, path) + + def test_unicode_highorder_chars(self): + path = text_('/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') + self.assertEqual( + self._callFUT(path), + (text_('\u6d41\u884c\u8d8b\u52bf', 'unicode_escape'),), + ) + + def test_element_urllquoted(self): + self.assertEqual( + self._callFUT('/foo/space%20thing/bar'), + (text_('foo'), text_('space thing'), text_('bar')), + ) + + def test_unicode_undecodeable_to_ascii(self): + path = text_(b'/La Pe\xc3\xb1a', 'utf-8') + self.assertRaises(UnicodeEncodeError, self._callFUT, path) + + +class TraversalPathInfoTests(unittest.TestCase): + def _callFUT(self, path): + from pyramid.traversal import traversal_path_info + + return traversal_path_info(path) + + def test_path_startswith_endswith(self): + self.assertEqual(self._callFUT('/foo/'), (text_('foo'),)) + + def test_empty_elements(self): + self.assertEqual(self._callFUT('foo///'), (text_('foo'),)) + + def test_onedot(self): + self.assertEqual( + self._callFUT('foo/./bar'), (text_('foo'), text_('bar')) + ) + + def test_twodots(self): + self.assertEqual(self._callFUT('foo/../bar'), (text_('bar'),)) + + def test_twodots_at_start(self): + self.assertEqual(self._callFUT('../../bar'), (text_('bar'),)) + + def test_segments_are_unicode(self): + result = self._callFUT('/foo/bar') + self.assertEqual(type(result[0]), text_type) + self.assertEqual(type(result[1]), text_type) + + def test_same_value_returned_if_cached(self): + result1 = self._callFUT('/foo/bar') + result2 = self._callFUT('/foo/bar') + self.assertEqual(result1, (text_('foo'), text_('bar'))) + self.assertEqual(result2, (text_('foo'), text_('bar'))) + + def test_unicode_simple(self): + path = text_('/abc') + self.assertEqual(self._callFUT(path), (text_('abc'),)) + + def test_highorder(self): + la = b'La Pe\xc3\xb1a' + latin1 = native_(la) + result = self._callFUT(latin1) + self.assertEqual(result, (text_(la, 'utf-8'),)) + + def test_highorder_undecodeable(self): + from pyramid.exceptions import URLDecodeError + + la = text_(b'La Pe\xc3\xb1a', 'utf-8') + notlatin1 = native_(la) + self.assertRaises(URLDecodeError, self._callFUT, notlatin1) + + +class ResourceTreeTraverserTests(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from pyramid.traversal import ResourceTreeTraverser + + return ResourceTreeTraverser + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def _getEnviron(self, **kw): + environ = {} + environ.update(kw) + return environ + + def test_class_conforms_to_ITraverser(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import ITraverser + + verifyClass(ITraverser, self._getTargetClass()) + + def test_instance_conforms_to_ITraverser(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ITraverser + + context = DummyContext() + verifyObject(ITraverser, self._makeOne(context)) + + def test_call_with_empty_pathinfo(self): + policy = self._makeOne(None) + environ = self._getEnviron() + request = DummyRequest(environ, path_info='') + result = policy(request) + self.assertEqual(result['context'], None) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], policy.root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_pathinfo_KeyError(self): + policy = self._makeOne(None) + environ = self._getEnviron() + request = DummyRequest(environ, toraise=KeyError) + result = policy(request) + self.assertEqual(result['context'], None) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], policy.root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_pathinfo_highorder(self): + path = text_(b'/Qu\xc3\xa9bec', 'utf-8') + foo = DummyContext(None, path) + root = DummyContext(foo, 'root') + policy = self._makeOne(root) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=path) + result = policy(request) + self.assertEqual(result['context'], foo) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (path[1:],)) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], policy.root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_pathel_with_no_getitem(self): + policy = self._makeOne(None) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=text_('/foo/bar')) + result = policy(request) + self.assertEqual(result['context'], None) + self.assertEqual(result['view_name'], 'foo') + self.assertEqual(result['subpath'], ('bar',)) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], policy.root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_withconn_getitem_emptypath_nosubpath(self): + root = DummyContext() + policy = self._makeOne(root) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=text_('')) + result = policy(request) + self.assertEqual(result['context'], root) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_withconn_getitem_withpath_nosubpath(self): + foo = DummyContext() + root = DummyContext(foo) + policy = self._makeOne(root) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=text_('/foo/bar')) + result = policy(request) + self.assertEqual(result['context'], foo) + self.assertEqual(result['view_name'], 'bar') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (text_('foo'),)) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_withconn_getitem_withpath_withsubpath(self): + foo = DummyContext() + root = DummyContext(foo) + policy = self._makeOne(root) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=text_('/foo/bar/baz/buz')) + result = policy(request) + self.assertEqual(result['context'], foo) + self.assertEqual(result['view_name'], 'bar') + self.assertEqual(result['subpath'], ('baz', 'buz')) + self.assertEqual(result['traversed'], (text_('foo'),)) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_explicit_viewname(self): + foo = DummyContext() + root = DummyContext(foo) + policy = self._makeOne(root) + environ = self._getEnviron() + request = DummyRequest(environ, path_info=text_('/@@foo')) + result = policy(request) + self.assertEqual(result['context'], root) + self.assertEqual(result['view_name'], 'foo') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_vh_root(self): + environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo/bar') + baz = DummyContext(None, 'baz') + bar = DummyContext(baz, 'bar') + foo = DummyContext(bar, 'foo') + root = DummyContext(foo, 'root') + policy = self._makeOne(root) + request = DummyRequest(environ, path_info=text_('/baz')) + result = policy(request) + self.assertEqual(result['context'], baz) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual( + result['traversed'], (text_('foo'), text_('bar'), text_('baz')) + ) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], bar) + self.assertEqual( + result['virtual_root_path'], (text_('foo'), text_('bar')) + ) + + def test_call_with_vh_root2(self): + environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo') + baz = DummyContext(None, 'baz') + bar = DummyContext(baz, 'bar') + foo = DummyContext(bar, 'foo') + root = DummyContext(foo, 'root') + policy = self._makeOne(root) + request = DummyRequest(environ, path_info=text_('/bar/baz')) + result = policy(request) + self.assertEqual(result['context'], baz) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual( + result['traversed'], (text_('foo'), text_('bar'), text_('baz')) + ) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], foo) + self.assertEqual(result['virtual_root_path'], (text_('foo'),)) + + def test_call_with_vh_root3(self): + environ = self._getEnviron(HTTP_X_VHM_ROOT='/') + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyContext(foo) + policy = self._makeOne(root) + request = DummyRequest(environ, path_info=text_('/foo/bar/baz')) + result = policy(request) + self.assertEqual(result['context'], baz) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual( + result['traversed'], (text_('foo'), text_('bar'), text_('baz')) + ) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_vh_root4(self): + environ = self._getEnviron(HTTP_X_VHM_ROOT='/foo/bar/baz') + baz = DummyContext(None, 'baz') + bar = DummyContext(baz, 'bar') + foo = DummyContext(bar, 'foo') + root = DummyContext(foo, 'root') + policy = self._makeOne(root) + request = DummyRequest(environ, path_info=text_('/')) + result = policy(request) + self.assertEqual(result['context'], baz) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual( + result['traversed'], (text_('foo'), text_('bar'), text_('baz')) + ) + self.assertEqual(result['root'], root) + self.assertEqual(result['virtual_root'], baz) + self.assertEqual( + result['virtual_root_path'], + (text_('foo'), text_('bar'), text_('baz')), + ) + + def test_call_with_vh_root_path_root(self): + policy = self._makeOne(None) + environ = self._getEnviron(HTTP_X_VHM_ROOT='/') + request = DummyRequest(environ, path_info=text_('/')) + result = policy(request) + self.assertEqual(result['context'], None) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], policy.root) + self.assertEqual(result['virtual_root_path'], ()) + + def test_call_with_vh_root_highorder(self): + path = text_(b'Qu\xc3\xa9bec', 'utf-8') + bar = DummyContext(None, 'bar') + foo = DummyContext(bar, path) + root = DummyContext(foo, 'root') + policy = self._makeOne(root) + if PY2: + vhm_root = b'/Qu\xc3\xa9bec' + else: + vhm_root = b'/Qu\xc3\xa9bec'.decode('latin-1') + environ = self._getEnviron(HTTP_X_VHM_ROOT=vhm_root) + request = DummyRequest(environ, path_info=text_('/bar')) + result = policy(request) + self.assertEqual(result['context'], bar) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (path, text_('bar'))) + self.assertEqual(result['root'], policy.root) + self.assertEqual(result['virtual_root'], foo) + self.assertEqual(result['virtual_root_path'], (path,)) + + def test_path_info_raises_unicodedecodeerror(self): + from pyramid.exceptions import URLDecodeError + + foo = DummyContext() + root = DummyContext(foo) + policy = self._makeOne(root) + environ = self._getEnviron() + toraise = UnicodeDecodeError('ascii', b'a', 2, 3, '5') + request = DummyRequest(environ, toraise=toraise) + request.matchdict = None + self.assertRaises(URLDecodeError, policy, request) + + def test_withroute_nothingfancy(self): + resource = DummyContext() + traverser = self._makeOne(resource) + request = DummyRequest({}) + request.matchdict = {} + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_with_subpath_string(self): + resource = DummyContext() + traverser = self._makeOne(resource) + matchdict = {'subpath': '/a/b/c'} + request = DummyRequest({}) + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ('a', 'b', 'c')) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_with_subpath_tuple(self): + resource = DummyContext() + traverser = self._makeOne(resource) + matchdict = {'subpath': ('a', 'b', 'c')} + request = DummyRequest({}) + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ('a', 'b', 'c')) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_and_traverse_string(self): + resource = DummyContext() + traverser = self._makeOne(resource) + matchdict = {'traverse': text_('foo/bar')} + request = DummyRequest({}) + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], 'foo') + self.assertEqual(result['subpath'], ('bar',)) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_and_traverse_tuple(self): + resource = DummyContext() + traverser = self._makeOne(resource) + matchdict = {'traverse': ('foo', 'bar')} + request = DummyRequest({}) + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], 'foo') + self.assertEqual(result['subpath'], ('bar',)) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_and_traverse_empty(self): + resource = DummyContext() + traverser = self._makeOne(resource) + matchdict = {'traverse': ''} + request = DummyRequest({}) + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], resource) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], ()) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], resource) + self.assertEqual(result['virtual_root_path'], ()) + + def test_withroute_and_traverse_and_vroot(self): + abc = DummyContext() + resource = DummyContext(next=abc) + environ = self._getEnviron(HTTP_X_VHM_ROOT='/abc') + request = DummyRequest(environ) + traverser = self._makeOne(resource) + matchdict = {'traverse': text_('/foo/bar')} + request.matchdict = matchdict + result = traverser(request) + self.assertEqual(result['context'], abc) + self.assertEqual(result['view_name'], 'foo') + self.assertEqual(result['subpath'], ('bar',)) + self.assertEqual(result['traversed'], ('abc', 'foo')) + self.assertEqual(result['root'], resource) + self.assertEqual(result['virtual_root'], abc) + self.assertEqual(result['virtual_root_path'], ('abc',)) + + +class FindInterfaceTests(unittest.TestCase): + def _callFUT(self, context, iface): + from pyramid.traversal import find_interface + + return find_interface(context, iface) + + def test_it_interface(self): + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyContext(foo) + root.__parent__ = None + root.__name__ = 'root' + foo.__parent__ = root + foo.__name__ = 'foo' + bar.__parent__ = foo + bar.__name__ = 'bar' + baz.__parent__ = bar + baz.__name__ = 'baz' + from zope.interface import directlyProvides + from zope.interface import Interface + + class IFoo(Interface): + pass + + directlyProvides(root, IFoo) + result = self._callFUT(baz, IFoo) + self.assertEqual(result.__name__, 'root') + + def test_it_class(self): + class DummyRoot(object): + def __init__(self, child): + self.child = child + + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyRoot(foo) + root.__parent__ = None + root.__name__ = 'root' + foo.__parent__ = root + foo.__name__ = 'foo' + bar.__parent__ = foo + bar.__name__ = 'bar' + baz.__parent__ = bar + baz.__name__ = 'baz' + result = self._callFUT(baz, DummyRoot) + self.assertEqual(result.__name__, 'root') + + +class FindRootTests(unittest.TestCase): + def _callFUT(self, context): + from pyramid.traversal import find_root + + return find_root(context) + + def test_it(self): + dummy = DummyContext() + baz = DummyContext() + baz.__parent__ = dummy + baz.__name__ = 'baz' + dummy.__parent__ = None + dummy.__name__ = None + result = self._callFUT(baz) + self.assertEqual(result, dummy) + + +class FindResourceTests(unittest.TestCase): + def _callFUT(self, context, name): + from pyramid.traversal import find_resource + + return find_resource(context, name) + + def _registerTraverser(self, traverser): + from pyramid.threadlocal import get_current_registry + + reg = get_current_registry() + from pyramid.interfaces import ITraverser + from zope.interface import Interface + + reg.registerAdapter(traverser, (Interface,), ITraverser) + + def test_list(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, ['']) + self.assertEqual(result, resource) + self.assertEqual(resource.request.environ['PATH_INFO'], '/') + + def test_generator(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + + def foo(): + yield '' + + result = self._callFUT(resource, foo()) + self.assertEqual(result, resource) + self.assertEqual(resource.request.environ['PATH_INFO'], '/') + + def test_self_string_found(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, '') + self.assertEqual(result, resource) + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_self_tuple_found(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, ()) + self.assertEqual(result, resource) + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_relative_string_found(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, 'baz') + self.assertEqual(result, baz) + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_relative_tuple_found(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, ('baz',)) + self.assertEqual(result, baz) + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_relative_string_notfound(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': 'bar'}) + self._registerTraverser(traverser) + self.assertRaises(KeyError, self._callFUT, resource, 'baz') + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_relative_tuple_notfound(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': 'bar'}) + self._registerTraverser(traverser) + self.assertRaises(KeyError, self._callFUT, resource, ('baz',)) + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_absolute_string_found(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, '/') + self.assertEqual(result, root) + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_absolute_tuple_found(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(resource, ('',)) + self.assertEqual(result, root) + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_absolute_string_notfound(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': 'fuz'}) + self._registerTraverser(traverser) + self.assertRaises(KeyError, self._callFUT, resource, '/') + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_absolute_tuple_notfound(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': 'fuz'}) + self._registerTraverser(traverser) + self.assertRaises(KeyError, self._callFUT, resource, ('',)) + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_absolute_unicode_found(self): + # test for bug wiggy found in wild, traceback stack: + # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF' + # wiggy's code: section=find_resource(page, root) + # find_resource L76: D = traverse(resource, path) + # traverse L291: return traverser(request) + # __call__ line 568: vpath_tuple = traversal_path(vpath) + # lru_cached line 91: f(*arg) + # traversal_path line 443: path.encode('ascii') + # UnicodeEncodeError: 'ascii' codec can't encode characters in + # position 1-12: ordinal not in range(128) + # + # solution: encode string to ascii in pyramid.traversal.traverse + # before passing it along to webob as path_info + from pyramid.traversal import ResourceTreeTraverser + + unprintable = DummyContext() + root = DummyContext(unprintable) + unprintable.__parent__ = root + unprintable.__name__ = text_( + b'/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8' + ) + root.__parent__ = None + root.__name__ = None + traverser = ResourceTreeTraverser + self._registerTraverser(traverser) + result = self._callFUT( + root, text_(b'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') + ) + self.assertEqual(result, unprintable) + + +class ResourcePathTests(unittest.TestCase): + def _callFUT(self, resource, *elements): + from pyramid.traversal import resource_path + + return resource_path(resource, *elements) + + def test_it(self): + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyContext(foo) + root.__parent__ = None + root.__name__ = None + foo.__parent__ = root + foo.__name__ = 'foo ' + bar.__parent__ = foo + bar.__name__ = 'bar' + baz.__parent__ = bar + baz.__name__ = 'baz' + result = self._callFUT(baz, 'this/theotherthing', 'that') + self.assertEqual(result, '/foo%20/bar/baz/this%2Ftheotherthing/that') + + def test_root_default(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + result = self._callFUT(root) + self.assertEqual(result, '/') + + def test_root_default_emptystring(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = '' + result = self._callFUT(root) + self.assertEqual(result, '/') + + def test_root_object_nonnull_name_direct(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = 'flubadub' + result = self._callFUT(root) + self.assertEqual(result, 'flubadub') # insane case + + def test_root_object_nonnull_name_indirect(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = 'flubadub' + other = DummyContext() + other.__parent__ = root + other.__name__ = 'barker' + result = self._callFUT(other) + self.assertEqual(result, 'flubadub/barker') # insane case + + def test_nonroot_default(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + other = DummyContext() + other.__parent__ = root + other.__name__ = 'other' + result = self._callFUT(other) + self.assertEqual(result, '/other') + + def test_path_with_None_itermediate_names(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + other = DummyContext() + other.__parent__ = root + other.__name__ = None + other2 = DummyContext() + other2.__parent__ = other + other2.__name__ = 'other2' + result = self._callFUT(other2) + self.assertEqual(result, '//other2') + + +class ResourcePathTupleTests(unittest.TestCase): + def _callFUT(self, resource, *elements): + from pyramid.traversal import resource_path_tuple + + return resource_path_tuple(resource, *elements) + + def test_it(self): + baz = DummyContext() + bar = DummyContext(baz) + foo = DummyContext(bar) + root = DummyContext(foo) + root.__parent__ = None + root.__name__ = None + foo.__parent__ = root + foo.__name__ = 'foo ' + bar.__parent__ = foo + bar.__name__ = 'bar' + baz.__parent__ = bar + baz.__name__ = 'baz' + result = self._callFUT(baz, 'this/theotherthing', 'that') + self.assertEqual( + result, ('', 'foo ', 'bar', 'baz', 'this/theotherthing', 'that') + ) + + def test_root_default(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + result = self._callFUT(root) + self.assertEqual(result, ('',)) + + def test_root_default_emptystring_name(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = '' + other = DummyContext() + other.__parent__ = root + other.__name__ = 'other' + result = self._callFUT(other) + self.assertEqual(result, ('', 'other')) + + def test_nonroot_default(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + other = DummyContext() + other.__parent__ = root + other.__name__ = 'other' + result = self._callFUT(other) + self.assertEqual(result, ('', 'other')) + + def test_path_with_None_itermediate_names(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + other = DummyContext() + other.__parent__ = root + other.__name__ = None + other2 = DummyContext() + other2.__parent__ = other + other2.__name__ = 'other2' + result = self._callFUT(other2) + self.assertEqual(result, ('', '', 'other2')) + + +class QuotePathSegmentTests(unittest.TestCase): + def _callFUT(self, s): + from pyramid.traversal import quote_path_segment + + return quote_path_segment(s) + + def test_unicode(self): + la = text_(b'/La Pe\xc3\xb1a', 'utf-8') + result = self._callFUT(la) + self.assertEqual(result, '%2FLa%20Pe%C3%B1a') + + def test_string(self): + s = '/ hello!' + result = self._callFUT(s) + self.assertEqual(result, '%2F%20hello!') + + def test_int(self): + s = 12345 + result = self._callFUT(s) + self.assertEqual(result, '12345') + + def test_long(self): + from pyramid.compat import long + import sys + + s = long(sys.maxsize + 1) + result = self._callFUT(s) + expected = str(s) + self.assertEqual(result, expected) + + def test_other(self): + class Foo(object): + def __str__(self): + return 'abc' + + s = Foo() + result = self._callFUT(s) + self.assertEqual(result, 'abc') + + +class ResourceURLTests(unittest.TestCase): + def _makeOne(self, context, url): + return self._getTargetClass()(context, url) + + def _getTargetClass(self): + from pyramid.traversal import ResourceURL + + return ResourceURL + + def test_instance_conforms_to_IResourceURL(self): + from pyramid.interfaces import IResourceURL + from zope.interface.verify import verifyObject + + context = DummyContext() + request = DummyRequest() + verifyObject(IResourceURL, self._makeOne(context, request)) + + def test_IResourceURL_attributes_with_vroot(self): + from pyramid.interfaces import VH_ROOT_KEY + + root = DummyContext() + root.__parent__ = None + root.__name__ = None + one = DummyContext() + one.__parent__ = root + one.__name__ = 'one' + two = DummyContext() + two.__parent__ = one + two.__name__ = 'two' + environ = {VH_ROOT_KEY: '/one'} + request = DummyRequest(environ) + context_url = self._makeOne(two, request) + self.assertEqual(context_url.physical_path, '/one/two/') + self.assertEqual(context_url.virtual_path, '/two/') + self.assertEqual( + context_url.physical_path_tuple, ('', 'one', 'two', '') + ) + self.assertEqual(context_url.virtual_path_tuple, ('', 'two', '')) + + def test_IResourceURL_attributes_vroot_ends_with_slash(self): + from pyramid.interfaces import VH_ROOT_KEY + + root = DummyContext() + root.__parent__ = None + root.__name__ = None + one = DummyContext() + one.__parent__ = root + one.__name__ = 'one' + two = DummyContext() + two.__parent__ = one + two.__name__ = 'two' + environ = {VH_ROOT_KEY: '/one/'} + request = DummyRequest(environ) + context_url = self._makeOne(two, request) + self.assertEqual(context_url.physical_path, '/one/two/') + self.assertEqual(context_url.virtual_path, '/two/') + self.assertEqual( + context_url.physical_path_tuple, ('', 'one', 'two', '') + ) + self.assertEqual(context_url.virtual_path_tuple, ('', 'two', '')) + + def test_IResourceURL_attributes_no_vroot(self): + root = DummyContext() + root.__parent__ = None + root.__name__ = None + one = DummyContext() + one.__parent__ = root + one.__name__ = 'one' + two = DummyContext() + two.__parent__ = one + two.__name__ = 'two' + environ = {} + request = DummyRequest(environ) + context_url = self._makeOne(two, request) + self.assertEqual(context_url.physical_path, '/one/two/') + self.assertEqual(context_url.virtual_path, '/one/two/') + self.assertEqual( + context_url.physical_path_tuple, ('', 'one', 'two', '') + ) + self.assertEqual( + context_url.virtual_path_tuple, ('', 'one', 'two', '') + ) + + +class TestVirtualRoot(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, resource, request): + from pyramid.traversal import virtual_root + + return virtual_root(resource, request) + + def _registerTraverser(self, traverser): + from pyramid.threadlocal import get_current_registry + + reg = get_current_registry() + from pyramid.interfaces import ITraverser + from zope.interface import Interface + + reg.registerAdapter(traverser, (Interface,), ITraverser) + + def test_virtual_root_no_virtual_root_path(self): + root = DummyContext() + root.__name__ = None + root.__parent__ = None + one = DummyContext() + one.__name__ = 'one' + one.__parent__ = root + request = DummyRequest() + result = self._callFUT(one, request) + self.assertEqual(result, root) + + def test_virtual_root_no_virtual_root_path_with_root_on_request(self): + context = DummyContext() + context.__parent__ = None + request = DummyRequest() + request.root = DummyContext() + result = self._callFUT(context, request) + self.assertEqual(result, request.root) + + def test_virtual_root_with_virtual_root_path(self): + from pyramid.interfaces import VH_ROOT_KEY + + root = DummyContext() + root.__parent__ = None + context = DummyContext() + context.__name__ = 'one' + context.__parent__ = root + traversed_to = DummyContext() + environ = {VH_ROOT_KEY: '/one'} + request = DummyRequest(environ) + traverser = make_traverser({'context': traversed_to, 'view_name': ''}) + self._registerTraverser(traverser) + result = self._callFUT(context, request) + self.assertEqual(result, traversed_to) + self.assertEqual(root.request.environ['PATH_INFO'], '/one') + + def test_default(self): + context = DummyContext() + request = _makeRequest() + request.environ['PATH_INFO'] = '/' + result = self._callFUT(context, request) + self.assertEqual(result, context) + + def test_default_no_registry_on_request(self): + context = DummyContext() + request = _makeRequest() + del request.registry + request.environ['PATH_INFO'] = '/' + result = self._callFUT(context, request) + self.assertEqual(result, context) + + +class TraverseTests(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, context, name): + from pyramid.traversal import traverse + + return traverse(context, name) + + def _registerTraverser(self, traverser): + from pyramid.threadlocal import get_current_registry + + reg = get_current_registry() + from pyramid.interfaces import ITraverser + from zope.interface import Interface + + reg.registerAdapter(traverser, (Interface,), ITraverser) + + def test_request_has_registry(self): + from pyramid.threadlocal import get_current_registry + + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ['']) + self.assertEqual(resource.request.registry, get_current_registry()) + + def test_list(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ['']) + self.assertEqual(resource.request.environ['PATH_INFO'], '/') + + def test_generator(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + + def foo(): + yield '' + + self._callFUT(resource, foo()) + self.assertEqual(resource.request.environ['PATH_INFO'], '/') + + def test_self_string_found(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, '') + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_self_unicode_found(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, text_('')) + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_self_tuple_found(self): + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ()) + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_relative_string_found(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, 'baz') + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_relative_tuple_found(self): + resource = DummyContext() + baz = DummyContext() + traverser = make_traverser({'context': baz, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ('baz',)) + self.assertEqual(resource.request.environ['PATH_INFO'], 'baz') + + def test_absolute_string_found(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, '/') + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_absolute_tuple_found(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ('',)) + self.assertEqual(root.wascontext, True) + self.assertEqual(root.request.environ['PATH_INFO'], '/') + + def test_empty_sequence(self): + root = DummyContext() + resource = DummyContext() + resource.__parent__ = root + resource.__name__ = 'baz' + traverser = make_traverser({'context': root, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, []) + self.assertEqual(resource.wascontext, True) + self.assertEqual(resource.request.environ['PATH_INFO'], '') + + def test_default_traverser(self): + resource = DummyContext() + result = self._callFUT(resource, '') + self.assertEqual(result['view_name'], '') + self.assertEqual(result['context'], resource) + + def test_requestfactory_overridden(self): + from pyramid.interfaces import IRequestFactory + from pyramid.request import Request + from pyramid.threadlocal import get_current_registry + + reg = get_current_registry() + + class MyRequest(Request): + pass + + reg.registerUtility(MyRequest, IRequestFactory) + resource = DummyContext() + traverser = make_traverser({'context': resource, 'view_name': ''}) + self._registerTraverser(traverser) + self._callFUT(resource, ['']) + self.assertEqual(resource.request.__class__, MyRequest) + + +class TestDefaultRootFactory(unittest.TestCase): + def _getTargetClass(self): + from pyramid.traversal import DefaultRootFactory + + return DefaultRootFactory + + def _makeOne(self, environ): + return self._getTargetClass()(environ) + + def test_it(self): + class DummyRequest(object): + pass + + root = self._makeOne(DummyRequest()) + self.assertEqual(root.__parent__, None) + self.assertEqual(root.__name__, None) + + +class Test__join_path_tuple(unittest.TestCase): + def _callFUT(self, tup): + from pyramid.traversal import _join_path_tuple + + return _join_path_tuple(tup) + + def test_empty_tuple(self): + # tests "or '/'" case + result = self._callFUT(()) + self.assertEqual(result, '/') + + def test_nonempty_tuple(self): + result = self._callFUT(('x',)) + self.assertEqual(result, 'x') + + def test_segments_with_unsafes(self): + safe_segments = tuple( + u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + u"-._~!$&'()*+,;=:@" + ) + result = self._callFUT(safe_segments) + self.assertEqual(result, u'/'.join(safe_segments)) + unsafe_segments = tuple( + chr(i) for i in range(0x20, 0x80) if not chr(i) in safe_segments + ) + (u'あ',) + result = self._callFUT(unsafe_segments) + self.assertEqual( + result, + u'/'.join( + ''.join( + '%%%02X' % (ord(c) if isinstance(c, str) else c) + for c in unsafe_segment.encode('utf-8') + ) + for unsafe_segment in unsafe_segments + ), + ) + + +def make_traverser(result): + class DummyTraverser(object): + def __init__(self, context): + self.context = context + context.wascontext = True + + def __call__(self, request): + self.context.request = request + return result + + return DummyTraverser + + +class DummyContext(object): + __parent__ = None + + def __init__(self, next=None, name=None): + self.next = next + self.__name__ = name + + def __getitem__(self, name): + if self.next is None: + raise KeyError(name) + return self.next + + def __repr__(self): + return '<DummyContext with name %s at id %s>' % ( + self.__name__, + id(self), + ) + + +class DummyRequest: + + application_url = ( + 'http://example.com:5432' + ) # app_url never ends with slash + matchdict = None + matched_route = None + + def __init__(self, environ=None, path_info=text_('/'), toraise=None): + if environ is None: + environ = {} + self.environ = environ + self._set_path_info(path_info) + self.toraise = toraise + + def _get_path_info(self): + if self.toraise: + raise self.toraise + return self._path_info + + def _set_path_info(self, v): + self._path_info = v + + path_info = property(_get_path_info, _set_path_info) + + +def _makeRequest(environ=None): + from pyramid.registry import Registry + + request = DummyRequest() + request.registry = Registry() + return request diff --git a/tests/test_tweens.py b/tests/test_tweens.py new file mode 100644 index 000000000..054f4360d --- /dev/null +++ b/tests/test_tweens.py @@ -0,0 +1,108 @@ +import unittest +from pyramid import testing + + +class Test_excview_tween_factory(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, handler, registry=None): + from pyramid.tweens import excview_tween_factory + + if registry is None: + registry = self.config.registry + return excview_tween_factory(handler, registry) + + def test_it_passthrough_no_exception(self): + dummy_response = DummyResponse() + + def handler(request): + return dummy_response + + tween = self._makeOne(handler) + request = DummyRequest() + result = tween(request) + self.assertTrue(result is dummy_response) + self.assertIsNone(request.exception) + self.assertIsNone(request.exc_info) + + def test_it_catches_notfound(self): + from pyramid.request import Request + from pyramid.httpexceptions import HTTPNotFound + + self.config.add_notfound_view(lambda exc, request: exc) + + def handler(request): + raise HTTPNotFound + + tween = self._makeOne(handler) + request = Request.blank('/') + request.registry = self.config.registry + result = tween(request) + self.assertEqual(result.status, '404 Not Found') + self.assertIsInstance(request.exception, HTTPNotFound) + self.assertEqual(request.exception, request.exc_info[1]) + + def test_it_catches_with_predicate(self): + from pyramid.request import Request + from pyramid.response import Response + + def excview(request): + return Response('foo') + + self.config.add_view(excview, context=ValueError, request_method='GET') + + def handler(request): + raise ValueError + + tween = self._makeOne(handler) + request = Request.blank('/') + request.registry = self.config.registry + result = tween(request) + self.assertTrue(b'foo' in result.body) + self.assertIsInstance(request.exception, ValueError) + self.assertEqual(request.exception, request.exc_info[1]) + + def test_it_reraises_on_mismatch(self): + from pyramid.request import Request + + def excview(request): # pragma: no cover + pass + + self.config.add_view(excview, context=ValueError, request_method='GET') + + def handler(request): + raise ValueError + + tween = self._makeOne(handler) + request = Request.blank('/') + request.registry = self.config.registry + request.method = 'POST' + self.assertRaises(ValueError, lambda: tween(request)) + self.assertIsNone(request.exception) + self.assertIsNone(request.exc_info) + + def test_it_reraises_on_no_match(self): + from pyramid.request import Request + + def handler(request): + raise ValueError + + tween = self._makeOne(handler) + request = Request.blank('/') + request.registry = self.config.registry + self.assertRaises(ValueError, lambda: tween(request)) + self.assertIsNone(request.exception) + self.assertIsNone(request.exc_info) + + +class DummyRequest: + exception = None + exc_info = None + + +class DummyResponse: + pass diff --git a/tests/test_url.py b/tests/test_url.py new file mode 100644 index 000000000..94a0a61c9 --- /dev/null +++ b/tests/test_url.py @@ -0,0 +1,1453 @@ +import os +import unittest + +from pyramid import testing + +from pyramid.compat import text_, WIN + + +class TestURLMethodsMixin(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, environ=None): + from pyramid.url import URLMethodsMixin + + if environ is None: + environ = {} + + class Request(URLMethodsMixin): + application_url = 'http://example.com:5432' + script_name = '' + + def __init__(self, environ): + self.environ = environ + + request = Request(environ) + request.registry = self.config.registry + return request + + def _registerResourceURL(self, reg): + from pyramid.interfaces import IResourceURL + from zope.interface import Interface + + class DummyResourceURL(object): + physical_path = '/context/' + virtual_path = '/context/' + + def __init__(self, context, request): + pass + + reg.registerAdapter( + DummyResourceURL, (Interface, Interface), IResourceURL + ) + return DummyResourceURL + + def test_resource_url_root_default(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_url(root) + self.assertEqual(result, 'http://example.com:5432/context/') + + def test_resource_url_extra_args(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'this/theotherthing', 'that') + self.assertEqual( + result, 'http://example.com:5432/context/this%2Ftheotherthing/that' + ) + + def test_resource_url_unicode_in_element_names(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + uc = text_(b'La Pe\xc3\xb1a', 'utf-8') + context = DummyContext() + result = request.resource_url(context, uc) + self.assertEqual( + result, 'http://example.com:5432/context/La%20Pe%C3%B1a' + ) + + def test_resource_url_at_sign_in_element_names(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, '@@myview') + self.assertEqual(result, 'http://example.com:5432/context/@@myview') + + def test_resource_url_element_names_url_quoted(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'a b c') + self.assertEqual(result, 'http://example.com:5432/context/a%20b%20c') + + def test_resource_url_with_query_str(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'a', query='(openlayers)') + self.assertEqual( + result, 'http://example.com:5432/context/a?(openlayers)' + ) + + def test_resource_url_with_query_dict(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + uc = text_(b'La Pe\xc3\xb1a', 'utf-8') + result = request.resource_url(context, 'a', query={'a': uc}) + self.assertEqual( + result, 'http://example.com:5432/context/a?a=La+Pe%C3%B1a' + ) + + def test_resource_url_with_query_seq(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + uc = text_(b'La Pe\xc3\xb1a', 'utf-8') + result = request.resource_url( + context, 'a', query=[('a', 'hi there'), ('b', uc)] + ) + self.assertEqual( + result, + 'http://example.com:5432/context/a?a=hi+there&b=La+Pe%C3%B1a', + ) + + def test_resource_url_with_query_empty(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'a', query=[]) + self.assertEqual(result, 'http://example.com:5432/context/a') + + def test_resource_url_with_query_None(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'a', query=None) + self.assertEqual(result, 'http://example.com:5432/context/a') + + def test_resource_url_anchor_is_after_root_when_no_elements(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, anchor='a') + self.assertEqual(result, 'http://example.com:5432/context/#a') + + def test_resource_url_anchor_is_after_elements_when_no_qs(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, 'a', anchor='b') + self.assertEqual(result, 'http://example.com:5432/context/a#b') + + def test_resource_url_anchor_is_after_qs_when_qs_is_present(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url( + context, 'a', query={'b': 'c'}, anchor='d' + ) + self.assertEqual(result, 'http://example.com:5432/context/a?b=c#d') + + def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + uc = text_(b'La Pe\xc3\xb1a', 'utf-8') + result = request.resource_url(context, anchor=uc) + self.assertEqual( + result, 'http://example.com:5432/context/#La%20Pe%C3%B1a' + ) + + def test_resource_url_anchor_is_urlencoded_safe(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, anchor=' /#?&+') + self.assertEqual(result, 'http://example.com:5432/context/#%20/%23?&+') + + def test_resource_url_anchor_is_None(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + context = DummyContext() + result = request.resource_url(context, anchor=None) + self.assertEqual(result, 'http://example.com:5432/context/') + + def test_resource_url_no_IResourceURL_registered(self): + # falls back to ResourceURL + root = DummyContext() + root.__name__ = '' + root.__parent__ = None + request = self._makeOne() + request.environ = {} + result = request.resource_url(root) + self.assertEqual(result, 'http://example.com:5432/') + + def test_resource_url_no_registry_on_request(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + del request.registry + root = DummyContext() + result = request.resource_url(root) + self.assertEqual(result, 'http://example.com:5432/context/') + + def test_resource_url_with_app_url(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_url(root, app_url='http://somewhere.com') + self.assertEqual(result, 'http://somewhere.com/context/') + + def test_resource_url_with_scheme(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_url(root, scheme='https') + self.assertEqual(result, 'https://example.com/context/') + + def test_resource_url_with_host(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_url(root, host='someotherhost.com') + self.assertEqual(result, 'http://someotherhost.com:8080/context/') + + def test_resource_url_with_port(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_url(root, port='8181') + self.assertEqual(result, 'http://example.com:8181/context/') + + def test_resource_url_with_local_url(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + self._registerResourceURL(request.registry) + root = DummyContext() + + def resource_url(req, info): + self.assertEqual(req, request) + self.assertEqual(info['virtual_path'], '/context/') + self.assertEqual(info['physical_path'], '/context/') + self.assertEqual(info['app_url'], 'http://example.com:5432') + return 'http://example.com/contextabc/' + + root.__resource_url__ = resource_url + result = request.resource_url(root) + self.assertEqual(result, 'http://example.com/contextabc/') + + def test_resource_url_with_route_name_no_remainder_on_adapter(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # no virtual_path_tuple on adapter + adapter.virtual_path = '/a/b/c/' + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url(root, route_name='foo') + self.assertEqual(result, 'http://example.com:5432/1/2/3') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_remainder_on_adapter(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url(root, route_name='foo') + self.assertEqual(result, 'http://example.com:5432/1/2/3') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_and_app_url(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url( + root, route_name='foo', app_url='app_url' + ) + self.assertEqual(result, 'app_url/1/2/3') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_and_scheme_host_port_etc(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url( + root, + route_name='foo', + scheme='scheme', + host='host', + port='port', + query={'a': '1'}, + anchor='anchor', + ) + self.assertEqual(result, 'scheme://host:port/1/2/3?a=1#anchor') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_and_route_kwargs(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url( + root, route_name='foo', route_kw={'a': '1', 'b': '2'} + ) + self.assertEqual(result, 'http://example.com:5432/1/2/3') + self.assertEqual( + route.kw, {'traverse': ('', 'a', 'b', 'c', ''), 'a': '1', 'b': '2'} + ) + + def test_resource_url_with_route_name_and_elements(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url(root, 'e1', 'e2', route_name='foo') + self.assertEqual(result, 'http://example.com:5432/1/2/3/e1/e2') + self.assertEqual(route.kw, {'traverse': ('', 'a', 'b', 'c', '')}) + + def test_resource_url_with_route_name_and_remainder_name(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '8080', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + adapter = self._registerResourceURL(request.registry) + # virtual_path_tuple on adapter + adapter.virtual_path_tuple = ('', 'a', 'b', 'c', '') + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route) + request.registry.registerUtility(mapper, IRoutesMapper) + root = DummyContext() + result = request.resource_url( + root, route_name='foo', route_remainder_name='fred' + ) + self.assertEqual(result, 'http://example.com:5432/1/2/3') + self.assertEqual(route.kw, {'fred': ('', 'a', 'b', 'c', '')}) + + def test_resource_path(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_path(root) + self.assertEqual(result, '/context/') + + def test_resource_path_kwarg(self): + request = self._makeOne() + self._registerResourceURL(request.registry) + root = DummyContext() + result = request.resource_path(root, anchor='abc') + self.assertEqual(result, '/context/#abc') + + def test_route_url_with_elements(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', 'extra1', 'extra2') + self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') + + def test_route_url_with_elements_path_endswith_slash(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3/')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', 'extra1', 'extra2') + self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') + + def test_route_url_no_elements(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url( + 'flub', a=1, b=2, c=3, _query={'a': 1}, _anchor=text_(b"foo") + ) + self.assertEqual(result, 'http://example.com:5432/1/2/3?a=1#foo') + + def test_route_url_with_query_None(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', a=1, b=2, c=3, _query=None) + self.assertEqual(result, 'http://example.com:5432/1/2/3') + + def test_route_url_with_anchor_binary(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _anchor=b"La Pe\xc3\xb1a") + + self.assertEqual( + result, 'http://example.com:5432/1/2/3#La%20Pe%C3%B1a' + ) + + def test_route_url_with_anchor_unicode(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + anchor = text_(b'La Pe\xc3\xb1a', 'utf-8') + result = request.route_url('flub', _anchor=anchor) + + self.assertEqual( + result, 'http://example.com:5432/1/2/3#La%20Pe%C3%B1a' + ) + + def test_route_url_with_anchor_None(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _anchor=None) + + self.assertEqual(result, 'http://example.com:5432/1/2/3') + + def test_route_url_with_query(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _query={'q': '1'}) + self.assertEqual(result, 'http://example.com:5432/1/2/3?q=1') + + def test_route_url_with_query_str(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _query='(openlayers)') + self.assertEqual(result, 'http://example.com:5432/1/2/3?(openlayers)') + + def test_route_url_with_empty_query(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _query={}) + self.assertEqual(result, 'http://example.com:5432/1/2/3') + + def test_route_url_with_app_url(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _app_url='http://example2.com') + self.assertEqual(result, 'http://example2.com/1/2/3') + + def test_route_url_with_host(self): + from pyramid.interfaces import IRoutesMapper + + environ = {'wsgi.url_scheme': 'http', 'SERVER_PORT': '5432'} + request = self._makeOne(environ) + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _host='someotherhost.com') + self.assertEqual(result, 'http://someotherhost.com:5432/1/2/3') + + def test_route_url_with_port(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '5432', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _port='8080') + self.assertEqual(result, 'http://example.com:8080/1/2/3') + + def test_route_url_with_scheme(self): + from pyramid.interfaces import IRoutesMapper + + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_PORT': '5432', + 'SERVER_NAME': 'example.com', + } + request = self._makeOne(environ) + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _scheme='https') + self.assertEqual(result, 'https://example.com/1/2/3') + + def test_route_url_generation_error(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(raise_exc=KeyError) + request.registry.registerUtility(mapper, IRoutesMapper) + mapper.raise_exc = KeyError + self.assertRaises(KeyError, request.route_url, 'flub', request, a=1) + + def test_route_url_generate_doesnt_receive_query_or_anchor(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + route = DummyRoute(result='') + mapper = DummyRoutesMapper(route=route) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _query=dict(name='some_name')) + self.assertEqual(route.kw, {}) # shouldnt have anchor/query + self.assertEqual(result, 'http://example.com:5432?name=some_name') + + def test_route_url_with_pregenerator(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + route = DummyRoute(result='/1/2/3') + + def pregenerator(request, elements, kw): + return ('a',), {'_app_url': 'http://example2.com'} + + route.pregenerator = pregenerator + mapper = DummyRoutesMapper(route=route) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub') + self.assertEqual(result, 'http://example2.com/1/2/3/a') + self.assertEqual(route.kw, {}) # shouldnt have anchor/query + + def test_route_url_with_anchor_app_url_elements_and_query(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute(result='/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url( + 'flub', + 'element1', + _app_url='http://example2.com', + _anchor='anchor', + _query={'q': '1'}, + ) + self.assertEqual( + result, 'http://example2.com/1/2/3/element1?q=1#anchor' + ) + + def test_route_url_integration_with_real_request(self): + # to try to replicate https://github.com/Pylons/pyramid/issues/213 + from pyramid.interfaces import IRoutesMapper + from pyramid.request import Request + + request = Request.blank('/') + request.registry = self.config.registry + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', 'extra1', 'extra2') + self.assertEqual(result, 'http://localhost/1/2/3/extra1/extra2') + + def test_current_route_url_current_request_has_no_route(self): + request = self._makeOne() + self.assertRaises(ValueError, request.current_route_url) + + def test_current_route_url_with_elements_query_and_anchor(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_url( + 'extra1', 'extra2', _query={'a': 1}, _anchor=text_(b"foo") + ) + self.assertEqual( + result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo' + ) + + def test_current_route_url_with_route_name(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_url( + 'extra1', + 'extra2', + _query={'a': 1}, + _anchor=text_(b"foo"), + _route_name='bar', + ) + self.assertEqual( + result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo' + ) + + def test_current_route_url_with_request_query(self): + from pyramid.interfaces import IRoutesMapper + from webob.multidict import GetDict + + request = self._makeOne() + request.GET = GetDict([('q', '123')], {}) + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_url() + self.assertEqual(result, 'http://example.com:5432/1/2/3?q=123') + + def test_current_route_url_with_request_query_duplicate_entries(self): + from pyramid.interfaces import IRoutesMapper + from webob.multidict import GetDict + + request = self._makeOne() + request.GET = GetDict( + [('q', '123'), ('b', '2'), ('b', '2'), ('q', '456')], {} + ) + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_url() + self.assertEqual( + result, 'http://example.com:5432/1/2/3?q=123&b=2&b=2&q=456' + ) + + def test_current_route_url_with_query_override(self): + from pyramid.interfaces import IRoutesMapper + from webob.multidict import GetDict + + request = self._makeOne() + request.GET = GetDict([('q', '123')], {}) + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_url(_query={'a': 1}) + self.assertEqual(result, 'http://example.com:5432/1/2/3?a=1') + + def test_current_route_path(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.script_name = '/script_name' + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.current_route_path( + 'extra1', 'extra2', _query={'a': 1}, _anchor=text_(b"foo") + ) + self.assertEqual(result, '/script_name/1/2/3/extra1/extra2?a=1#foo') + + def test_route_path_with_elements(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + request.script_name = '' + result = request.route_path( + 'flub', + 'extra1', + 'extra2', + a=1, + b=2, + c=3, + _query={'a': 1}, + _anchor=text_(b"foo"), + ) + self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo') + + def test_route_path_with_script_name(self): + from pyramid.interfaces import IRoutesMapper + + request = self._makeOne() + request.script_name = '/foo' + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_path( + 'flub', + 'extra1', + 'extra2', + a=1, + b=2, + c=3, + _query={'a': 1}, + _anchor=text_(b"foo"), + ) + self.assertEqual(result, '/foo/1/2/3/extra1/extra2?a=1#foo') + + def test_static_url_staticurlinfo_notfound(self): + request = self._makeOne() + self.assertRaises(ValueError, request.static_url, 'static/foo.css') + + def test_static_url_abspath(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + info = DummyStaticURLInfo('abc') + registry = request.registry + registry.registerUtility(info, IStaticURLInfo) + abspath = makeabs('static', 'foo.css') + result = request.static_url(abspath) + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, (makeabs('static', 'foo.css'), request, {}) + ) + request = self._makeOne() + + def test_static_url_found_rel(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = request.static_url('static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual(info.args, ('tests:static/foo.css', request, {})) + + def test_static_url_abs(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = request.static_url('tests:static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual(info.args, ('tests:static/foo.css', request, {})) + + def test_static_url_found_abs_no_registry_on_request(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + registry = request.registry + info = DummyStaticURLInfo('abc') + registry.registerUtility(info, IStaticURLInfo) + del request.registry + result = request.static_url('tests:static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual(info.args, ('tests:static/foo.css', request, {})) + + def test_static_url_abspath_integration_with_staticurlinfo(self): + from pyramid.interfaces import IStaticURLInfo + from pyramid.config.views import StaticURLInfo + + info = StaticURLInfo() + here = os.path.abspath(os.path.dirname(__file__)) + info.add(self.config, 'absstatic', here) + request = self._makeOne() + registry = request.registry + registry.registerUtility(info, IStaticURLInfo) + abspath = os.path.join(here, 'test_url.py') + result = request.static_url(abspath) + self.assertEqual( + result, 'http://example.com:5432/absstatic/test_url.py' + ) + + def test_static_url_noscheme_uses_scheme_from_request(self): + from pyramid.interfaces import IStaticURLInfo + from pyramid.config.views import StaticURLInfo + + info = StaticURLInfo() + here = os.path.abspath(os.path.dirname(__file__)) + info.add(self.config, '//subdomain.example.com/static', here) + request = self._makeOne({'wsgi.url_scheme': 'https'}) + registry = request.registry + registry.registerUtility(info, IStaticURLInfo) + abspath = os.path.join(here, 'test_url.py') + result = request.static_url(abspath) + self.assertEqual( + result, 'https://subdomain.example.com/static/test_url.py' + ) + + def test_static_path_abspath(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + request.script_name = '/foo' + info = DummyStaticURLInfo('abc') + registry = request.registry + registry.registerUtility(info, IStaticURLInfo) + abspath = makeabs('static', 'foo.css') + result = request.static_path(abspath) + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, + (makeabs('static', 'foo.css'), request, {'_app_url': '/foo'}), + ) + + def test_static_path_found_rel(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + request.script_name = '/foo' + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = request.static_path('static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, ('tests:static/foo.css', request, {'_app_url': '/foo'}) + ) + + def test_static_path_abs(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + request.script_name = '/foo' + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = request.static_path('tests:static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, ('tests:static/foo.css', request, {'_app_url': '/foo'}) + ) + + def test_static_path(self): + from pyramid.interfaces import IStaticURLInfo + + request = self._makeOne() + request.script_name = '/foo' + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = request.static_path('static/foo.css') + self.assertEqual(result, 'abc') + self.assertEqual( + info.args, ('tests:static/foo.css', request, {'_app_url': '/foo'}) + ) + + def test_partial_application_url_with_http_host_default_port_http(self): + environ = {'wsgi.url_scheme': 'http', 'HTTP_HOST': 'example.com:80'} + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'http://example.com') + + def test_partial_application_url_with_http_host_default_port_https(self): + environ = {'wsgi.url_scheme': 'https', 'HTTP_HOST': 'example.com:443'} + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'https://example.com') + + def test_partial_application_url_with_http_host_nondefault_port_http(self): + environ = {'wsgi.url_scheme': 'http', 'HTTP_HOST': 'example.com:8080'} + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'http://example.com:8080') + + def test_partial_application_url_with_http_host_nondefault_port_https( + self + ): + environ = {'wsgi.url_scheme': 'https', 'HTTP_HOST': 'example.com:4443'} + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'https://example.com:4443') + + def test_partial_application_url_with_http_host_no_colon(self): + environ = { + 'wsgi.url_scheme': 'http', + 'HTTP_HOST': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'http://example.com') + + def test_partial_application_url_no_http_host(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url() + self.assertEqual(result, 'http://example.com') + + def test_partial_application_replace_port(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url(port=8080) + self.assertEqual(result, 'http://example.com:8080') + + def test_partial_application_replace_scheme_https_special_case(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url(scheme='https') + self.assertEqual(result, 'https://example.com') + + def test_partial_application_replace_scheme_https_special_case_avoid(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url(scheme='https', port='8080') + self.assertEqual(result, 'https://example.com:8080') + + def test_partial_application_replace_scheme_http_special_case(self): + environ = { + 'wsgi.url_scheme': 'https', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '8080', + } + request = self._makeOne(environ) + result = request._partial_application_url(scheme='http') + self.assertEqual(result, 'http://example.com') + + def test_partial_application_replace_scheme_http_special_case_avoid(self): + environ = { + 'wsgi.url_scheme': 'https', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '8000', + } + request = self._makeOne(environ) + result = request._partial_application_url(scheme='http', port='8080') + self.assertEqual(result, 'http://example.com:8080') + + def test_partial_application_replace_host_no_port(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url(host='someotherhost.com') + self.assertEqual(result, 'http://someotherhost.com') + + def test_partial_application_replace_host_with_port(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '8000', + } + request = self._makeOne(environ) + result = request._partial_application_url( + host='someotherhost.com:8080' + ) + self.assertEqual(result, 'http://someotherhost.com:8080') + + def test_partial_application_replace_host_and_port(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url( + host='someotherhost.com:8080', port='8000' + ) + self.assertEqual(result, 'http://someotherhost.com:8000') + + def test_partial_application_replace_host_port_and_scheme(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '80', + } + request = self._makeOne(environ) + result = request._partial_application_url( + host='someotherhost.com:8080', port='8000', scheme='https' + ) + self.assertEqual(result, 'https://someotherhost.com:8000') + + def test_partial_application_url_with_custom_script_name(self): + environ = { + 'wsgi.url_scheme': 'http', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '8000', + } + request = self._makeOne(environ) + request.script_name = '/abc' + result = request._partial_application_url() + self.assertEqual(result, 'http://example.com:8000/abc') + + +class Test_route_url(unittest.TestCase): + def _callFUT(self, route_name, request, *elements, **kw): + from pyramid.url import route_url + + return route_url(route_name, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def route_url(self, route_name, *elements, **kw): + self.route_name = route_name + self.elements = elements + self.kw = kw + return 'route url' + + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'route url') + self.assertEqual(request.route_name, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_route_path(unittest.TestCase): + def _callFUT(self, route_name, request, *elements, **kw): + from pyramid.url import route_path + + return route_path(route_name, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def route_path(self, route_name, *elements, **kw): + self.route_name = route_name + self.elements = elements + self.kw = kw + return 'route path' + + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'route path') + self.assertEqual(request.route_name, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_resource_url(unittest.TestCase): + def _callFUT(self, resource, request, *elements, **kw): + from pyramid.url import resource_url + + return resource_url(resource, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def resource_url(self, resource, *elements, **kw): + self.resource = resource + self.elements = elements + self.kw = kw + return 'resource url' + + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'resource url') + self.assertEqual(request.resource, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_static_url(unittest.TestCase): + def _callFUT(self, path, request, **kw): + from pyramid.url import static_url + + return static_url(path, request, **kw) + + def _makeRequest(self): + class Request(object): + def static_url(self, path, **kw): + self.path = path + self.kw = kw + return 'static url' + + return Request() + + def test_it_abs(self): + request = self._makeRequest() + result = self._callFUT('/foo/bar/abc', request, _app_url='') + self.assertEqual(result, 'static url') + self.assertEqual(request.path, '/foo/bar/abc') + self.assertEqual(request.kw, {'_app_url': ''}) + + def test_it_absspec(self): + request = self._makeRequest() + result = self._callFUT('foo:abc', request, _anchor='anchor') + self.assertEqual(result, 'static url') + self.assertEqual(request.path, 'foo:abc') + self.assertEqual(request.kw, {'_anchor': 'anchor'}) + + def test_it_rel(self): + request = self._makeRequest() + result = self._callFUT('abc', request, _app_url='') + self.assertEqual(result, 'static url') + self.assertEqual(request.path, 'tests:abc') + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_static_path(unittest.TestCase): + def _callFUT(self, path, request, **kw): + from pyramid.url import static_path + + return static_path(path, request, **kw) + + def _makeRequest(self): + class Request(object): + def static_path(self, path, **kw): + self.path = path + self.kw = kw + return 'static path' + + return Request() + + def test_it_abs(self): + request = self._makeRequest() + result = self._callFUT('/foo/bar/abc', request, _anchor='anchor') + self.assertEqual(result, 'static path') + self.assertEqual(request.path, '/foo/bar/abc') + self.assertEqual(request.kw, {'_anchor': 'anchor'}) + + def test_it_absspec(self): + request = self._makeRequest() + result = self._callFUT('foo:abc', request, _anchor='anchor') + self.assertEqual(result, 'static path') + self.assertEqual(request.path, 'foo:abc') + self.assertEqual(request.kw, {'_anchor': 'anchor'}) + + def test_it_rel(self): + request = self._makeRequest() + result = self._callFUT('abc', request, _app_url='') + self.assertEqual(result, 'static path') + self.assertEqual(request.path, 'tests:abc') + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_current_route_url(unittest.TestCase): + def _callFUT(self, request, *elements, **kw): + from pyramid.url import current_route_url + + return current_route_url(request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def current_route_url(self, *elements, **kw): + self.elements = elements + self.kw = kw + return 'current route url' + + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT(request, 'abc', _app_url='') + self.assertEqual(result, 'current route url') + self.assertEqual(request.elements, ('abc',)) + self.assertEqual(request.kw, {'_app_url': ''}) + + +class Test_current_route_path(unittest.TestCase): + def _callFUT(self, request, *elements, **kw): + from pyramid.url import current_route_path + + return current_route_path(request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def current_route_path(self, *elements, **kw): + self.elements = elements + self.kw = kw + return 'current route path' + + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT(request, 'abc', _anchor='abc') + self.assertEqual(result, 'current route path') + self.assertEqual(request.elements, ('abc',)) + self.assertEqual(request.kw, {'_anchor': 'abc'}) + + +class Test_external_static_url_integration(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeRequest(self): + from pyramid.request import Request + + return Request.blank('/') + + def test_generate_external_url(self): + self.config.add_route('acme', 'https://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual( + request.route_url('acme', foo='bar'), 'https://acme.org/path/bar' + ) + + def test_generate_external_url_without_scheme(self): + self.config.add_route('acme', '//acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual( + request.route_url('acme', foo='bar'), 'http://acme.org/path/bar' + ) + + def test_generate_external_url_with_explicit_scheme(self): + self.config.add_route('acme', '//acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual( + request.route_url('acme', foo='bar', _scheme='https'), + 'https://acme.org/path/bar', + ) + + def test_generate_external_url_with_explicit_app_url(self): + self.config.add_route('acme', 'http://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertRaises( + ValueError, + request.route_url, + 'acme', + foo='bar', + _app_url='http://fakeme.com', + ) + + def test_generate_external_url_route_path(self): + self.config.add_route('acme', 'https://acme.org/path/{foo}') + request = self._makeRequest() + request.registry = self.config.registry + self.assertRaises(ValueError, request.route_path, 'acme', foo='bar') + + def test_generate_external_url_with_pregenerator(self): + def pregenerator(request, elements, kw): + kw['_query'] = {'q': 'foo'} + return elements, kw + + self.config.add_route( + 'acme', 'https://acme.org/path/{foo}', pregenerator=pregenerator + ) + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual( + request.route_url('acme', foo='bar'), + 'https://acme.org/path/bar?q=foo', + ) + + def test_external_url_with_route_prefix(self): + def includeme(config): + config.add_route('acme', '//acme.org/{foo}') + + self.config.include(includeme, route_prefix='some_prefix') + request = self._makeRequest() + request.registry = self.config.registry + self.assertEqual( + request.route_url('acme', foo='bar'), 'http://acme.org/bar' + ) + + +class Test_with_route_prefix(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeRequest(self, route): + from pyramid.request import Request + + return Request.blank(route) + + def test_old_route_is_preserved(self): + self.config.route_prefix = 'old_prefix' + with self.config.route_prefix_context('new_addon'): + assert 'new_addon' in self.config.route_prefix + + assert 'old_prefix' == self.config.route_prefix + + def test_route_prefix_none(self): + self.config.route_prefix = 'old_prefix' + with self.config.route_prefix_context(None): + assert 'old_prefix' == self.config.route_prefix + + assert 'old_prefix' == self.config.route_prefix + + def test_route_prefix_empty(self): + self.config.route_prefix = 'old_prefix' + with self.config.route_prefix_context(''): + assert 'old_prefix' == self.config.route_prefix + + assert 'old_prefix' == self.config.route_prefix + + def test_route_has_prefix(self): + with self.config.route_prefix_context('bar'): + self.config.add_route('acme', '/foo') + request = self._makeRequest('/') + self.assertEqual(request.route_url('acme'), 'http://localhost/bar/foo') + + def test_route_does_not_have_prefix(self): + with self.config.route_prefix_context('bar'): + pass + + self.config.add_route('acme', '/foo') + request = self._makeRequest('/') + self.assertEqual(request.route_url('acme'), 'http://localhost/foo') + + def test_error_reset_prefix(self): + self.config.route_prefix = 'old_prefix' + + try: + with self.config.route_prefix_context('new_prefix'): + raise RuntimeError + except RuntimeError: + pass + + assert self.config.route_prefix == 'old_prefix' + + +class DummyContext(object): + def __init__(self, next=None): + self.next = next + + +class DummyRoutesMapper: + raise_exc = None + + def __init__(self, route=None, raise_exc=False): + self.route = route + + def get_route(self, route_name): + return self.route + + +class DummyRoute: + pregenerator = None + name = 'route' + + def __init__(self, result='/1/2/3'): + self.result = result + + def generate(self, kw): + self.kw = kw + return self.result + + +class DummyStaticURLInfo: + def __init__(self, result): + self.result = result + + def generate(self, path, request, **kw): + self.args = path, request, kw + return self.result + + +def makeabs(*elements): + if WIN: # pragma: no cover + return r'c:\\' + os.path.sep.join(elements) + else: + return os.path.sep + os.path.sep.join(elements) diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py new file mode 100644 index 000000000..772250e89 --- /dev/null +++ b/tests/test_urldispatch.py @@ -0,0 +1,655 @@ +import unittest +from pyramid import testing +from pyramid.compat import text_, PY2 + + +class TestRoute(unittest.TestCase): + def _getTargetClass(self): + from pyramid.urldispatch import Route + + return Route + + def _makeOne(self, *arg): + return self._getTargetClass()(*arg) + + def test_provides_IRoute(self): + from pyramid.interfaces import IRoute + from zope.interface.verify import verifyObject + + verifyObject(IRoute, self._makeOne('name', 'pattern')) + + def test_ctor(self): + import types + + route = self._makeOne('name', ':path', 'factory') + self.assertEqual(route.pattern, ':path') + self.assertEqual(route.path, ':path') + self.assertEqual(route.name, 'name') + self.assertEqual(route.factory, 'factory') + self.assertIsInstance(route.generate, types.FunctionType) + self.assertIsInstance(route.match, types.FunctionType) + + def test_ctor_defaults(self): + import types + + route = self._makeOne('name', ':path') + self.assertEqual(route.pattern, ':path') + self.assertEqual(route.path, ':path') + self.assertEqual(route.name, 'name') + self.assertEqual(route.factory, None) + self.assertIsInstance(route.generate, types.FunctionType) + self.assertIsInstance(route.match, types.FunctionType) + + def test_match(self): + route = self._makeOne('name', ':path') + self.assertEqual(route.match('/whatever'), {'path': 'whatever'}) + + def test_generate(self): + route = self._makeOne('name', ':path') + self.assertEqual(route.generate({'path': 'abc'}), '/abc') + + +class RoutesMapperTests(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getRequest(self, **kw): + from pyramid.threadlocal import get_current_registry + + environ = {'SERVER_NAME': 'localhost', 'wsgi.url_scheme': 'http'} + environ.update(kw) + request = DummyRequest(environ) + reg = get_current_registry() + request.registry = reg + return request + + def _getTargetClass(self): + from pyramid.urldispatch import RoutesMapper + + return RoutesMapper + + def _makeOne(self): + klass = self._getTargetClass() + return klass() + + def test_provides_IRoutesMapper(self): + from pyramid.interfaces import IRoutesMapper + from zope.interface.verify import verifyObject + + verifyObject(IRoutesMapper, self._makeOne()) + + def test_no_route_matches(self): + mapper = self._makeOne() + request = self._getRequest(PATH_INFO='/') + result = mapper(request) + self.assertEqual(result['match'], None) + self.assertEqual(result['route'], None) + + def test_connect_name_exists_removes_old(self): + mapper = self._makeOne() + mapper.connect('foo', 'archives/:action/:article') + mapper.connect('foo', 'archives/:action/:article2') + self.assertEqual(len(mapper.routelist), 1) + self.assertEqual(len(mapper.routes), 1) + self.assertEqual( + mapper.routes['foo'].pattern, 'archives/:action/:article2' + ) + self.assertEqual( + mapper.routelist[0].pattern, 'archives/:action/:article2' + ) + + def test_connect_static(self): + mapper = self._makeOne() + mapper.connect('foo', 'archives/:action/:article', static=True) + self.assertEqual(len(mapper.routelist), 0) + self.assertEqual(len(mapper.routes), 1) + self.assertEqual( + mapper.routes['foo'].pattern, 'archives/:action/:article' + ) + + def test_connect_static_overridden(self): + mapper = self._makeOne() + mapper.connect('foo', 'archives/:action/:article', static=True) + self.assertEqual(len(mapper.routelist), 0) + self.assertEqual(len(mapper.routes), 1) + self.assertEqual( + mapper.routes['foo'].pattern, 'archives/:action/:article' + ) + mapper.connect('foo', 'archives/:action/:article2') + self.assertEqual(len(mapper.routelist), 1) + self.assertEqual(len(mapper.routes), 1) + self.assertEqual( + mapper.routes['foo'].pattern, 'archives/:action/:article2' + ) + self.assertEqual( + mapper.routelist[0].pattern, 'archives/:action/:article2' + ) + + def test___call__pathinfo_cant_be_decoded(self): + from pyramid.exceptions import URLDecodeError + + mapper = self._makeOne() + if PY2: + path_info = b'\xff\xfe\xe6\x00' + else: + path_info = b'\xff\xfe\xe6\x00'.decode('latin-1') + request = self._getRequest(PATH_INFO=path_info) + self.assertRaises(URLDecodeError, mapper, request) + + def test___call__route_matches(self): + mapper = self._makeOne() + mapper.connect('foo', 'archives/:action/:article') + request = self._getRequest(PATH_INFO='/archives/action1/article1') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['foo']) + self.assertEqual(result['match']['action'], 'action1') + self.assertEqual(result['match']['article'], 'article1') + + def test___call__route_matches_with_predicates(self): + mapper = self._makeOne() + mapper.connect( + 'foo', 'archives/:action/:article', predicates=[lambda *arg: True] + ) + request = self._getRequest(PATH_INFO='/archives/action1/article1') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['foo']) + self.assertEqual(result['match']['action'], 'action1') + self.assertEqual(result['match']['article'], 'article1') + + def test___call__route_fails_to_match_with_predicates(self): + mapper = self._makeOne() + mapper.connect( + 'foo', + 'archives/:action/article1', + predicates=[lambda *arg: True, lambda *arg: False], + ) + mapper.connect('bar', 'archives/:action/:article') + request = self._getRequest(PATH_INFO='/archives/action1/article1') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['bar']) + self.assertEqual(result['match']['action'], 'action1') + self.assertEqual(result['match']['article'], 'article1') + + def test___call__custom_predicate_gets_info(self): + mapper = self._makeOne() + + def pred(info, request): + self.assertEqual(info['match'], {'action': 'action1'}) + self.assertEqual(info['route'], mapper.routes['foo']) + return True + + mapper.connect('foo', 'archives/:action/article1', predicates=[pred]) + request = self._getRequest(PATH_INFO='/archives/action1/article1') + mapper(request) + + def test_cc_bug(self): + # "unordered" as reported in IRC by author of + # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ + mapper = self._makeOne() + mapper.connect('rdf', 'licenses/:license_code/:license_version/rdf') + mapper.connect( + 'juri', 'licenses/:license_code/:license_version/:jurisdiction' + ) + + request = self._getRequest(PATH_INFO='/licenses/1/v2/rdf') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['rdf']) + self.assertEqual(result['match']['license_code'], '1') + self.assertEqual(result['match']['license_version'], 'v2') + + request = self._getRequest(PATH_INFO='/licenses/1/v2/usa') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['juri']) + self.assertEqual(result['match']['license_code'], '1') + self.assertEqual(result['match']['license_version'], 'v2') + self.assertEqual(result['match']['jurisdiction'], 'usa') + + def test___call__root_route_matches(self): + mapper = self._makeOne() + mapper.connect('root', '') + request = self._getRequest(PATH_INFO='/') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['root']) + self.assertEqual(result['match'], {}) + + def test___call__root_route_matches2(self): + mapper = self._makeOne() + mapper.connect('root', '/') + request = self._getRequest(PATH_INFO='/') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['root']) + self.assertEqual(result['match'], {}) + + def test___call__root_route_when_path_info_empty(self): + mapper = self._makeOne() + mapper.connect('root', '/') + request = self._getRequest(PATH_INFO='') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['root']) + self.assertEqual(result['match'], {}) + + def test___call__root_route_when_path_info_notempty(self): + mapper = self._makeOne() + mapper.connect('root', '/') + request = self._getRequest(PATH_INFO='/') + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['root']) + self.assertEqual(result['match'], {}) + + def test___call__no_path_info(self): + mapper = self._makeOne() + mapper.connect('root', '/') + request = self._getRequest() + result = mapper(request) + self.assertEqual(result['route'], mapper.routes['root']) + self.assertEqual(result['match'], {}) + + def test_has_routes(self): + mapper = self._makeOne() + self.assertEqual(mapper.has_routes(), False) + mapper.connect('whatever', 'archives/:action/:article') + self.assertEqual(mapper.has_routes(), True) + + def test_get_routes(self): + from pyramid.urldispatch import Route + + mapper = self._makeOne() + self.assertEqual(mapper.get_routes(), []) + mapper.connect('whatever', 'archives/:action/:article') + routes = mapper.get_routes() + self.assertEqual(len(routes), 1) + self.assertEqual(routes[0].__class__, Route) + + def test_get_route_matches(self): + mapper = self._makeOne() + mapper.connect('whatever', 'archives/:action/:article') + result = mapper.get_route('whatever') + self.assertEqual(result.pattern, 'archives/:action/:article') + + def test_get_route_misses(self): + mapper = self._makeOne() + result = mapper.get_route('whatever') + self.assertEqual(result, None) + + def test_generate(self): + mapper = self._makeOne() + + def generator(kw): + return 123 + + route = DummyRoute(generator) + mapper.routes['abc'] = route + self.assertEqual(mapper.generate('abc', {}), 123) + + +class TestCompileRoute(unittest.TestCase): + def _callFUT(self, pattern): + from pyramid.urldispatch import _compile_route + + return _compile_route(pattern) + + def test_no_star(self): + matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') + self.assertEqual( + matcher('/foo/baz/biz/buz/bar'), {'baz': 'baz', 'buz': 'buz'} + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual(generator({'baz': 1, 'buz': 2}), '/foo/1/biz/2/bar') + + def test_with_star(self): + matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar*traverse') + self.assertEqual( + matcher('/foo/baz/biz/buz/bar'), + {'baz': 'baz', 'buz': 'buz', 'traverse': ()}, + ) + self.assertEqual( + matcher('/foo/baz/biz/buz/bar/everything/else/here'), + { + 'baz': 'baz', + 'buz': 'buz', + 'traverse': ('everything', 'else', 'here'), + }, + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual( + generator({'baz': 1, 'buz': 2, 'traverse': '/a/b'}), + '/foo/1/biz/2/bar/a/b', + ) + + def test_with_bracket_star(self): + matcher, generator = self._callFUT( + '/foo/{baz}/biz/{buz}/bar{remainder:.*}' + ) + self.assertEqual( + matcher('/foo/baz/biz/buz/bar'), + {'baz': 'baz', 'buz': 'buz', 'remainder': ''}, + ) + self.assertEqual( + matcher('/foo/baz/biz/buz/bar/everything/else/here'), + {'baz': 'baz', 'buz': 'buz', 'remainder': '/everything/else/here'}, + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual( + generator({'baz': 1, 'buz': 2, 'remainder': '/a/b'}), + '/foo/1/biz/2/bar/a/b', + ) + + def test_no_beginning_slash(self): + matcher, generator = self._callFUT('foo/:baz/biz/:buz/bar') + self.assertEqual( + matcher('/foo/baz/biz/buz/bar'), {'baz': 'baz', 'buz': 'buz'} + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual(generator({'baz': 1, 'buz': 2}), '/foo/1/biz/2/bar') + + def test_custom_regex(self): + matcher, generator = self._callFUT( + 'foo/{baz}/biz/{buz:[^/\\.]+}.{bar}' + ) + self.assertEqual( + matcher('/foo/baz/biz/buz.bar'), + {'baz': 'baz', 'buz': 'buz', 'bar': 'bar'}, + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual( + generator({'baz': 1, 'buz': 2, 'bar': 'html'}), '/foo/1/biz/2.html' + ) + + def test_custom_regex_with_colons(self): + matcher, generator = self._callFUT( + 'foo/{baz}/biz/{buz:(?:[^/\\.]+)}.{bar}' + ) + self.assertEqual( + matcher('/foo/baz/biz/buz.bar'), + {'baz': 'baz', 'buz': 'buz', 'bar': 'bar'}, + ) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual( + generator({'baz': 1, 'buz': 2, 'bar': 'html'}), '/foo/1/biz/2.html' + ) + + def test_mixed_newstyle_oldstyle_pattern_defaults_to_newstyle(self): + # pattern: '\\/foo\\/(?P<baz>abc)\\/biz\\/(?P<buz>[^/]+)\\/bar$' + # note presence of :abc in pattern (oldstyle match) + matcher, generator = self._callFUT('foo/{baz:abc}/biz/{buz}/bar') + self.assertEqual( + matcher('/foo/abc/biz/buz/bar'), {'baz': 'abc', 'buz': 'buz'} + ) + self.assertEqual(generator({'baz': 1, 'buz': 2}), '/foo/1/biz/2/bar') + + def test_custom_regex_with_embedded_squigglies(self): + matcher, generator = self._callFUT('/{buz:\\d{4}}') + self.assertEqual(matcher('/2001'), {'buz': '2001'}) + self.assertEqual(matcher('/200'), None) + self.assertEqual(generator({'buz': 2001}), '/2001') + + def test_custom_regex_with_embedded_squigglies2(self): + matcher, generator = self._callFUT('/{buz:\\d{3,4}}') + self.assertEqual(matcher('/2001'), {'buz': '2001'}) + self.assertEqual(matcher('/200'), {'buz': '200'}) + self.assertEqual(matcher('/20'), None) + self.assertEqual(generator({'buz': 2001}), '/2001') + + def test_custom_regex_with_embedded_squigglies3(self): + matcher, generator = self._callFUT( + '/{buz:(\\d{2}|\\d{4})-[a-zA-Z]{3,4}-\\d{2}}' + ) + self.assertEqual(matcher('/2001-Nov-15'), {'buz': '2001-Nov-15'}) + self.assertEqual(matcher('/99-June-10'), {'buz': '99-June-10'}) + self.assertEqual(matcher('/2-Nov-15'), None) + self.assertEqual(matcher('/200-Nov-15'), None) + self.assertEqual(matcher('/2001-No-15'), None) + self.assertEqual(generator({'buz': '2001-Nov-15'}), '/2001-Nov-15') + self.assertEqual(generator({'buz': '99-June-10'}), '/99-June-10') + + def test_pattern_with_high_order_literal(self): + pattern = text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8') + matcher, generator = self._callFUT(pattern) + self.assertEqual( + matcher(text_(b'/La Pe\xc3\xb1a/x', 'utf-8')), {'x': 'x'} + ) + self.assertEqual(generator({'x': '1'}), '/La%20Pe%C3%B1a/1') + + def test_pattern_generate_with_high_order_dynamic(self): + pattern = '/{x}' + _, generator = self._callFUT(pattern) + self.assertEqual( + generator({'x': text_(b'La Pe\xc3\xb1a', 'utf-8')}), + '/La%20Pe%C3%B1a', + ) + + def test_docs_sample_generate(self): + # sample from urldispatch.rst + pattern = text_(b'/La Pe\xc3\xb1a/{city}', 'utf-8') + _, generator = self._callFUT(pattern) + self.assertEqual( + generator({'city': text_(b'Qu\xc3\xa9bec', 'utf-8')}), + '/La%20Pe%C3%B1a/Qu%C3%A9bec', + ) + + def test_generate_with_mixedtype_values(self): + pattern = '/{city}/{state}' + _, generator = self._callFUT(pattern) + result = generator( + { + 'city': text_(b'Qu\xc3\xa9bec', 'utf-8'), + 'state': b'La Pe\xc3\xb1a', + } + ) + self.assertEqual(result, '/Qu%C3%A9bec/La%20Pe%C3%B1a') + # should be a native string + self.assertEqual(type(result), str) + + def test_highorder_pattern_utf8(self): + pattern = b'/La Pe\xc3\xb1a/{city}' + self.assertRaises(ValueError, self._callFUT, pattern) + + def test_generate_with_string_remainder_and_unicode_replacement(self): + pattern = text_(b'/abc*remainder', 'utf-8') + _, generator = self._callFUT(pattern) + result = generator( + {'remainder': text_(b'/Qu\xc3\xa9bec/La Pe\xc3\xb1a', 'utf-8')} + ) + self.assertEqual(result, '/abc/Qu%C3%A9bec/La%20Pe%C3%B1a') + # should be a native string + self.assertEqual(type(result), str) + + def test_generate_with_string_remainder_and_nonstring_replacement(self): + pattern = text_(b'/abc/*remainder', 'utf-8') + _, generator = self._callFUT(pattern) + result = generator({'remainder': None}) + self.assertEqual(result, '/abc/None') + # should be a native string + self.assertEqual(type(result), str) + + +class TestCompileRouteFunctional(unittest.TestCase): + def matches(self, pattern, path, expected): + from pyramid.urldispatch import _compile_route + + matcher = _compile_route(pattern)[0] + result = matcher(path) + self.assertEqual(result, expected) + + def generates(self, pattern, dict, result): + from pyramid.urldispatch import _compile_route + + self.assertEqual(_compile_route(pattern)[1](dict), result) + + def test_matcher_functional_notdynamic(self): + self.matches('/', '', None) + self.matches('', '', None) + self.matches('/', '/foo', None) + self.matches('/foo/', '/foo', None) + self.matches('', '/', {}) + self.matches('/', '/', {}) + + def test_matcher_functional_newstyle(self): + self.matches('/{x}', '', None) + self.matches('/{x}', '/', None) + self.matches('/abc/{def}', '/abc/', None) + self.matches('/{x}', '/a', {'x': 'a'}) + self.matches('zzz/{x}', '/zzz/abc', {'x': 'abc'}) + self.matches( + 'zzz/{x}*traverse', '/zzz/abc', {'x': 'abc', 'traverse': ()} + ) + self.matches( + 'zzz/{x}*traverse', + '/zzz/abc/def/g', + {'x': 'abc', 'traverse': ('def', 'g')}, + ) + self.matches('*traverse', '/zzz/abc', {'traverse': ('zzz', 'abc')}) + self.matches('*traverse', '/zzz/ abc', {'traverse': ('zzz', ' abc')}) + # '/La%20Pe%C3%B1a' + self.matches( + '{x}', + text_(b'/La Pe\xc3\xb1a', 'utf-8'), + {'x': text_(b'La Pe\xc3\xb1a', 'utf-8')}, + ) + # '/La%20Pe%C3%B1a/x' + self.matches( + '*traverse', + text_(b'/La Pe\xc3\xb1a/x'), + {'traverse': (text_(b'La Pe\xc3\xb1a'), 'x')}, + ) + self.matches('/foo/{id}.html', '/foo/bar.html', {'id': 'bar'}) + self.matches( + '/{num:[0-9]+}/*traverse', + '/555/abc/def', + {'num': '555', 'traverse': ('abc', 'def')}, + ) + self.matches( + '/{num:[0-9]*}/*traverse', + '/555/abc/def', + {'num': '555', 'traverse': ('abc', 'def')}, + ) + self.matches('zzz/{_}', '/zzz/abc', {'_': 'abc'}) + self.matches('zzz/{_abc}', '/zzz/abc', {'_abc': 'abc'}) + self.matches('zzz/{abc_def}', '/zzz/abc', {'abc_def': 'abc'}) + + def test_matcher_functional_oldstyle(self): + self.matches('/:x', '', None) + self.matches('/:x', '/', None) + self.matches('/abc/:def', '/abc/', None) + self.matches('/:x', '/a', {'x': 'a'}) + self.matches('zzz/:x', '/zzz/abc', {'x': 'abc'}) + self.matches( + 'zzz/:x*traverse', '/zzz/abc', {'x': 'abc', 'traverse': ()} + ) + self.matches( + 'zzz/:x*traverse', + '/zzz/abc/def/g', + {'x': 'abc', 'traverse': ('def', 'g')}, + ) + self.matches('*traverse', '/zzz/abc', {'traverse': ('zzz', 'abc')}) + self.matches('*traverse', '/zzz/ abc', {'traverse': ('zzz', ' abc')}) + # '/La%20Pe%C3%B1a' + # pattern, path, expected + self.matches( + ':x', + text_(b'/La Pe\xc3\xb1a', 'utf-8'), + {'x': text_(b'La Pe\xc3\xb1a', 'utf-8')}, + ) + # '/La%20Pe%C3%B1a/x' + self.matches( + '*traverse', + text_(b'/La Pe\xc3\xb1a/x', 'utf-8'), + {'traverse': (text_(b'La Pe\xc3\xb1a', 'utf-8'), 'x')}, + ) + self.matches('/foo/:id.html', '/foo/bar.html', {'id': 'bar'}) + self.matches('/foo/:id_html', '/foo/bar_html', {'id_html': 'bar_html'}) + self.matches('zzz/:_', '/zzz/abc', {'_': 'abc'}) + self.matches('zzz/:_abc', '/zzz/abc', {'_abc': 'abc'}) + self.matches('zzz/:abc_def', '/zzz/abc', {'abc_def': 'abc'}) + + def test_generator_functional_notdynamic(self): + self.generates('', {}, '/') + self.generates('/', {}, '/') + + def test_generator_functional_newstyle(self): + self.generates('/{x}', {'x': ''}, '/') + self.generates('/{x}', {'x': 'a'}, '/a') + self.generates('/{x}', {'x': 'a/b/c'}, '/a/b/c') + self.generates('/{x}', {'x': ':@&+$,'}, '/:@&+$,') + self.generates('zzz/{x}', {'x': 'abc'}, '/zzz/abc') + self.generates( + 'zzz/{x}*traverse', {'x': 'abc', 'traverse': ''}, '/zzz/abc' + ) + self.generates( + 'zzz/{x}*traverse', + {'x': 'abc', 'traverse': '/def/g'}, + '/zzz/abc/def/g', + ) + self.generates( + 'zzz/{x}*traverse', + {'x': ':@&+$,', 'traverse': '/:@&+$,'}, + '/zzz/:@&+$,/:@&+$,', + ) + self.generates( + '/{x}', + {'x': text_(b'/La Pe\xc3\xb1a', 'utf-8')}, + '//La%20Pe%C3%B1a', + ) + self.generates( + '/{x}*y', + {'x': text_(b'/La Pe\xc3\xb1a', 'utf-8'), 'y': '/rest/of/path'}, + '//La%20Pe%C3%B1a/rest/of/path', + ) + self.generates( + '*traverse', + {'traverse': ('a', text_(b'La Pe\xf1a'))}, + '/a/La%20Pe%C3%B1a', + ) + self.generates('/foo/{id}.html', {'id': 'bar'}, '/foo/bar.html') + self.generates('/foo/{_}', {'_': '20'}, '/foo/20') + self.generates('/foo/{_abc}', {'_abc': '20'}, '/foo/20') + self.generates('/foo/{abc_def}', {'abc_def': '20'}, '/foo/20') + + def test_generator_functional_oldstyle(self): + self.generates('/:x', {'x': ''}, '/') + self.generates('/:x', {'x': 'a'}, '/a') + self.generates('zzz/:x', {'x': 'abc'}, '/zzz/abc') + self.generates( + 'zzz/:x*traverse', {'x': 'abc', 'traverse': ''}, '/zzz/abc' + ) + self.generates( + 'zzz/:x*traverse', + {'x': 'abc', 'traverse': '/def/g'}, + '/zzz/abc/def/g', + ) + self.generates( + '/:x', + {'x': text_(b'/La Pe\xc3\xb1a', 'utf-8')}, + '//La%20Pe%C3%B1a', + ) + self.generates( + '/:x*y', + {'x': text_(b'/La Pe\xc3\xb1a', 'utf-8'), 'y': '/rest/of/path'}, + '//La%20Pe%C3%B1a/rest/of/path', + ) + self.generates( + '*traverse', + {'traverse': ('a', text_(b'La Pe\xf1a'))}, + '/a/La%20Pe%C3%B1a', + ) + self.generates('/foo/:id.html', {'id': 'bar'}, '/foo/bar.html') + self.generates('/foo/:_', {'_': '20'}, '/foo/20') + self.generates('/foo/:_abc', {'_abc': '20'}, '/foo/20') + self.generates('/foo/:abc_def', {'abc_def': '20'}, '/foo/20') + + +class DummyContext(object): + """ """ + + +class DummyRequest(object): + def __init__(self, environ): + self.environ = environ + + +class DummyRoute(object): + def __init__(self, generator): + self.generate = generator diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 000000000..a36655f6f --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,1242 @@ +import unittest +from pyramid.compat import PY2, text_, bytes_ + + +class Test_InstancePropertyHelper(unittest.TestCase): + def _makeOne(self): + cls = self._getTargetClass() + return cls() + + def _getTargetClass(self): + from pyramid.util import InstancePropertyHelper + + return InstancePropertyHelper + + def test_callable(self): + def worker(obj): + return obj.bar + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker) + foo.bar = 1 + self.assertEqual(1, foo.worker) + foo.bar = 2 + self.assertEqual(2, foo.worker) + + def test_callable_with_name(self): + def worker(obj): + return obj.bar + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker, name='x') + foo.bar = 1 + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + + def test_callable_with_reify(self): + def worker(obj): + return obj.bar + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker, reify=True) + foo.bar = 1 + self.assertEqual(1, foo.worker) + foo.bar = 2 + self.assertEqual(1, foo.worker) + + def test_callable_with_name_reify(self): + def worker(obj): + return obj.bar + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker, name='x') + helper.set_property(foo, worker, name='y', reify=True) + foo.bar = 1 + self.assertEqual(1, foo.y) + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + self.assertEqual(1, foo.y) + + def test_property_without_name(self): + def worker(obj): # pragma: no cover + pass + + foo = Dummy() + helper = self._getTargetClass() + self.assertRaises( + ValueError, helper.set_property, foo, property(worker) + ) + + def test_property_with_name(self): + def worker(obj): + return obj.bar + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, property(worker), name='x') + foo.bar = 1 + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + + def test_property_with_reify(self): + def worker(obj): # pragma: no cover + pass + + foo = Dummy() + helper = self._getTargetClass() + self.assertRaises( + ValueError, + helper.set_property, + foo, + property(worker), + name='x', + reify=True, + ) + + def test_override_property(self): + def worker(obj): # pragma: no cover + pass + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker, name='x') + + def doit(): + foo.x = 1 + + self.assertRaises(AttributeError, doit) + + def test_override_reify(self): + def worker(obj): # pragma: no cover + pass + + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, worker, name='x', reify=True) + foo.x = 1 + self.assertEqual(1, foo.x) + foo.x = 2 + self.assertEqual(2, foo.x) + + def test_reset_property(self): + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, lambda _: 1, name='x') + self.assertEqual(1, foo.x) + helper.set_property(foo, lambda _: 2, name='x') + self.assertEqual(2, foo.x) + + def test_reset_reify(self): + """ This is questionable behavior, but may as well get notified + if it changes.""" + foo = Dummy() + helper = self._getTargetClass() + helper.set_property(foo, lambda _: 1, name='x', reify=True) + self.assertEqual(1, foo.x) + helper.set_property(foo, lambda _: 2, name='x', reify=True) + self.assertEqual(1, foo.x) + + def test_make_property(self): + from pyramid.decorator import reify + + helper = self._getTargetClass() + name, fn = helper.make_property(lambda x: 1, name='x', reify=True) + self.assertEqual(name, 'x') + self.assertTrue(isinstance(fn, reify)) + + def test_apply_properties_with_iterable(self): + foo = Dummy() + helper = self._getTargetClass() + x = helper.make_property(lambda _: 1, name='x', reify=True) + y = helper.make_property(lambda _: 2, name='y') + helper.apply_properties(foo, [x, y]) + self.assertEqual(1, foo.x) + self.assertEqual(2, foo.y) + + def test_apply_properties_with_dict(self): + foo = Dummy() + helper = self._getTargetClass() + x_name, x_fn = helper.make_property(lambda _: 1, name='x', reify=True) + y_name, y_fn = helper.make_property(lambda _: 2, name='y') + helper.apply_properties(foo, {x_name: x_fn, y_name: y_fn}) + self.assertEqual(1, foo.x) + self.assertEqual(2, foo.y) + + def test_make_property_unicode(self): + from pyramid.compat import text_ + from pyramid.exceptions import ConfigurationError + + cls = self._getTargetClass() + if PY2: + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + else: + name = b'La Pe\xc3\xb1a' + + def make_bad_name(): + cls.make_property(lambda x: 1, name=name, reify=True) + + self.assertRaises(ConfigurationError, make_bad_name) + + def test_add_property(self): + helper = self._makeOne() + helper.add_property(lambda obj: obj.bar, name='x', reify=True) + helper.add_property(lambda obj: obj.bar, name='y') + self.assertEqual(len(helper.properties), 2) + foo = Dummy() + helper.apply(foo) + foo.bar = 1 + self.assertEqual(foo.x, 1) + self.assertEqual(foo.y, 1) + foo.bar = 2 + self.assertEqual(foo.x, 1) + self.assertEqual(foo.y, 2) + + def test_apply_multiple_times(self): + helper = self._makeOne() + helper.add_property(lambda obj: 1, name='x') + foo, bar = Dummy(), Dummy() + helper.apply(foo) + self.assertEqual(foo.x, 1) + helper.add_property(lambda obj: 2, name='x') + helper.apply(bar) + self.assertEqual(foo.x, 1) + self.assertEqual(bar.x, 2) + + +class Test_InstancePropertyMixin(unittest.TestCase): + def _makeOne(self): + cls = self._getTargetClass() + + class Foo(cls): + pass + + return Foo() + + def _getTargetClass(self): + from pyramid.util import InstancePropertyMixin + + return InstancePropertyMixin + + def test_callable(self): + def worker(obj): + return obj.bar + + foo = self._makeOne() + foo.set_property(worker) + foo.bar = 1 + self.assertEqual(1, foo.worker) + foo.bar = 2 + self.assertEqual(2, foo.worker) + + def test_callable_with_name(self): + def worker(obj): + return obj.bar + + foo = self._makeOne() + foo.set_property(worker, name='x') + foo.bar = 1 + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + + def test_callable_with_reify(self): + def worker(obj): + return obj.bar + + foo = self._makeOne() + foo.set_property(worker, reify=True) + foo.bar = 1 + self.assertEqual(1, foo.worker) + foo.bar = 2 + self.assertEqual(1, foo.worker) + + def test_callable_with_name_reify(self): + def worker(obj): + return obj.bar + + foo = self._makeOne() + foo.set_property(worker, name='x') + foo.set_property(worker, name='y', reify=True) + foo.bar = 1 + self.assertEqual(1, foo.y) + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + self.assertEqual(1, foo.y) + + def test_property_without_name(self): + def worker(obj): # pragma: no cover + pass + + foo = self._makeOne() + self.assertRaises(ValueError, foo.set_property, property(worker)) + + def test_property_with_name(self): + def worker(obj): + return obj.bar + + foo = self._makeOne() + foo.set_property(property(worker), name='x') + foo.bar = 1 + self.assertEqual(1, foo.x) + foo.bar = 2 + self.assertEqual(2, foo.x) + + def test_property_with_reify(self): + def worker(obj): # pragma: no cover + pass + + foo = self._makeOne() + self.assertRaises( + ValueError, + foo.set_property, + property(worker), + name='x', + reify=True, + ) + + def test_override_property(self): + def worker(obj): # pragma: no cover + pass + + foo = self._makeOne() + foo.set_property(worker, name='x') + + def doit(): + foo.x = 1 + + self.assertRaises(AttributeError, doit) + + def test_override_reify(self): + def worker(obj): # pragma: no cover + pass + + foo = self._makeOne() + foo.set_property(worker, name='x', reify=True) + foo.x = 1 + self.assertEqual(1, foo.x) + foo.x = 2 + self.assertEqual(2, foo.x) + + def test_reset_property(self): + foo = self._makeOne() + foo.set_property(lambda _: 1, name='x') + self.assertEqual(1, foo.x) + foo.set_property(lambda _: 2, name='x') + self.assertEqual(2, foo.x) + + def test_reset_reify(self): + """ This is questionable behavior, but may as well get notified + if it changes.""" + foo = self._makeOne() + foo.set_property(lambda _: 1, name='x', reify=True) + self.assertEqual(1, foo.x) + foo.set_property(lambda _: 2, name='x', reify=True) + self.assertEqual(1, foo.x) + + def test_new_class_keeps_parent_module_name(self): + foo = self._makeOne() + self.assertEqual(foo.__module__, 'tests.test_util') + self.assertEqual(foo.__class__.__module__, 'tests.test_util') + foo.set_property(lambda _: 1, name='x', reify=True) + self.assertEqual(foo.__module__, 'tests.test_util') + self.assertEqual(foo.__class__.__module__, 'tests.test_util') + + +class Test_WeakOrderedSet(unittest.TestCase): + def _makeOne(self): + from pyramid.config import WeakOrderedSet + + return WeakOrderedSet() + + def test_ctor(self): + wos = self._makeOne() + self.assertEqual(len(wos), 0) + self.assertEqual(wos.last, None) + + def test_add_item(self): + wos = self._makeOne() + reg = Dummy() + wos.add(reg) + self.assertEqual(list(wos), [reg]) + self.assertTrue(reg in wos) + self.assertEqual(wos.last, reg) + + def test_add_multiple_items(self): + wos = self._makeOne() + reg1 = Dummy() + reg2 = Dummy() + wos.add(reg1) + wos.add(reg2) + self.assertEqual(len(wos), 2) + self.assertEqual(list(wos), [reg1, reg2]) + self.assertTrue(reg1 in wos) + self.assertTrue(reg2 in wos) + self.assertEqual(wos.last, reg2) + + def test_add_duplicate_items(self): + wos = self._makeOne() + reg = Dummy() + wos.add(reg) + wos.add(reg) + self.assertEqual(len(wos), 1) + self.assertEqual(list(wos), [reg]) + self.assertTrue(reg in wos) + self.assertEqual(wos.last, reg) + + def test_weakref_removal(self): + wos = self._makeOne() + reg = Dummy() + wos.add(reg) + wos.remove(reg) + self.assertEqual(len(wos), 0) + self.assertEqual(list(wos), []) + self.assertEqual(wos.last, None) + + def test_last_updated(self): + wos = self._makeOne() + reg = Dummy() + reg2 = Dummy() + wos.add(reg) + wos.add(reg2) + wos.remove(reg2) + self.assertEqual(len(wos), 1) + self.assertEqual(list(wos), [reg]) + self.assertEqual(wos.last, reg) + + def test_empty(self): + wos = self._makeOne() + reg = Dummy() + reg2 = Dummy() + wos.add(reg) + wos.add(reg2) + wos.empty() + self.assertEqual(len(wos), 0) + self.assertEqual(list(wos), []) + self.assertEqual(wos.last, None) + + +class Test_strings_differ(unittest.TestCase): + def _callFUT(self, *args, **kw): + from pyramid.util import strings_differ + + return strings_differ(*args, **kw) + + def test_it_bytes(self): + self.assertFalse(self._callFUT(b'foo', b'foo')) + self.assertTrue(self._callFUT(b'123', b'345')) + self.assertTrue(self._callFUT(b'1234', b'123')) + self.assertTrue(self._callFUT(b'123', b'1234')) + + def test_it_native_str(self): + self.assertFalse(self._callFUT('123', '123')) + self.assertTrue(self._callFUT('123', '1234')) + + def test_it_with_internal_comparator(self): + result = self._callFUT(b'foo', b'foo', compare_digest=None) + self.assertFalse(result) + + result = self._callFUT(b'123', b'abc', compare_digest=None) + self.assertTrue(result) + + def test_it_with_external_comparator(self): + class DummyComparator(object): + called = False + + def __init__(self, ret_val): + self.ret_val = ret_val + + def __call__(self, a, b): + self.called = True + return self.ret_val + + dummy_compare = DummyComparator(True) + result = self._callFUT(b'foo', b'foo', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertFalse(result) + + dummy_compare = DummyComparator(False) + result = self._callFUT(b'123', b'345', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertTrue(result) + + dummy_compare = DummyComparator(False) + result = self._callFUT(b'abc', b'abc', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertTrue(result) + + +class Test_object_description(unittest.TestCase): + def _callFUT(self, object): + from pyramid.util import object_description + + return object_description(object) + + def test_string(self): + self.assertEqual(self._callFUT('abc'), 'abc') + + def test_int(self): + self.assertEqual(self._callFUT(1), '1') + + def test_bool(self): + self.assertEqual(self._callFUT(True), 'True') + + def test_None(self): + self.assertEqual(self._callFUT(None), 'None') + + def test_float(self): + self.assertEqual(self._callFUT(1.2), '1.2') + + def test_tuple(self): + self.assertEqual(self._callFUT(('a', 'b')), "('a', 'b')") + + def test_set(self): + if PY2: + self.assertEqual(self._callFUT(set(['a'])), "set(['a'])") + else: + self.assertEqual(self._callFUT(set(['a'])), "{'a'}") + + def test_list(self): + self.assertEqual(self._callFUT(['a']), "['a']") + + def test_dict(self): + self.assertEqual(self._callFUT({'a': 1}), "{'a': 1}") + + def test_nomodule(self): + o = object() + self.assertEqual(self._callFUT(o), 'object %s' % str(o)) + + def test_module(self): + import pyramid + + self.assertEqual(self._callFUT(pyramid), 'module pyramid') + + def test_method(self): + self.assertEqual( + self._callFUT(self.test_method), + 'method test_method of class tests.test_util.' + 'Test_object_description', + ) + + def test_class(self): + self.assertEqual( + self._callFUT(self.__class__), + 'class tests.test_util.Test_object_description', + ) + + def test_function(self): + self.assertEqual( + self._callFUT(dummyfunc), 'function tests.test_util.dummyfunc' + ) + + def test_instance(self): + inst = Dummy() + self.assertEqual(self._callFUT(inst), "object %s" % str(inst)) + + def test_shortened_repr(self): + inst = ['1'] * 1000 + self.assertEqual(self._callFUT(inst), str(inst)[:100] + ' ... ]') + + +class TestTopologicalSorter(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.util import TopologicalSorter + + return TopologicalSorter(*arg, **kw) + + def test_remove(self): + inst = self._makeOne() + inst.names.append('name') + inst.name2val['name'] = 1 + inst.req_after.add('name') + inst.req_before.add('name') + inst.name2after['name'] = ('bob',) + inst.name2before['name'] = ('fred',) + inst.order.append(('bob', 'name')) + inst.order.append(('name', 'fred')) + inst.remove('name') + self.assertFalse(inst.names) + self.assertFalse(inst.req_before) + self.assertFalse(inst.req_after) + self.assertFalse(inst.name2before) + self.assertFalse(inst.name2after) + self.assertFalse(inst.name2val) + self.assertFalse(inst.order) + + def test_add(self): + from pyramid.util import LAST + + sorter = self._makeOne() + sorter.add('name', 'factory') + self.assertEqual(sorter.names, ['name']) + self.assertEqual(sorter.name2val, {'name': 'factory'}) + self.assertEqual(sorter.order, [('name', LAST)]) + sorter.add('name2', 'factory2') + self.assertEqual(sorter.names, ['name', 'name2']) + self.assertEqual( + sorter.name2val, {'name': 'factory', 'name2': 'factory2'} + ) + self.assertEqual(sorter.order, [('name', LAST), ('name2', LAST)]) + sorter.add('name3', 'factory3', before='name2') + self.assertEqual(sorter.names, ['name', 'name2', 'name3']) + self.assertEqual( + sorter.name2val, + {'name': 'factory', 'name2': 'factory2', 'name3': 'factory3'}, + ) + self.assertEqual( + sorter.order, [('name', LAST), ('name2', LAST), ('name3', 'name2')] + ) + + def test_sorted_ordering_1(self): + sorter = self._makeOne() + sorter.add('name1', 'factory1') + sorter.add('name2', 'factory2') + self.assertEqual( + sorter.sorted(), [('name1', 'factory1'), ('name2', 'factory2')] + ) + + def test_sorted_ordering_2(self): + from pyramid.util import FIRST + + sorter = self._makeOne() + sorter.add('name1', 'factory1') + sorter.add('name2', 'factory2', after=FIRST) + self.assertEqual( + sorter.sorted(), [('name2', 'factory2'), ('name1', 'factory1')] + ) + + def test_sorted_ordering_3(self): + from pyramid.util import FIRST + + sorter = self._makeOne() + add = sorter.add + add('auth', 'auth_factory', after='browserid') + add('dbt', 'dbt_factory') + add('retry', 'retry_factory', before='txnmgr', after='exceptionview') + add('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', after='exceptionview') + add('exceptionview', 'excview_factory', after=FIRST) + self.assertEqual( + sorter.sorted(), + [ + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ('dbt', 'dbt_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ], + ) + + def test_sorted_ordering_4(self): + from pyramid.util import FIRST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', after=FIRST) + add('auth', 'auth_factory', after='browserid') + add('retry', 'retry_factory', before='txnmgr', after='exceptionview') + add('browserid', 'browserid_factory') + add('txnmgr', 'txnmgr_factory', after='exceptionview') + add('dbt', 'dbt_factory') + self.assertEqual( + sorter.sorted(), + [ + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('dbt', 'dbt_factory'), + ], + ) + + def test_sorted_ordering_5(self): + from pyramid.util import LAST, FIRST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory') + add('auth', 'auth_factory', after=FIRST) + add('retry', 'retry_factory', before='txnmgr', after='exceptionview') + add('browserid', 'browserid_factory', after=FIRST) + add('txnmgr', 'txnmgr_factory', after='exceptionview', before=LAST) + add('dbt', 'dbt_factory') + self.assertEqual( + sorter.sorted(), + [ + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('txnmgr', 'txnmgr_factory'), + ('dbt', 'dbt_factory'), + ], + ) + + def test_sorted_ordering_missing_before_partial(self): + from pyramid.exceptions import ConfigurationError + + sorter = self._makeOne() + add = sorter.add + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', after='browserid') + add('retry', 'retry_factory', before='txnmgr', after='exceptionview') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, sorter.sorted) + + def test_sorted_ordering_missing_after_partial(self): + from pyramid.exceptions import ConfigurationError + + sorter = self._makeOne() + add = sorter.add + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', after='txnmgr') + add('retry', 'retry_factory', before='dbt', after='exceptionview') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, sorter.sorted) + + def test_sorted_ordering_missing_before_and_after_partials(self): + from pyramid.exceptions import ConfigurationError + + sorter = self._makeOne() + add = sorter.add + add('dbt', 'dbt_factory') + add('auth', 'auth_factory', after='browserid') + add('retry', 'retry_factory', before='foo', after='txnmgr') + add('browserid', 'browserid_factory') + self.assertRaises(ConfigurationError, sorter.sorted) + + def test_sorted_ordering_missing_before_partial_with_fallback(self): + from pyramid.util import LAST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', before=LAST) + add('auth', 'auth_factory', after='browserid') + add( + 'retry', + 'retry_factory', + before=('txnmgr', LAST), + after='exceptionview', + ) + add('browserid', 'browserid_factory') + add('dbt', 'dbt_factory') + self.assertEqual( + sorter.sorted(), + [ + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('dbt', 'dbt_factory'), + ], + ) + + def test_sorted_ordering_missing_after_partial_with_fallback(self): + from pyramid.util import FIRST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', after=FIRST) + add('auth', 'auth_factory', after=('txnmgr', 'browserid')) + add('retry', 'retry_factory', after='exceptionview') + add('browserid', 'browserid_factory') + add('dbt', 'dbt_factory') + self.assertEqual( + sorter.sorted(), + [ + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ('browserid', 'browserid_factory'), + ('auth', 'auth_factory'), + ('dbt', 'dbt_factory'), + ], + ) + + def test_sorted_ordering_with_partial_fallbacks(self): + from pyramid.util import LAST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', before=('wontbethere', LAST)) + add('retry', 'retry_factory', after='exceptionview') + add( + 'browserid', 'browserid_factory', before=('wont2', 'exceptionview') + ) + self.assertEqual( + sorter.sorted(), + [ + ('browserid', 'browserid_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_sorted_ordering_with_multiple_matching_fallbacks(self): + from pyramid.util import LAST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', before=LAST) + add('retry', 'retry_factory', after='exceptionview') + add( + 'browserid', 'browserid_factory', before=('retry', 'exceptionview') + ) + self.assertEqual( + sorter.sorted(), + [ + ('browserid', 'browserid_factory'), + ('exceptionview', 'excview_factory'), + ('retry', 'retry_factory'), + ], + ) + + def test_sorted_ordering_with_missing_fallbacks(self): + from pyramid.exceptions import ConfigurationError + from pyramid.util import LAST + + sorter = self._makeOne() + add = sorter.add + add('exceptionview', 'excview_factory', before=LAST) + add('retry', 'retry_factory', after='exceptionview') + add('browserid', 'browserid_factory', before=('txnmgr', 'auth')) + self.assertRaises(ConfigurationError, sorter.sorted) + + def test_sorted_ordering_conflict_direct(self): + from pyramid.exceptions import CyclicDependencyError + + sorter = self._makeOne() + add = sorter.add + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', before='browserid', after='browserid') + self.assertRaises(CyclicDependencyError, sorter.sorted) + + def test_sorted_ordering_conflict_indirect(self): + from pyramid.exceptions import CyclicDependencyError + + sorter = self._makeOne() + add = sorter.add + add('browserid', 'browserid_factory') + add('auth', 'auth_factory', before='browserid') + add('dbt', 'dbt_factory', after='browserid', before='auth') + self.assertRaises(CyclicDependencyError, sorter.sorted) + + +class TestSentinel(unittest.TestCase): + def test_repr(self): + from pyramid.util import Sentinel + + r = repr(Sentinel('ABC')) + self.assertEqual(r, 'ABC') + + +class TestCallableName(unittest.TestCase): + def test_valid_ascii(self): + from pyramid.util import get_callable_name + from pyramid.compat import text_ + + if PY2: + name = text_(b'hello world', 'utf-8') + else: + name = b'hello world' + + self.assertEqual(get_callable_name(name), 'hello world') + + def test_invalid_ascii(self): + from pyramid.util import get_callable_name + from pyramid.compat import text_ + from pyramid.exceptions import ConfigurationError + + def get_bad_name(): + if PY2: + name = text_(b'La Pe\xc3\xb1a', 'utf-8') + else: + name = b'La Pe\xc3\xb1a' + + get_callable_name(name) + + self.assertRaises(ConfigurationError, get_bad_name) + + +class Test_hide_attrs(unittest.TestCase): + def _callFUT(self, obj, *attrs): + from pyramid.util import hide_attrs + + return hide_attrs(obj, *attrs) + + def _makeDummy(self): + from pyramid.decorator import reify + + class Dummy(object): + x = 1 + + @reify + def foo(self): + return self.x + + return Dummy() + + def test_restores_attrs(self): + obj = self._makeDummy() + obj.bar = 'asdf' + orig_foo = obj.foo + with self._callFUT(obj, 'foo', 'bar'): + obj.foo = object() + obj.bar = 'nope' + self.assertEqual(obj.foo, orig_foo) + self.assertEqual(obj.bar, 'asdf') + + def test_restores_attrs_on_exception(self): + obj = self._makeDummy() + orig_foo = obj.foo + try: + with self._callFUT(obj, 'foo'): + obj.foo = object() + raise RuntimeError() + except RuntimeError: + self.assertEqual(obj.foo, orig_foo) + else: # pragma: no cover + self.fail("RuntimeError not raised") + + def test_restores_attrs_to_none(self): + obj = self._makeDummy() + obj.foo = None + with self._callFUT(obj, 'foo'): + obj.foo = object() + self.assertEqual(obj.foo, None) + + def test_deletes_attrs(self): + obj = self._makeDummy() + with self._callFUT(obj, 'foo'): + obj.foo = object() + self.assertTrue('foo' not in obj.__dict__) + + def test_does_not_delete_attr_if_no_attr_to_delete(self): + obj = self._makeDummy() + with self._callFUT(obj, 'foo'): + pass + self.assertTrue('foo' not in obj.__dict__) + + +def dummyfunc(): # pragma: no cover + pass + + +class Dummy(object): + pass + + +class Test_is_same_domain(unittest.TestCase): + def _callFUT(self, *args, **kw): + from pyramid.util import is_same_domain + + return is_same_domain(*args, **kw) + + def test_it(self): + self.assertTrue(self._callFUT("example.com", "example.com")) + self.assertFalse(self._callFUT("evil.com", "example.com")) + self.assertFalse(self._callFUT("evil.example.com", "example.com")) + self.assertFalse(self._callFUT("example.com", "")) + + def test_with_wildcard(self): + self.assertTrue(self._callFUT("example.com", ".example.com")) + self.assertTrue(self._callFUT("good.example.com", ".example.com")) + + def test_with_port(self): + self.assertTrue(self._callFUT("example.com:8080", "example.com:8080")) + self.assertFalse(self._callFUT("example.com:8080", "example.com")) + self.assertFalse(self._callFUT("example.com", "example.com:8080")) + + +class Test_make_contextmanager(unittest.TestCase): + def _callFUT(self, *args, **kw): + from pyramid.util import make_contextmanager + + return make_contextmanager(*args, **kw) + + def test_with_None(self): + mgr = self._callFUT(None) + with mgr() as ctx: + self.assertIsNone(ctx) + + def test_with_generator(self): + def mygen(ctx): + yield ctx + + mgr = self._callFUT(mygen) + with mgr('a') as ctx: + self.assertEqual(ctx, 'a') + + def test_with_multiple_yield_generator(self): + def mygen(): + yield 'a' + yield 'b' + + mgr = self._callFUT(mygen) + try: + with mgr() as ctx: + self.assertEqual(ctx, 'a') + except RuntimeError: + pass + else: # pragma: no cover + raise AssertionError('expected raise from multiple yields') + + def test_with_regular_fn(self): + def mygen(): + return 'a' + + mgr = self._callFUT(mygen) + with mgr() as ctx: + self.assertEqual(ctx, 'a') + + +class Test_takes_one_arg(unittest.TestCase): + def _callFUT(self, view, attr=None, argname=None): + from pyramid.util import takes_one_arg + + return takes_one_arg(view, attr=attr, argname=argname) + + def test_requestonly_newstyle_class_no_init(self): + class foo(object): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_requestonly_newstyle_class_init_toomanyargs(self): + class foo(object): + def __init__(self, context, request): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_requestonly_newstyle_class_init_onearg_named_request(self): + class foo(object): + def __init__(self, request): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_onearg_named_somethingelse(self): + class foo(object): + def __init__(self, req): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_not_request(self): + class foo(object): + def __init__(self, context, request=None): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_request(self): + class foo(object): + def __init__(self, request, foo=1, bar=2): + """ """ + + self.assertTrue(self._callFUT(foo, argname='request')) + + def test_newstyle_class_init_firstname_request_with_secondname(self): + class foo(object): + def __init__(self, request, two): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_noargs(self): + class foo(object): + def __init__(): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_no_init(self): + class foo: + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_toomanyargs(self): + class foo: + def __init__(self, context, request): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_request(self): + class foo: + def __init__(self, request): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_somethingelse(self): + class foo: + def __init__(self, req): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_not_request(self): + class foo: + def __init__(self, context, request=None): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_request(self): + class foo: + def __init__(self, request, foo=1, bar=2): + """ """ + + self.assertTrue(self._callFUT(foo, argname='request'), True) + + def test_oldstyle_class_init_noargs(self): + class foo: + def __init__(): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_function_toomanyargs(self): + def foo(context, request): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_function_with_attr_false(self): + def bar(context, request): + """ """ + + def foo(context, request): + """ """ + + foo.bar = bar + self.assertFalse(self._callFUT(foo, 'bar')) + + def test_function_with_attr_true(self): + def bar(context, request): + """ """ + + def foo(request): + """ """ + + foo.bar = bar + self.assertTrue(self._callFUT(foo, 'bar')) + + def test_function_onearg_named_request(self): + def foo(request): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_function_onearg_named_somethingelse(self): + def foo(req): + """ """ + + self.assertTrue(self._callFUT(foo)) + + def test_function_defaultargs_firstname_not_request(self): + def foo(context, request=None): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_function_defaultargs_firstname_request(self): + def foo(request, foo=1, bar=2): + """ """ + + self.assertTrue(self._callFUT(foo, argname='request')) + + def test_function_noargs(self): + def foo(): + """ """ + + self.assertFalse(self._callFUT(foo)) + + def test_instance_toomanyargs(self): + class Foo: + def __call__(self, context, request): + """ """ + + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_request(self): + class Foo: + def __call__(self, request): + """ """ + + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_somethingelse(self): + class Foo: + def __call__(self, req): + """ """ + + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_not_request(self): + class Foo: + def __call__(self, context, request=None): + """ """ + + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_request(self): + class Foo: + def __call__(self, request, foo=1, bar=2): + """ """ + + foo = Foo() + self.assertTrue(self._callFUT(foo, argname='request'), True) + + def test_instance_nocall(self): + class Foo: + pass + + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_method_onearg_named_request(self): + class Foo: + def method(self, request): + """ """ + + foo = Foo() + self.assertTrue(self._callFUT(foo.method)) + + def test_function_annotations(self): + def foo(bar): + """ """ + + # avoid SyntaxErrors in python2, this if effectively nop + getattr(foo, '__annotations__', {}).update({'bar': 'baz'}) + self.assertTrue(self._callFUT(foo)) + + +class TestSimpleSerializer(unittest.TestCase): + def _makeOne(self): + from pyramid.util import SimpleSerializer + + return SimpleSerializer() + + def test_loads(self): + inst = self._makeOne() + self.assertEqual(inst.loads(b'abc'), text_('abc')) + + def test_dumps(self): + inst = self._makeOne() + self.assertEqual(inst.dumps('abc'), bytes_('abc')) diff --git a/tests/test_view.py b/tests/test_view.py new file mode 100644 index 000000000..f82480169 --- /dev/null +++ b/tests/test_view.py @@ -0,0 +1,1279 @@ +import unittest +from zope.interface import Interface +from zope.interface import implementer +import sys + +from pyramid import testing +from pyramid.interfaces import IRequest +from pyramid.interfaces import IResponse + + +class BaseTest(object): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _registerView(self, reg, app, name): + from pyramid.interfaces import IViewClassifier + + for_ = (IViewClassifier, IRequest, IContext) + from pyramid.interfaces import IView + + reg.registerAdapter(app, for_, IView, name) + + def _makeEnviron(self, **extras): + environ = { + 'wsgi.url_scheme': 'http', + 'wsgi.version': (1, 0), + 'SERVER_NAME': 'localhost', + 'SERVER_PORT': '8080', + 'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/', + } + environ.update(extras) + return environ + + def _makeRequest(self, **environ): + from pyramid.request import Request + from pyramid.registry import Registry + + environ = self._makeEnviron(**environ) + request = Request(environ) + request.registry = Registry() + return request + + def _makeContext(self): + from zope.interface import directlyProvides + + context = DummyContext() + directlyProvides(context, IContext) + return context + + +class Test_notfound_view_config(BaseTest, unittest.TestCase): + def _makeOne(self, **kw): + from pyramid.view import notfound_view_config + + return notfound_view_config(**kw) + + def test_ctor(self): + inst = self._makeOne( + attr='attr', path_info='path_info', append_slash=True + ) + self.assertEqual( + inst.__dict__, + {'attr': 'attr', 'path_info': 'path_info', 'append_slash': True}, + ) + + def test_it_function(self): + def view(request): # pragma: no cover + pass + + decorator = self._makeOne( + attr='attr', renderer='renderer', append_slash=True + ) + venusian = DummyVenusian() + decorator.venusian = venusian + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual( + settings, + [ + { + 'attr': 'attr', + 'venusian': venusian, + 'append_slash': True, + 'renderer': 'renderer', + '_info': 'codeinfo', + 'view': None, + } + ], + ) + + def test_it_class(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + class view(object): + pass + + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['attr'], 'view') + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_call_with_venusian_args(self): + decorator = self._makeOne(_depth=1, _category='foo') + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + category = attachments[0][2] + depth = attachments[0][3] + self.assertEqual(depth, 2) + self.assertEqual(category, 'foo') + + +class Test_forbidden_view_config(BaseTest, unittest.TestCase): + def _makeOne(self, **kw): + from pyramid.view import forbidden_view_config + + return forbidden_view_config(**kw) + + def test_ctor(self): + inst = self._makeOne(attr='attr', path_info='path_info') + self.assertEqual( + inst.__dict__, {'attr': 'attr', 'path_info': 'path_info'} + ) + + def test_it_function(self): + def view(request): # pragma: no cover + pass + + decorator = self._makeOne(attr='attr', renderer='renderer') + venusian = DummyVenusian() + decorator.venusian = venusian + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual( + settings, + [ + { + 'attr': 'attr', + 'venusian': venusian, + 'renderer': 'renderer', + '_info': 'codeinfo', + 'view': None, + } + ], + ) + + def test_it_class(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + class view(object): + pass + + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['attr'], 'view') + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_call_with_venusian_args(self): + decorator = self._makeOne(_depth=1, _category='foo') + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + category = attachments[0][2] + depth = attachments[0][3] + self.assertEqual(depth, 2) + self.assertEqual(category, 'foo') + + +class Test_exception_view_config(BaseTest, unittest.TestCase): + def _makeOne(self, *args, **kw): + from pyramid.view import exception_view_config + + return exception_view_config(*args, **kw) + + def test_ctor(self): + inst = self._makeOne(context=Exception, path_info='path_info') + self.assertEqual( + inst.__dict__, {'context': Exception, 'path_info': 'path_info'} + ) + + def test_ctor_positional_exception(self): + inst = self._makeOne(Exception, path_info='path_info') + self.assertEqual( + inst.__dict__, {'context': Exception, 'path_info': 'path_info'} + ) + + def test_ctor_positional_extras(self): + from pyramid.exceptions import ConfigurationError + + self.assertRaises( + ConfigurationError, lambda: self._makeOne(Exception, True) + ) + + def test_it_function(self): + def view(request): # pragma: no cover + pass + + decorator = self._makeOne(context=Exception, renderer='renderer') + venusian = DummyVenusian() + decorator.venusian = venusian + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual( + settings, + [ + { + 'venusian': venusian, + 'context': Exception, + 'renderer': 'renderer', + '_info': 'codeinfo', + 'view': None, + } + ], + ) + + def test_it_class(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + class view(object): + pass + + wrapped = decorator(view) + self.assertTrue(wrapped is view) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['attr'], 'view') + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_call_with_venusian_args(self): + decorator = self._makeOne(_depth=1, _category='foo') + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + category = attachments[0][2] + depth = attachments[0][3] + self.assertEqual(depth, 2) + self.assertEqual(category, 'foo') + + +class RenderViewToResponseTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.view import render_view_to_response + + return render_view_to_response(*arg, **kw) + + def test_call_no_view_registered(self): + request = self._makeRequest() + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_no_registry_on_request(self): + request = self._makeRequest() + del request.registry + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_view_registered_secure(self): + request = self._makeRequest() + context = self._makeContext() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + response = self._callFUT( + context, request, name='registered', secure=True + ) + self.assertEqual(response.status, '200 OK') + + def test_call_view_registered_insecure_no_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + response = self._callFUT( + context, request, name='registered', secure=False + ) + self.assertEqual(response.status, '200 OK') + + def test_call_view_registered_insecure_with_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + + def anotherview(context, request): + return DummyResponse('anotherview') + + view.__call_permissive__ = anotherview + self._registerView(request.registry, view, 'registered') + response = self._callFUT( + context, request, name='registered', secure=False + ) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.app_iter, ['anotherview']) + + def test_call_view_with_request_iface_on_request(self): + # See https://github.com/Pylons/pyramid/issues/1643 + from zope.interface import Interface + + class IWontBeFound(Interface): + pass + + context = self._makeContext() + request = self._makeRequest() + request.request_iface = IWontBeFound + response = DummyResponse('aview') + view = make_view(response) + self._registerView(request.registry, view, 'aview') + response = self._callFUT(context, request, name='aview') + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.app_iter, ['aview']) + + +class RenderViewToIterableTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.view import render_view_to_iterable + + return render_view_to_iterable(*arg, **kw) + + def test_call_no_view_registered(self): + request = self._makeRequest() + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_view_registered_secure(self): + request = self._makeRequest() + context = self._makeContext() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + iterable = self._callFUT( + context, request, name='registered', secure=True + ) + self.assertEqual(iterable, ()) + + def test_call_view_registered_insecure_no_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + iterable = self._callFUT( + context, request, name='registered', secure=False + ) + self.assertEqual(iterable, ()) + + def test_call_view_registered_insecure_with_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + + def anotherview(context, request): + return DummyResponse(b'anotherview') + + view.__call_permissive__ = anotherview + self._registerView(request.registry, view, 'registered') + iterable = self._callFUT( + context, request, name='registered', secure=False + ) + self.assertEqual(iterable, [b'anotherview']) + + def test_verify_output_bytestring(self): + from pyramid.request import Request + from pyramid.config import Configurator + from pyramid.view import render_view + from webob.compat import text_type + + config = Configurator(settings={}) + + def view(request): + request.response.text = text_type('<body></body>') + return request.response + + config.add_view(name='test', view=view) + config.commit() + + r = Request({}) + r.registry = config.registry + self.assertEqual(render_view(object(), r, 'test'), b'<body></body>') + + def test_call_request_has_no_registry(self): + request = self._makeRequest() + del request.registry + registry = self.config.registry + context = self._makeContext() + response = DummyResponse() + view = make_view(response) + self._registerView(registry, view, 'registered') + iterable = self._callFUT( + context, request, name='registered', secure=True + ) + self.assertEqual(iterable, ()) + + +class RenderViewTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from pyramid.view import render_view + + return render_view(*arg, **kw) + + def test_call_no_view_registered(self): + request = self._makeRequest() + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_view_registered_secure(self): + request = self._makeRequest() + context = self._makeContext() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + s = self._callFUT(context, request, name='registered', secure=True) + self.assertEqual(s, b'') + + def test_call_view_registered_insecure_no_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + s = self._callFUT(context, request, name='registered', secure=False) + self.assertEqual(s, b'') + + def test_call_view_registered_insecure_with_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + + def anotherview(context, request): + return DummyResponse(b'anotherview') + + view.__call_permissive__ = anotherview + self._registerView(request.registry, view, 'registered') + s = self._callFUT(context, request, name='registered', secure=False) + self.assertEqual(s, b'anotherview') + + +class TestViewConfigDecorator(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getTargetClass(self): + from pyramid.view import view_config + + return view_config + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + def test_create_defaults(self): + decorator = self._makeOne() + self.assertEqual(decorator.__dict__, {}) + + def test_create_context_trumps_for(self): + decorator = self._makeOne(context='123', for_='456') + self.assertEqual(decorator.context, '123') + + def test_create_for_trumps_context_None(self): + decorator = self._makeOne(context=None, for_='456') + self.assertEqual(decorator.context, '456') + + def test_create_nondefaults(self): + decorator = self._makeOne( + name=None, + request_type=None, + for_=None, + permission='foo', + mapper='mapper', + decorator='decorator', + match_param='match_param', + ) + self.assertEqual(decorator.name, None) + self.assertEqual(decorator.request_type, None) + self.assertEqual(decorator.context, None) + self.assertEqual(decorator.permission, 'foo') + self.assertEqual(decorator.mapper, 'mapper') + self.assertEqual(decorator.decorator, 'decorator') + self.assertEqual(decorator.match_param, 'match_param') + + def test_create_with_other_predicates(self): + decorator = self._makeOne(foo=1) + self.assertEqual(decorator.foo, 1) + + def test_create_decorator_tuple(self): + decorator = self._makeOne(decorator=('decorator1', 'decorator2')) + self.assertEqual(decorator.decorator, ('decorator1', 'decorator2')) + + def test_call_function(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 3) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_call_class(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + class foo(object): + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['attr'], 'foo') + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_call_class_attr_already_set(self): + decorator = self._makeOne(attr='abc') + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + class foo(object): + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['attr'], 'abc') + self.assertEqual(settings[0]['_info'], 'codeinfo') + + def test_stacking(self): + decorator1 = self._makeOne(name='1') + venusian1 = DummyVenusian() + decorator1.venusian = venusian1 + venusian2 = DummyVenusian() + decorator2 = self._makeOne(name='2') + decorator2.venusian = venusian2 + + def foo(): # pragma: no cover + pass + + wrapped1 = decorator1(foo) + wrapped2 = decorator2(wrapped1) + self.assertTrue(wrapped1 is foo) + self.assertTrue(wrapped2 is foo) + config1 = call_venusian(venusian1) + self.assertEqual(len(config1.settings), 1) + self.assertEqual(config1.settings[0]['name'], '1') + config2 = call_venusian(venusian2) + self.assertEqual(len(config2.settings), 1) + self.assertEqual(config2.settings[0]['name'], '2') + + def test_call_as_method(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + + def foo(self): # pragma: no cover + pass + + def bar(self): # pragma: no cover + pass + + class foo(object): + foomethod = decorator(foo) + barmethod = decorator(bar) + + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 2) + self.assertEqual(settings[0]['attr'], 'foo') + self.assertEqual(settings[1]['attr'], 'bar') + + def test_with_custom_predicates(self): + decorator = self._makeOne(custom_predicates=(1,)) + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(context, request): # pragma: no cover + pass + + decorated = decorator(foo) + self.assertTrue(decorated is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(settings[0]['custom_predicates'], (1,)) + + def test_call_with_renderer_string(self): + import tests + + decorator = self._makeOne(renderer='fixtures/minimal.pt') + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + renderer = settings[0]['renderer'] + self.assertEqual(renderer, 'fixtures/minimal.pt') + self.assertEqual(config.pkg, tests) + + def test_call_with_renderer_dict(self): + import tests + + decorator = self._makeOne(renderer={'a': 1}) + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + config = call_venusian(venusian) + settings = config.settings + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['renderer'], {'a': 1}) + self.assertEqual(config.pkg, tests) + + def test_call_with_renderer_IRendererInfo(self): + import tests + from pyramid.interfaces import IRendererInfo + + @implementer(IRendererInfo) + class DummyRendererHelper(object): + pass + + renderer_helper = DummyRendererHelper() + decorator = self._makeOne(renderer=renderer_helper) + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + wrapped = decorator(foo) + self.assertTrue(wrapped is foo) + context = DummyVenusianContext() + config = call_venusian(venusian, context) + settings = config.settings + self.assertEqual(len(settings), 1) + renderer = settings[0]['renderer'] + self.assertTrue(renderer is renderer_helper) + self.assertEqual(config.pkg, tests) + + def test_call_withdepth(self): + decorator = self._makeOne(_depth=1) + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + depth = attachments[0][3] + self.assertEqual(depth, 2) + + def test_call_withoutcategory(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + category = attachments[0][2] + self.assertEqual(category, 'pyramid') + + def test_call_withcategory(self): + decorator = self._makeOne(_category='not_pyramid') + venusian = DummyVenusian() + decorator.venusian = venusian + + def foo(): # pragma: no cover + pass + + decorator(foo) + attachments = venusian.attachments + category = attachments[0][2] + self.assertEqual(category, 'not_pyramid') + + +class Test_append_slash_notfound_view(BaseTest, unittest.TestCase): + def _callFUT(self, context, request): + from pyramid.view import append_slash_notfound_view + + return append_slash_notfound_view(context, request) + + def _registerMapper(self, reg, match=True): + from pyramid.interfaces import IRoutesMapper + + class DummyRoute(object): + def __init__(self, val): + self.val = val + + def match(self, path): + return self.val + + class DummyMapper(object): + def __init__(self): + self.routelist = [DummyRoute(match)] + + def get_routes(self): + return self.routelist + + mapper = DummyMapper() + reg.registerUtility(mapper, IRoutesMapper) + return mapper + + def test_context_is_not_exception(self): + request = self._makeRequest(PATH_INFO='/abc') + request.exception = ExceptionResponse() + context = DummyContext() + response = self._callFUT(context, request) + self.assertEqual(response.status, '404 Not Found') + self.assertEqual(response.app_iter, ['Not Found']) + + def test_no_mapper(self): + request = self._makeRequest(PATH_INFO='/abc') + context = ExceptionResponse() + response = self._callFUT(context, request) + self.assertEqual(response.status, '404 Not Found') + + def test_no_path(self): + request = self._makeRequest() + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '404 Not Found') + + def test_mapper_path_already_slash_ending(self): + request = self._makeRequest(PATH_INFO='/abc/') + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '404 Not Found') + + def test_no_route_matches(self): + request = self._makeRequest(PATH_INFO='/abc') + context = ExceptionResponse() + mapper = self._registerMapper(request.registry, True) + mapper.routelist[0].val = None + response = self._callFUT(context, request) + self.assertEqual(response.status, '404 Not Found') + + def test_matches(self): + request = self._makeRequest(PATH_INFO='/abc') + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '307 Temporary Redirect') + self.assertEqual(response.location, '/abc/') + + def test_matches_with_script_name(self): + request = self._makeRequest(PATH_INFO='/abc', SCRIPT_NAME='/foo') + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '307 Temporary Redirect') + self.assertEqual(response.location, '/foo/abc/') + + def test_with_query_string(self): + request = self._makeRequest(PATH_INFO='/abc', QUERY_STRING='a=1&b=2') + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '307 Temporary Redirect') + self.assertEqual(response.location, '/abc/?a=1&b=2') + + +class TestAppendSlashNotFoundViewFactory(BaseTest, unittest.TestCase): + def _makeOne(self, notfound_view): + from pyramid.view import AppendSlashNotFoundViewFactory + + return AppendSlashNotFoundViewFactory(notfound_view) + + def test_custom_notfound_view(self): + request = self._makeRequest(PATH_INFO='/abc') + context = ExceptionResponse() + + def custom_notfound(context, request): + return 'OK' + + view = self._makeOne(custom_notfound) + response = view(context, request) + self.assertEqual(response, 'OK') + + +class Test_default_exceptionresponse_view(unittest.TestCase): + def _callFUT(self, context, request): + from pyramid.view import default_exceptionresponse_view + + return default_exceptionresponse_view(context, request) + + def test_is_exception(self): + context = Exception() + result = self._callFUT(context, None) + self.assertTrue(result is context) + + def test_is_not_exception_context_is_false_still_chose(self): + request = DummyRequest() + request.exception = 0 + result = self._callFUT(None, request) + self.assertTrue(result is None) + + def test_is_not_exception_no_request_exception(self): + context = object() + request = DummyRequest() + request.exception = None + result = self._callFUT(context, request) + self.assertTrue(result is context) + + def test_is_not_exception_request_exception(self): + context = object() + request = DummyRequest() + request.exception = 'abc' + result = self._callFUT(context, request) + self.assertEqual(result, 'abc') + + +class Test_view_defaults(unittest.TestCase): + def test_it(self): + from pyramid.view import view_defaults + + @view_defaults(route_name='abc', renderer='def') + class Foo(object): + pass + + self.assertEqual(Foo.__view_defaults__['route_name'], 'abc') + self.assertEqual(Foo.__view_defaults__['renderer'], 'def') + + def test_it_inheritance_not_overridden(self): + from pyramid.view import view_defaults + + @view_defaults(route_name='abc', renderer='def') + class Foo(object): + pass + + class Bar(Foo): + pass + + self.assertEqual(Bar.__view_defaults__['route_name'], 'abc') + self.assertEqual(Bar.__view_defaults__['renderer'], 'def') + + def test_it_inheritance_overriden(self): + from pyramid.view import view_defaults + + @view_defaults(route_name='abc', renderer='def') + class Foo(object): + pass + + @view_defaults(route_name='ghi') + class Bar(Foo): + pass + + self.assertEqual(Bar.__view_defaults__['route_name'], 'ghi') + self.assertFalse('renderer' in Bar.__view_defaults__) + + def test_it_inheritance_overriden_empty(self): + from pyramid.view import view_defaults + + @view_defaults(route_name='abc', renderer='def') + class Foo(object): + pass + + @view_defaults() + class Bar(Foo): + pass + + self.assertEqual(Bar.__view_defaults__, {}) + + +class TestViewMethodsMixin(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, environ=None): + from pyramid.decorator import reify + from pyramid.view import ViewMethodsMixin + + if environ is None: + environ = {} + + class Request(ViewMethodsMixin): + def __init__(self, environ): + self.environ = environ + + @reify + def response(self): + return DummyResponse() + + request = Request(environ) + request.registry = self.config.registry + return request + + def test_it(self): + def exc_view(exc, request): + self.assertTrue(exc is dummy_exc) + self.assertTrue(request.exception is dummy_exc) + return DummyResponse(b'foo') + + self.config.add_view(exc_view, context=RuntimeError) + request = self._makeOne() + dummy_exc = RuntimeError() + try: + raise dummy_exc + except RuntimeError: + response = request.invoke_exception_view() + self.assertEqual(response.app_iter, [b'foo']) + else: # pragma: no cover + self.fail() + + def test_it_hides_attrs(self): + def exc_view(exc, request): + self.assertTrue(exc is not orig_exc) + self.assertTrue(request.exception is not orig_exc) + self.assertTrue(request.exc_info is not orig_exc_info) + self.assertTrue(request.response is not orig_response) + request.response.app_iter = [b'bar'] + return request.response + + self.config.add_view(exc_view, context=RuntimeError) + request = self._makeOne() + orig_exc = request.exception = DummyContext() + orig_exc_info = request.exc_info = DummyContext() + orig_response = request.response = DummyResponse(b'foo') + try: + raise RuntimeError + except RuntimeError as ex: + response = request.invoke_exception_view() + self.assertEqual(response.app_iter, [b'bar']) + self.assertTrue(request.exception is ex) + self.assertTrue(request.exc_info[1] is ex) + self.assertTrue(request.response is orig_response) + else: # pragma: no cover + self.fail() + + def test_it_supports_alternate_requests(self): + def exc_view(exc, request): + self.assertTrue(request is other_req) + from pyramid.threadlocal import get_current_request + + self.assertTrue(get_current_request() is other_req) + return DummyResponse(b'foo') + + self.config.add_view(exc_view, context=RuntimeError) + request = self._makeOne() + other_req = self._makeOne() + try: + raise RuntimeError + except RuntimeError: + response = request.invoke_exception_view(request=other_req) + self.assertEqual(response.app_iter, [b'foo']) + else: # pragma: no cover + self.fail() + + def test_it_supports_threadlocal_registry(self): + def exc_view(exc, request): + return DummyResponse(b'foo') + + self.config.add_view(exc_view, context=RuntimeError) + request = self._makeOne() + del request.registry + try: + raise RuntimeError + except RuntimeError: + response = request.invoke_exception_view() + self.assertEqual(response.app_iter, [b'foo']) + else: # pragma: no cover + self.fail() + + def test_it_raises_if_no_registry(self): + request = self._makeOne() + del request.registry + from pyramid.threadlocal import manager + + manager.push({'registry': None, 'request': request}) + try: + raise RuntimeError + except RuntimeError: + try: + request.invoke_exception_view() + except RuntimeError as e: + self.assertEqual(e.args[0], "Unable to retrieve registry") + else: # pragma: no cover + self.fail() + finally: + manager.pop() + + def test_it_supports_alternate_exc_info(self): + def exc_view(exc, request): + self.assertTrue(request.exc_info is exc_info) + return DummyResponse(b'foo') + + self.config.add_view(exc_view, context=RuntimeError) + request = self._makeOne() + try: + raise RuntimeError + except RuntimeError: + exc_info = sys.exc_info() + response = request.invoke_exception_view(exc_info=exc_info) + self.assertEqual(response.app_iter, [b'foo']) + + def test_it_rejects_secured_view(self): + from pyramid.exceptions import Forbidden + + def exc_view(exc, request): # pragma: no cover + pass + + self.config.testing_securitypolicy(permissive=False) + self.config.add_view(exc_view, context=RuntimeError, permission='view') + request = self._makeOne() + try: + raise RuntimeError + except RuntimeError: + self.assertRaises(Forbidden, request.invoke_exception_view) + else: # pragma: no cover + self.fail() + + def test_it_allows_secured_view(self): + def exc_view(exc, request): + return DummyResponse(b'foo') + + self.config.testing_securitypolicy(permissive=False) + self.config.add_view(exc_view, context=RuntimeError, permission='view') + request = self._makeOne() + try: + raise RuntimeError + except RuntimeError: + response = request.invoke_exception_view(secure=False) + self.assertEqual(response.app_iter, [b'foo']) + else: # pragma: no cover + self.fail() + + def test_it_raises_if_not_found(self): + from pyramid.httpexceptions import HTTPNotFound + + request = self._makeOne() + dummy_exc = RuntimeError() + try: + raise dummy_exc + except RuntimeError: + self.assertRaises(HTTPNotFound, request.invoke_exception_view) + else: # pragma: no cover + self.fail() + + def test_it_reraises_if_not_found(self): + request = self._makeOne() + dummy_exc = RuntimeError() + try: + raise dummy_exc + except RuntimeError: + self.assertRaises( + RuntimeError, + lambda: request.invoke_exception_view(reraise=True), + ) + else: # pragma: no cover + self.fail() + + def test_it_raises_predicate_mismatch(self): + from pyramid.exceptions import PredicateMismatch + + def exc_view(exc, request): # pragma: no cover + pass + + self.config.add_view( + exc_view, context=Exception, request_method='POST' + ) + request = self._makeOne() + request.method = 'GET' + dummy_exc = RuntimeError() + try: + raise dummy_exc + except RuntimeError: + self.assertRaises(PredicateMismatch, request.invoke_exception_view) + else: # pragma: no cover + self.fail() + + def test_it_reraises_after_predicate_mismatch(self): + def exc_view(exc, request): # pragma: no cover + pass + + self.config.add_view( + exc_view, context=Exception, request_method='POST' + ) + request = self._makeOne() + request.method = 'GET' + dummy_exc = RuntimeError() + try: + raise dummy_exc + except RuntimeError: + self.assertRaises( + RuntimeError, + lambda: request.invoke_exception_view(reraise=True), + ) + else: # pragma: no cover + self.fail() + + +class ExceptionResponse(Exception): + status = '404 Not Found' + app_iter = ['Not Found'] + headerlist = [] + + +class DummyContext: + pass + + +def make_view(response): + def view(context, request): + return response + + return view + + +class DummyRequest: + exception = None + request_iface = IRequest + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + + +@implementer(IResponse) +class DummyResponse(object): + headerlist = () + app_iter = () + status = '200 OK' + environ = None + + def __init__(self, body=None): + if body is None: + self.app_iter = () + else: + self.app_iter = [body] + + +class IContext(Interface): + pass + + +class DummyVenusianInfo(object): + scope = 'notaclass' + module = sys.modules['tests'] + codeinfo = 'codeinfo' + + +class DummyVenusian(object): + def __init__(self, info=None): + if info is None: + info = DummyVenusianInfo() + self.info = info + self.attachments = [] + + def attach(self, wrapped, callback, category=None, depth=1): + self.attachments.append((wrapped, callback, category, depth)) + return self.info + + +class DummyRegistry(object): + pass + + +class DummyConfig(object): + def __init__(self): + self.settings = [] + self.registry = DummyRegistry() + + def add_view(self, **kw): + self.settings.append(kw) + + add_notfound_view = add_forbidden_view = add_exception_view = add_view + + def with_package(self, pkg): + self.pkg = pkg + return self + + +class DummyVenusianContext(object): + def __init__(self): + self.config = DummyConfig() + + +def call_venusian(venusian, context=None): + if context is None: + context = DummyVenusianContext() + for wrapped, callback, category, depth in venusian.attachments: + callback(context, None, None) + return context.config diff --git a/tests/test_viewderivers.py b/tests/test_viewderivers.py new file mode 100644 index 000000000..f01cb490e --- /dev/null +++ b/tests/test_viewderivers.py @@ -0,0 +1,2158 @@ +import unittest +from zope.interface import implementer + +from pyramid import testing +from pyramid.exceptions import ConfigurationError +from pyramid.interfaces import IResponse, IRequest + + +class TestDeriveView(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + self.config.set_default_csrf_options(require_csrf=False) + + def tearDown(self): + self.config = None + testing.tearDown() + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.config.registry + return request + + def _registerLogger(self): + from pyramid.interfaces import IDebugLogger + + logger = DummyLogger() + self.config.registry.registerUtility(logger, IDebugLogger) + return logger + + def _registerSecurityPolicy(self, permissive): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + + policy = DummySecurityPolicy(permissive) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) + + def test_function_returns_non_adaptable(self): + def view(request): + return None + + result = self.config.derive_view(view) + self.assertFalse(result is view) + try: + result(None, None) + except ValueError as e: + self.assertEqual( + e.args[0], + 'Could not convert return value of the view callable function ' + 'tests.test_viewderivers.view into a response ' + 'object. The value returned was None. You may have forgotten ' + 'to return a value from the view callable.', + ) + else: # pragma: no cover + raise AssertionError + + def test_function_returns_non_adaptable_dict(self): + def view(request): + return {'a': 1} + + result = self.config.derive_view(view) + self.assertFalse(result is view) + try: + result(None, None) + except ValueError as e: + self.assertEqual( + e.args[0], + "Could not convert return value of the view callable function " + "tests.test_viewderivers.view into a response " + "object. The value returned was {'a': 1}. You may have " + "forgotten to define a renderer in the view configuration.", + ) + else: # pragma: no cover + raise AssertionError + + def test_instance_returns_non_adaptable(self): + class AView(object): + def __call__(self, request): + return None + + view = AView() + result = self.config.derive_view(view) + self.assertFalse(result is view) + try: + result(None, None) + except ValueError as e: + msg = e.args[0] + self.assertTrue( + msg.startswith( + 'Could not convert return value of the view callable ' + 'object <tests.test_viewderivers.' + ) + ) + self.assertTrue( + msg.endswith( + '> into a response object. The value returned was None. ' + 'You may have forgotten to return a value from the view ' + 'callable.' + ) + ) + else: # pragma: no cover + raise AssertionError + + def test_function_returns_true_Response_no_renderer(self): + from pyramid.response import Response + + r = Response('Hello') + + def view(request): + return r + + result = self.config.derive_view(view) + self.assertFalse(result is view) + response = result(None, None) + self.assertEqual(response, r) + + def test_function_returns_true_Response_with_renderer(self): + from pyramid.response import Response + + r = Response('Hello') + + def view(request): + return r + + result = self.config.derive_view(view) + self.assertFalse(result is view) + response = result(None, None) + self.assertEqual(response, r) + + def test_requestonly_default_method_returns_non_adaptable(self): + request = DummyRequest() + + class AView(object): + def __init__(self, request): + pass + + def __call__(self): + return None + + result = self.config.derive_view(AView) + self.assertFalse(result is AView) + try: + result(None, request) + except ValueError as e: + self.assertEqual( + e.args[0], + 'Could not convert return value of the view callable ' + 'method __call__ of ' + 'class tests.test_viewderivers.AView into a ' + 'response object. The value returned was None. You may have ' + 'forgotten to return a value from the view callable.', + ) + else: # pragma: no cover + raise AssertionError + + def test_requestonly_nondefault_method_returns_non_adaptable(self): + request = DummyRequest() + + class AView(object): + def __init__(self, request): + pass + + def theviewmethod(self): + return None + + result = self.config.derive_view(AView, attr='theviewmethod') + self.assertFalse(result is AView) + try: + result(None, request) + except ValueError as e: + self.assertEqual( + e.args[0], + 'Could not convert return value of the view callable ' + 'method theviewmethod of ' + 'class tests.test_viewderivers.AView into a ' + 'response object. The value returned was None. You may have ' + 'forgotten to return a value from the view callable.', + ) + else: # pragma: no cover + raise AssertionError + + def test_requestonly_function(self): + response = DummyResponse() + + def view(request): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(result(None, None), response) + + def test_requestonly_function_with_renderer(self): + response = DummyResponse() + + class moo(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, 'OK') + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + def view(request): + return 'OK' + + result = self.config.derive_view(view, renderer=moo()) + self.assertFalse(result.__wraps__ is view) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_requestonly_function_with_renderer_request_override(self): + def moo(info): + def inner(value, system): + self.assertEqual(value, 'OK') + self.assertEqual(system['request'], request) + self.assertEqual(system['context'], context) + return b'moo' + + return inner + + def view(request): + return 'OK' + + self.config.add_renderer('moo', moo) + result = self.config.derive_view(view, renderer='string') + self.assertFalse(result is view) + request = self._makeRequest() + request.override_renderer = 'moo' + context = testing.DummyResource() + self.assertEqual(result(context, request).body, b'moo') + + def test_requestonly_function_with_renderer_request_has_view(self): + response = DummyResponse() + + class moo(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, 'OK') + self.assertEqual(view_inst, 'view') + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + def view(request): + return 'OK' + + result = self.config.derive_view(view, renderer=moo()) + self.assertFalse(result.__wraps__ is view) + request = self._makeRequest() + request.__view__ = 'view' + context = testing.DummyResource() + r = result(context, request) + self.assertEqual(r, response) + self.assertFalse(hasattr(request, '__view__')) + + def test_class_without_attr(self): + response = DummyResponse() + + class View(object): + def __init__(self, request): + pass + + def __call__(self): + return response + + result = self.config.derive_view(View) + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, View) + + def test_class_with_attr(self): + response = DummyResponse() + + class View(object): + def __init__(self, request): + pass + + def another(self): + return response + + result = self.config.derive_view(View, attr='another') + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, View) + + def test_as_function_context_and_request(self): + def view(context, request): + return 'OK' + + result = self.config.derive_view(view) + self.assertTrue(result.__wraps__ is view) + self.assertFalse(hasattr(result, '__call_permissive__')) + self.assertEqual(view(None, None), 'OK') + + def test_as_function_requestonly(self): + response = DummyResponse() + + def view(request): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), response) + + def test_as_newstyle_class_context_and_request(self): + response = DummyResponse() + + class view(object): + def __init__(self, context, request): + pass + + def __call__(self): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, view) + + def test_as_newstyle_class_requestonly(self): + response = DummyResponse() + + class view(object): + def __init__(self, context, request): + pass + + def __call__(self): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, view) + + def test_as_oldstyle_class_context_and_request(self): + response = DummyResponse() + + class view: + def __init__(self, context, request): + pass + + def __call__(self): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, view) + + def test_as_oldstyle_class_requestonly(self): + response = DummyResponse() + + class view: + def __init__(self, context, request): + pass + + def __call__(self): + return response + + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), response) + self.assertEqual(request.__view__.__class__, view) + + def test_as_instance_context_and_request(self): + response = DummyResponse() + + class View: + def __call__(self, context, request): + return response + + view = View() + result = self.config.derive_view(view) + self.assertTrue(result.__wraps__ is view) + self.assertFalse(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), response) + + def test_as_instance_requestonly(self): + response = DummyResponse() + + class View: + def __call__(self, request): + return response + + view = View() + result = self.config.derive_view(view) + self.assertFalse(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertTrue('test_viewderivers' in result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), response) + + def test_with_debug_authorization_no_authpol(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed " + "(no authorization policy in use)", + ) + + def test_with_debug_authorization_authn_policy_no_authz_policy(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict(debug_authorization=True) + from pyramid.interfaces import IAuthenticationPolicy + + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + logger = self._registerLogger() + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed " + "(no authorization policy in use)", + ) + + def test_with_debug_authorization_authz_policy_no_authn_policy(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict(debug_authorization=True) + from pyramid.interfaces import IAuthorizationPolicy + + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) + logger = self._registerLogger() + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed " + "(no authorization policy in use)", + ) + + def test_with_debug_authorization_no_permission(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + self._registerSecurityPolicy(True) + logger = self._registerLogger() + result = self.config._derive_view(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed (" + "no permission registered)", + ) + + def test_debug_auth_permission_authpol_permitted(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + self._registerSecurityPolicy(True) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__.__wraps__, view) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): True", + ) + + def test_debug_auth_permission_authpol_permitted_no_request(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + self._registerSecurityPolicy(True) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__.__wraps__, view) + self.assertEqual(result(None, None), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url None (view name " + "None against context None): True", + ) + + def test_debug_auth_permission_authpol_denied(self): + from pyramid.httpexceptions import HTTPForbidden + + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + self._registerSecurityPolicy(False) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__.__wraps__, view) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertRaises(HTTPForbidden, result, None, request) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): False", + ) + + def test_debug_auth_permission_authpol_denied2(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + self._registerLogger() + self._registerSecurityPolicy(False) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + permitted = result.__permitted__(None, None) + self.assertEqual(permitted, False) + + def test_debug_auth_permission_authpol_overridden(self): + from pyramid.security import NO_PERMISSION_REQUIRED + + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + self._registerSecurityPolicy(False) + result = self.config._derive_view( + view, permission=NO_PERMISSION_REQUIRED + ) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): " + "Allowed (NO_PERMISSION_REQUIRED)", + ) + + def test_debug_auth_permission_authpol_permitted_excview(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True + ) + logger = self._registerLogger() + self._registerSecurityPolicy(True) + result = self.config._derive_view( + view, context=Exception, permission='view' + ) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__.__wraps__, view) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(Exception(), request), response) + self.assertEqual(len(logger.messages), 1) + self.assertEqual( + logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context Exception()): True", + ) + + def test_secured_view_authn_policy_no_authz_policy(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = {} + from pyramid.interfaces import IAuthenticationPolicy + + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + + def test_secured_view_authz_policy_no_authn_policy(self): + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = {} + from pyramid.interfaces import IAuthorizationPolicy + + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) + result = self.config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertFalse(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), response) + + def test_secured_view_raises_forbidden_no_name(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + from pyramid.httpexceptions import HTTPForbidden + + response = DummyResponse() + view = lambda *arg: response + self.config.registry.settings = {} + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) + result = self.config._derive_view(view, permission='view') + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + try: + result(None, request) + except HTTPForbidden as e: + self.assertEqual( + e.message, 'Unauthorized: <lambda> failed permission check' + ) + else: # pragma: no cover + raise AssertionError + + def test_secured_view_raises_forbidden_with_name(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + from pyramid.httpexceptions import HTTPForbidden + + def myview(request): # pragma: no cover + pass + + self.config.registry.settings = {} + policy = DummySecurityPolicy(False) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) + result = self.config._derive_view(myview, permission='view') + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + try: + result(None, request) + except HTTPForbidden as e: + self.assertEqual( + e.message, 'Unauthorized: myview failed permission check' + ) + else: # pragma: no cover + raise AssertionError + + def test_secured_view_skipped_by_default_on_exception_view(self): + from pyramid.request import Request + from pyramid.security import NO_PERMISSION_REQUIRED + + def view(request): + raise ValueError + + def excview(request): + return 'hello' + + self._registerSecurityPolicy(False) + self.config.add_settings({'debug_authorization': True}) + self.config.set_default_permission('view') + self.config.add_view( + view, name='foo', permission=NO_PERMISSION_REQUIRED + ) + self.config.add_view(excview, context=ValueError, renderer='string') + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + response = request.get_response(app) + self.assertTrue(b'hello' in response.body) + + def test_secured_view_failed_on_explicit_exception_view(self): + from pyramid.httpexceptions import HTTPForbidden + from pyramid.request import Request + from pyramid.security import NO_PERMISSION_REQUIRED + + def view(request): + raise ValueError + + def excview(request): # pragma: no cover + pass + + self._registerSecurityPolicy(False) + self.config.add_view( + view, name='foo', permission=NO_PERMISSION_REQUIRED + ) + self.config.add_view( + excview, context=ValueError, renderer='string', permission='view' + ) + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + try: + request.get_response(app) + except HTTPForbidden: + pass + else: # pragma: no cover + raise AssertionError + + def test_secured_view_passed_on_explicit_exception_view(self): + from pyramid.request import Request + from pyramid.security import NO_PERMISSION_REQUIRED + + def view(request): + raise ValueError + + def excview(request): + return 'hello' + + self._registerSecurityPolicy(True) + self.config.add_view( + view, name='foo', permission=NO_PERMISSION_REQUIRED + ) + self.config.add_view( + excview, context=ValueError, renderer='string', permission='view' + ) + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + request.headers['X-CSRF-Token'] = 'foo' + response = request.get_response(app) + self.assertTrue(b'hello' in response.body) + + def test_predicate_mismatch_view_has_no_name(self): + from pyramid.exceptions import PredicateMismatch + + response = DummyResponse() + view = lambda *arg: response + + def predicate1(context, request): + return False + + predicate1.text = lambda *arg: 'text' + result = self.config._derive_view(view, predicates=[predicate1]) + request = self._makeRequest() + request.method = 'POST' + try: + result(None, None) + except PredicateMismatch as e: + self.assertEqual( + e.detail, 'predicate mismatch for view <lambda> (text)' + ) + else: # pragma: no cover + raise AssertionError + + def test_predicate_mismatch_view_has_name(self): + from pyramid.exceptions import PredicateMismatch + + def myview(request): # pragma: no cover + pass + + def predicate1(context, request): + return False + + predicate1.text = lambda *arg: 'text' + result = self.config._derive_view(myview, predicates=[predicate1]) + request = self._makeRequest() + request.method = 'POST' + try: + result(None, None) + except PredicateMismatch as e: + self.assertEqual( + e.detail, 'predicate mismatch for view myview (text)' + ) + else: # pragma: no cover + raise AssertionError + + def test_predicate_mismatch_exception_has_text_in_detail(self): + from pyramid.exceptions import PredicateMismatch + + def myview(request): # pragma: no cover + pass + + def predicate1(context, request): + return True + + predicate1.text = lambda *arg: 'pred1' + + def predicate2(context, request): + return False + + predicate2.text = lambda *arg: 'pred2' + result = self.config._derive_view( + myview, predicates=[predicate1, predicate2] + ) + request = self._makeRequest() + request.method = 'POST' + try: + result(None, None) + except PredicateMismatch as e: + self.assertEqual( + e.detail, 'predicate mismatch for view myview (pred2)' + ) + else: # pragma: no cover + raise AssertionError + + def test_with_predicates_all(self): + response = DummyResponse() + view = lambda *arg: response + predicates = [] + + def predicate1(context, request): + predicates.append(True) + return True + + def predicate2(context, request): + predicates.append(True) + return True + + result = self.config._derive_view( + view, predicates=[predicate1, predicate2] + ) + request = self._makeRequest() + request.method = 'POST' + next = result(None, None) + self.assertEqual(next, response) + self.assertEqual(predicates, [True, True]) + + def test_with_predicates_checker(self): + view = lambda *arg: 'OK' + predicates = [] + + def predicate1(context, request): + predicates.append(True) + return True + + def predicate2(context, request): + predicates.append(True) + return True + + result = self.config._derive_view( + view, predicates=[predicate1, predicate2] + ) + request = self._makeRequest() + request.method = 'POST' + next = result.__predicated__(None, None) + self.assertEqual(next, True) + self.assertEqual(predicates, [True, True]) + + def test_with_predicates_notall(self): + from pyramid.httpexceptions import HTTPNotFound + + view = lambda *arg: 'OK' + predicates = [] + + def predicate1(context, request): + predicates.append(True) + return True + + predicate1.text = lambda *arg: 'text' + + def predicate2(context, request): + predicates.append(True) + return False + + predicate2.text = lambda *arg: 'text' + result = self.config._derive_view( + view, predicates=[predicate1, predicate2] + ) + request = self._makeRequest() + request.method = 'POST' + self.assertRaises(HTTPNotFound, result, None, None) + self.assertEqual(predicates, [True, True]) + + def test_with_wrapper_viewname(self): + from pyramid.response import Response + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + + inner_response = Response('OK') + + def inner_view(context, request): + return inner_response + + def outer_view(context, request): + self.assertEqual(request.wrapped_response, inner_response) + self.assertEqual(request.wrapped_body, inner_response.body) + self.assertEqual( + request.wrapped_view.__original_view__, inner_view + ) + return Response(b'outer ' + request.wrapped_body) + + self.config.registry.registerAdapter( + outer_view, (IViewClassifier, None, None), IView, 'owrap' + ) + result = self.config._derive_view( + inner_view, viewname='inner', wrapper_viewname='owrap' + ) + self.assertFalse(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + response = result(None, request) + self.assertEqual(response.body, b'outer OK') + + def test_with_wrapper_viewname_notfound(self): + from pyramid.response import Response + + inner_response = Response('OK') + + def inner_view(context, request): + return inner_response + + wrapped = self.config._derive_view( + inner_view, viewname='inner', wrapper_viewname='owrap' + ) + request = self._makeRequest() + self.assertRaises(ValueError, wrapped, None, request) + + def test_as_newstyle_class_context_and_request_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View(object): + def __init__(self, context, request): + pass + + def index(self): + return {'a': '1'} + + result = self.config._derive_view( + View, renderer=renderer(), attr='index' + ) + self.assertFalse(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_as_newstyle_class_requestonly_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View(object): + def __init__(self, request): + pass + + def index(self): + return {'a': '1'} + + result = self.config.derive_view( + View, renderer=renderer(), attr='index' + ) + self.assertFalse(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_as_oldstyle_cls_context_request_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View: + def __init__(self, context, request): + pass + + def index(self): + return {'a': '1'} + + result = self.config.derive_view( + View, renderer=renderer(), attr='index' + ) + self.assertFalse(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_as_oldstyle_cls_requestonly_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View: + def __init__(self, request): + pass + + def index(self): + return {'a': '1'} + + result = self.config.derive_view( + View, renderer=renderer(), attr='index' + ) + self.assertFalse(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_as_instance_context_and_request_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View: + def index(self, context, request): + return {'a': '1'} + + view = View() + result = self.config.derive_view( + view, renderer=renderer(), attr='index' + ) + self.assertFalse(result is view) + self.assertEqual(result.__module__, view.__module__) + self.assertEqual(result.__doc__, view.__doc__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_as_instance_requestonly_attr_and_renderer(self): + response = DummyResponse() + + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a': '1'}) + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return response + + def clone(self): + return self + + class View: + def index(self, request): + return {'a': '1'} + + view = View() + result = self.config.derive_view( + view, renderer=renderer(), attr='index' + ) + self.assertFalse(result is view) + self.assertEqual(result.__module__, view.__module__) + self.assertEqual(result.__doc__, view.__doc__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), response) + + def test_with_view_mapper_config_specified(self): + response = DummyResponse() + + class mapper(object): + def __init__(self, **kw): + self.kw = kw + + def __call__(self, view): + def wrapped(context, request): + return response + + return wrapped + + def view(context, request): # pragma: no cover + return 'NOTOK' + + result = self.config._derive_view(view, mapper=mapper) + self.assertFalse(result.__wraps__ is view) + self.assertEqual(result(None, None), response) + + def test_with_view_mapper_view_specified(self): + from pyramid.response import Response + + response = Response() + + def mapper(**kw): + def inner(view): + def superinner(context, request): + self.assertEqual(request, None) + return response + + return superinner + + return inner + + def view(context, request): # pragma: no cover + return 'NOTOK' + + view.__view_mapper__ = mapper + result = self.config.derive_view(view) + self.assertFalse(result.__wraps__ is view) + self.assertEqual(result(None, None), response) + + def test_with_view_mapper_default_mapper_specified(self): + from pyramid.response import Response + + response = Response() + + def mapper(**kw): + def inner(view): + def superinner(context, request): + self.assertEqual(request, None) + return response + + return superinner + + return inner + + self.config.set_view_mapper(mapper) + + def view(context, request): # pragma: no cover + return 'NOTOK' + + result = self.config.derive_view(view) + self.assertFalse(result.__wraps__ is view) + self.assertEqual(result(None, None), response) + + def test_attr_wrapped_view_branching_default_phash(self): + from pyramid.config.predicates import DEFAULT_PHASH + + def view(context, request): # pragma: no cover + pass + + result = self.config._derive_view(view, phash=DEFAULT_PHASH) + self.assertEqual(result.__wraps__, view) + + def test_attr_wrapped_view_branching_nondefault_phash(self): + def view(context, request): # pragma: no cover + pass + + result = self.config._derive_view(view, phash='nondefault') + self.assertNotEqual(result, view) + + def test_http_cached_view_integer(self): + import datetime + from pyramid.response import Response + + response = Response('OK') + + def inner_view(context, request): + return response + + result = self.config._derive_view(inner_view, http_cache=3600) + self.assertFalse(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) + result = result(None, request) + self.assertEqual(result, response) + headers = dict(result.headerlist) + expires = parse_httpdate(headers['Expires']) + assert_similar_datetime(expires, when) + self.assertEqual(headers['Cache-Control'], 'max-age=3600') + + def test_http_cached_view_timedelta(self): + import datetime + from pyramid.response import Response + + response = Response('OK') + + def inner_view(context, request): + return response + + result = self.config._derive_view( + inner_view, http_cache=datetime.timedelta(hours=1) + ) + self.assertFalse(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) + result = result(None, request) + self.assertEqual(result, response) + headers = dict(result.headerlist) + expires = parse_httpdate(headers['Expires']) + assert_similar_datetime(expires, when) + self.assertEqual(headers['Cache-Control'], 'max-age=3600') + + def test_http_cached_view_tuple(self): + import datetime + from pyramid.response import Response + + response = Response('OK') + + def inner_view(context, request): + return response + + result = self.config._derive_view( + inner_view, http_cache=(3600, {'public': True}) + ) + self.assertFalse(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + when = datetime.datetime.utcnow() + datetime.timedelta(hours=1) + result = result(None, request) + self.assertEqual(result, response) + headers = dict(result.headerlist) + expires = parse_httpdate(headers['Expires']) + assert_similar_datetime(expires, when) + self.assertEqual(headers['Cache-Control'], 'max-age=3600, public') + + def test_http_cached_view_tuple_seconds_None(self): + from pyramid.response import Response + + response = Response('OK') + + def inner_view(context, request): + return response + + result = self.config._derive_view( + inner_view, http_cache=(None, {'public': True}) + ) + self.assertFalse(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + result = result(None, request) + self.assertEqual(result, response) + headers = dict(result.headerlist) + self.assertFalse('Expires' in headers) + self.assertEqual(headers['Cache-Control'], 'public') + + def test_http_cached_view_prevent_auto_set(self): + from pyramid.response import Response + + response = Response() + response.cache_control.prevent_auto = True + + def inner_view(context, request): + return response + + result = self.config._derive_view(inner_view, http_cache=3600) + request = self._makeRequest() + result = result(None, request) + self.assertEqual(result, response) # doesn't blow up + headers = dict(result.headerlist) + self.assertFalse('Expires' in headers) + self.assertFalse('Cache-Control' in headers) + + def test_http_cached_prevent_http_cache_in_settings(self): + self.config.registry.settings['prevent_http_cache'] = True + from pyramid.response import Response + + response = Response() + + def inner_view(context, request): + return response + + result = self.config._derive_view(inner_view, http_cache=3600) + request = self._makeRequest() + result = result(None, request) + self.assertEqual(result, response) + headers = dict(result.headerlist) + self.assertFalse('Expires' in headers) + self.assertFalse('Cache-Control' in headers) + + def test_http_cached_view_bad_tuple(self): + def view(request): # pragma: no cover + pass + + self.assertRaises( + ConfigurationError, + self.config._derive_view, + view, + http_cache=(None,), + ) + + def test_csrf_view_ignores_GET(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.method = 'GET' + view = self.config._derive_view(inner_view, require_csrf=True) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_fails_with_bad_POST_header(self): + from pyramid.exceptions import BadCSRFToken + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.headers = {'X-CSRF-Token': 'bar'} + view = self.config._derive_view(inner_view, require_csrf=True) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_passes_with_good_POST_header(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.headers = {'X-CSRF-Token': 'foo'} + view = self.config._derive_view(inner_view, require_csrf=True) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_fails_with_bad_POST_token(self): + from pyramid.exceptions import BadCSRFToken + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'csrf_token': 'bar'} + view = self.config._derive_view(inner_view, require_csrf=True) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_passes_with_good_POST_token(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'csrf_token': 'foo'} + view = self.config._derive_view(inner_view, require_csrf=True) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_https_domain(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "https" + request.domain = "example.com" + request.host_port = "443" + request.referrer = "https://example.com/login/" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'csrf_token': 'foo'} + view = self.config._derive_view(inner_view, require_csrf=True) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_fails_on_bad_PUT_header(self): + from pyramid.exceptions import BadCSRFToken + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.scheme = "http" + request.method = 'PUT' + request.session = DummySession({'csrf_token': 'foo'}) + request.headers = {'X-CSRF-Token': 'bar'} + view = self.config._derive_view(inner_view, require_csrf=True) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_fails_on_bad_referrer(self): + from pyramid.exceptions import BadCSRFOrigin + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.method = "POST" + request.scheme = "https" + request.host_port = "443" + request.domain = "example.com" + request.referrer = "https://not-example.com/evil/" + request.registry.settings = {} + view = self.config._derive_view(inner_view, require_csrf=True) + self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) + + def test_csrf_view_fails_on_bad_origin(self): + from pyramid.exceptions import BadCSRFOrigin + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.method = "POST" + request.scheme = "https" + request.host_port = "443" + request.domain = "example.com" + request.headers = {"Origin": "https://not-example.com/evil/"} + request.registry.settings = {} + view = self.config._derive_view(inner_view, require_csrf=True) + self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) + + def test_csrf_view_enabled_by_default(self): + from pyramid.exceptions import BadCSRFToken + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options(require_csrf=True) + view = self.config._derive_view(inner_view) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_enabled_via_callback(self): + def callback(request): + return True + + from pyramid.exceptions import BadCSRFToken + + def inner_view(request): # pragma: no cover + pass + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options( + require_csrf=True, callback=callback + ) + view = self.config._derive_view(inner_view) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_disabled_via_callback(self): + def callback(request): + return False + + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options( + require_csrf=True, callback=callback + ) + view = self.config._derive_view(inner_view) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_uses_custom_csrf_token(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'DUMMY': 'foo'} + self.config.set_default_csrf_options(require_csrf=True, token='DUMMY') + view = self.config._derive_view(inner_view) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_uses_custom_csrf_header(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.headers = {'DUMMY': 'foo'} + self.config.set_default_csrf_options(require_csrf=True, header='DUMMY') + view = self.config._derive_view(inner_view) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_uses_custom_methods(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'PUT' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options( + require_csrf=True, safe_methods=['PUT'] + ) + view = self.config._derive_view(inner_view) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_uses_view_option_override(self): + response = DummyResponse() + + def inner_view(request): + return response + + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + request.POST = {'csrf_token': 'bar'} + self.config.set_default_csrf_options(require_csrf=True) + view = self.config._derive_view(inner_view, require_csrf=False) + result = view(None, request) + self.assertTrue(result is response) + + def test_csrf_view_skipped_by_default_on_exception_view(self): + from pyramid.request import Request + + def view(request): + raise ValueError + + def excview(request): + return 'hello' + + self.config.set_default_csrf_options(require_csrf=True) + self.config.set_session_factory( + lambda request: DummySession({'csrf_token': 'foo'}) + ) + self.config.add_view(view, name='foo', require_csrf=False) + self.config.add_view(excview, context=ValueError, renderer='string') + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + response = request.get_response(app) + self.assertTrue(b'hello' in response.body) + + def test_csrf_view_failed_on_explicit_exception_view(self): + from pyramid.exceptions import BadCSRFToken + from pyramid.request import Request + + def view(request): + raise ValueError + + def excview(request): # pragma: no cover + pass + + self.config.set_default_csrf_options(require_csrf=True) + self.config.set_session_factory( + lambda request: DummySession({'csrf_token': 'foo'}) + ) + self.config.add_view(view, name='foo', require_csrf=False) + self.config.add_view( + excview, context=ValueError, renderer='string', require_csrf=True + ) + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + try: + request.get_response(app) + except BadCSRFToken: + pass + else: # pragma: no cover + raise AssertionError + + def test_csrf_view_passed_on_explicit_exception_view(self): + from pyramid.request import Request + + def view(request): + raise ValueError + + def excview(request): + return 'hello' + + self.config.set_default_csrf_options(require_csrf=True) + self.config.set_session_factory( + lambda request: DummySession({'csrf_token': 'foo'}) + ) + self.config.add_view(view, name='foo', require_csrf=False) + self.config.add_view( + excview, context=ValueError, renderer='string', require_csrf=True + ) + app = self.config.make_wsgi_app() + request = Request.blank('/foo', base_url='http://example.com') + request.method = 'POST' + request.headers['X-CSRF-Token'] = 'foo' + response = request.get_response(app) + self.assertTrue(b'hello' in response.body) + + +class TestDerivationOrder(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + self.config = None + testing.tearDown() + + def test_right_order_user_sorted(self): + from pyramid.interfaces import IViewDerivers + + self.config.add_view_deriver(None, 'deriv1') + self.config.add_view_deriver( + None, 'deriv2', 'decorated_view', 'deriv1' + ) + self.config.add_view_deriver(None, 'deriv3', 'deriv2', 'deriv1') + + derivers = self.config.registry.getUtility(IViewDerivers) + derivers_sorted = derivers.sorted() + dlist = [d for (d, _) in derivers_sorted] + self.assertEqual( + [ + 'secured_view', + 'csrf_view', + 'owrapped_view', + 'http_cached_view', + 'decorated_view', + 'deriv2', + 'deriv3', + 'deriv1', + 'rendered_view', + 'mapped_view', + ], + dlist, + ) + + def test_right_order_implicit(self): + from pyramid.interfaces import IViewDerivers + + self.config.add_view_deriver(None, 'deriv1') + self.config.add_view_deriver(None, 'deriv2') + self.config.add_view_deriver(None, 'deriv3') + + derivers = self.config.registry.getUtility(IViewDerivers) + derivers_sorted = derivers.sorted() + dlist = [d for (d, _) in derivers_sorted] + self.assertEqual( + [ + 'secured_view', + 'csrf_view', + 'owrapped_view', + 'http_cached_view', + 'decorated_view', + 'deriv3', + 'deriv2', + 'deriv1', + 'rendered_view', + 'mapped_view', + ], + dlist, + ) + + def test_right_order_under_rendered_view(self): + from pyramid.interfaces import IViewDerivers + + self.config.add_view_deriver( + None, 'deriv1', 'rendered_view', 'mapped_view' + ) + + derivers = self.config.registry.getUtility(IViewDerivers) + derivers_sorted = derivers.sorted() + dlist = [d for (d, _) in derivers_sorted] + self.assertEqual( + [ + 'secured_view', + 'csrf_view', + 'owrapped_view', + 'http_cached_view', + 'decorated_view', + 'rendered_view', + 'deriv1', + 'mapped_view', + ], + dlist, + ) + + def test_right_order_under_rendered_view_others(self): + from pyramid.interfaces import IViewDerivers + + self.config.add_view_deriver( + None, 'deriv1', 'rendered_view', 'mapped_view' + ) + self.config.add_view_deriver(None, 'deriv2') + self.config.add_view_deriver(None, 'deriv3') + + derivers = self.config.registry.getUtility(IViewDerivers) + derivers_sorted = derivers.sorted() + dlist = [d for (d, _) in derivers_sorted] + self.assertEqual( + [ + 'secured_view', + 'csrf_view', + 'owrapped_view', + 'http_cached_view', + 'decorated_view', + 'deriv3', + 'deriv2', + 'rendered_view', + 'deriv1', + 'mapped_view', + ], + dlist, + ) + + +class TestAddDeriver(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + self.config = None + testing.tearDown() + + def test_add_single_deriver(self): + response = DummyResponse() + response.deriv = False + view = lambda *arg: response + + def deriv(view, info): + self.assertFalse(response.deriv) + response.deriv = True + return view + + result = self.config._derive_view(view) + self.assertFalse(response.deriv) + self.config.add_view_deriver(deriv, 'test_deriv') + + result = self.config._derive_view(view) # noqa: F841 + self.assertTrue(response.deriv) + + def test_override_deriver(self): + flags = {} + + class AView: + def __init__(self): + self.response = DummyResponse() + + def deriv1(view, info): + flags['deriv1'] = True + return view + + def deriv2(view, info): + flags['deriv2'] = True + return view + + view1 = AView() + self.config.add_view_deriver(deriv1, 'test_deriv') + result = self.config._derive_view(view1) + self.assertTrue(flags.get('deriv1')) + self.assertFalse(flags.get('deriv2')) + + flags.clear() + view2 = AView() + self.config.add_view_deriver(deriv2, 'test_deriv') + result = self.config._derive_view(view2) # noqa: F841 + self.assertFalse(flags.get('deriv1')) + self.assertTrue(flags.get('deriv2')) + + def test_override_mapped_view(self): + from pyramid.viewderivers import VIEW + + response = DummyResponse() + view = lambda *arg: response + flags = {} + + def deriv1(view, info): + flags['deriv1'] = True + return view + + result = self.config._derive_view(view) + self.assertFalse(flags.get('deriv1')) + + flags.clear() + self.config.add_view_deriver( + deriv1, name='mapped_view', under='rendered_view', over=VIEW + ) + result = self.config._derive_view(view) # noqa: F841 + self.assertTrue(flags.get('deriv1')) + + def test_add_multi_derivers_ordered(self): + from pyramid.viewderivers import INGRESS + + response = DummyResponse() + view = lambda *arg: response + response.deriv = [] + + def deriv1(view, info): + response.deriv.append('deriv1') + return view + + def deriv2(view, info): + response.deriv.append('deriv2') + return view + + def deriv3(view, info): + response.deriv.append('deriv3') + return view + + self.config.add_view_deriver(deriv1, 'deriv1') + self.config.add_view_deriver(deriv2, 'deriv2', INGRESS, 'deriv1') + self.config.add_view_deriver(deriv3, 'deriv3', 'deriv2', 'deriv1') + result = self.config._derive_view(view) # noqa: F841 + self.assertEqual(response.deriv, ['deriv1', 'deriv3', 'deriv2']) + + def test_add_deriver_without_name(self): + from pyramid.interfaces import IViewDerivers + + def deriv1(view, info): # pragma: no cover + pass + + self.config.add_view_deriver(deriv1) + derivers = self.config.registry.getUtility(IViewDerivers) + self.assertTrue('deriv1' in derivers.names) + + def test_add_deriver_reserves_ingress(self): + from pyramid.exceptions import ConfigurationError + from pyramid.viewderivers import INGRESS + + def deriv1(view, info): # pragma: no cover + pass + + self.assertRaises( + ConfigurationError, self.config.add_view_deriver, deriv1, INGRESS + ) + + def test_add_deriver_enforces_ingress_is_first(self): + from pyramid.exceptions import ConfigurationError + from pyramid.viewderivers import INGRESS + + def deriv1(view, info): # pragma: no cover + pass + + try: + self.config.add_view_deriver(deriv1, over=INGRESS) + except ConfigurationError as ex: + self.assertTrue('cannot be over INGRESS' in ex.args[0]) + else: # pragma: no cover + raise AssertionError + + def test_add_deriver_enforces_view_is_last(self): + from pyramid.exceptions import ConfigurationError + from pyramid.viewderivers import VIEW + + def deriv1(view, info): # pragma: no cover + pass + + try: + self.config.add_view_deriver(deriv1, under=VIEW) + except ConfigurationError as ex: + self.assertTrue('cannot be under VIEW' in ex.args[0]) + else: # pragma: no cover + raise AssertionError + + def test_add_deriver_enforces_mapped_view_is_last(self): + from pyramid.exceptions import ConfigurationError + + def deriv1(view, info): # pragma: no cover + pass + + try: + self.config.add_view_deriver(deriv1, 'deriv1', under='mapped_view') + except ConfigurationError as ex: + self.assertTrue('cannot be under "mapped_view"' in ex.args[0]) + else: # pragma: no cover + raise AssertionError + + +class TestDeriverIntegration(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + self.config = None + testing.tearDown() + + def _getViewCallable( + self, config, ctx_iface=None, request_iface=None, name='' + ): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), + IView, + name=name, + default=None, + ) + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def test_view_options(self): + response = DummyResponse() + view = lambda *arg: response + response.deriv = [] + + def deriv1(view, info): + response.deriv.append(info.options['deriv1']) + return view + + deriv1.options = ('deriv1',) + + def deriv2(view, info): + response.deriv.append(info.options['deriv2']) + return view + + deriv2.options = ('deriv2',) + + self.config.add_view_deriver(deriv1, 'deriv1') + self.config.add_view_deriver(deriv2, 'deriv2') + self.config.add_view(view, deriv1='test1', deriv2='test2') + + wrapper = self._getViewCallable(self.config) + request = self._makeRequest(self.config) + request.method = 'GET' + self.assertEqual(wrapper(None, request), response) + self.assertEqual(['test1', 'test2'], response.deriv) + + def test_unexpected_view_options(self): + from pyramid.exceptions import ConfigurationError + + def deriv1(view, info): # pragma: no cover + pass + + self.config.add_view_deriver(deriv1, 'deriv1') + self.assertRaises( + ConfigurationError, + lambda: self.config.add_view(lambda r: {}, deriv1='test1'), + ) + + +@implementer(IResponse) +class DummyResponse(object): + content_type = None + default_content_type = None + body = None + + +class DummyRequest: + subpath = () + matchdict = None + request_iface = IRequest + + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + self.params = {} + self.POST = {} + self.cookies = {} + self.headers = {} + self.response = DummyResponse() + + +class DummyLogger: + def __init__(self): + self.messages = [] + + def info(self, msg): + self.messages.append(msg) + + warn = info + debug = info + + +class DummySecurityPolicy: + def __init__(self, permitted=True): + self.permitted = permitted + + def effective_principals(self, request): + return [] + + def permits(self, context, principals, permission): + return self.permitted + + +class DummySession(dict): + def get_csrf_token(self): + return self['csrf_token'] + + +def parse_httpdate(s): + import datetime + + # cannot use %Z, must use literal GMT; Jython honors timezone + # but CPython does not + return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT") + + +def assert_similar_datetime(one, two): + for attr in ('year', 'month', 'day', 'hour', 'minute'): + one_attr = getattr(one, attr) + two_attr = getattr(two, attr) + if not one_attr == two_attr: # pragma: no cover + raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr)) diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py new file mode 100644 index 000000000..a5a955621 --- /dev/null +++ b/tests/test_wsgi.py @@ -0,0 +1,139 @@ +import unittest + + +class WSGIAppTests(unittest.TestCase): + def _callFUT(self, app): + from pyramid.wsgi import wsgiapp + + return wsgiapp(app) + + def test_wsgiapp_none(self): + self.assertRaises(ValueError, self._callFUT, None) + + def test_decorator(self): + context = DummyContext() + request = DummyRequest() + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + + def test_decorator_object_instance(self): + context = DummyContext() + request = DummyRequest() + app = DummyApp() + decorator = self._callFUT(app) + response = decorator(context, request) + self.assertEqual(response, app) + + +class WSGIApp2Tests(unittest.TestCase): + def _callFUT(self, app): + from pyramid.wsgi import wsgiapp2 + + return wsgiapp2(app) + + def test_wsgiapp2_none(self): + self.assertRaises(ValueError, self._callFUT, None) + + def test_decorator_with_subpath_and_view_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = ('subpath',) + request.environ = { + 'SCRIPT_NAME': '/foo', + 'PATH_INFO': '/b/view_name/subpath', + } + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/subpath') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b/view_name') + + def test_decorator_with_subpath_no_view_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = ('subpath',) + request.environ = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/b/subpath'} + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/subpath') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b') + + def test_decorator_no_subpath_with_view_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/b/view_name'} + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/b/view_name') + + def test_decorator_traversed_empty_with_view_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/view_name'} + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo/view_name') + + def test_decorator_traversed_empty_no_view_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/'} + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo') + + def test_decorator_traversed_empty_no_view_name_no_script_name(self): + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = {'SCRIPT_NAME': '', 'PATH_INFO': '/'} + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], '') + + def test_decorator_on_callable_object_instance(self): + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/'} + app = DummyApp() + decorator = self._callFUT(app) + response = decorator(context, request) + self.assertEqual(response, app) + self.assertEqual(request.environ['PATH_INFO'], '/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/foo') + + +def dummyapp(environ, start_response): + """ """ + + +class DummyApp(object): + def __call__(self, environ, start_response): + """ """ + + +class DummyContext: + pass + + +class DummyRequest: + def get_response(self, application): + return application + + def copy(self): + self.copied = True + return self |
