From 64372401084889a440c9d990a0febc221e3e4b5c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 25 Oct 2010 10:29:31 -0400 Subject: first pass at converting bfg to pyramid namespace --- .gitignore | 6 + pyramid/__init__.py | 1 + pyramid/authentication.py | 441 ++ pyramid/authorization.py | 133 + pyramid/chameleon_text.py | 139 + pyramid/chameleon_zpt.py | 121 + pyramid/compat/__init__.py | 143 + pyramid/configuration.py | 2697 ++++++++++++ pyramid/decorator.py | 20 + pyramid/encode.py | 107 + pyramid/events.py | 163 + pyramid/exceptions.py | 92 + pyramid/i18n.py | 322 ++ pyramid/includes/__init__.py | 1 + pyramid/includes/configure.zcml | 5 + pyramid/includes/meta.zcml | 117 + pyramid/interfaces.py | 379 ++ pyramid/location.py | 66 + pyramid/log.py | 16 + pyramid/paster.py | 111 + .../paster_templates/alchemy/+package+/__init__.py | 2 + .../alchemy/+package+/configure.zcml | 23 + .../paster_templates/alchemy/+package+/models.py | 88 + .../paster_templates/alchemy/+package+/run.py_tmpl | 23 + .../alchemy/+package+/templates/model.pt | 103 + .../alchemy/+package+/templates/root.pt | 101 + .../alchemy/+package+/templates/static/default.css | 380 ++ .../+package+/templates/static/images/img01.gif | Bin 0 -> 3840 bytes .../+package+/templates/static/images/img02.gif | Bin 0 -> 4689 bytes .../+package+/templates/static/images/img03.gif | Bin 0 -> 229 bytes .../+package+/templates/static/images/img04.gif | Bin 0 -> 92 bytes .../+package+/templates/static/images/spacer.gif | Bin 0 -> 43 bytes .../+package+/templates/static/templatelicense.txt | 243 ++ .../alchemy/+package+/views.py_tmpl | 5 + .../paster_templates/alchemy/+project+.ini_tmpl | 22 + pyramid/paster_templates/alchemy/CHANGES.txt_tmpl | 4 + pyramid/paster_templates/alchemy/README.txt_tmpl | 4 + pyramid/paster_templates/alchemy/setup.cfg_tmpl | 27 + pyramid/paster_templates/alchemy/setup.py_tmpl | 45 + .../routesalchemy/+package+/__init__.py | 2 + .../routesalchemy/+package+/configure.zcml | 18 + .../routesalchemy/+package+/models.py | 44 + .../routesalchemy/+package+/run.py_tmpl | 23 + .../+package+/templates/mytemplate.pt | 99 + .../+package+/templates/static/default.css | 380 ++ .../+package+/templates/static/images/img01.gif | Bin 0 -> 3840 bytes .../+package+/templates/static/images/img02.gif | Bin 0 -> 4689 bytes .../+package+/templates/static/images/img03.gif | Bin 0 -> 229 bytes .../+package+/templates/static/images/img04.gif | Bin 0 -> 92 bytes .../+package+/templates/static/images/spacer.gif | Bin 0 -> 43 bytes .../+package+/templates/static/templatelicense.txt | 243 ++ .../routesalchemy/+package+/tests.py_tmpl | 24 + .../routesalchemy/+package+/views.py_tmpl | 7 + .../routesalchemy/+project+.ini_tmpl | 22 + .../routesalchemy/CHANGES.txt_tmpl | 4 + .../paster_templates/routesalchemy/README.txt_tmpl | 4 + .../paster_templates/routesalchemy/setup.cfg_tmpl | 27 + .../paster_templates/routesalchemy/setup.py_tmpl | 45 + .../paster_templates/starter/+package+/__init__.py | 2 + .../starter/+package+/configure.zcml | 17 + .../paster_templates/starter/+package+/models.py | 7 + .../paster_templates/starter/+package+/run.py_tmpl | 15 + .../starter/+package+/templates/mytemplate.pt | 99 + .../starter/+package+/templates/static/default.css | 380 ++ .../+package+/templates/static/images/img01.gif | Bin 0 -> 3840 bytes .../+package+/templates/static/images/img02.gif | Bin 0 -> 4689 bytes .../+package+/templates/static/images/img03.gif | Bin 0 -> 229 bytes .../+package+/templates/static/images/img04.gif | Bin 0 -> 92 bytes .../+package+/templates/static/images/spacer.gif | Bin 0 -> 43 bytes .../+package+/templates/static/templatelicense.txt | 243 ++ .../starter/+package+/tests.py_tmpl | 20 + .../starter/+package+/views.py_tmpl | 2 + .../paster_templates/starter/+project+.ini_tmpl | 15 + pyramid/paster_templates/starter/CHANGES.txt_tmpl | 4 + pyramid/paster_templates/starter/README.txt_tmpl | 4 + pyramid/paster_templates/starter/setup.cfg_tmpl | 27 + pyramid/paster_templates/starter/setup.py_tmpl | 36 + .../paster_templates/zodb/+package+/__init__.py | 2 + .../paster_templates/zodb/+package+/configure.zcml | 17 + pyramid/paster_templates/zodb/+package+/models.py | 12 + .../paster_templates/zodb/+package+/run.py_tmpl | 23 + .../zodb/+package+/templates/mytemplate.pt | 99 + .../zodb/+package+/templates/static/default.css | 380 ++ .../+package+/templates/static/images/img01.gif | Bin 0 -> 3840 bytes .../+package+/templates/static/images/img02.gif | Bin 0 -> 4689 bytes .../+package+/templates/static/images/img03.gif | Bin 0 -> 229 bytes .../+package+/templates/static/images/img04.gif | Bin 0 -> 92 bytes .../+package+/templates/static/images/spacer.gif | Bin 0 -> 43 bytes .../+package+/templates/static/templatelicense.txt | 243 ++ .../paster_templates/zodb/+package+/tests.py_tmpl | 19 + .../paster_templates/zodb/+package+/views.py_tmpl | 2 + pyramid/paster_templates/zodb/+project+.ini_tmpl | 22 + pyramid/paster_templates/zodb/CHANGES.txt_tmpl | 4 + pyramid/paster_templates/zodb/README.txt_tmpl | 4 + pyramid/paster_templates/zodb/setup.cfg_tmpl | 27 + pyramid/paster_templates/zodb/setup.py_tmpl | 41 + pyramid/path.py | 69 + pyramid/registry.py | 24 + pyramid/renderers.py | 320 ++ pyramid/request.py | 229 ++ pyramid/resource.py | 201 + pyramid/router.py | 189 + pyramid/scripting.py | 25 + pyramid/security.py | 270 ++ pyramid/settings.py | 85 + pyramid/static.py | 213 + pyramid/testing.py | 818 ++++ pyramid/tests/__init__.py | 1 + pyramid/tests/ccbugapp/__init__.py | 1 + pyramid/tests/ccbugapp/configure.zcml | 15 + pyramid/tests/ccbugapp/views.py | 10 + pyramid/tests/exceptionviewapp/__init__.py | 1 + pyramid/tests/exceptionviewapp/configure.zcml | 44 + pyramid/tests/exceptionviewapp/models.py | 18 + pyramid/tests/exceptionviewapp/views.py | 17 + pyramid/tests/fixtureapp/__init__.py | 1 + pyramid/tests/fixtureapp/another.zcml | 10 + pyramid/tests/fixtureapp/configure.zcml | 37 + pyramid/tests/fixtureapp/models.py | 8 + pyramid/tests/fixtureapp/subpackage/__init__.py | 1 + .../tests/fixtureapp/subpackage/templates/bar.pt | 2 + .../tests/fixtureapp/subpackage/yetanother.zcml | 8 + pyramid/tests/fixtureapp/templates/fixture.pt | 6 + pyramid/tests/fixtureapp/views.py | 22 + pyramid/tests/fixtures/minimal.pt | 3 + pyramid/tests/fixtures/minimal.txt | 1 + pyramid/tests/fixtures/nonminimal.txt | 1 + pyramid/tests/fixtures/pp.pt | 3 + pyramid/tests/fixtures/static/index.html | 1 + pyramid/tests/fixtures/static/subdir/index.html | 1 + pyramid/tests/grokkedapp/__init__.py | 90 + pyramid/tests/grokkedapp/another.py | 62 + pyramid/tests/grokkedapp/configure.zcml | 6 + pyramid/tests/grokkedapp/pod/notinit.py | 5 + pyramid/tests/grokkedapp/subpackage/__init__.py | 5 + pyramid/tests/grokkedapp/subpackage/notinit.py | 5 + .../subpackage/subsubpackage/__init__.py | 5 + pyramid/tests/hybridapp/__init__.py | 1 + pyramid/tests/hybridapp/configure.zcml | 117 + pyramid/tests/hybridapp/views.py | 39 + pyramid/tests/localeapp/__init__.py | 1 + pyramid/tests/localeapp/locale/GARBAGE | 1 + pyramid/tests/localeapp/locale/be/LC_MESSAGES | 1 + .../localeapp/locale/de/LC_MESSAGES/deformsite.mo | Bin 0 -> 543 bytes .../localeapp/locale/de/LC_MESSAGES/deformsite.po | 31 + .../localeapp/locale/en/LC_MESSAGES/deformsite.mo | Bin 0 -> 543 bytes .../localeapp/locale/en/LC_MESSAGES/deformsite.po | 31 + pyramid/tests/restbugapp/__init__.py | 1 + pyramid/tests/restbugapp/configure.zcml | 25 + pyramid/tests/restbugapp/views.py | 15 + pyramid/tests/routesapp/__init__.py | 1 + pyramid/tests/routesapp/configure.zcml | 12 + pyramid/tests/routesapp/models.py | 5 + pyramid/tests/routesapp/templates/fixture.pt | 6 + pyramid/tests/routesapp/views.py | 8 + pyramid/tests/test_authentication.py | 664 +++ pyramid/tests/test_authorization.py | 189 + pyramid/tests/test_chameleon_text.py | 198 + pyramid/tests/test_chameleon_zpt.py | 194 + pyramid/tests/test_compat.py | 9 + pyramid/tests/test_configuration.py | 4335 ++++++++++++++++++++ pyramid/tests/test_decorator.py | 29 + pyramid/tests/test_docs.py | 35 + pyramid/tests/test_encode.py | 61 + pyramid/tests/test_events.py | 181 + pyramid/tests/test_exceptions.py | 45 + pyramid/tests/test_i18n.py | 394 ++ pyramid/tests/test_integration.py | 236 ++ pyramid/tests/test_location.py | 40 + pyramid/tests/test_log.py | 16 + pyramid/tests/test_paster.py | 186 + pyramid/tests/test_path.py | 172 + pyramid/tests/test_registry.py | 42 + pyramid/tests/test_renderers.py | 499 +++ pyramid/tests/test_request.py | 231 ++ pyramid/tests/test_resource.py | 431 ++ pyramid/tests/test_router.py | 1034 +++++ pyramid/tests/test_scripting.py | 81 + pyramid/tests/test_security.py | 395 ++ pyramid/tests/test_settings.py | 203 + pyramid/tests/test_static.py | 343 ++ pyramid/tests/test_testing.py | 749 ++++ pyramid/tests/test_threadlocal.py | 95 + pyramid/tests/test_traversal.py | 1033 +++++ pyramid/tests/test_url.py | 304 ++ pyramid/tests/test_urldispatch.py | 318 ++ pyramid/tests/test_view.py | 499 +++ pyramid/tests/test_wsgi.py | 112 + pyramid/tests/test_zcml.py | 1283 ++++++ pyramid/tests/viewdecoratorapp/__init__.py | 1 + pyramid/tests/viewdecoratorapp/configure.zcml | 6 + pyramid/tests/viewdecoratorapp/views/__init__.py | 1 + .../tests/viewdecoratorapp/views/templates/foo.pt | 3 + pyramid/tests/viewdecoratorapp/views/views.py | 17 + pyramid/threadlocal.py | 61 + pyramid/traversal.py | 674 +++ pyramid/url.py | 327 ++ pyramid/urldispatch.py | 148 + pyramid/view.py | 551 +++ pyramid/wsgi.py | 97 + pyramid/zcml.py | 903 ++++ repoze/bfg/__init__.py | 1 - repoze/bfg/authentication.py | 441 -- repoze/bfg/authorization.py | 133 - repoze/bfg/chameleon_text.py | 139 - repoze/bfg/chameleon_zpt.py | 121 - repoze/bfg/compat/__init__.py | 143 - repoze/bfg/configuration.py | 2697 ------------ repoze/bfg/decorator.py | 20 - repoze/bfg/encode.py | 107 - repoze/bfg/events.py | 163 - repoze/bfg/exceptions.py | 92 - repoze/bfg/i18n.py | 322 -- repoze/bfg/includes/__init__.py | 1 - repoze/bfg/includes/configure.zcml | 5 - repoze/bfg/includes/meta.zcml | 117 - repoze/bfg/interfaces.py | 379 -- repoze/bfg/location.py | 66 - repoze/bfg/log.py | 16 - repoze/bfg/paster.py | 111 - .../paster_templates/alchemy/+package+/__init__.py | 2 - .../alchemy/+package+/configure.zcml | 23 - .../paster_templates/alchemy/+package+/models.py | 88 - .../paster_templates/alchemy/+package+/run.py_tmpl | 23 - .../alchemy/+package+/templates/model.pt | 103 - .../alchemy/+package+/templates/root.pt | 101 - .../alchemy/+package+/templates/static/default.css | 380 -- .../+package+/templates/static/images/img01.gif | Bin 3840 -> 0 bytes .../+package+/templates/static/images/img02.gif | Bin 4689 -> 0 bytes .../+package+/templates/static/images/img03.gif | Bin 229 -> 0 bytes .../+package+/templates/static/images/img04.gif | Bin 92 -> 0 bytes .../+package+/templates/static/images/spacer.gif | Bin 43 -> 0 bytes .../+package+/templates/static/templatelicense.txt | 243 -- .../alchemy/+package+/views.py_tmpl | 5 - .../paster_templates/alchemy/+project+.ini_tmpl | 22 - .../bfg/paster_templates/alchemy/CHANGES.txt_tmpl | 4 - .../bfg/paster_templates/alchemy/README.txt_tmpl | 4 - repoze/bfg/paster_templates/alchemy/setup.cfg_tmpl | 27 - repoze/bfg/paster_templates/alchemy/setup.py_tmpl | 45 - .../routesalchemy/+package+/__init__.py | 2 - .../routesalchemy/+package+/configure.zcml | 18 - .../routesalchemy/+package+/models.py | 44 - .../routesalchemy/+package+/run.py_tmpl | 23 - .../+package+/templates/mytemplate.pt | 99 - .../+package+/templates/static/default.css | 380 -- .../+package+/templates/static/images/img01.gif | Bin 3840 -> 0 bytes .../+package+/templates/static/images/img02.gif | Bin 4689 -> 0 bytes .../+package+/templates/static/images/img03.gif | Bin 229 -> 0 bytes .../+package+/templates/static/images/img04.gif | Bin 92 -> 0 bytes .../+package+/templates/static/images/spacer.gif | Bin 43 -> 0 bytes .../+package+/templates/static/templatelicense.txt | 243 -- .../routesalchemy/+package+/tests.py_tmpl | 24 - .../routesalchemy/+package+/views.py_tmpl | 7 - .../routesalchemy/+project+.ini_tmpl | 22 - .../routesalchemy/CHANGES.txt_tmpl | 4 - .../paster_templates/routesalchemy/README.txt_tmpl | 4 - .../paster_templates/routesalchemy/setup.cfg_tmpl | 27 - .../paster_templates/routesalchemy/setup.py_tmpl | 45 - .../paster_templates/starter/+package+/__init__.py | 2 - .../starter/+package+/configure.zcml | 17 - .../paster_templates/starter/+package+/models.py | 7 - .../paster_templates/starter/+package+/run.py_tmpl | 15 - .../starter/+package+/templates/mytemplate.pt | 99 - .../starter/+package+/templates/static/default.css | 380 -- .../+package+/templates/static/images/img01.gif | Bin 3840 -> 0 bytes .../+package+/templates/static/images/img02.gif | Bin 4689 -> 0 bytes .../+package+/templates/static/images/img03.gif | Bin 229 -> 0 bytes .../+package+/templates/static/images/img04.gif | Bin 92 -> 0 bytes .../+package+/templates/static/images/spacer.gif | Bin 43 -> 0 bytes .../+package+/templates/static/templatelicense.txt | 243 -- .../starter/+package+/tests.py_tmpl | 20 - .../starter/+package+/views.py_tmpl | 2 - .../paster_templates/starter/+project+.ini_tmpl | 15 - .../bfg/paster_templates/starter/CHANGES.txt_tmpl | 4 - .../bfg/paster_templates/starter/README.txt_tmpl | 4 - repoze/bfg/paster_templates/starter/setup.cfg_tmpl | 27 - repoze/bfg/paster_templates/starter/setup.py_tmpl | 36 - .../paster_templates/zodb/+package+/__init__.py | 2 - .../paster_templates/zodb/+package+/configure.zcml | 17 - .../bfg/paster_templates/zodb/+package+/models.py | 12 - .../paster_templates/zodb/+package+/run.py_tmpl | 23 - .../zodb/+package+/templates/mytemplate.pt | 99 - .../zodb/+package+/templates/static/default.css | 380 -- .../+package+/templates/static/images/img01.gif | Bin 3840 -> 0 bytes .../+package+/templates/static/images/img02.gif | Bin 4689 -> 0 bytes .../+package+/templates/static/images/img03.gif | Bin 229 -> 0 bytes .../+package+/templates/static/images/img04.gif | Bin 92 -> 0 bytes .../+package+/templates/static/images/spacer.gif | Bin 43 -> 0 bytes .../+package+/templates/static/templatelicense.txt | 243 -- .../paster_templates/zodb/+package+/tests.py_tmpl | 19 - .../paster_templates/zodb/+package+/views.py_tmpl | 2 - .../bfg/paster_templates/zodb/+project+.ini_tmpl | 22 - repoze/bfg/paster_templates/zodb/CHANGES.txt_tmpl | 4 - repoze/bfg/paster_templates/zodb/README.txt_tmpl | 4 - repoze/bfg/paster_templates/zodb/setup.cfg_tmpl | 27 - repoze/bfg/paster_templates/zodb/setup.py_tmpl | 41 - repoze/bfg/path.py | 69 - repoze/bfg/registry.py | 24 - repoze/bfg/renderers.py | 320 -- repoze/bfg/request.py | 229 -- repoze/bfg/resource.py | 201 - repoze/bfg/router.py | 189 - repoze/bfg/scripting.py | 25 - repoze/bfg/security.py | 270 -- repoze/bfg/settings.py | 85 - repoze/bfg/static.py | 213 - repoze/bfg/testing.py | 818 ---- repoze/bfg/tests/__init__.py | 1 - repoze/bfg/tests/ccbugapp/__init__.py | 1 - repoze/bfg/tests/ccbugapp/configure.zcml | 15 - repoze/bfg/tests/ccbugapp/views.py | 10 - repoze/bfg/tests/exceptionviewapp/__init__.py | 1 - repoze/bfg/tests/exceptionviewapp/configure.zcml | 44 - repoze/bfg/tests/exceptionviewapp/models.py | 18 - repoze/bfg/tests/exceptionviewapp/views.py | 17 - repoze/bfg/tests/fixtureapp/__init__.py | 1 - repoze/bfg/tests/fixtureapp/another.zcml | 10 - repoze/bfg/tests/fixtureapp/configure.zcml | 37 - repoze/bfg/tests/fixtureapp/models.py | 8 - repoze/bfg/tests/fixtureapp/subpackage/__init__.py | 1 - .../tests/fixtureapp/subpackage/templates/bar.pt | 2 - .../tests/fixtureapp/subpackage/yetanother.zcml | 8 - repoze/bfg/tests/fixtureapp/templates/fixture.pt | 6 - repoze/bfg/tests/fixtureapp/views.py | 22 - repoze/bfg/tests/fixtures/minimal.pt | 3 - repoze/bfg/tests/fixtures/minimal.txt | 1 - repoze/bfg/tests/fixtures/nonminimal.txt | 1 - repoze/bfg/tests/fixtures/pp.pt | 3 - repoze/bfg/tests/fixtures/static/index.html | 1 - repoze/bfg/tests/fixtures/static/subdir/index.html | 1 - repoze/bfg/tests/grokkedapp/__init__.py | 90 - repoze/bfg/tests/grokkedapp/another.py | 62 - repoze/bfg/tests/grokkedapp/configure.zcml | 6 - repoze/bfg/tests/grokkedapp/pod/notinit.py | 5 - repoze/bfg/tests/grokkedapp/subpackage/__init__.py | 5 - repoze/bfg/tests/grokkedapp/subpackage/notinit.py | 5 - .../subpackage/subsubpackage/__init__.py | 5 - repoze/bfg/tests/hybridapp/__init__.py | 1 - repoze/bfg/tests/hybridapp/configure.zcml | 117 - repoze/bfg/tests/hybridapp/views.py | 39 - repoze/bfg/tests/localeapp/__init__.py | 1 - repoze/bfg/tests/localeapp/locale/GARBAGE | 1 - repoze/bfg/tests/localeapp/locale/be/LC_MESSAGES | 1 - .../localeapp/locale/de/LC_MESSAGES/deformsite.mo | Bin 543 -> 0 bytes .../localeapp/locale/de/LC_MESSAGES/deformsite.po | 31 - .../localeapp/locale/en/LC_MESSAGES/deformsite.mo | Bin 543 -> 0 bytes .../localeapp/locale/en/LC_MESSAGES/deformsite.po | 31 - repoze/bfg/tests/restbugapp/__init__.py | 1 - repoze/bfg/tests/restbugapp/configure.zcml | 25 - repoze/bfg/tests/restbugapp/views.py | 15 - repoze/bfg/tests/routesapp/__init__.py | 1 - repoze/bfg/tests/routesapp/configure.zcml | 12 - repoze/bfg/tests/routesapp/models.py | 5 - repoze/bfg/tests/routesapp/templates/fixture.pt | 6 - repoze/bfg/tests/routesapp/views.py | 8 - repoze/bfg/tests/test_authentication.py | 664 --- repoze/bfg/tests/test_authorization.py | 189 - repoze/bfg/tests/test_chameleon_text.py | 198 - repoze/bfg/tests/test_chameleon_zpt.py | 194 - repoze/bfg/tests/test_compat.py | 9 - repoze/bfg/tests/test_configuration.py | 4335 -------------------- repoze/bfg/tests/test_decorator.py | 29 - repoze/bfg/tests/test_docs.py | 35 - repoze/bfg/tests/test_encode.py | 61 - repoze/bfg/tests/test_events.py | 181 - repoze/bfg/tests/test_exceptions.py | 45 - repoze/bfg/tests/test_i18n.py | 394 -- repoze/bfg/tests/test_integration.py | 236 -- repoze/bfg/tests/test_location.py | 40 - repoze/bfg/tests/test_log.py | 16 - repoze/bfg/tests/test_paster.py | 186 - repoze/bfg/tests/test_path.py | 172 - repoze/bfg/tests/test_registry.py | 42 - repoze/bfg/tests/test_renderers.py | 499 --- repoze/bfg/tests/test_request.py | 231 -- repoze/bfg/tests/test_resource.py | 431 -- repoze/bfg/tests/test_router.py | 1034 ----- repoze/bfg/tests/test_scripting.py | 81 - repoze/bfg/tests/test_security.py | 395 -- repoze/bfg/tests/test_settings.py | 203 - repoze/bfg/tests/test_static.py | 343 -- repoze/bfg/tests/test_testing.py | 749 ---- repoze/bfg/tests/test_threadlocal.py | 95 - repoze/bfg/tests/test_traversal.py | 1033 ----- repoze/bfg/tests/test_url.py | 304 -- repoze/bfg/tests/test_urldispatch.py | 318 -- repoze/bfg/tests/test_view.py | 499 --- repoze/bfg/tests/test_wsgi.py | 112 - repoze/bfg/tests/test_zcml.py | 1283 ------ repoze/bfg/tests/viewdecoratorapp/__init__.py | 1 - repoze/bfg/tests/viewdecoratorapp/configure.zcml | 6 - .../bfg/tests/viewdecoratorapp/views/__init__.py | 1 - .../tests/viewdecoratorapp/views/templates/foo.pt | 3 - repoze/bfg/tests/viewdecoratorapp/views/views.py | 17 - repoze/bfg/threadlocal.py | 61 - repoze/bfg/traversal.py | 674 --- repoze/bfg/url.py | 327 -- repoze/bfg/urldispatch.py | 148 - repoze/bfg/view.py | 551 --- repoze/bfg/wsgi.py | 97 - repoze/bfg/zcml.py | 903 ---- 401 files changed, 29411 insertions(+), 29405 deletions(-) create mode 100644 .gitignore create mode 100644 pyramid/__init__.py create mode 100644 pyramid/authentication.py create mode 100644 pyramid/authorization.py create mode 100644 pyramid/chameleon_text.py create mode 100644 pyramid/chameleon_zpt.py create mode 100644 pyramid/compat/__init__.py create mode 100644 pyramid/configuration.py create mode 100644 pyramid/decorator.py create mode 100644 pyramid/encode.py create mode 100644 pyramid/events.py create mode 100644 pyramid/exceptions.py create mode 100644 pyramid/i18n.py create mode 100644 pyramid/includes/__init__.py create mode 100644 pyramid/includes/configure.zcml create mode 100644 pyramid/includes/meta.zcml create mode 100644 pyramid/interfaces.py create mode 100644 pyramid/location.py create mode 100644 pyramid/log.py create mode 100644 pyramid/paster.py create mode 100755 pyramid/paster_templates/alchemy/+package+/__init__.py create mode 100644 pyramid/paster_templates/alchemy/+package+/configure.zcml create mode 100755 pyramid/paster_templates/alchemy/+package+/models.py create mode 100644 pyramid/paster_templates/alchemy/+package+/run.py_tmpl create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/model.pt create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/root.pt create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/default.css create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/images/img01.gif create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/images/img02.gif create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/images/img03.gif create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/images/img04.gif create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/images/spacer.gif create mode 100644 pyramid/paster_templates/alchemy/+package+/templates/static/templatelicense.txt create mode 100644 pyramid/paster_templates/alchemy/+package+/views.py_tmpl create mode 100644 pyramid/paster_templates/alchemy/+project+.ini_tmpl create mode 100644 pyramid/paster_templates/alchemy/CHANGES.txt_tmpl create mode 100644 pyramid/paster_templates/alchemy/README.txt_tmpl create mode 100644 pyramid/paster_templates/alchemy/setup.cfg_tmpl create mode 100644 pyramid/paster_templates/alchemy/setup.py_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/+package+/__init__.py create mode 100644 pyramid/paster_templates/routesalchemy/+package+/configure.zcml create mode 100644 pyramid/paster_templates/routesalchemy/+package+/models.py create mode 100644 pyramid/paster_templates/routesalchemy/+package+/run.py_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/default.css create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif create mode 100644 pyramid/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt create mode 100644 pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/+package+/views.py_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/+project+.ini_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/CHANGES.txt_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/README.txt_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/setup.cfg_tmpl create mode 100644 pyramid/paster_templates/routesalchemy/setup.py_tmpl create mode 100644 pyramid/paster_templates/starter/+package+/__init__.py create mode 100644 pyramid/paster_templates/starter/+package+/configure.zcml create mode 100644 pyramid/paster_templates/starter/+package+/models.py create mode 100644 pyramid/paster_templates/starter/+package+/run.py_tmpl create mode 100644 pyramid/paster_templates/starter/+package+/templates/mytemplate.pt create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/default.css create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/images/img01.gif create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/images/img02.gif create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/images/img03.gif create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/images/img04.gif create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/images/spacer.gif create mode 100644 pyramid/paster_templates/starter/+package+/templates/static/templatelicense.txt create mode 100644 pyramid/paster_templates/starter/+package+/tests.py_tmpl create mode 100644 pyramid/paster_templates/starter/+package+/views.py_tmpl create mode 100644 pyramid/paster_templates/starter/+project+.ini_tmpl create mode 100644 pyramid/paster_templates/starter/CHANGES.txt_tmpl create mode 100644 pyramid/paster_templates/starter/README.txt_tmpl create mode 100644 pyramid/paster_templates/starter/setup.cfg_tmpl create mode 100644 pyramid/paster_templates/starter/setup.py_tmpl create mode 100644 pyramid/paster_templates/zodb/+package+/__init__.py create mode 100644 pyramid/paster_templates/zodb/+package+/configure.zcml create mode 100644 pyramid/paster_templates/zodb/+package+/models.py create mode 100644 pyramid/paster_templates/zodb/+package+/run.py_tmpl create mode 100644 pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/default.css create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/images/img01.gif create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/images/img02.gif create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/images/img03.gif create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/images/img04.gif create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/images/spacer.gif create mode 100644 pyramid/paster_templates/zodb/+package+/templates/static/templatelicense.txt create mode 100644 pyramid/paster_templates/zodb/+package+/tests.py_tmpl create mode 100644 pyramid/paster_templates/zodb/+package+/views.py_tmpl create mode 100644 pyramid/paster_templates/zodb/+project+.ini_tmpl create mode 100644 pyramid/paster_templates/zodb/CHANGES.txt_tmpl create mode 100644 pyramid/paster_templates/zodb/README.txt_tmpl create mode 100644 pyramid/paster_templates/zodb/setup.cfg_tmpl create mode 100644 pyramid/paster_templates/zodb/setup.py_tmpl create mode 100644 pyramid/path.py create mode 100644 pyramid/registry.py create mode 100644 pyramid/renderers.py create mode 100644 pyramid/request.py create mode 100644 pyramid/resource.py create mode 100644 pyramid/router.py create mode 100644 pyramid/scripting.py create mode 100644 pyramid/security.py create mode 100644 pyramid/settings.py create mode 100644 pyramid/static.py create mode 100644 pyramid/testing.py create mode 100644 pyramid/tests/__init__.py create mode 100644 pyramid/tests/ccbugapp/__init__.py create mode 100644 pyramid/tests/ccbugapp/configure.zcml create mode 100644 pyramid/tests/ccbugapp/views.py create mode 100644 pyramid/tests/exceptionviewapp/__init__.py create mode 100644 pyramid/tests/exceptionviewapp/configure.zcml create mode 100644 pyramid/tests/exceptionviewapp/models.py create mode 100644 pyramid/tests/exceptionviewapp/views.py create mode 100644 pyramid/tests/fixtureapp/__init__.py create mode 100644 pyramid/tests/fixtureapp/another.zcml create mode 100644 pyramid/tests/fixtureapp/configure.zcml create mode 100644 pyramid/tests/fixtureapp/models.py create mode 100644 pyramid/tests/fixtureapp/subpackage/__init__.py create mode 100644 pyramid/tests/fixtureapp/subpackage/templates/bar.pt create mode 100644 pyramid/tests/fixtureapp/subpackage/yetanother.zcml create mode 100644 pyramid/tests/fixtureapp/templates/fixture.pt create mode 100644 pyramid/tests/fixtureapp/views.py create mode 100644 pyramid/tests/fixtures/minimal.pt create mode 100644 pyramid/tests/fixtures/minimal.txt create mode 100644 pyramid/tests/fixtures/nonminimal.txt create mode 100644 pyramid/tests/fixtures/pp.pt create mode 100644 pyramid/tests/fixtures/static/index.html create mode 100644 pyramid/tests/fixtures/static/subdir/index.html create mode 100644 pyramid/tests/grokkedapp/__init__.py create mode 100644 pyramid/tests/grokkedapp/another.py create mode 100644 pyramid/tests/grokkedapp/configure.zcml create mode 100644 pyramid/tests/grokkedapp/pod/notinit.py create mode 100644 pyramid/tests/grokkedapp/subpackage/__init__.py create mode 100644 pyramid/tests/grokkedapp/subpackage/notinit.py create mode 100644 pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py create mode 100644 pyramid/tests/hybridapp/__init__.py create mode 100644 pyramid/tests/hybridapp/configure.zcml create mode 100644 pyramid/tests/hybridapp/views.py create mode 100644 pyramid/tests/localeapp/__init__.py create mode 100644 pyramid/tests/localeapp/locale/GARBAGE create mode 100644 pyramid/tests/localeapp/locale/be/LC_MESSAGES create mode 100644 pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo create mode 100644 pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po create mode 100644 pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo create mode 100644 pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po create mode 100644 pyramid/tests/restbugapp/__init__.py create mode 100644 pyramid/tests/restbugapp/configure.zcml create mode 100644 pyramid/tests/restbugapp/views.py create mode 100644 pyramid/tests/routesapp/__init__.py create mode 100644 pyramid/tests/routesapp/configure.zcml create mode 100644 pyramid/tests/routesapp/models.py create mode 100644 pyramid/tests/routesapp/templates/fixture.pt create mode 100644 pyramid/tests/routesapp/views.py create mode 100644 pyramid/tests/test_authentication.py create mode 100644 pyramid/tests/test_authorization.py create mode 100644 pyramid/tests/test_chameleon_text.py create mode 100644 pyramid/tests/test_chameleon_zpt.py create mode 100644 pyramid/tests/test_compat.py create mode 100644 pyramid/tests/test_configuration.py create mode 100644 pyramid/tests/test_decorator.py create mode 100644 pyramid/tests/test_docs.py create mode 100644 pyramid/tests/test_encode.py create mode 100644 pyramid/tests/test_events.py create mode 100644 pyramid/tests/test_exceptions.py create mode 100644 pyramid/tests/test_i18n.py create mode 100644 pyramid/tests/test_integration.py create mode 100644 pyramid/tests/test_location.py create mode 100644 pyramid/tests/test_log.py create mode 100644 pyramid/tests/test_paster.py create mode 100644 pyramid/tests/test_path.py create mode 100644 pyramid/tests/test_registry.py create mode 100644 pyramid/tests/test_renderers.py create mode 100644 pyramid/tests/test_request.py create mode 100644 pyramid/tests/test_resource.py create mode 100644 pyramid/tests/test_router.py create mode 100644 pyramid/tests/test_scripting.py create mode 100644 pyramid/tests/test_security.py create mode 100644 pyramid/tests/test_settings.py create mode 100644 pyramid/tests/test_static.py create mode 100644 pyramid/tests/test_testing.py create mode 100644 pyramid/tests/test_threadlocal.py create mode 100644 pyramid/tests/test_traversal.py create mode 100644 pyramid/tests/test_url.py create mode 100644 pyramid/tests/test_urldispatch.py create mode 100644 pyramid/tests/test_view.py create mode 100644 pyramid/tests/test_wsgi.py create mode 100644 pyramid/tests/test_zcml.py create mode 100644 pyramid/tests/viewdecoratorapp/__init__.py create mode 100644 pyramid/tests/viewdecoratorapp/configure.zcml create mode 100644 pyramid/tests/viewdecoratorapp/views/__init__.py create mode 100644 pyramid/tests/viewdecoratorapp/views/templates/foo.pt create mode 100644 pyramid/tests/viewdecoratorapp/views/views.py create mode 100644 pyramid/threadlocal.py create mode 100644 pyramid/traversal.py create mode 100644 pyramid/url.py create mode 100644 pyramid/urldispatch.py create mode 100644 pyramid/view.py create mode 100644 pyramid/wsgi.py create mode 100644 pyramid/zcml.py delete mode 100644 repoze/bfg/__init__.py delete mode 100644 repoze/bfg/authentication.py delete mode 100644 repoze/bfg/authorization.py delete mode 100644 repoze/bfg/chameleon_text.py delete mode 100644 repoze/bfg/chameleon_zpt.py delete mode 100644 repoze/bfg/compat/__init__.py delete mode 100644 repoze/bfg/configuration.py delete mode 100644 repoze/bfg/decorator.py delete mode 100644 repoze/bfg/encode.py delete mode 100644 repoze/bfg/events.py delete mode 100644 repoze/bfg/exceptions.py delete mode 100644 repoze/bfg/i18n.py delete mode 100644 repoze/bfg/includes/__init__.py delete mode 100644 repoze/bfg/includes/configure.zcml delete mode 100644 repoze/bfg/includes/meta.zcml delete mode 100644 repoze/bfg/interfaces.py delete mode 100644 repoze/bfg/location.py delete mode 100644 repoze/bfg/log.py delete mode 100644 repoze/bfg/paster.py delete mode 100755 repoze/bfg/paster_templates/alchemy/+package+/__init__.py delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/configure.zcml delete mode 100755 repoze/bfg/paster_templates/alchemy/+package+/models.py delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/run.py_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/model.pt delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/root.pt delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/default.css delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img01.gif delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img02.gif delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img03.gif delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img04.gif delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/spacer.gif delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/templates/static/templatelicense.txt delete mode 100644 repoze/bfg/paster_templates/alchemy/+package+/views.py_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/+project+.ini_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/CHANGES.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/README.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/setup.cfg_tmpl delete mode 100644 repoze/bfg/paster_templates/alchemy/setup.py_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/models.py delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/setup.cfg_tmpl delete mode 100644 repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/+package+/__init__.py delete mode 100644 repoze/bfg/paster_templates/starter/+package+/configure.zcml delete mode 100644 repoze/bfg/paster_templates/starter/+package+/models.py delete mode 100644 repoze/bfg/paster_templates/starter/+package+/run.py_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/mytemplate.pt delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/default.css delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/images/img01.gif delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/images/img02.gif delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/images/img03.gif delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/images/img04.gif delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/images/spacer.gif delete mode 100644 repoze/bfg/paster_templates/starter/+package+/templates/static/templatelicense.txt delete mode 100644 repoze/bfg/paster_templates/starter/+package+/tests.py_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/+package+/views.py_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/+project+.ini_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/CHANGES.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/README.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/setup.cfg_tmpl delete mode 100644 repoze/bfg/paster_templates/starter/setup.py_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/__init__.py delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/configure.zcml delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/models.py delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/run.py_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/mytemplate.pt delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/default.css delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img01.gif delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img02.gif delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img03.gif delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img04.gif delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/images/spacer.gif delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/templates/static/templatelicense.txt delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/tests.py_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/+package+/views.py_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/+project+.ini_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/CHANGES.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/README.txt_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/setup.cfg_tmpl delete mode 100644 repoze/bfg/paster_templates/zodb/setup.py_tmpl delete mode 100644 repoze/bfg/path.py delete mode 100644 repoze/bfg/registry.py delete mode 100644 repoze/bfg/renderers.py delete mode 100644 repoze/bfg/request.py delete mode 100644 repoze/bfg/resource.py delete mode 100644 repoze/bfg/router.py delete mode 100644 repoze/bfg/scripting.py delete mode 100644 repoze/bfg/security.py delete mode 100644 repoze/bfg/settings.py delete mode 100644 repoze/bfg/static.py delete mode 100644 repoze/bfg/testing.py delete mode 100644 repoze/bfg/tests/__init__.py delete mode 100644 repoze/bfg/tests/ccbugapp/__init__.py delete mode 100644 repoze/bfg/tests/ccbugapp/configure.zcml delete mode 100644 repoze/bfg/tests/ccbugapp/views.py delete mode 100644 repoze/bfg/tests/exceptionviewapp/__init__.py delete mode 100644 repoze/bfg/tests/exceptionviewapp/configure.zcml delete mode 100644 repoze/bfg/tests/exceptionviewapp/models.py delete mode 100644 repoze/bfg/tests/exceptionviewapp/views.py delete mode 100644 repoze/bfg/tests/fixtureapp/__init__.py delete mode 100644 repoze/bfg/tests/fixtureapp/another.zcml delete mode 100644 repoze/bfg/tests/fixtureapp/configure.zcml delete mode 100644 repoze/bfg/tests/fixtureapp/models.py delete mode 100644 repoze/bfg/tests/fixtureapp/subpackage/__init__.py delete mode 100644 repoze/bfg/tests/fixtureapp/subpackage/templates/bar.pt delete mode 100644 repoze/bfg/tests/fixtureapp/subpackage/yetanother.zcml delete mode 100644 repoze/bfg/tests/fixtureapp/templates/fixture.pt delete mode 100644 repoze/bfg/tests/fixtureapp/views.py delete mode 100644 repoze/bfg/tests/fixtures/minimal.pt delete mode 100644 repoze/bfg/tests/fixtures/minimal.txt delete mode 100644 repoze/bfg/tests/fixtures/nonminimal.txt delete mode 100644 repoze/bfg/tests/fixtures/pp.pt delete mode 100644 repoze/bfg/tests/fixtures/static/index.html delete mode 100644 repoze/bfg/tests/fixtures/static/subdir/index.html delete mode 100644 repoze/bfg/tests/grokkedapp/__init__.py delete mode 100644 repoze/bfg/tests/grokkedapp/another.py delete mode 100644 repoze/bfg/tests/grokkedapp/configure.zcml delete mode 100644 repoze/bfg/tests/grokkedapp/pod/notinit.py delete mode 100644 repoze/bfg/tests/grokkedapp/subpackage/__init__.py delete mode 100644 repoze/bfg/tests/grokkedapp/subpackage/notinit.py delete mode 100644 repoze/bfg/tests/grokkedapp/subpackage/subsubpackage/__init__.py delete mode 100644 repoze/bfg/tests/hybridapp/__init__.py delete mode 100644 repoze/bfg/tests/hybridapp/configure.zcml delete mode 100644 repoze/bfg/tests/hybridapp/views.py delete mode 100644 repoze/bfg/tests/localeapp/__init__.py delete mode 100644 repoze/bfg/tests/localeapp/locale/GARBAGE delete mode 100644 repoze/bfg/tests/localeapp/locale/be/LC_MESSAGES delete mode 100644 repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo delete mode 100644 repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po delete mode 100644 repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo delete mode 100644 repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po delete mode 100644 repoze/bfg/tests/restbugapp/__init__.py delete mode 100644 repoze/bfg/tests/restbugapp/configure.zcml delete mode 100644 repoze/bfg/tests/restbugapp/views.py delete mode 100644 repoze/bfg/tests/routesapp/__init__.py delete mode 100644 repoze/bfg/tests/routesapp/configure.zcml delete mode 100644 repoze/bfg/tests/routesapp/models.py delete mode 100644 repoze/bfg/tests/routesapp/templates/fixture.pt delete mode 100644 repoze/bfg/tests/routesapp/views.py delete mode 100644 repoze/bfg/tests/test_authentication.py delete mode 100644 repoze/bfg/tests/test_authorization.py delete mode 100644 repoze/bfg/tests/test_chameleon_text.py delete mode 100644 repoze/bfg/tests/test_chameleon_zpt.py delete mode 100644 repoze/bfg/tests/test_compat.py delete mode 100644 repoze/bfg/tests/test_configuration.py delete mode 100644 repoze/bfg/tests/test_decorator.py delete mode 100644 repoze/bfg/tests/test_docs.py delete mode 100644 repoze/bfg/tests/test_encode.py delete mode 100644 repoze/bfg/tests/test_events.py delete mode 100644 repoze/bfg/tests/test_exceptions.py delete mode 100644 repoze/bfg/tests/test_i18n.py delete mode 100644 repoze/bfg/tests/test_integration.py delete mode 100644 repoze/bfg/tests/test_location.py delete mode 100644 repoze/bfg/tests/test_log.py delete mode 100644 repoze/bfg/tests/test_paster.py delete mode 100644 repoze/bfg/tests/test_path.py delete mode 100644 repoze/bfg/tests/test_registry.py delete mode 100644 repoze/bfg/tests/test_renderers.py delete mode 100644 repoze/bfg/tests/test_request.py delete mode 100644 repoze/bfg/tests/test_resource.py delete mode 100644 repoze/bfg/tests/test_router.py delete mode 100644 repoze/bfg/tests/test_scripting.py delete mode 100644 repoze/bfg/tests/test_security.py delete mode 100644 repoze/bfg/tests/test_settings.py delete mode 100644 repoze/bfg/tests/test_static.py delete mode 100644 repoze/bfg/tests/test_testing.py delete mode 100644 repoze/bfg/tests/test_threadlocal.py delete mode 100644 repoze/bfg/tests/test_traversal.py delete mode 100644 repoze/bfg/tests/test_url.py delete mode 100644 repoze/bfg/tests/test_urldispatch.py delete mode 100644 repoze/bfg/tests/test_view.py delete mode 100644 repoze/bfg/tests/test_wsgi.py delete mode 100644 repoze/bfg/tests/test_zcml.py delete mode 100644 repoze/bfg/tests/viewdecoratorapp/__init__.py delete mode 100644 repoze/bfg/tests/viewdecoratorapp/configure.zcml delete mode 100644 repoze/bfg/tests/viewdecoratorapp/views/__init__.py delete mode 100644 repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt delete mode 100644 repoze/bfg/tests/viewdecoratorapp/views/views.py delete mode 100644 repoze/bfg/threadlocal.py delete mode 100644 repoze/bfg/traversal.py delete mode 100644 repoze/bfg/url.py delete mode 100644 repoze/bfg/urldispatch.py delete mode 100644 repoze/bfg/view.py delete mode 100644 repoze/bfg/wsgi.py delete mode 100644 repoze/bfg/zcml.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..9f06987a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.egg +*.egg-info +*.pyc +*.pt.py +*.txt.py + diff --git a/pyramid/__init__.py b/pyramid/__init__.py new file mode 100644 index 000000000..f33bdbc35 --- /dev/null +++ b/pyramid/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) #pragma NO COVERAGE diff --git a/pyramid/authentication.py b/pyramid/authentication.py new file mode 100644 index 000000000..54e106af5 --- /dev/null +++ b/pyramid/authentication.py @@ -0,0 +1,441 @@ +from codecs import utf_8_decode +from codecs import utf_8_encode +import datetime +import time + +from paste.auth import auth_tkt +from paste.request import get_cookies + +from zope.interface import implements + +from repoze.bfg.interfaces import IAuthenticationPolicy + +from repoze.bfg.request import add_global_response_headers +from repoze.bfg.security import Authenticated +from repoze.bfg.security import Everyone + +class CallbackAuthenticationPolicy(object): + """ Abstract class """ + def authenticated_userid(self, request): + userid = self._get_userid(request) + if userid is None: + return None + if self.callback is None: + return userid + if self.callback(userid, request) is not None: # is not None! + return userid + + def effective_principals(self, request): + effective_principals = [Everyone] + userid = self._get_userid(request) + if userid is None: + return effective_principals + if self.callback is None: + groups = [] + else: + groups = self.callback(userid, request) + if groups is None: # is None! + return effective_principals + effective_principals.append(Authenticated) + effective_principals.append(userid) + effective_principals.extend(groups) + + return effective_principals + + +class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): + """ A :mod:`repoze.bfg` :term:`authentication policy` which + obtains data from the :mod:`repoze.who` 1.X WSGI 'API' (the + ``repoze.who.identity`` key in the WSGI environment). + + Constructor Arguments + + ``identifier_name`` + + Default: ``auth_tkt``. The :mod:`repoze.who` plugin name that + performs remember/forget. Optional. + + ``callback`` + + Default: ``None``. A callback passed the :mod:`repoze.who` + identity and the :term:`request`, expected to return ``None`` + if the user represented by the identity doesn't exist or a + sequence of group identifiers (possibly empty) if the user + does exist. If ``callback`` is None, the userid will be + assumed to exist with no groups. + """ + implements(IAuthenticationPolicy) + + def __init__(self, identifier_name='auth_tkt', callback=None): + self.identifier_name = identifier_name + self.callback = callback + + def _get_identity(self, request): + return request.environ.get('repoze.who.identity') + + def _get_identifier(self, request): + plugins = request.environ.get('repoze.who.plugins') + if plugins is None: + return None + identifier = plugins[self.identifier_name] + return identifier + + def authenticated_userid(self, request): + identity = self._get_identity(request) + if identity is None: + return None + if self.callback is None: + return identity['repoze.who.userid'] + if self.callback(identity, request) is not None: # is not None! + return identity['repoze.who.userid'] + + def effective_principals(self, request): + effective_principals = [Everyone] + identity = self._get_identity(request) + if identity is None: + return effective_principals + if self.callback is None: + groups = [] + else: + groups = self.callback(identity, request) + if groups is None: # is None! + return effective_principals + userid = identity['repoze.who.userid'] + effective_principals.append(Authenticated) + effective_principals.append(userid) + effective_principals.extend(groups) + + return effective_principals + + def remember(self, request, principal, **kw): + identifier = self._get_identifier(request) + if identifier is None: + return [] + environ = request.environ + identity = {'repoze.who.userid':principal} + return identifier.remember(environ, identity) + + def forget(self, request): + identifier = self._get_identifier(request) + if identifier is None: + return [] + identity = self._get_identity(request) + return identifier.forget(request.environ, identity) + +class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy): + """ A :mod:`repoze.bfg` :term:`authentication policy` which + obtains data from the ``REMOTE_USER`` WSGI environment variable. + + Constructor Arguments + + ``environ_key`` + + Default: ``REMOTE_USER``. The key in the WSGI environ which + provides the userid. + + ``callback`` + + Default: ``None``. A callback passed the userid and the request, + expected to return None if the userid doesn't exist or a sequence + of group identifiers (possibly empty) if the user does exist. + If ``callback`` is None, the userid will be assumed to exist with no + groups. + """ + implements(IAuthenticationPolicy) + + def __init__(self, environ_key='REMOTE_USER', callback=None): + self.environ_key = environ_key + self.callback = callback + + def _get_userid(self, request): + return request.environ.get(self.environ_key) + + def remember(self, request, principal, **kw): + return [] + + def forget(self, request): + return [] + +class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): + """ A :mod:`repoze.bfg` :term:`authentication policy` which + obtains data from an :class:`paste.auth.auth_tkt` cookie. + + Constructor Arguments + + ``secret`` + + The secret (a string) used for auth_tkt cookie signing. + Required. + + ``callback`` + + Default: ``None``. A callback passed the userid and the + request, expected to return ``None`` if the userid doesn't + exist or a sequence of group identifiers (possibly empty) if + the user does exist. If ``callback`` is ``None``, the userid + will be assumed to exist with no groups. Optional. + + ``cookie_name`` + + Default: ``repoze.bfg.auth_tkt``. The cookie name used + (string). Optional. + + ``secure`` + + Default: ``False``. Only send the cookie back over a secure + conn. Optional. + + ``include_ip`` + + Default: ``False``. Make the requesting IP address part of + the authentication data in the cookie. Optional. + + ``timeout`` + + Default: ``None``. Maximum number of seconds which a newly + issued ticket will be considered valid. After this amount of + time, the ticket will expire (effectively logging the user + out). If this value is ``None``, the ticket never expires. + Optional. + + ``reissue_time`` + + Default: ``None``. If this parameter is set, it represents the + number of seconds that must pass before an authentication token + cookie is reissued. The duration is measured as the number of + seconds since the last auth_tkt cookie was issued and 'now'. + If the ``timeout`` value is ``None``, this parameter has no + effect. If this parameter is provided, and the value of + ``timeout`` is not ``None``, the value of ``reissue_time`` must + be smaller than value of ``timeout``. A good rule of thumb: if + you want auto-reissued cookies: set this to the ``timeout`` + value divided by ten. If this value is ``0``, a new ticket + cookie will be reissued on every request which needs + authentication. Optional. + + ``max_age`` + + Default: ``None``. The max age of the auth_tkt cookie, in + seconds. This differs from ``timeout`` inasmuch as ``timeout`` + represents the lifetime of the ticket contained in the cookie, + while this value represents the lifetime of the cookie itself. + When this value is set, the cookie's ``Max-Age`` and + ``Expires`` settings will be set, allowing the auth_tkt cookie + to last between browser sessions. It is typically nonsensical + to set this to a value that is lower than ``timeout`` or + ``reissue_time``, although it is not explicitly prevented. + Optional. + + ``path`` + + Default: ``/``. The path for which the auth_tkt cookie is valid. + May be desirable if the application only serves part of a domain. + Optional. + + ``http_only`` + + Default: ``False``. Hide cookie from JavaScript by setting the + HttpOnly flag. Not honored by all browsers. + Optional. + """ + implements(IAuthenticationPolicy) + def __init__(self, + secret, + callback=None, + cookie_name='repoze.bfg.auth_tkt', + secure=False, + include_ip=False, + timeout=None, + reissue_time=None, + max_age=None, + path="/", + http_only=False, + ): + self.cookie = AuthTktCookieHelper( + secret, + cookie_name=cookie_name, + secure=secure, + include_ip=include_ip, + timeout=timeout, + reissue_time=reissue_time, + max_age=max_age, + http_only=http_only, + path=path, + ) + self.callback = callback + + def _get_userid(self, request): + result = self.cookie.identify(request) + if result: + return result['userid'] + + def remember(self, request, principal, **kw): + """ Accepts the following kw args: ``max_age``.""" + return self.cookie.remember(request, principal, **kw) + + def forget(self, request): + return self.cookie.forget(request) + +def b64encode(v): + return v.encode('base64').strip().replace('\n', '') + +def b64decode(v): + return v.decode('base64') + +EXPIRE = object() + +class AuthTktCookieHelper(object): + auth_tkt = auth_tkt # for tests + + userid_type_decoders = { + 'int':int, + 'unicode':lambda x: utf_8_decode(x)[0], # bw compat for old cookies + 'b64unicode': lambda x: utf_8_decode(b64decode(x))[0], + 'b64str': lambda x: b64decode(x), + } + + userid_type_encoders = { + int: ('int', str), + long: ('int', str), + unicode: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])), + str: ('b64str', lambda x: b64encode(x)), + } + + def __init__(self, secret, cookie_name='auth_tkt', secure=False, + include_ip=False, timeout=None, reissue_time=None, + max_age=None, http_only=False, path="/"): + self.secret = secret + self.cookie_name = cookie_name + self.include_ip = include_ip + self.secure = secure + self.timeout = timeout + if reissue_time is not None and timeout is not None: + if reissue_time > timeout: + raise ValueError('reissue_time must be lower than timeout') + self.reissue_time = reissue_time + self.max_age = max_age + self.http_only = http_only + self.path = path + + static_flags = [] + if self.secure: + static_flags.append('; Secure') + if self.http_only: + static_flags.append('; HttpOnly') + self.static_flags = "".join(static_flags) + + def _get_cookies(self, environ, value, max_age=None): + if max_age is EXPIRE: + max_age = "; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT" + elif max_age is not None: + later = datetime.datetime.utcnow() + datetime.timedelta( + seconds=int(max_age)) + # Wdy, DD-Mon-YY HH:MM:SS GMT + expires = later.strftime('%a, %d %b %Y %H:%M:%S GMT') + # the Expires header is *required* at least for IE7 (IE7 does + # not respect Max-Age) + max_age = "; Max-Age=%s; Expires=%s" % (max_age, expires) + else: + max_age = '' + + cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) + wild_domain = '.' + cur_domain + + cookies = [ + ('Set-Cookie', '%s="%s"; Path=%s%s%s' % ( + self.cookie_name, value, self.path, max_age, self.static_flags)), + ('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % ( + self.cookie_name, value, self.path, cur_domain, max_age, + self.static_flags)), + ('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % ( + self.cookie_name, value, self.path, wild_domain, max_age, + self.static_flags)) + ] + + return cookies + + def identify(self, request): + environ = request.environ + cookies = get_cookies(environ) + cookie = cookies.get(self.cookie_name) + + if cookie is None or not cookie.value: + return None + + if self.include_ip: + remote_addr = environ['REMOTE_ADDR'] + else: + remote_addr = '0.0.0.0' + + try: + timestamp, userid, tokens, user_data = self.auth_tkt.parse_ticket( + self.secret, cookie.value, remote_addr) + except self.auth_tkt.BadTicket: + return None + + now = time.time() + + if self.timeout and ( (timestamp + self.timeout) < now ): + return None + + userid_typename = 'userid_type:' + user_data_info = user_data.split('|') + for datum in filter(None, user_data_info): + if datum.startswith(userid_typename): + userid_type = datum[len(userid_typename):] + decoder = self.userid_type_decoders.get(userid_type) + if decoder: + userid = decoder(userid) + + reissue = self.reissue_time is not None + + if not hasattr(request, '_authtkt_reissued'): + if reissue and ( (now - timestamp) > self.reissue_time): + headers = self.remember(request, userid, max_age=self.max_age) + add_global_response_headers(request, headers) + request._authtkt_reissued = True + + environ['REMOTE_USER_TOKENS'] = tokens + environ['REMOTE_USER_DATA'] = user_data + environ['AUTH_TYPE'] = 'cookie' + + identity = {} + identity['timestamp'] = timestamp + identity['userid'] = userid + identity['tokens'] = tokens + identity['userdata'] = user_data + return identity + + def forget(self, request): + # return a set of expires Set-Cookie headers + environ = request.environ + return self._get_cookies(environ, '', max_age=EXPIRE) + + def remember(self, request, userid, max_age=None): + max_age = max_age or self.max_age + environ = request.environ + + if self.include_ip: + remote_addr = environ['REMOTE_ADDR'] + else: + remote_addr = '0.0.0.0' + + user_data = '' + + encoding_data = self.userid_type_encoders.get(type(userid)) + if encoding_data: + encoding, encoder = encoding_data + userid = encoder(userid) + user_data = 'userid_type:%s' % encoding + + ticket = self.auth_tkt.AuthTicket( + self.secret, + userid, + remote_addr, + user_data=user_data, + cookie_name=self.cookie_name, + secure=self.secure) + + cookie_value = ticket.cookie_value() + return self._get_cookies(environ, cookie_value, max_age) + diff --git a/pyramid/authorization.py b/pyramid/authorization.py new file mode 100644 index 000000000..8c46b4a33 --- /dev/null +++ b/pyramid/authorization.py @@ -0,0 +1,133 @@ +from zope.interface import implements + +from repoze.bfg.interfaces import IAuthorizationPolicy + +from repoze.bfg.location import lineage +from repoze.bfg.security import ACLAllowed +from repoze.bfg.security import ACLDenied +from repoze.bfg.security import Allow +from repoze.bfg.security import Deny +from repoze.bfg.security import Everyone + +class ACLAuthorizationPolicy(object): + """ An :term:`authorization policy` which consults an :term:`ACL` + object attached to a :term:`context` to determine authorization + information about a :term:`principal` or multiple principals. + If the context is part of a :term:`lineage`, the context's parents + are consulted for ACL information too. The following is true + about this security policy. + + - When checking whether the 'current' user is permitted (via the + ``permits`` method), the security policy consults the + ``context`` for an ACL first. If no ACL exists on the context, + or one does exist but the ACL does not explicitly allow or deny + access for any of the effective principals, consult the + context's parent ACL, and so on, until the lineage is exhausted + or we determine that the policy permits or denies. + + During this processing, if any :data:`repoze.bfg.security.Deny` + ACE is found matching any principal in ``principals``, stop + processing by returning an + :class:`repoze.bfg.security.ACLDenied` instance (equals + ``False``) immediately. If any + :data:`repoze.bfg.security.Allow` ACE is found matching any + principal, stop processing by returning an + :class:`repoze.bfg.security.ACLAllowed` instance (equals + ``True``) immediately. If we exhaust the context's + :term:`lineage`, and no ACE has explicitly permitted or denied + access, return an instance of + :class:`repoze.bfg.security.ACLDenied` (equals ``False``). + + - When computing principals allowed by a permission via the + :func:`repoze.bfg.security.principals_allowed_by_permission` + method, we compute the set of principals that are explicitly + granted the ``permission`` in the provided ``context``. We do + this by walking 'up' the object graph *from the root* to the + context. During this walking process, if we find an explicit + :data:`repoze.bfg.security.Allow` ACE for a principal that + matches the ``permission``, the principal is included in the + allow list. However, if later in the walking process that + principal is mentioned in any :data:`repoze.bfg.security.Deny` + ACE for the permission, the principal is removed from the allow + list. If a :data:`repoze.bfg.security.Deny` to the principal + :data:`repoze.bfg.security.Everyone` is encountered during the + walking process that matches the ``permission``, the allow list + is cleared for all principals encountered in previous ACLs. The + walking process ends after we've processed the any ACL directly + attached to ``context``; a set of principals is returned. + """ + + implements(IAuthorizationPolicy) + + def permits(self, context, principals, permission): + """ Return an instance of + :class:`repoze.bfg.security.ACLAllowed` instance if the policy + permits access, return an instance of + :class:`repoze.bfg.security.ACLDenied` if not.""" + + acl = '' + + for location in lineage(context): + try: + acl = location.__acl__ + except AttributeError: + continue + + for ace in acl: + ace_action, ace_principal, ace_permissions = ace + if ace_principal in principals: + if not hasattr(ace_permissions, '__iter__'): + ace_permissions = [ace_permissions] + if permission in ace_permissions: + if ace_action == Allow: + return ACLAllowed(ace, acl, permission, + principals, location) + else: + return ACLDenied(ace, acl, permission, + principals, location) + + # default deny (if no ACL in lineage at all, or if none of the + # principals were mentioned in any ACE we found) + return ACLDenied( + '', + acl, + permission, + principals, + context) + + def principals_allowed_by_permission(self, context, permission): + """ Return the set of principals explicitly granted the + permission named ``permission`` according to the ACL directly + attached to the ``context`` as well as inherited ACLs based on + the :term:`lineage`.""" + allowed = set() + + for location in reversed(list(lineage(context))): + # NB: we're walking *up* the object graph from the root + try: + acl = location.__acl__ + except AttributeError: + continue + + allowed_here = set() + denied_here = set() + + for ace_action, ace_principal, ace_permissions in acl: + if not hasattr(ace_permissions, '__iter__'): + ace_permissions = [ace_permissions] + if ace_action == Allow and permission in ace_permissions: + if not ace_principal in denied_here: + allowed_here.add(ace_principal) + if ace_action == Deny and permission in ace_permissions: + denied_here.add(ace_principal) + if ace_principal == Everyone: + # clear the entire allowed set, as we've hit a + # deny of Everyone ala (Deny, Everyone, ALL) + allowed = set() + break + elif ace_principal in allowed: + allowed.remove(ace_principal) + + allowed.update(allowed_here) + + return allowed diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py new file mode 100644 index 000000000..4a9d2655e --- /dev/null +++ b/pyramid/chameleon_text.py @@ -0,0 +1,139 @@ +import sys + +from zope.interface import implements + +try: + from chameleon.core.template import TemplateFile + TemplateFile # prevent pyflakes complaining about a redefinition below +except ImportError: # pragma: no cover + exc_class, exc, tb = sys.exc_info() + # Chameleon doesn't work on non-CPython platforms + class TemplateFile(object): + def __init__(self, *arg, **kw): + raise ImportError, exc, tb + +try: + from chameleon.zpt.language import Parser + Parser # prevent pyflakes complaining about a redefinition below +except ImportError: # pragma: no cover + # Chameleon doesn't work on non-CPython platforms + class Parser(object): + pass + +from repoze.bfg.interfaces import ITemplateRenderer +from repoze.bfg.interfaces import IChameleonTranslate + +from repoze.bfg.decorator import reify +from repoze.bfg import renderers +from repoze.bfg.path import caller_package +from repoze.bfg.settings import get_settings +from repoze.bfg.threadlocal import get_current_registry + +class TextTemplateFile(TemplateFile): + default_parser = Parser() + + def __init__(self, filename, parser=None, format='text', doctype=None, + **kwargs): + if parser is None: + parser = self.default_parser + super(TextTemplateFile, self).__init__(filename, parser, format, + doctype, **kwargs) + +def renderer_factory(path): + return renderers.template_renderer_factory(path, TextTemplateRenderer) + +class TextTemplateRenderer(object): + implements(ITemplateRenderer) + def __init__(self, path): + self.path = path + + @reify # avoid looking up reload_templates before manager pushed + def template(self): + settings = get_settings() + debug = False + auto_reload = False + if settings: + # using .get here is a strategy to be kind to old *tests* rather + # than being kind to any existing production system + auto_reload = settings.get('reload_templates') + debug = settings.get('debug_templates') + reg = get_current_registry() + translate = None + if reg is not None: + translate = reg.queryUtility(IChameleonTranslate) + return TextTemplateFile(self.path, + auto_reload=auto_reload, + debug=debug, + translate=translate) + + def implementation(self): + return self.template + + def __call__(self, value, system): + try: + system.update(value) + except (TypeError, ValueError): + raise ValueError('renderer was passed non-dictionary as value') + result = self.template(**system) + return result + +def get_renderer(path): + """ Return a callable object which can be used to render a + :term:`Chameleon` text template using the template implied by the + ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.get_renderer` instead. + """ + package = caller_package() + factory = renderers.RendererHelper(path, package=package) + return factory.get_renderer() + +def get_template(path): + """ Return the underyling object representing a :term:`Chameleon` + text template using the template implied by the ``path`` argument. + The ``path`` argument may be a package-relative path, an absolute + path, or a :term:`resource specification`. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + the ``implementation()`` method of a template renderer retrieved via + :func:`repoze.bfg.renderers.get_renderer` instead. + """ + package = caller_package() + factory = renderers.RendererHelper(path, package=package) + return factory.get_renderer().implementation() + +def render_template(path, **kw): + """ Render a :term:`Chameleon` text template using the template + implied by the ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. The arguments in ``*kw`` are passed as top-level + names to the template, and so may be used within the template + itself. Returns a string. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.render` instead. + """ + package = caller_package() + request = kw.pop('request', None) + renderer = renderers.RendererHelper(path, package=package) + return renderer.render(kw, None, request=request) + +def render_template_to_response(path, **kw): + """ Render a :term:`Chameleon` text template using the template + implied by the ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. The arguments in ``*kw`` are passed as top-level + names to the template, and so may be used within the template + itself. Returns a :term:`Response` object with the body as the + template result. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.render_to_response` instead. + """ + package = caller_package() + request = kw.pop('request', None) + renderer = renderers.RendererHelper(path, package=package) + return renderer.render_to_response(kw, None, request=request) diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py new file mode 100644 index 000000000..3c1c59f1c --- /dev/null +++ b/pyramid/chameleon_zpt.py @@ -0,0 +1,121 @@ +import sys + +from zope.interface import implements + +try: + from chameleon.zpt.template import PageTemplateFile + PageTemplateFile # prevent pyflakes complaining about a redefinition below +except ImportError: # pragma: no cover + exc_class, exc, tb = sys.exc_info() + # Chameleon doesn't work on non-CPython platforms + class PageTemplateFile(object): + def __init__(self, *arg, **kw): + raise ImportError, exc, tb + +from repoze.bfg.interfaces import IChameleonTranslate +from repoze.bfg.interfaces import ITemplateRenderer + +from repoze.bfg.decorator import reify +from repoze.bfg.path import caller_package +from repoze.bfg import renderers +from repoze.bfg.settings import get_settings +from repoze.bfg.threadlocal import get_current_registry + +def renderer_factory(path): + return renderers.template_renderer_factory(path, ZPTTemplateRenderer) + +class ZPTTemplateRenderer(object): + implements(ITemplateRenderer) + def __init__(self, path): + self.path = path + + @reify # avoid looking up reload_templates before manager pushed + def template(self): + settings = get_settings() + debug = False + auto_reload = False + if settings: + # using .get here is a strategy to be kind to old *tests* rather + # than being kind to any existing production system + auto_reload = settings.get('reload_templates') + debug = settings.get('debug_templates') + reg = get_current_registry() + translate = None + if reg is not None: + translate = reg.queryUtility(IChameleonTranslate) + return PageTemplateFile(self.path, + auto_reload=auto_reload, + debug=debug, + translate=translate) + + def implementation(self): + return self.template + + def __call__(self, value, system): + try: + system.update(value) + except (TypeError, ValueError): + raise ValueError('renderer was passed non-dictionary as value') + result = self.template(**system) + return result + +def get_renderer(path): + """ Return a callable object which can be used to render a + :term:`Chameleon` ZPT template using the template implied by the + ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.get_renderer` instead. + """ + package = caller_package() + factory = renderers.RendererHelper(path, package=package) + return factory.get_renderer() + +def get_template(path): + """ Return the underyling object representing a :term:`Chameleon` + ZPT template using the template implied by the ``path`` argument. + The ``path`` argument may be a package-relative path, an absolute + path, or a :term:`resource specification`. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + the ``implementation()`` method of a template renderer retrieved via + :func:`repoze.bfg.renderers.get_renderer` instead. + """ + package = caller_package() + factory = renderers.RendererHelper(path, package=package) + return factory.get_renderer().implementation() + +def render_template(path, **kw): + """ Render a :term:`Chameleon` ZPT template using the template + implied by the ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. The arguments in ``*kw`` are passed as top-level + names to the template, and so may be used within the template + itself. Returns a string. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.render` instead. + """ + package = caller_package() + request = kw.pop('request', None) + renderer = renderers.RendererHelper(path, package=package) + return renderer.render(kw, None, request=request) + +def render_template_to_response(path, **kw): + """ Render a :term:`Chameleon` ZPT template using the template + implied by the ``path`` argument. The ``path`` argument may be a + package-relative path, an absolute path, or a :term:`resource + specification`. The arguments in ``*kw`` are passed as top-level + names to the template, and so may be used within the template + itself. Returns a :term:`Response` object with the body as the + template result. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use + :func:`repoze.bfg.renderers.render_to_response` instead. + """ + package = caller_package() + request = kw.pop('request', None) + renderer = renderers.RendererHelper(path, package=package) + return renderer.render_to_response(kw, None, request=request) diff --git a/pyramid/compat/__init__.py b/pyramid/compat/__init__.py new file mode 100644 index 000000000..205175132 --- /dev/null +++ b/pyramid/compat/__init__.py @@ -0,0 +1,143 @@ +# Some code in this file was lifted wholesale from Django +# (see +# http://code.djangoproject.com/browser/django/trunk/LICENSE for +# license text; BSD-like) + +# License for code in this file that was taken from Python 2.5. + +# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +# -------------------------------------------- +# +# 1. This LICENSE AGREEMENT is between the Python Software Foundation +# ("PSF"), and the Individual or Organization ("Licensee") accessing and +# otherwise using this software ("Python") in source or binary form and +# its associated documentation. +# +# 2. Subject to the terms and conditions of this License Agreement, PSF +# hereby grants Licensee a nonexclusive, royalty-free, world-wide +# license to reproduce, analyze, test, perform and/or display publicly, +# prepare derivative works, distribute, and otherwise use Python +# alone or in any derivative version, provided, however, that PSF's +# License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +# 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation; +# All Rights Reserved" are retained in Python alone or in any derivative +# version prepared by Licensee. +# +# 3. In the event Licensee prepares a derivative work that is based on +# or incorporates Python or any part thereof, and wants to make +# the derivative work available to others as provided herein, then +# Licensee hereby agrees to include in any such work a brief summary of +# the changes made to Python. +# +# 4. PSF is making Python available to Licensee on an "AS IS" +# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +# INFRINGE ANY THIRD PARTY RIGHTS. +# +# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. +# +# 6. This License Agreement will automatically terminate upon a material +# breach of its terms and conditions. +# +# 7. Nothing in this License Agreement shall be deemed to create any +# relationship of agency, partnership, or joint venture between PSF and +# Licensee. This License Agreement does not grant permission to use PSF +# trademarks or trade name in a trademark sense to endorse or promote +# products or services of Licensee, or any third party. +# +# 8. By copying, installing or otherwise using Python, Licensee +# agrees to be bound by the terms and conditions of this License +# Agreement. + + +try: # pragma: no cover + from functools import wraps +except ImportError: #pragma no cover + # < 2.5 + def curry(_curried_func, *args, **kwargs): + def _curried(*moreargs, **morekwargs): + return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) + return _curried + + ### Begin from Python 2.5 functools.py ################################### + # Summary of changes made to the Python 2.5 code below: + # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility + # in Django. + # * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except + # block to make it compatible with Python 2.3, which doesn't allow + # assigning to ``__name__``. + + # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software + # Foundation. + # All Rights Reserved. + ########################################################################## + + # update_wrapper() and wraps() are tools to help write + # wrapper functions that can handle naive introspection + + WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') + WRAPPER_UPDATES = ('__dict__',) + def update_wrapper(wrapper, + wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Update a wrapper function to look like the wrapped function + + wrapper is the function to be updated + wrapped is the original function + assigned is a tuple naming the attributes assigned directly + from the wrapped function to the wrapper function (defaults to + functools.WRAPPER_ASSIGNMENTS) + updated is a tuple naming the attributes off the wrapper that + are updated with the corresponding attribute from the wrapped + function (defaults to functools.WRAPPER_UPDATES) + """ + for attr in assigned: + setattr(wrapper, attr, getattr(wrapped, attr)) + + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr)) + # Return the wrapper so this can be used as a decorator via curry() + return wrapper + + def wraps(wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Decorator factory to apply update_wrapper() to a wrapper function + + Returns a decorator that invokes update_wrapper() with the decorated + function as the wrapper argument and the arguments to wraps() as the + remaining arguments. Default arguments are as for update_wrapper(). + This is a convenience function to simplify applying curry() to + update_wrapper(). + """ + return curry(update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + +### End from Python 2.5 functools.py ########################################## + +try: + all = all +except NameError: # pragma: no cover + def all(iterable): + for element in iterable: + if not element: + return False + return True + +try: + import json +except ImportError: # pragma: no cover + import simplejson as json + +try: + from hashlib import md5 +except ImportError: # pragma: no cover + import md5 + md5 = md5.new + diff --git a/pyramid/configuration.py b/pyramid/configuration.py new file mode 100644 index 000000000..88bdcba58 --- /dev/null +++ b/pyramid/configuration.py @@ -0,0 +1,2697 @@ +import os +import re +import sys +import threading +import inspect +import pkg_resources + +import venusian + +from translationstring import ChameleonTranslate + +from zope.configuration import xmlconfig + +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface.interfaces import IInterface +from zope.interface import implements + +from repoze.bfg.interfaces import IAuthenticationPolicy +from repoze.bfg.interfaces import IAuthorizationPolicy +from repoze.bfg.interfaces import IChameleonTranslate +from repoze.bfg.interfaces import IDebugLogger +from repoze.bfg.interfaces import IDefaultPermission +from repoze.bfg.interfaces import IDefaultRootFactory +from repoze.bfg.interfaces import IExceptionViewClassifier +from repoze.bfg.interfaces import ILocaleNegotiator +from repoze.bfg.interfaces import IMultiView +from repoze.bfg.interfaces import IPackageOverrides +from repoze.bfg.interfaces import IRendererFactory +from repoze.bfg.interfaces import IRendererGlobalsFactory +from repoze.bfg.interfaces import IRequest +from repoze.bfg.interfaces import IRequestFactory +from repoze.bfg.interfaces import IRootFactory +from repoze.bfg.interfaces import IRouteRequest +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import ISecuredView +from repoze.bfg.interfaces import ISettings +from repoze.bfg.interfaces import IStaticURLInfo +from repoze.bfg.interfaces import ITranslationDirectories +from repoze.bfg.interfaces import ITraverser +from repoze.bfg.interfaces import IView +from repoze.bfg.interfaces import IViewClassifier +from repoze.bfg.interfaces import IExceptionResponse +from repoze.bfg.interfaces import IException + +from repoze.bfg import chameleon_text +from repoze.bfg import chameleon_zpt +from repoze.bfg import renderers +from repoze.bfg.renderers import RendererHelper +from repoze.bfg.authorization import ACLAuthorizationPolicy +from repoze.bfg.compat import all +from repoze.bfg.compat import md5 +from repoze.bfg.events import ApplicationCreated +from repoze.bfg.exceptions import Forbidden +from repoze.bfg.exceptions import NotFound +from repoze.bfg.exceptions import PredicateMismatch +from repoze.bfg.exceptions import ConfigurationError +from repoze.bfg.i18n import get_localizer +from repoze.bfg.log import make_stream_logger +from repoze.bfg.path import caller_package +from repoze.bfg.path import package_path +from repoze.bfg.path import package_of +from repoze.bfg.registry import Registry +from repoze.bfg.request import route_request_iface +from repoze.bfg.resource import PackageOverrides +from repoze.bfg.resource import resolve_resource_spec +from repoze.bfg.settings import Settings +from repoze.bfg.static import StaticURLInfo +from repoze.bfg.threadlocal import get_current_registry +from repoze.bfg.threadlocal import get_current_request +from repoze.bfg.threadlocal import manager +from repoze.bfg.traversal import traversal_path +from repoze.bfg.traversal import DefaultRootFactory +from repoze.bfg.traversal import find_interface +from repoze.bfg.urldispatch import RoutesMapper +from repoze.bfg.view import render_view_to_response +from repoze.bfg.view import default_exceptionresponse_view + +MAX_ORDER = 1 << 30 +DEFAULT_PHASH = md5().hexdigest() + +DEFAULT_RENDERERS = ( + ('.pt', chameleon_zpt.renderer_factory), + ('.txt', chameleon_text.renderer_factory), + ('json', renderers.json_renderer_factory), + ('string', renderers.string_renderer_factory), + ) + +_marker = object() + +class Configurator(object): + """ + A Configurator is used to configure a :mod:`repoze.bfg` + :term:`application registry`. + + The Configurator accepts a number of arguments: ``registry``, + ``package``, ``settings``, ``root_factory``, + ``authentication_policy``, ``authorization_policy``, ``renderers`` + ``debug_logger``, ``locale_negotiator``, ``request_factory``, and + ``renderer_globals_factory``. + + If the ``registry`` argument is passed as a non-``None`` value, it + must be an instance of the :class:`repoze.bfg.registry.Registry` + class representing the registry to configure. If ``registry`` is + ``None``, the configurator will create a + :class:`repoze.bfg.registry.Registry` instance itself; it will + also perform some default configuration that would not otherwise + be done. After construction, the configurator may be used to add + configuration to the registry. The overall state of a registry is + called the 'configuration state'. + + .. warning:: If a ``registry`` is passed to the Configurator + constructor, all other constructor arguments except ``package`` + are ignored. + + If the ``package`` argument is passed, it must be a reference to a + Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a + :term:`dotted Python name` to same. This value is used as a basis + to convert relative paths passed to various configuration methods, + such as methods which accept a ``renderer`` argument, into + absolute paths. If ``None`` is passed (the default), the package + is assumed to be the Python package in which the *caller* of the + ``Configurator`` constructor lives. + + If the ``settings`` argument is passed, it should be a Python + dictionary representing the deployment settings for this + application. These are later retrievable using the + :meth:`repoze.bfg.configuration.Configurator.get_settings` and + :func:`repoze.bfg.settings.get_settings` APIs. + + If the ``root_factory`` argument is passed, it should be an object + representing the default :term:`root factory` for your application + or a :term:`dotted Python name` to same. If it is ``None``, a + default root factory will be used. + + If ``authentication_policy`` is passed, it should be an instance + of an :term:`authentication policy` or a :term:`dotted Python + name` to same. + + If ``authorization_policy`` is passed, it should be an instance of + an :term:`authorization policy` or a :term:`dotted Python name` to + same. + + .. note:: A ``ConfigurationError`` will be raised when an + authorization policy is supplied without also supplying an + authentication policy (authorization requires authentication). + + If ``renderers`` is passed, it should be a list of tuples + representing a set of :term:`renderer` factories which should be + configured into this application (each tuple representing a set of + positional values that should be passed to + :meth:`repoze.bfg.configuration.Configurator.add_renderer`). If + it is not passed, a default set of renderer factories is used. + + If ``debug_logger`` is not passed, a default debug logger that + logs to stderr will be used. If it is passed, it should be an + instance of the :class:`logging.Logger` (PEP 282) standard library + class or a :term:`dotted Python name` to same. The debug logger + is used by :mod:`repoze.bfg` itself to log warnings and + authorization debugging information. + + If ``locale_negotiator`` is passed, it should be a :term:`locale + negotiator` implementation or a :term:`dotted Python name` to + same. See :ref:`custom_locale_negotiator`. + + If ``request_factory`` is passed, it should be a :term:`request + factory` implementation or a :term:`dotted Python name` to same. + See :ref:`custom_request_factory`. By default it is ``None``, + which means use the default request factory. + + If ``renderer_globals_factory`` is passed, it should be a + :term:`renderer globals` factory implementation or a :term:`dotted + Python name` to same. See :ref:`custom_renderer_globals_factory`. + By default, it is ``None``, which means use no renderer globals + factory. + + If ``default_permission`` is passed, it should be a + :term:`permission` string to be used as the default permission for + all view configuration registrations performed against this + Configurator. An example of a permission string:``'view'``. + Adding a default permission makes it unnecessary to protect each + view configuration with an explicit permission, unless your + application policy requires some exception for a particular view. + By default, ``default_permission`` is ``None``, meaning that view + configurations which do not explicitly declare a permission will + always be executable by entirely anonymous users (any + authorization policy in effect is ignored). See also + :ref:`setting_a_default_permission`. + """ + + manager = manager # for testing injection + venusian = venusian # for testing injection + def __init__(self, + registry=None, + package=None, + settings=None, + root_factory=None, + authentication_policy=None, + authorization_policy=None, + renderers=DEFAULT_RENDERERS, + debug_logger=None, + locale_negotiator=None, + request_factory=None, + renderer_globals_factory=None, + default_permission=None): + if package is None: + package = caller_package() + name_resolver = DottedNameResolver(package) + self.name_resolver = name_resolver + self.package_name = name_resolver.package_name + self.package = name_resolver.package + self.registry = registry + if registry is None: + registry = Registry(self.package_name) + self.registry = registry + self.setup_registry( + settings=settings, + root_factory=root_factory, + authentication_policy=authentication_policy, + authorization_policy=authorization_policy, + renderers=renderers, + debug_logger=debug_logger, + locale_negotiator=locale_negotiator, + request_factory=request_factory, + renderer_globals_factory=renderer_globals_factory, + default_permission=default_permission, + ) + + def _set_settings(self, mapping): + settings = Settings(mapping or {}) + self.registry.registerUtility(settings, ISettings) + return settings + + def _set_root_factory(self, factory): + """ Add a :term:`root factory` to the current configuration + state. If the ``factory`` argument is ``None`` a default root + factory will be registered.""" + factory = self.maybe_dotted(factory) + if factory is None: + factory = DefaultRootFactory + self.registry.registerUtility(factory, IRootFactory) + self.registry.registerUtility(factory, IDefaultRootFactory) # b/c + + def _set_authentication_policy(self, policy, _info=u''): + """ Add a :mod:`repoze.bfg` :term:`authentication policy` to + the current configuration.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthenticationPolicy, info=_info) + + def _set_authorization_policy(self, policy, _info=u''): + """ Add a :mod:`repoze.bfg` :term:`authorization policy` to + the current configuration state (also accepts a :term:`dotted + Python name`.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthorizationPolicy, info=_info) + + def _make_spec(self, path_or_spec): + package, filename = resolve_resource_spec(path_or_spec, + self.package_name) + if package is None: + return filename # absolute filename + return '%s:%s' % (package, filename) + + def _split_spec(self, path_or_spec): + return resolve_resource_spec(path_or_spec, self.package_name) + + def _derive_view(self, view, permission=None, predicates=(), + attr=None, renderer_name=None, wrapper_viewname=None, + viewname=None, accept=None, order=MAX_ORDER, + phash=DEFAULT_PHASH): + view = self.maybe_dotted(view) + authn_policy = self.registry.queryUtility(IAuthenticationPolicy) + authz_policy = self.registry.queryUtility(IAuthorizationPolicy) + settings = self.registry.queryUtility(ISettings) + logger = self.registry.queryUtility(IDebugLogger) + mapped_view = _map_view(view, attr, renderer_name, self.registry, + self.package) + owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) + secured_view = _secure_view(owrapped_view, permission, + authn_policy, authz_policy) + debug_view = _authdebug_view(secured_view, permission, + authn_policy, authz_policy, settings, + logger) + predicated_view = _predicate_wrap(debug_view, predicates) + derived_view = _attr_wrap(predicated_view, accept, order, phash) + return derived_view + + def _override(self, package, path, override_package, override_prefix, + _info=u'', PackageOverrides=PackageOverrides): + pkg_name = package.__name__ + override_pkg_name = override_package.__name__ + override = self.registry.queryUtility( + IPackageOverrides, name=pkg_name) + if override is None: + override = PackageOverrides(package) + self.registry.registerUtility(override, IPackageOverrides, + name=pkg_name, info=_info) + override.insert(path, override_pkg_name, override_prefix) + + def _set_security_policies(self, authentication, authorization=None): + if authorization is None: + authorization = ACLAuthorizationPolicy() # default + if authorization and not authentication: + raise ConfigurationError( + 'If the "authorization" is passed a value, ' + 'the "authentication" argument must also be ' + 'passed a value; authorization requires authentication.') + self._set_authentication_policy(authentication) + self._set_authorization_policy(authorization) + + def _fix_registry(self): + """ Fix up a ZCA component registry that is not a + repoze.bfg.registry.Registry by adding analogues of + ``has_listeners`` and ``notify`` through monkey-patching.""" + + if not hasattr(self.registry, 'notify'): + def notify(*events): + [ _ for _ in self.registry.subscribers(events, None) ] + self.registry.notify = notify + + if not hasattr(self.registry, 'has_listeners'): + self.registry.has_listeners = True + + # API + + def with_package(self, package): + """ Return a new Configurator instance with the same registry + as this configurator using the package supplied as the + ``package`` argument to the new configurator. ``package`` may + be an actual Python package object or a Python dotted name + representing a package.""" + return self.__class__(registry=self.registry, package=package) + + def maybe_dotted(self, dotted): + """ Resolve the :term:`dotted Python name` ``dotted`` to a + global Python object. If ``dotted`` is not a string, return + it without attempting to do any name resolution. If + ``dotted`` is a relative dotted name (e.g. ``.foo.bar``, + consider it relative to the ``package`` argument supplied to + this Configurator's constructor.""" + return self.name_resolver.maybe_resolve(dotted) + + def absolute_resource_spec(self, relative_spec): + """ Resolve the potentially relative :term:`resource + specification` string passed as ``relative_spec`` into an + absolute resource specification string and return the string. + Use the ``package`` of this configurator as the package to + which the resource specification will be considered relative + when generating an absolute resource specification. If the + provided ``relative_spec`` argument is already absolute, or if + the ``relative_spec`` is not a string, it is simply returned.""" + if not isinstance(relative_spec, basestring): + return relative_spec + return self._make_spec(relative_spec) + + def setup_registry(self, settings=None, root_factory=None, + authentication_policy=None, authorization_policy=None, + renderers=DEFAULT_RENDERERS, debug_logger=None, + locale_negotiator=None, request_factory=None, + renderer_globals_factory=None, + default_permission=None): + """ When you pass a non-``None`` ``registry`` argument to the + :term:`Configurator` constructor, no initial 'setup' is + performed against the registry. This is because the registry + you pass in may have already been initialized for use under + :mod:`repoze.bfg` via a different configurator. However, in + some circumstances, such as when you want to use the Zope + 'global` registry instead of a registry created as a result of + the Configurator constructor, or when you want to reset the + initial setup of a registry, you *do* want to explicitly + initialize the registry associated with a Configurator for use + under :mod:`repoze.bfg`. Use ``setup_registry`` to do this + initialization. + + ``setup_registry`` configures settings, a root factory, + security policies, renderers, a debug logger, a locale + negotiator, and various other settings using the + configurator's current registry, as per the descriptions in + the Configurator constructor.""" + self._fix_registry() + self._set_settings(settings) + self._set_root_factory(root_factory) + debug_logger = self.maybe_dotted(debug_logger) + if debug_logger is None: + debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr) + registry = self.registry + registry.registerUtility(debug_logger, IDebugLogger) + registry.registerUtility(debug_logger, IDebugLogger, + 'repoze.bfg.debug') # b /c + if authentication_policy or authorization_policy: + self._set_security_policies(authentication_policy, + authorization_policy) + for name, renderer in renderers: + self.add_renderer(name, renderer) + self.add_view(default_exceptionresponse_view, + context=IExceptionResponse) + if locale_negotiator: + locale_negotiator = self.maybe_dotted(locale_negotiator) + registry.registerUtility(locale_negotiator, ILocaleNegotiator) + if request_factory: + request_factory = self.maybe_dotted(request_factory) + self.set_request_factory(request_factory) + if renderer_globals_factory: + renderer_globals_factory=self.maybe_dotted(renderer_globals_factory) + self.set_renderer_globals_factory(renderer_globals_factory) + if default_permission: + self.set_default_permission(default_permission) + + # getSiteManager is a unit testing dep injection + def hook_zca(self, getSiteManager=None): + """ Call :func:`zope.component.getSiteManager.sethook` with + the argument + :data:`repoze.bfg.threadlocal.get_current_registry`, causing + the :term:`Zope Component Architecture` 'global' APIs such as + :func:`zope.component.getSiteManager`, + :func:`zope.component.getAdapter` and others to use the + :mod:`repoze.bfg` :term:`application registry` rather than the + Zope 'global' registry. If :mod:`zope.component` cannot be + imported, this method will raise an :exc:`ImportError`.""" + if getSiteManager is None: + from zope.component import getSiteManager + getSiteManager.sethook(get_current_registry) + + # getSiteManager is a unit testing dep injection + def unhook_zca(self, getSiteManager=None): + """ Call :func:`zope.component.getSiteManager.reset` to undo + the action of + :meth:`repoze.bfg.configuration.Configurator.hook_zca`. If + :mod:`zope.component` cannot be imported, this method will + raise an :exc:`ImportError`.""" + if getSiteManager is None: # pragma: no cover + from zope.component import getSiteManager + getSiteManager.reset() + + def begin(self, request=None): + """ Indicate that application or test configuration has begun. + This pushes a dictionary containing the :term:`application + registry` implied by ``registry`` attribute of this + configurator and the :term:`request` implied by the + ``request`` argument on to the :term:`thread local` stack + consulted by various :mod:`repoze.bfg.threadlocal` API + functions.""" + self.manager.push({'registry':self.registry, 'request':request}) + + def end(self): + """ Indicate that application or test configuration has ended. + This pops the last value pushed on to the :term:`thread local` + stack (usually by the ``begin`` method) and returns that + value. + """ + return self.manager.pop() + + def derive_view(self, view, attr=None, renderer=None): + """ + + Create a :term:`view callable` using the function, instance, + or class (or :term:`dotted Python name` referring to the same) + provided as ``view`` object. + + This is API is useful to framework extenders who create + pluggable systems which need to register 'proxy' view + callables for functions, instances, or classes which meet the + requirements of being a :mod:`repoze.bfg` view callable. For + example, a ``some_other_framework`` function in another + framework may want to allow a user to supply a view callable, + but he may want to wrap the view callable in his own before + registering the wrapper as a :mod:`repoze.bfg` view callable. + Because a :mod:`repoze.bfg` view callable can be any of a + number of valid objects, the framework extender will not know + how to call the user-supplied object. Running it through + ``derive_view`` normalizes it to a callable which accepts two + arguments: ``context`` and ``request``. + + For example: + + .. code-block:: python + + def some_other_framework(user_supplied_view): + config = Configurator(reg) + proxy_view = config.derive_view(user_supplied_view) + def my_wrapper(context, request): + do_something_that_mutates(request) + return proxy_view(context, request) + config.add_view(my_wrapper) + + The ``view`` object provided should be one of the following: + + - A function or another non-class callable object that accepts + a :term:`request` as a single positional argument and which + returns a :term:`response` object. + + - A function or other non-class callable object that accepts + two positional arguments, ``context, request`` and which + returns a :term:`response` object. + + - A class which accepts a single positional argument in its + constructor named ``request``, and which has a ``__call__`` + method that accepts no arguments that returns a + :term:`response` object. + + - A class which accepts two positional arguments named + ``context, request``, and which has a ``__call__`` method + that accepts no arguments that returns a :term:`response` + object. + + - A :term:`dotted Python name` which refers to any of the + kinds of objects above. + + This API returns a callable which accepts the arguments + ``context, request`` and which returns the result of calling + the provided ``view`` object. + + The ``attr`` keyword argument is most useful when the view + object is a class. It names the method that should be used as + the callable. If ``attr`` is not provided, the attribute + effectively defaults to ``__call__``. See + :ref:`class_as_view` for more information. + + The ``renderer`` keyword argument should be a renderer + name. If supplied, it will cause the returned callable to use + a :term:`renderer` to convert the user-supplied view result to + a :term:`response` object. If a ``renderer`` argument is not + supplied, the user-supplied view must itself return a + :term:`response` object. """ + + return self._derive_view(view, attr=attr, renderer_name=renderer) + + def add_subscriber(self, subscriber, iface=None, info=u''): + """Add an event :term:`subscriber` for the event stream + implied by the supplied ``iface`` interface. The + ``subscriber`` argument represents a callable object (or a + :term:`dotted Python name` which identifies a callable); it + will be called with a single object ``event`` whenever + :mod:`repoze.bfg` emits an :term:`event` associated with the + ``iface``, which may be an :term:`interface` or a class or a + :term:`dotted Python name` to a global object representing an + interface or a class. Using the default ``iface`` value, + ``None`` will cause the subscriber to be registered for all + event types. See :ref:`events_chapter` for more information + about events and subscribers.""" + dotted = self.maybe_dotted + subscriber, iface = dotted(subscriber), dotted(iface) + if iface is None: + iface = (Interface,) + if not isinstance(iface, (tuple, list)): + iface = (iface,) + self.registry.registerHandler(subscriber, iface, info=info) + return subscriber + + def add_settings(self, settings=None, **kw): + """Augment the ``settings`` argument passed in to the + Configurator constructor with one or more 'setting' key/value + pairs. A setting is a single key/value pair in the + dictionary-ish object returned from the API + :func:`repoze.bfg.settings.get_settings` and + :meth:`repoze.bfg.configuration.Configurator.get_settings`. + + You may pass a dictionary:: + + config.add_settings({'external_uri':'http://example.com'}) + + Or a set of key/value pairs:: + + config.add_settings(external_uri='http://example.com') + + This function is useful when you need to test code that calls + the :func:`repoze.bfg.settings.get_settings` API (or the + :meth:`repoze.bfg.configuration.Configurator.get_settings` + API) and which uses return values from that API. + + .. note:: This method is new as of :mod:`repoze.bfg` 1.2. + """ + if settings is None: + settings = {} + utility = self.registry.queryUtility(ISettings) + if utility is None: + utility = self._set_settings(settings) + utility.update(settings) + utility.update(kw) + + def get_settings(self): + """ + Return a 'settings' object for the current application. A + 'settings' object is a dictionary-like object that contains + key/value pairs based on the dictionary passed as the ``settings`` + argument to the :class:`repoze.bfg.configuration.Configurator` + constructor or the :func:`repoze.bfg.router.make_app` API. + + .. note:: For backwards compatibility, dictionary keys can also be + looked up as attributes of the settings object. + + .. note:: the :class:`repoze.bfg.settings.get_settings` function + performs the same duty.""" + return self.registry.queryUtility(ISettings) + + def make_wsgi_app(self): + """ Returns a :mod:`repoze.bfg` WSGI application representing + the current configuration state and sends a + :class:`repoze.bfg.interfaces.IApplicationCreated` + event to all listeners.""" + from repoze.bfg.router import Router # avoid circdep + app = Router(self.registry) + # We push the registry on to the stack here in case any code + # that depends on the registry threadlocal APIs used in + # listeners subscribed to the IApplicationCreated event. + self.manager.push({'registry':self.registry, 'request':None}) + try: + self.registry.notify(ApplicationCreated(app)) + finally: + self.manager.pop() + return app + + def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): + """ Load configuration from a :term:`ZCML` file into the + current configuration state. The ``spec`` argument is an + absolute filename, a relative filename, or a :term:`resource + specification`, defaulting to ``configure.zcml`` (relative to + the package of the configurator's caller).""" + package_name, filename = self._split_spec(spec) + if package_name is None: # absolute filename + package = self.package + else: + __import__(package_name) + package = sys.modules[package_name] + + lock.acquire() + self.manager.push({'registry':self.registry, 'request':None}) + try: + xmlconfig.file(filename, package, execute=True) + finally: + lock.release() + self.manager.pop() + return self.registry + + def add_view(self, view=None, name="", for_=None, permission=_marker, + request_type=None, route_name=None, request_method=None, + request_param=None, containment=None, attr=None, + renderer=None, wrapper=None, xhr=False, accept=None, + header=None, path_info=None, custom_predicates=(), + context=None, _info=u''): + """ Add a :term:`view configuration` to the current + configuration state. Arguments to ``add_view`` are broken + down below into *predicate* arguments and *non-predicate* + arguments. Predicate arguments narrow the circumstances in + which the view callable will be invoked when a request is + presented to :mod:`repoze.bfg`; non-predicate arguments are + informational. + + Non-Predicate Arguments + + view + + A :term:`view callable` or a :term:`dotted Python name` + which refers to a view callable. This argument is required + unless a ``renderer`` argument also exists. If a + ``renderer`` argument is passed, and a ``view`` argument is + not provided, the view callable defaults to a callable that + returns an empty dictionary (see + :ref:`views_which_use_a_renderer`). + + permission + + The name of a :term:`permission` that the user must possess + in order to invoke the :term:`view callable`. See + :ref:`view_security_section` for more information about view + security and permissions. If ``permission`` is omitted, a + *default* permission may be used for this view registration + if one was named as the + :class:`repoze.bfg.configuration.Configurator` constructor's + ``default_permission`` argument, or if + :meth:`repoze.bfg.configuration.Configurator.set_default_permission` + was used prior to this view registration. Pass ``None`` as + the permission to explicitly indicate that the view should + always be executable by entirely anonymous users, regardless + of the default permission, bypassing any + :term:`authorization policy` that may be in effect. + + attr + + The view machinery defaults to using the ``__call__`` method + of the :term:`view callable` (or the function itself, if the + view callable is a function) to obtain a response. The + ``attr`` value allows you to vary the method attribute used + to obtain the response. For example, if your view was a + class, and the class has a method named ``index`` and you + wanted to use this method instead of the class' ``__call__`` + method to return the response, you'd say ``attr="index"`` in the + view configuration for the view. This is + most useful when the view definition is a class. + + renderer + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``) naming a :term:`renderer` + implementation. If the ``renderer`` value does not contain + a dot ``.``, the specified string will be used to look up a + renderer implementation, and that renderer implementation + will be used to construct a response from the view return + value. If the ``renderer`` value contains a dot (``.``), + the specified term will be treated as a path, and the + filename extension of the last element in the path will be + used to look up the renderer implementation, which will be + passed the full path. The renderer implementation will be + used to construct a :term:`response` from the view return + value. + + Note that if the view itself returns a :term:`response` (see + :ref:`the_response`), the specified renderer implementation + is never called. + + When the renderer is a path, although a path is usually just + a simple relative pathname (e.g. ``templates/foo.pt``, + implying that a template named "foo.pt" is in the + "templates" directory relative to the directory of the + current :term:`package` of the Configurator), a path can be + absolute, starting with a slash on UNIX or a drive letter + prefix on Windows. The path can alternately be a + :term:`resource specification` in the form + ``some.dotted.package_name:relative/path``, making it + possible to address template resources which live in a + separate package. + + The ``renderer`` attribute is optional. If it is not + defined, the "null" renderer is assumed (no rendering is + performed and the value is passed back to the upstream + :mod:`repoze.bfg` machinery unmolested). + + wrapper + + The :term:`view name` of a different :term:`view + configuration` which will receive the response body of this + view as the ``request.wrapped_body`` attribute of its own + :term:`request`, and the :term:`response` returned by this + view as the ``request.wrapped_response`` attribute of its + own request. Using a wrapper makes it possible to "chain" + views together to form a composite response. The response + of the outermost wrapper view will be returned to the user. + The wrapper view will be found as any view is found: see + :ref:`view_lookup`. The "best" wrapper view will be found + based on the lookup ordering: "under the hood" this wrapper + view is looked up via + ``repoze.bfg.view.render_view_to_response(context, request, + 'wrapper_viewname')``. The context and request of a wrapper + view is the same context and request of the inner view. If + this attribute is unspecified, no view wrapping is done. + + Predicate Arguments + + name + + The :term:`view name`. Read :ref:`traversal_chapter` to + understand the concept of a view name. + + context + + An object or a :term:`dotted Python name` referring to an + interface or class object that the :term:`context` must be + an instance of, *or* the :term:`interface` that the + :term:`context` must provide in order for this view to be + found and called. This predicate is true when the + :term:`context` is an instance of the represented class or + if the :term:`context` provides the represented interface; + it is otherwise false. This argument may also be provided + to ``add_view`` as ``for_`` (an older, still-supported + spelling). + + route_name + + This value must match the ``name`` of a :term:`route + configuration` declaration (see :ref:`urldispatch_chapter`) + that must match before this view will be called. Note that + the ``route`` configuration referred to by ``route_name`` + usually has a ``*traverse`` token in the value of its + ``path``, representing a part of the path that will be used + by :term:`traversal` against the result of the route's + :term:`root factory`. + + .. warning:: Using this argument services an advanced + feature that isn't often used unless you want to perform + traversal *after* a route has matched. See + :ref:`hybrid_chapter` for more information on using this + advanced feature. + + request_type + + This value should be an :term:`interface` that the + :term:`request` must provide in order for this view to be + found and called. This value exists only for backwards + compatibility purposes. + + request_method + + This value can either be one of the strings ``GET``, + ``POST``, ``PUT``, ``DELETE``, or ``HEAD`` representing an + HTTP ``REQUEST_METHOD``. A view declaration with this + argument ensures that the view will only be called when the + request's ``method`` attribute (aka the ``REQUEST_METHOD`` of + the WSGI environment) string matches the supplied value. + + request_param + + This value can be any string. A view declaration with this + argument ensures that the view will only be called when the + :term:`request` has a key in the ``request.params`` + dictionary (an HTTP ``GET`` or ``POST`` variable) that has a + name which matches the supplied value. If the value + supplied has a ``=`` sign in it, + e.g. ``request_params="foo=123"``, then the key (``foo``) + must both exist in the ``request.params`` dictionary, *and* + the value must match the right hand side of the expression + (``123``) for the view to "match" the current request. + + containment + + This value should be a Python class or :term:`interface` or + a :term:`dotted Python name` to such an object that a parent + object in the :term:`lineage` must provide in order for this + view to be found and called. The nodes in your object graph + must be "location-aware" to use this feature. See + :ref:`location_aware` for more information about + location-awareness. + + xhr + + This value should be either ``True`` or ``False``. If this + value is specified and is ``True``, the :term:`request` + must possess an ``HTTP_X_REQUESTED_WITH`` (aka + ``X-Requested-With``) header that has the value + ``XMLHttpRequest`` for this view to be found and called. + This is useful for detecting AJAX requests issued from + jQuery, Prototype and other Javascript libraries. + + accept + + The value of this argument represents a match query for one + or more mimetypes in the ``Accept`` HTTP request header. If + this value is specified, it must be in one of the following + forms: a mimetype match token in the form ``text/plain``, a + wildcard mimetype match token in the form ``text/*`` or a + match-all wildcard mimetype match token in the form ``*/*``. + If any of the forms matches the ``Accept`` header of the + request, this predicate will be true. + + header + + This value represents an HTTP header name or a header + name/value pair. If the value contains a ``:`` (colon), it + will be considered a name/value pair + (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The + value portion should be a regular expression. If the value + does not contain a colon, the entire value will be + considered to be the header name + (e.g. ``If-Modified-Since``). If the value evaluates to a + header name only without a value, the header specified by + the name must be present in the request for this predicate + to be true. If the value evaluates to a header name/value + pair, the header specified by the name must be present in + the request *and* the regular expression specified as the + value must match the header value. Whether or not the value + represents a header name or a header name/value pair, the + case of the header name is not significant. + + path_info + + This value represents a regular expression pattern that will + be tested against the ``PATH_INFO`` WSGI environment + variable. If the regex matches, this predicate will be + ``True``. + + + custom_predicates + + This value should be a sequence of references to custom + predicate callables. Use custom predicates when no set of + predefined predicates do what you need. Custom predicates + can be combined with predefined predicates as necessary. + Each custom predicate callable should accept two arguments: + ``context`` and ``request`` and should return either + ``True`` or ``False`` after doing arbitrary evaluation of + the context and/or the request. If all callables return + ``True``, the associated view callable will be considered + viable for a given request. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. + """ + view = self.maybe_dotted(view) + context = self.maybe_dotted(context) + for_ = self.maybe_dotted(for_) + containment = self.maybe_dotted(containment) + + if not view: + if renderer: + def view(context, request): + return {} + else: + raise ConfigurationError('"view" was not specified and ' + 'no "renderer" specified') + + if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'): + # b/w compat for 1.0 + request_method = request_type + request_type = None + + if request_type is not None: + request_type = self.maybe_dotted(request_type) + if not IInterface.providedBy(request_type): + raise ConfigurationError( + 'request_type must be an interface, not %s' % request_type) + + request_iface = IRequest + + if route_name is not None: + request_iface = self.registry.queryUtility(IRouteRequest, + name=route_name) + if request_iface is None: + deferred_views = getattr(self.registry, + 'deferred_route_views', None) + if deferred_views is None: + deferred_views = self.registry.deferred_route_views = {} + info = dict( + view=view, name=name, for_=for_, permission=permission, + request_type=request_type, route_name=route_name, + request_method=request_method, request_param=request_param, + containment=containment, attr=attr, + renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept, + header=header, path_info=path_info, + custom_predicates=custom_predicates, context=context, + _info=u'' + ) + view_info = deferred_views.setdefault(route_name, []) + view_info.append(info) + return + + order, predicates, phash = _make_predicates(xhr=xhr, + request_method=request_method, path_info=path_info, + request_param=request_param, header=header, accept=accept, + containment=containment, request_type=request_type, + custom=custom_predicates) + + if permission is _marker: + # intent: will be None if no default permission is registered + permission = self.registry.queryUtility(IDefaultPermission) + + derived_view = self._derive_view(view, permission, predicates, attr, + renderer, wrapper, name, accept, order, + phash) + + if context is None: + context = for_ + + r_context = context + if r_context is None: + r_context = Interface + if not IInterface.providedBy(r_context): + r_context = implementedBy(r_context) + + registered = self.registry.adapters.registered + + # A multiviews is a set of views which are registered for + # exactly the same context type/request type/name triad. Each + # consituent view in a multiview differs only by the + # predicates which it possesses. + + # To find a previously registered view for a context + # type/request type/name triad, we need to use the + # ``registered`` method of the adapter registry rather than + # ``lookup``. ``registered`` ignores interface inheritance + # for the required and provided arguments, returning only a + # view registered previously with the *exact* triad we pass + # in. + + # We need to do this three times, because we use three + # different interfaces as the ``provided`` interface while + # doing registrations, and ``registered`` performs exact + # matches on all the arguments it receives. + + old_view = None + + for view_type in (IView, ISecuredView, IMultiView): + old_view = registered((IViewClassifier, request_iface, r_context), + view_type, name) + if old_view is not None: + break + + isexc = isexception(context) + + def regclosure(): + if hasattr(view, '__call_permissive__'): + view_iface = ISecuredView + else: + view_iface = IView + self.registry.registerAdapter( + derived_view, + (IViewClassifier, request_iface, context), + view_iface, name, info=_info) + if isexc: + self.registry.registerAdapter( + derived_view, + (IExceptionViewClassifier, request_iface, context), + view_iface, name, info=_info) + + is_multiview = IMultiView.providedBy(old_view) + old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) + + if old_view is None: + # - No component was yet registered for any of our I*View + # interfaces exactly; this is the first view for this + # triad. + regclosure() + + elif (not is_multiview) and (old_phash == phash): + # - A single view component was previously registered with + # the same predicate hash as this view; this registration + # is therefore an override. + regclosure() + + else: + # - A view or multiview was already registered for this + # triad, and the new view is not an override. + + # XXX we could try to be more efficient here and register + # a non-secured view for a multiview if none of the + # multiview's consituent views have a permission + # associated with them, but this code is getting pretty + # rough already + if is_multiview: + multiview = old_view + else: + multiview = MultiView(name) + old_accept = getattr(old_view, '__accept__', None) + old_order = getattr(old_view, '__order__', MAX_ORDER) + multiview.add(old_view, old_order, old_accept, old_phash) + multiview.add(derived_view, order, accept, phash) + for view_type in (IView, ISecuredView): + # unregister any existing views + self.registry.adapters.unregister( + (IViewClassifier, request_iface, r_context), + view_type, name=name) + if isexc: + self.registry.adapters.unregister( + (IExceptionViewClassifier, request_iface, r_context), + view_type, name=name) + self.registry.registerAdapter( + multiview, + (IViewClassifier, request_iface, context), + IMultiView, name=name, info=_info) + if isexc: + self.registry.registerAdapter( + multiview, + (IExceptionViewClassifier, request_iface, context), + IMultiView, name=name, info=_info) + + def add_route(self, + name, + pattern=None, + view=None, + view_for=None, + permission=None, + factory=None, + for_=None, + header=None, + xhr=False, + accept=None, + path_info=None, + request_method=None, + request_param=None, + traverse=None, + custom_predicates=(), + view_permission=None, + renderer=None, + view_renderer=None, + view_context=None, + view_attr=None, + use_global_views=False, + path=None, + pregenerator=None, + _info=u''): + """ Add a :term:`route configuration` to the current + configuration state, as well as possibly a :term:`view + configuration` to be used to specify a :term:`view callable` + that will be invoked when this route matches. The arguments + to this method are divided into *predicate*, *non-predicate*, + and *view-related* types. :term:`Route predicate` arguments + narrow the circumstances in which a route will be match a + request; non-predicate arguments are informational. + + Non-Predicate Arguments + + name + + The name of the route, e.g. ``myroute``. This attribute is + required. It must be unique among all defined routes in a given + application. + + factory + + A Python object (often a function or a class) or a + :term:`dotted Python name` which refers to the same object + that will generate a :mod:`repoze.bfg` :term:`context` + object when this route matches. For example, + ``mypackage.models.MyFactoryClass``. If this argument is + not specified, a default root factory will be used. + + traverse + + If you would like to cause the :term:`context` to be + something other than the :term:`root` object when this route + matches, you can spell a traversal pattern as the + ``traverse`` argument. This traversal pattern will be used + as the traversal path: traversal will begin at the root + object implied by this route (either the global root, or the + object returned by the ``factory`` associated with this + route). + + The syntax of the ``traverse`` argument is the same as it is + for ``pattern``. For example, if the ``pattern`` provided to + ``add_route`` is ``articles/:article/edit``, and the + ``traverse`` argument provided to ``add_route`` is + ``/:article``, when a request comes in that causes the route + to match in such a way that the ``article`` match value is + '1' (when the request URI is ``/articles/1/edit``), the + traversal path will be generated as ``/1``. This means that + the root object's ``__getitem__`` will be called with the + name ``1`` during the traversal phase. If the ``1`` object + exists, it will become the :term:`context` of the request. + :ref:`traversal_chapter` has more information about + traversal. + + If the traversal path contains segment marker names which + are not present in the ``pattern`` argument, a runtime error + will occur. The ``traverse`` pattern should not contain + segment markers that do not exist in the ``pattern`` + argument. + + A similar combining of routing and traversal is available + when a route is matched which contains a ``*traverse`` + remainder marker in its pattern (see + :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` + argument to add_route allows you to associate route patterns + with an arbitrary traversal path without using a a + ``*traverse`` remainder marker; instead you can use other + match information. + + Note that the ``traverse`` argument to ``add_route`` is + ignored when attached to a route that has a ``*traverse`` + remainder marker in its pattern. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.3. + + pregenerator + + This option should be a callable object that implements the + :class:`repoze.bfg.interfaces.IRoutePregenerator` + interface. A :term:`pregenerator` is a callable called by + the :mod:`repoze.bfg.url.route_url` function to augment or + replace the arguments it is passed when generating a URL + for the route. This is a feature not often used directly + by applications, it is meant to be hooked by frameworks + that use :mod:`repoze.bfg` as a base. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.3. + + Predicate Arguments + + pattern + + The pattern of the route e.g. ``ideas/:idea``. This + argument is required. See :ref:`route_path_pattern_syntax` + for information about the syntax of route patterns. If the + pattern doesn't match the current URL, route matching + continues. + + .. note:: For backwards compatibility purposes (as of + :mod:`repoze.bfg` 1.3), a ``path`` keyword argument + passed to this function will be used to represent the + pattern value if the ``pattern`` argument is ``None``. + If both ``path`` and ``pattern`` are passed, ``pattern`` + wins. + + xhr + + This value should be either ``True`` or ``False``. If this + value is specified and is ``True``, the :term:`request` must + possess an ``HTTP_X_REQUESTED_WITH`` (aka + ``X-Requested-With``) header for this route to match. This + is useful for detecting AJAX requests issued from jQuery, + Prototype and other Javascript libraries. If this predicate + returns ``False``, route matching continues. + + request_method + + A string representing an HTTP method name, e.g. ``GET``, + ``POST``, ``HEAD``, ``DELETE``, ``PUT``. If this argument + is not specified, this route will match if the request has + *any* request method. If this predicate returns ``False``, + route matching continues. + + path_info + + This value represents a regular expression pattern that will + be tested against the ``PATH_INFO`` WSGI environment + variable. If the regex matches, this predicate will return + ``True``. If this predicate returns ``False``, route + matching continues. + + request_param + + This value can be any string. A view declaration with this + argument ensures that the associated route will only match + when the request has a key in the ``request.params`` + dictionary (an HTTP ``GET`` or ``POST`` variable) that has a + name which matches the supplied value. If the value + supplied as the argument has a ``=`` sign in it, + e.g. ``request_params="foo=123"``, then the key + (``foo``) must both exist in the ``request.params`` dictionary, and + the value must match the right hand side of the expression (``123``) + for the route to "match" the current request. If this predicate + returns ``False``, route matching continues. + + header + + This argument represents an HTTP header name or a header + name/value pair. If the argument contains a ``:`` (colon), + it will be considered a name/value pair + (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If + the value contains a colon, the value portion should be a + regular expression. If the value does not contain a colon, + the entire value will be considered to be the header name + (e.g. ``If-Modified-Since``). If the value evaluates to a + header name only without a value, the header specified by + the name must be present in the request for this predicate + to be true. If the value evaluates to a header name/value + pair, the header specified by the name must be present in + the request *and* the regular expression specified as the + value must match the header value. Whether or not the value + represents a header name or a header name/value pair, the + case of the header name is not significant. If this + predicate returns ``False``, route matching continues. + + accept + + This value represents a match query for one or more + mimetypes in the ``Accept`` HTTP request header. If this + value is specified, it must be in one of the following + forms: a mimetype match token in the form ``text/plain``, a + wildcard mimetype match token in the form ``text/*`` or a + match-all wildcard mimetype match token in the form ``*/*``. + If any of the forms matches the ``Accept`` header of the + request, this predicate will be true. If this predicate + returns ``False``, route matching continues. + + custom_predicates + + This value should be a sequence of references to custom + predicate callables. Use custom predicates when no set of + predefined predicates does what you need. Custom predicates + can be combined with predefined predicates as necessary. + Each custom predicate callable should accept two arguments: + ``info`` and ``request`` and should return either ``True`` + or ``False`` after doing arbitrary evaluation of the info + and/or the request. If all custom and non-custom predicate + callables return ``True`` the associated route will be + considered viable for a given request. If any predicate + callable returns ``False``, route matching continues. Note + that the value ``info`` passed to a custom route predicate + is a dictionary containing matching information; see + :ref:`custom_route_predicates` for more information about + ``info``. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. + + .. note:: The ``info`` argument passed to a custom predicate + in versions prior to :mod:`repoze.bfg` 1.3 was + always ``None``. + + View-Related Arguments + + view + + A Python object or :term:`dotted Python name` to the same + object that will be used as a view callable when this route + matches. e.g. ``mypackage.views.my_view``. + + view_context + + A class or an :term:`interface` or :term:`dotted Python + name` to the same object which the :term:`context` of the + view should match for the view named by the route to be + used. This argument is only useful if the ``view`` + attribute is used. If this attribute is not specified, the + default (``None``) will be used. + + If the ``view`` argument is not provided, this argument has + no effect. + + This attribute can also be spelled as ``for_`` or ``view_for``. + + view_permission + + The permission name required to invoke the view associated + with this route. e.g. ``edit``. (see + :ref:`using_security_with_urldispatch` for more information + about permissions). + + If the ``view`` attribute is not provided, this argument has + no effect. + + This argument can also be spelled as ``permission``. + + view_renderer + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``). If the renderer value is a + single term (does not contain a dot ``.``), the specified + term will be used to look up a renderer implementation, and + that renderer implementation will be used to construct a + response from the view return value. If the renderer term + contains a dot (``.``), the specified term will be treated + as a path, and the filename extension of the last element in + the path will be used to look up the renderer + implementation, which will be passed the full path. The + renderer implementation will be used to construct a response + from the view return value. See + :ref:`views_which_use_a_renderer` for more information. + + If the ``view`` argument is not provided, this argument has + no effect. + + This argument can also be spelled as ``renderer``. + + view_attr + + The view machinery defaults to using the ``__call__`` method + of the view callable (or the function itself, if the view + callable is a function) to obtain a response dictionary. + The ``attr`` value allows you to vary the method attribute + used to obtain the response. For example, if your view was + a class, and the class has a method named ``index`` and you + wanted to use this method instead of the class' ``__call__`` + method to return the response, you'd say ``attr="index"`` in + the view configuration for the view. This is + most useful when the view definition is a class. + + If the ``view`` argument is not provided, this argument has no + effect. + + use_global_views + + When a request matches this route, and view lookup cannot + find a view which has a ``route_name`` predicate argument + that matches the route, try to fall back to using a view + that otherwise matches the context, request, and view name + (but which does not match the route_name predicate). + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. + + """ + # these are route predicates; if they do not match, the next route + # in the routelist will be tried + ignored, predicates, ignored = _make_predicates( + xhr=xhr, + request_method=request_method, + path_info=path_info, + request_param=request_param, + header=header, + accept=accept, + traverse=traverse, + custom=custom_predicates + ) + + + request_iface = self.registry.queryUtility(IRouteRequest, name=name) + if request_iface is None: + bases = use_global_views and (IRequest,) or () + request_iface = route_request_iface(name, bases) + self.registry.registerUtility( + request_iface, IRouteRequest, name=name) + deferred_views = getattr(self.registry, 'deferred_route_views', {}) + view_info = deferred_views.pop(name, ()) + for info in view_info: + self.add_view(**info) + + if view: + if view_context is None: + view_context = view_for + if view_context is None: + view_context = for_ + view_permission = view_permission or permission + view_renderer = view_renderer or renderer + self.add_view( + permission=view_permission, + context=view_context, + view=view, + name='', + route_name=name, + renderer=view_renderer, + attr=view_attr, + _info=_info, + ) + + mapper = self.get_routes_mapper() + + factory = self.maybe_dotted(factory) + if pattern is None: + pattern = path + if pattern is None: + raise ConfigurationError('"pattern" argument may not be None') + return mapper.connect(name, pattern, factory, predicates=predicates, + pregenerator=pregenerator) + + def get_routes_mapper(self): + """ Return the :term:`routes mapper` object associated with + this configurator's :term:`registry`.""" + mapper = self.registry.queryUtility(IRoutesMapper) + if mapper is None: + mapper = RoutesMapper() + self.registry.registerUtility(mapper, IRoutesMapper) + return mapper + + def scan(self, package=None, categories=None, _info=u''): + """ Scan a Python package and any of its subpackages for + objects marked with :term:`configuration decoration` such as + :class:`repoze.bfg.view.bfg_view`. Any decorated object found + will influence the current configuration state. + + The ``package`` argument should be a Python :term:`package` or + module object (or a :term:`dotted Python name` which refers to + such a package or module). If ``package`` is ``None``, the + package of the *caller* is used. + + The ``categories`` argument, if provided, should be the + :term:`Venusian` 'scan categories' to use during scanning. + Providing this argument is not often necessary; specifying + scan categories is an extremely advanced usage. + + By default, ``categories`` is ``None`` which will execute + *all* Venusian decorator callbacks including + :mod:`repoze.bfg`-related decorators such as ``bfg_view``. If + this is not desirable because the codebase has other + Venusian-using decorators that aren't meant to be invoked + during a particular scan, use ``('bfg',)`` as a ``categories`` + value to limit the execution of decorator callbacks to only + those registered by :mod:`repoze.bfg` itself. Or pass a + sequence of Venusian scan categories as necessary + (e.g. ``('bfg', 'myframework')``) to limit the decorators + called to the set of categories required. + """ + package = self.maybe_dotted(package) + if package is None: # pragma: no cover + package = caller_package() + + scanner = self.venusian.Scanner(config=self) + scanner.scan(package, categories=categories) + + def add_renderer(self, name, factory, _info=u''): + """ + Add a :mod:`repoze.bfg` :term:`renderer` factory to the + current configuration state. + + The ``name`` argument is the renderer name. + + The ``factory`` argument is Python reference to an + implementation of a :term:`renderer` factory or a + :term:`dotted Python name` to same. + + Note that this function must be called *before* any + ``add_view`` invocation that names the renderer name as an + argument. As a result, it's usually a better idea to pass + globally used renderers into the ``Configurator`` constructor + in the sequence of renderers passed as ``renderer`` than it is + to use this method. + """ + factory = self.maybe_dotted(factory) + self.registry.registerUtility( + factory, IRendererFactory, name=name, info=_info) + + def override_resource(self, to_override, override_with, + _info=u'', _override=None,): + """ Add a :mod:`repoze.bfg` resource override to the current + configuration state. + + ``to_override`` is a :term:`resource specification` to the + resource being overridden. + + ``override_with`` is a :term:`resource specification` to the + resource that is performing the override. + + See :ref:`resources_chapter` for more + information about resource overrides.""" + if to_override == override_with: + raise ConfigurationError('You cannot override a resource with ' + 'itself') + + package = to_override + path = '' + if ':' in to_override: + package, path = to_override.split(':', 1) + + override_package = override_with + override_prefix = '' + if ':' in override_with: + override_package, override_prefix = override_with.split(':', 1) + + if path and path.endswith('/'): + if override_prefix and (not override_prefix.endswith('/')): + raise ConfigurationError( + 'A directory cannot be overridden with a file (put a slash ' + 'at the end of override_with if necessary)') + + if override_prefix and override_prefix.endswith('/'): + if path and (not path.endswith('/')): + raise ConfigurationError( + 'A file cannot be overridden with a directory (put a slash ' + 'at the end of to_override if necessary)') + + __import__(package) + __import__(override_package) + package = sys.modules[package] + override_package = sys.modules[override_package] + + override = _override or self._override # test jig + override(package, path, override_package, override_prefix, + _info=_info) + + def set_forbidden_view(self, view=None, attr=None, renderer=None, + wrapper=None, _info=u''): + """ Add a default forbidden view to the current configuration + state. + + .. warning:: This method has been deprecated in + :mod:`repoze.bfg` 1.3. *Do not use it for new development; + it should only be used to support older code bases which + depend upon it.* See :ref:`changing_the_forbidden_view` to + see how a forbidden view should be registered in new + projects. + + .. note:: For backwards compatibility with :mod:`repoze.bfg` + 1.2, unlike an 'exception view' as described in + :ref:`exception_views`, a ``context, request`` view + callable registered using this API should not expect to + receive the exception as its first (``context``) argument. + Instead it should expect to receive the 'real' context as + found via context-finding or ``None`` if no context could + be found. The exception causing the registered view to be + called is however still available as ``request.exception``. + + The ``view`` argument should be a :term:`view callable` or a + :term:`dotted Python name` which refers to a view callable. + + The ``attr`` argument should be the attribute of the view + callable used to retrieve the response (see the ``add_view`` + method's ``attr`` argument for a description). + + The ``renderer`` argument should be the name of (or path to) a + :term:`renderer` used to generate a response for this view + (see the + :meth:`repoze.bfg.configuration.Configurator.add_view` + method's ``renderer`` argument for information about how a + configurator relates to a renderer). + + The ``wrapper`` argument should be the name of another view + which will wrap this view when rendered (see the ``add_view`` + method's ``wrapper`` argument for a description).""" + view = self._derive_view(view, attr=attr, renderer_name=renderer) + def bwcompat_view(context, request): + context = getattr(request, 'context', None) + return view(context, request) + return self.add_view(bwcompat_view, context=Forbidden, + wrapper=wrapper, _info=_info) + + def set_notfound_view(self, view=None, attr=None, renderer=None, + wrapper=None, _info=u''): + """ Add a default not found view to the current configuration + state. + + .. warning:: This method has been deprecated in + :mod:`repoze.bfg` 1.3. *Do not use it for new development; + it should only be used to support older code bases which + depend upon it.* See :ref:`changing_the_notfound_view` to + see how a not found view should be registered in new + projects. + + ..note:: For backwards compatibility with :mod:`repoze.bfg` + 1.2, unlike an 'exception view' as described in + :ref:`exception_views`, a ``context, request`` view + callable registered using this API should not expect to + receive the exception as its first (``context``) argument. + Instead it should expect to receive the 'real' context as + found via context-finding or ``None`` if no context could + be found. The exception causing the registered view to be + called is however still available as ``request.exception``. + + The ``view`` argument should be a :term:`view callable` or a + :term:`dotted Python name` which refers to a view callable. + + The ``attr`` argument should be the attribute of the view + callable used to retrieve the response (see the ``add_view`` + method's ``attr`` argument for a description). + + The ``renderer`` argument should be the name of (or path to) a + :term:`renderer` used to generate a response for this view + (see the + :meth:`repoze.bfg.configuration.Configurator.add_view` + method's ``renderer`` argument for information about how a + configurator relates to a renderer). + + The ``wrapper`` argument should be the name of another view + which will wrap this view when rendered (see the ``add_view`` + method's ``wrapper`` argument for a description). + """ + view = self._derive_view(view, attr=attr, renderer_name=renderer) + def bwcompat_view(context, request): + context = getattr(request, 'context', None) + return view(context, request) + return self.add_view(bwcompat_view, context=NotFound, + wrapper=wrapper, _info=_info) + + def set_request_factory(self, factory): + """ The object passed as ``factory`` should be an object (or a + :term:`dotted Python name` which refers to an object) which + will be used by the :mod:`repoze.bfg` router to create all + request objects. This factory object must have the same + methods and attributes as the + :class:`repoze.bfg.request.Request` class (particularly + ``__call__``, and ``blank``). + + .. note:: Using the :meth:``request_factory`` argument to the + :class:`repoze.bfg.configuration.Configurator` constructor + can be used to achieve the same purpose. + """ + factory = self.maybe_dotted(factory) + self.registry.registerUtility(factory, IRequestFactory) + + def set_renderer_globals_factory(self, factory): + """ The object passed as ``factory`` should be an callable (or + a :term:`dotted Python name` which refers to an callable) that + will be used by the :mod:`repoze.bfg` rendering machinery as a + renderers global factory (see :ref:`adding_renderer_globals`). + + The ``factory`` callable must accept a single argument named + ``system`` (which will be a dictionary) and it must return a + dictionary. When an application uses a renderer, the + factory's return dictionary will be merged into the ``system`` + dictionary, and therefore will be made available to the code + which uses the renderer. + + .. note:: Using the :meth:`renderer_globals_factory` + argument to the + :class:`repoze.bfg.configuration.Configurator` constructor + can be used to achieve the same purpose. + """ + factory = self.maybe_dotted(factory) + self.registry.registerUtility(factory, IRendererGlobalsFactory) + + def set_locale_negotiator(self, negotiator): + """ + Set the :term:`locale negotiator` for this application. The + :term:`locale negotiator` is a callable which accepts a + :term:`request` object and which returns a :term:`locale + name`. The ``negotiator`` argument should be the locale + negotiator implementation or a :term:`dotted Python name` + which refers to such an implementation. + + Later calls to this method override earlier calls; there can + be only one locale negotiator active at a time within an + application. See :ref:`activating_translation` for more + information. + + .. note: This API is new as of :mod:`repoze.bfg` version 1.3. + + .. note:: Using the ``locale_negotiator`` argument to the + :class:`repoze.bfg.configuration.Configurator` constructor + can be used to achieve the same purpose. + """ + negotiator = self.maybe_dotted(negotiator) + self.registry.registerUtility(negotiator, ILocaleNegotiator) + + def set_default_permission(self, permission): + """ + Set the default permission to be used by all subsequent + :term:`view configuration` registrations. ``permission`` + should be a :term:`permission` string to be used as the + default permission. An example of a permission + string:``'view'``. Adding a default permission makes it + unnecessary to protect each view configuration with an + explicit permission, unless your application policy requires + some exception for a particular view. + + If a default permission is *not* set, views represented by + view configuration registrations which do not explicitly + declare a permission will be executable by entirely anonymous + users (any authorization policy is ignored). + + Later calls to this method override earlier calls; there can + be only one default permission active at a time within an + application. + + See also :ref:`setting_a_default_permission`. + + .. note: This API is new as of :mod:`repoze.bfg` version 1.3. + + .. note:: Using the ``default_permission`` argument to the + :class:`repoze.bfg.configuration.Configurator` constructor + can be used to achieve the same purpose. + """ + self.registry.registerUtility(permission, IDefaultPermission) + + def add_translation_dirs(self, *specs): + """ Add one or more :term:`translation directory` paths to the + current configuration state. The ``specs`` argument is a + sequence that may contain absolute directory paths + (e.g. ``/usr/share/locale``) or :term:`resource specification` + names naming a directory path (e.g. ``some.package:locale``) + or a combination of the two. + + Example: + + .. code-block:: python + + add_translations_dirs('/usr/share/locale', 'some.package:locale') + + .. note: This API is new as of :mod:`repoze.bfg` version 1.3. + """ + for spec in specs: + + package_name, filename = self._split_spec(spec) + if package_name is None: # absolute filename + directory = filename + else: + __import__(package_name) + package = sys.modules[package_name] + directory = os.path.join(package_path(package), filename) + + if not os.path.isdir(os.path.realpath(directory)): + raise ConfigurationError('"%s" is not a directory' % directory) + + tdirs = self.registry.queryUtility(ITranslationDirectories) + if tdirs is None: + tdirs = [] + self.registry.registerUtility(tdirs, ITranslationDirectories) + + tdirs.insert(0, directory) + + if specs: + + # We actually only need an IChameleonTranslate function + # utility to be registered zero or one times. We register the + # same function once for each added translation directory, + # which does too much work, but has the same effect. + + def translator(msg): + request = get_current_request() + localizer = get_localizer(request) + return localizer.translate(msg) + + ctranslate = ChameleonTranslate(translator) + self.registry.registerUtility(ctranslate, IChameleonTranslate) + + def add_static_view(self, name, path, **kw): + """ Add a view used to render static resources such as images + and CSS files. + + The ``name`` argument is a string representing :term:`view + name` of the view which is registered. It may alternately be + a *url prefix*. + + The ``path`` argument is the path on disk where the static + files reside. This can be an absolute path, a + package-relative path, or a :term:`resource specification`. + + The ``cache_max_age`` keyword argument is input to set the + ``Expires`` and ``Cache-Control`` headers for static resources + served. Note that this argument has no effect when the + ``name`` is a *url prefix*. + + *Usage* + + The ``add_static_view`` function is typically used in + conjunction with the :func:`repoze.bfg.url.static_url` + function. ``add_static_view`` adds a view which renders a + static resource when some URL is visited; + :func:`repoze.bfg.url.static_url` generates a URL to that + resource. + + The ``name`` argument to ``add_static_view`` is usually a + :term:`view name`. When this is the case, the + :func:`repoze.bfg.url.static_url` API will generate a URL + which points to a BFG view, which will serve up a set of + resources that live in the package itself. For example: + + .. code-block:: python + + add_static_view('images', 'mypackage:images/') + + Code that registers such a view can generate URLs to the view + via :func:`repoze.bfg.url.static_url`: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that represents a simple view name, as it is above, subsequent + calls to :func:`repoze.bfg.url.static_url` with paths that + start with the ``path`` argument passed to ``add_static_view`` + will generate a URL something like ``http:///images/logo.png``, which will cause the ``logo.png`` file + in the ``images`` subdirectory of the ``mypackage`` package to + be served. + + ``add_static_view`` can alternately be used with a ``name`` + argument which is a *URL*, causing static resources to be + served from an external webserver. This happens when the + ``name`` argument is a URL (detected as any string with a + slash in it). In this mode, the ``name`` is used as the URL + prefix when generating a URL using + :func:`repoze.bfg.url.static_url`. For example, if + ``add_static_view`` is called like so: + + .. code-block:: python + + add_static_view('http://example.com/images', 'mypackage:images/') + + Subsequently, the URLs generated by + :func:`repoze.bfg.url.static_url` for that static view will be + prefixed with ``http://example.com/images``: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that is the URL prefix ``http://example.com/images``, + subsequent calls to :func:`repoze.bfg.url.static_url` with + paths that start with the ``path`` argument passed to + ``add_static_view`` will generate a URL something like + ``http://example.com/logo.png``. The external webserver + listening on ``example.com`` must be itself configured to + respond properly to such a request. + + See :ref:`static_resources_section` for more information. + """ + spec = self._make_spec(path) + info = self.registry.queryUtility(IStaticURLInfo) + if info is None: + info = StaticURLInfo(self) + self.registry.registerUtility(info, IStaticURLInfo) + + info.add(name, spec, **kw) + + # testing API + def testing_securitypolicy(self, userid=None, groupids=(), + permissive=True): + """Unit/integration testing helper: Registers a pair of faux + :mod:`repoze.bfg` security policies: a :term:`authentication + policy` and a :term:`authorization policy`. + + The behavior of the registered :term:`authorization policy` + depends on the ``permissive`` argument. If ``permissive`` is + true, a permissive :term:`authorization policy` is registered; + this policy allows all access. If ``permissive`` is false, a + nonpermissive :term:`authorization policy` is registered; this + policy denies all access. + + The behavior of the registered :term:`authentication policy` + depends on the values provided for the ``userid`` and + ``groupids`` argument. The authentication policy will return + the userid identifier implied by the ``userid`` argument and + the group ids implied by the ``groupids`` argument when the + :func:`repoze.bfg.security.authenticated_userid` or + :func:`repoze.bfg.security.effective_principals` APIs are + used. + + This function is most useful when testing code that uses + the APIs named :func:`repoze.bfg.security.has_permission`, + :func:`repoze.bfg.security.authenticated_userid`, + :func:`repoze.bfg.security.effective_principals`, and + :func:`repoze.bfg.security.principals_allowed_by_permission`. + """ + from repoze.bfg.testing import DummySecurityPolicy + policy = DummySecurityPolicy(userid, groupids, permissive) + self.registry.registerUtility(policy, IAuthorizationPolicy) + self.registry.registerUtility(policy, IAuthenticationPolicy) + + def testing_models(self, models): + """Unit/integration testing helper: registers a dictionary of + :term:`model` objects that can be resolved via the + :func:`repoze.bfg.traversal.find_model` API. + + The :func:`repoze.bfg.traversal.find_model` API is called with + a path as one of its arguments. If the dictionary you + register when calling this method contains that path as a + string key (e.g. ``/foo/bar`` or ``foo/bar``), the + corresponding value will be returned to ``find_model`` (and + thus to your code) when + :func:`repoze.bfg.traversal.find_model` is called with an + equivalent path string or tuple. + """ + class DummyTraverserFactory: + def __init__(self, context): + self.context = context + + def __call__(self, request): + path = request['PATH_INFO'] + ob = models[path] + traversed = traversal_path(path) + return {'context':ob, 'view_name':'','subpath':(), + 'traversed':traversed, 'virtual_root':ob, + 'virtual_root_path':(), 'root':ob} + self.registry.registerAdapter(DummyTraverserFactory, (Interface,), + ITraverser) + return models + + def testing_add_subscriber(self, event_iface=None): + """Unit/integration testing helper: Registers a + :term:`subscriber` which listens for events of the type + ``event_iface``. This method returns a list object which is + appended to by the subscriber whenever an event is captured. + + When an event is dispatched that matches the value implied by + the ``event_iface`` argument, that event will be appended to + the list. You can then compare the values in the list to + expected event notifications. This method is useful when + testing code that wants to call + :meth:`repoze.bfg.registry.Registry.notify`, + :func:`zope.component.event.dispatch` or + :func:`zope.component.event.objectEventNotify`. + + The default value of ``event_iface`` (``None``) implies a + subscriber registered for *any* kind of event. + """ + event_iface = self.maybe_dotted(event_iface) + L = [] + def subscriber(*event): + L.extend(event) + self.add_subscriber(subscriber, event_iface) + return L + + def testing_add_renderer(self, path, renderer=None): + """Unit/integration testing helper: register a renderer at + ``path`` (usually a relative filename ala ``templates/foo.pt`` + or a resource specification) and return the renderer object. + If the ``renderer`` argument is None, a 'dummy' renderer will + be used. This function is useful when testing code that calls + the :func:`repoze.bfg.renderers.render` function or + :func:`repoze.bfg.renderers.render_to_response` function or + any other ``render_*`` or ``get_*`` API of the + :mod:`repoze.bfg.renderers` module. + + Note that calling this method for with a ``path`` argument + representing a renderer factory type (e.g. for ``foo.pt`` + usually implies the ``chameleon_zpt`` renderer factory) + clobbers any existing renderer factory registered for that + type. + + .. note:: This method is also available under the alias + ``testing_add_template`` (an older name for it). + + .. note:: This method is new in :mod:`repoze.bfg` 1.3 (the + method named ``testing_add_template`` had the same signature + and purpose in previous releases).. + """ + from repoze.bfg.testing import DummyRendererFactory + helper = RendererHelper(path, registry=self.registry) + factory = helper.factory + if not isinstance(factory, DummyRendererFactory): + factory = DummyRendererFactory(helper.renderer_type, helper.factory) + self.registry.registerUtility(factory, IRendererFactory, + name=helper.renderer_type) + + from repoze.bfg.testing import DummyTemplateRenderer + if renderer is None: + renderer = DummyTemplateRenderer() + factory.add(helper.renderer_name, renderer) + return renderer + + testing_add_template = testing_add_renderer + +def _make_predicates(xhr=None, request_method=None, path_info=None, + request_param=None, header=None, accept=None, + containment=None, request_type=None, + traverse=None, custom=()): + + # PREDICATES + # ---------- + # + # Given an argument list, a predicate list is computed. + # Predicates are added to a predicate list in (presumed) + # computation expense order. All predicates associated with a + # view or route must evaluate true for the view or route to + # "match" during a request. Elsewhere in the code, we evaluate + # predicates using a generator expression. The fastest predicate + # should be evaluated first, then the next fastest, and so on, as + # if one returns false, the remainder of the predicates won't need + # to be evaluated. + # + # While we compute predicates, we also compute a predicate hash + # (aka phash) that can be used by a caller to identify identical + # predicate lists. + # + # ORDERING + # -------- + # + # A "order" is computed for the predicate list. An order is + # a scoring. + # + # Each predicate is associated with a weight value, which is a + # multiple of 2. The weight of a predicate symbolizes the + # relative potential "importance" of the predicate to all other + # predicates. A larger weight indicates greater importance. + # + # All weights for a given predicate list are bitwise ORed together + # to create a "score"; this score is then subtracted from + # MAX_ORDER and divided by an integer representing the number of + # predicates+1 to determine the order. + # + # The order represents the ordering in which a "multiview" ( a + # collection of views that share the same context/request/name + # triad but differ in other ways via predicates) will attempt to + # call its set of views. Views with lower orders will be tried + # first. The intent is to a) ensure that views with more + # predicates are always evaluated before views with fewer + # predicates and b) to ensure a stable call ordering of views that + # share the same number of predicates. Views which do not have + # any predicates get an order of MAX_ORDER, meaning that they will + # be tried very last. + + predicates = [] + weights = [] + h = md5() + + if xhr: + def xhr_predicate(context, request): + return request.is_xhr + weights.append(1 << 1) + predicates.append(xhr_predicate) + h.update('xhr:%r' % bool(xhr)) + + if request_method is not None: + def request_method_predicate(context, request): + return request.method == request_method + weights.append(1 << 2) + predicates.append(request_method_predicate) + h.update('request_method:%r' % request_method) + + if path_info is not None: + try: + path_info_val = re.compile(path_info) + except re.error, why: + raise ConfigurationError(why[0]) + def path_info_predicate(context, request): + return path_info_val.match(request.path_info) is not None + weights.append(1 << 3) + predicates.append(path_info_predicate) + h.update('path_info:%r' % path_info) + + if request_param is not None: + request_param_val = None + if '=' in request_param: + request_param, request_param_val = request_param.split('=', 1) + def request_param_predicate(context, request): + if request_param_val is None: + return request_param in request.params + return request.params.get(request_param) == request_param_val + weights.append(1 << 4) + predicates.append(request_param_predicate) + h.update('request_param:%r=%r' % (request_param, request_param_val)) + + if header is not None: + header_name = header + header_val = None + if ':' in header: + header_name, header_val = header.split(':', 1) + try: + header_val = re.compile(header_val) + except re.error, why: + raise ConfigurationError(why[0]) + def header_predicate(context, request): + if header_val is None: + return header_name in request.headers + val = request.headers.get(header_name) + if val is None: + return False + return header_val.match(val) is not None + weights.append(1 << 5) + predicates.append(header_predicate) + h.update('header:%r=%r' % (header_name, header_val)) + + if accept is not None: + def accept_predicate(context, request): + return accept in request.accept + weights.append(1 << 6) + predicates.append(accept_predicate) + h.update('accept:%r' % accept) + + if containment is not None: + def containment_predicate(context, request): + return find_interface(context, containment) is not None + weights.append(1 << 7) + predicates.append(containment_predicate) + h.update('containment:%r' % hash(containment)) + + if request_type is not None: + def request_type_predicate(context, request): + return request_type.providedBy(request) + weights.append(1 << 8) + predicates.append(request_type_predicate) + h.update('request_type:%r' % hash(request_type)) + + if traverse is not None: + # ``traverse`` can only be used as a *route* "predicate"; it + # adds 'traverse' to the matchdict if it's specified in the + # routing args. This causes the ModelGraphTraverser to use + # the resolved traverse pattern as the traversal path. + from repoze.bfg.urldispatch import _compile_route + _, tgenerate = _compile_route(traverse) + def traverse_predicate(context, request): + if 'traverse' in context: + return True + m = context['match'] + tvalue = tgenerate(m) + m['traverse'] = traversal_path(tvalue) + return True + # This isn't actually a predicate, it's just a infodict + # modifier that injects ``traverse`` into the matchdict. As a + # result, the ``traverse_predicate`` function above always + # returns True, and we don't need to update the hash or attach + # a weight to it + predicates.append(traverse_predicate) + + if custom: + for num, predicate in enumerate(custom): + predicates.append(predicate) + # using hash() here rather than id() is intentional: we + # want to allow custom predicates that are part of + # frameworks to be able to define custom __hash__ + # functions for custom predicates, so that the hash output + # of predicate instances which are "logically the same" + # may compare equal. + h.update('custom%s:%r' % (num, hash(predicate))) + weights.append(1 << 10) + + score = 0 + for bit in weights: + score = score | bit + order = (MAX_ORDER - score) / (len(predicates) + 1) + phash = h.hexdigest() + return order, predicates, phash + +class MultiView(object): + implements(IMultiView) + + def __init__(self, name): + self.name = name + self.media_views = {} + self.views = [] + self.accepts = [] + + def add(self, view, order, accept=None, phash=None): + if phash is not None: + for i, (s, v, h) in enumerate(list(self.views)): + if phash == h: + self.views[i] = (order, view, phash) + return + + if accept is None or '*' in accept: + self.views.append((order, view, phash)) + self.views.sort() + else: + subset = self.media_views.setdefault(accept, []) + subset.append((order, view, phash)) + subset.sort() + accepts = set(self.accepts) + accepts.add(accept) + self.accepts = list(accepts) # dedupe + + def get_views(self, request): + if self.accepts and hasattr(request, 'accept'): + accepts = self.accepts[:] + views = [] + while accepts: + match = request.accept.best_match(accepts) + if match is None: + break + subset = self.media_views[match] + views.extend(subset) + accepts.remove(match) + views.extend(self.views) + return views + return self.views + + def match(self, context, request): + for order, view, phash in self.get_views(request): + if not hasattr(view, '__predicated__'): + return view + if view.__predicated__(context, request): + return view + raise PredicateMismatch(self.name) + + def __permitted__(self, context, request): + view = self.match(context, request) + if hasattr(view, '__permitted__'): + return view.__permitted__(context, request) + return True + + def __call_permissive__(self, context, request): + view = self.match(context, request) + view = getattr(view, '__call_permissive__', view) + return view(context, request) + + def __call__(self, context, request): + for order, view, phash in self.get_views(request): + try: + return view(context, request) + except PredicateMismatch: + continue + raise PredicateMismatch(self.name) + +def decorate_view(wrapped_view, original_view): + if wrapped_view is original_view: + return False + wrapped_view.__module__ = original_view.__module__ + wrapped_view.__doc__ = original_view.__doc__ + try: + wrapped_view.__name__ = original_view.__name__ + except AttributeError: + wrapped_view.__name__ = repr(original_view) + try: + wrapped_view.__permitted__ = original_view.__permitted__ + except AttributeError: + pass + try: + wrapped_view.__call_permissive__ = original_view.__call_permissive__ + except AttributeError: + pass + try: + wrapped_view.__predicated__ = original_view.__predicated__ + except AttributeError: + pass + try: + wrapped_view.__accept__ = original_view.__accept__ + except AttributeError: + pass + try: + wrapped_view.__order__ = original_view.__order__ + except AttributeError: + pass + return True + +def requestonly(class_or_callable, attr=None): + """ Return true of the class or callable accepts only a request argument, + as opposed to something that accepts context, request """ + if attr is None: + attr = '__call__' + if inspect.isfunction(class_or_callable): + fn = class_or_callable + elif inspect.isclass(class_or_callable): + try: + fn = class_or_callable.__init__ + except AttributeError: + return False + else: + try: + fn = getattr(class_or_callable, attr) + except AttributeError: + return False + + try: + argspec = inspect.getargspec(fn) + except TypeError: + return False + + args = argspec[0] + defaults = argspec[3] + + if hasattr(fn, 'im_func'): + # it's an instance method + if not args: + return False + args = args[1:] + if not args: + return False + + if len(args) == 1: + return True + + elif args[0] == 'request': + if len(args) - len(defaults) == 1: + return True + + return False + +def is_response(ob): + if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and + hasattr(ob, 'status') ): + return True + return False + +def _map_view(view, attr=None, renderer_name=None, registry=None, package=None): + wrapped_view = view + + helper = None + if renderer_name: + helper = RendererHelper(renderer_name, + registry=registry, + package=package) + + if inspect.isclass(view): + # If the object we've located is a class, turn it into a + # function that operates like a Zope view (when it's invoked, + # construct an instance using 'context' and 'request' as + # position arguments, then immediately invoke the __call__ + # method of the instance with no arguments; __call__ should + # return an IResponse). + if requestonly(view, attr): + # its __init__ accepts only a single request argument, + # instead of both context and request + def _bfg_class_requestonly_view(context, request): + inst = view(request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = {'view':inst, 'renderer_name':renderer_name, + 'context':context, 'request':request} + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _bfg_class_requestonly_view + else: + # its __init__ accepts both context and request + def _bfg_class_view(context, request): + inst = view(context, request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = {'view':inst, 'renderer_name':renderer_name, + 'context':context, 'request':request} + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _bfg_class_view + + elif requestonly(view, attr): + # its __call__ accepts only a single request argument, + # instead of both context and request + def _bfg_requestonly_view(context, request): + if attr is None: + response = view(request) + else: + response = getattr(view, attr)(request) + + if helper is not None: + if not is_response(response): + system = {'view':view, 'renderer_name':renderer_name, + 'context':context, 'request':request} + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _bfg_requestonly_view + + elif attr: + def _bfg_attr_view(context, request): + response = getattr(view, attr)(context, request) + if helper is not None: + if not is_response(response): + system = {'view':view, 'renderer_name':renderer_name, + 'context':context, 'request':request} + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _bfg_attr_view + + elif helper is not None: + def _rendered_view(context, request): + response = view(context, request) + if not is_response(response): + system = {'view':view, 'renderer_name':renderer_name, + 'context':context, 'request':request} + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _rendered_view + + decorate_view(wrapped_view, view) + return wrapped_view + +def _owrap_view(view, viewname, wrapper_viewname): + if not wrapper_viewname: + return view + def _owrapped_view(context, request): + response = view(context, request) + request.wrapped_response = response + request.wrapped_body = response.body + request.wrapped_view = view + wrapped_response = render_view_to_response(context, request, + wrapper_viewname) + if wrapped_response is None: + raise ValueError( + 'No wrapper view named %r found when executing view ' + 'named %r' % (wrapper_viewname, viewname)) + return wrapped_response + decorate_view(_owrapped_view, view) + return _owrapped_view + +def _predicate_wrap(view, predicates): + if not predicates: + return view + def predicate_wrapper(context, request): + if all((predicate(context, request) for predicate in predicates)): + return view(context, request) + raise PredicateMismatch('predicate mismatch for view %s' % view) + def checker(context, request): + return all((predicate(context, request) for predicate in + predicates)) + predicate_wrapper.__predicated__ = checker + decorate_view(predicate_wrapper, view) + return predicate_wrapper + +def _secure_view(view, permission, authn_policy, authz_policy): + wrapped_view = view + if authn_policy and authz_policy and (permission is not None): + def _secured_view(context, request): + principals = authn_policy.effective_principals(request) + if authz_policy.permits(context, principals, permission): + return view(context, request) + msg = getattr(request, 'authdebug_message', + 'Unauthorized: %s failed permission check' % view) + raise Forbidden(msg) + _secured_view.__call_permissive__ = view + def _permitted(context, request): + principals = authn_policy.effective_principals(request) + return authz_policy.permits(context, principals, permission) + _secured_view.__permitted__ = _permitted + wrapped_view = _secured_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _authdebug_view(view, permission, authn_policy, authz_policy, settings, + logger): + wrapped_view = view + if settings and settings.get('debug_authorization', False): + def _authdebug_view(context, request): + view_name = getattr(request, 'view_name', None) + + if authn_policy and authz_policy: + if permission is None: + msg = 'Allowed (no permission registered)' + else: + principals = authn_policy.effective_principals(request) + msg = str(authz_policy.permits(context, principals, + permission)) + else: + msg = 'Allowed (no authorization policy in use)' + + view_name = getattr(request, 'view_name', None) + url = getattr(request, 'url', None) + msg = ('debug_authorization of url %s (view name %r against ' + 'context %r): %s' % (url, view_name, context, msg)) + logger and logger.debug(msg) + if request is not None: + request.authdebug_message = msg + return view(context, request) + + wrapped_view = _authdebug_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _attr_wrap(view, accept, order, phash): + # this is a little silly but we don't want to decorate the original + # function with attributes that indicate accept, order, and phash, + # so we use a wrapper + if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH): + return view # defaults + def attr_view(context, request): + return view(context, request) + attr_view.__accept__ = accept + attr_view.__order__ = order + attr_view.__phash__ = phash + decorate_view(attr_view, view) + return attr_view + +def isexception(o): + if IInterface.providedBy(o): + if IException.isEqualOrExtendedBy(o): + return True + return ( + isinstance(o, Exception) or + (inspect.isclass(o) and (issubclass(o, Exception))) + ) + +# note that ``options`` is a b/w compat alias for ``settings`` and +# ``Configurator`` is a testing dep inj +def make_app(root_factory, package=None, filename='configure.zcml', + settings=None, options=None, Configurator=Configurator): + """ Return a Router object, representing a fully configured + :mod:`repoze.bfg` WSGI application. + + .. warning:: Use of this function is deprecated as of + :mod:`repoze.bfg` 1.2. You should instead use a + :class:`repoze.bfg.configuration.Configurator` instance to + perform startup configuration as shown in + :ref:`configuration_narr`. + + ``root_factory`` must be a callable that accepts a :term:`request` + object and which returns a traversal root object. The traversal + root returned by the root factory is the *default* traversal root; + it can be overridden on a per-view basis. ``root_factory`` may be + ``None``, in which case a 'default default' traversal root is + used. + + ``package`` is a Python :term:`package` or module representing the + application's package. It is optional, defaulting to ``None``. + ``package`` may be ``None``. If ``package`` is ``None``, the + ``filename`` passed or the value in the ``options`` dictionary + named ``configure_zcml`` must be a) absolute pathname to a + :term:`ZCML` file that represents the application's configuration + *or* b) a :term:`resource specification` to a :term:`ZCML` file in + the form ``dotted.package.name:relative/file/path.zcml``. + + ``filename`` is the filesystem path to a ZCML file (optionally + relative to the package path) that should be parsed to create the + application registry. It defaults to ``configure.zcml``. It can + also be a ;term:`resource specification` in the form + ``dotted_package_name:relative/file/path.zcml``. Note that if any + value for ``configure_zcml`` is passed within the ``settings`` + dictionary, the value passed as ``filename`` will be ignored, + replaced with the ``configure_zcml`` value. + + ``settings``, if used, should be a dictionary containing runtime + settings (e.g. the key/value pairs in an app section of a + PasteDeploy file), with each key representing the option and the + key's value representing the specific option value, + e.g. ``{'reload_templates':True}``. Note that the keyword + parameter ``options`` is a backwards compatibility alias for the + ``settings`` keyword parameter. + """ + settings = settings or options or {} + zcml_file = settings.get('configure_zcml', filename) + config = Configurator(package=package, settings=settings, + root_factory=root_factory) + config.hook_zca() + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() + + +class DottedNameResolver(object): + """ This class resolves dotted name references to 'global' Python + objects (objects which can be imported) to those objects. + + Two dotted name styles are supported during deserialization: + + - ``pkg_resources``-style dotted names where non-module attributes + of a package are separated from the rest of the path using a ':' + e.g. ``package.module:attr``. + + - ``zope.dottedname``-style dotted names where non-module + attributes of a package are separated from the rest of the path + using a '.' e.g. ``package.module.attr``. + + These styles can be used interchangeably. If the serialization + contains a ``:`` (colon), the ``pkg_resources`` resolution + mechanism will be chosen, otherwise the ``zope.dottedname`` + resolution mechanism will be chosen. + + The constructor accepts a single argument named ``package`` which + should be a one of: + + - a Python module or package object + + - A fully qualified (not relative) dotted name to a module or package + + - The value ``None`` + + The ``package`` is used when relative dotted names are supplied to + the resolver's ``resolve`` and ``maybe_resolve`` methods. A + dotted name which has a ``.`` (dot) or ``:`` (colon) as its first + character is treated as relative. + + If the value ``None`` is supplied as the package name, the + resolver will only be able to resolve fully qualified (not + relative) names. Any attempt to resolve a relative name when the + ``package`` is ``None`` will result in an + :exc:`repoze.bfg.configuration.ConfigurationError` exception. + + If a *module* or *module name* (as opposed to a package or package + name) is supplied as ``package``, its containing package is + computed and this package used to derive the package name (all + names are resolved relative to packages, never to modules). For + example, if the ``package`` argument to this type was passed the + string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to + the ``resolve`` method, the resulting import would be for + ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module + object, not a package object. + + If a *package* or *package name* (as opposed to a module or module + name) is supplied as ``package``, this package will be used to + relative compute dotted names. For example, if the ``package`` + argument to this type was passed the string ``xml.dom``, and + ``.minidom`` is supplied to the ``resolve`` method, the resulting + import would be for ``xml.minidom``. + + When a dotted name cannot be resolved, a + :class:`repoze.bfg.exceptions.ConfigurationError` error is raised. + """ + def __init__(self, package): + if package is None: + self.package_name = None + self.package = None + else: + if isinstance(package, basestring): + try: + __import__(package) + except ImportError: + raise ConfigurationError( + 'The dotted name %r cannot be imported' % (package,)) + package = sys.modules[package] + self.package = package_of(package) + self.package_name = self.package.__name__ + + def _pkg_resources_style(self, value): + """ package.module:attr style """ + if value.startswith('.') or value.startswith(':'): + if not self.package_name: + raise ConfigurationError( + 'relative name %r irresolveable without ' + 'package_name' % (value,)) + if value in ['.', ':']: + value = self.package_name + else: + value = self.package_name + value + return pkg_resources.EntryPoint.parse( + 'x=%s' % value).load(False) + + def _zope_dottedname_style(self, value): + """ package.module.attr style """ + module = self.package_name and self.package_name or None + if value == '.': + if self.package_name is None: + raise ConfigurationError( + 'relative name %r irresolveable without package' % (value,)) + name = module.split('.') + else: + name = value.split('.') + if not name[0]: + if module is None: + raise ConfigurationError( + 'relative name %r irresolveable without ' + 'package' % (value,) + ) + module = module.split('.') + name.pop(0) + while not name[0]: + module.pop() + name.pop(0) + name = module + name + + used = name.pop(0) + found = __import__(used) + for n in name: + used += '.' + n + try: + found = getattr(found, n) + except AttributeError: + __import__(used) + found = getattr(found, n) # pragma: no cover + + return found + + def resolve(self, dotted): + if not isinstance(dotted, basestring): + raise ConfigurationError('%r is not a string' % (dotted,)) + return self.maybe_resolve(dotted) + + def maybe_resolve(self, dotted): + if isinstance(dotted, basestring): + try: + if ':' in dotted: + return self._pkg_resources_style(dotted) + else: + return self._zope_dottedname_style(dotted) + except ImportError: + raise ConfigurationError( + 'The dotted name %r cannot be imported' % (dotted,)) + return dotted + diff --git a/pyramid/decorator.py b/pyramid/decorator.py new file mode 100644 index 000000000..98d7b36b5 --- /dev/null +++ b/pyramid/decorator.py @@ -0,0 +1,20 @@ +class reify(object): + + """ Put the result of a method which uses this (non-data) + descriptor decorator in the instance dict after the first call, + effectively replacing the decorator with an instance variable.""" + + def __init__(self, wrapped): + self.wrapped = wrapped + try: + self.__doc__ = wrapped.__doc__ + except: # pragma: no cover + pass + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val + diff --git a/pyramid/encode.py b/pyramid/encode.py new file mode 100644 index 000000000..127c405ed --- /dev/null +++ b/pyramid/encode.py @@ -0,0 +1,107 @@ +import re + +always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789' '_.-') +_safemaps = {} +_must_quote = {} + +def url_quote(s, safe=''): + """quote('abc def') -> 'abc%20def' + + Faster version of Python stdlib urllib.quote which also quotes + the '/' character. + + Each part of a URL, e.g. the path info, the query, etc., has a + different set of reserved characters that must be quoted. + + RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists + the following reserved characters. + + reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + "$" | "," + + Each of these characters is reserved in some component of a URL, + but not necessarily in all of them. + + Unlike the default version of this function in the Python stdlib, + by default, the quote function is intended for quoting individual + path segments instead of an already composed path that might have + '/' characters in it. Thus, it *will* encode any '/' character it + finds in a string. + """ + cachekey = (safe, always_safe) + try: + safe_map = _safemaps[cachekey] + if not _must_quote[cachekey].search(s): + return s + except KeyError: + safe += always_safe + _must_quote[cachekey] = re.compile(r'[^%s]' % safe) + safe_map = {} + for i in range(256): + c = chr(i) + safe_map[c] = (c in safe) and c or ('%%%02X' % i) + _safemaps[cachekey] = safe_map + res = map(safe_map.__getitem__, s) + return ''.join(res) + +def quote_plus(s, safe=''): + """ Version of stdlib quote_plus which uses faster url_quote """ + if ' ' in s: + s = url_quote(s, safe + ' ') + return s.replace(' ', '+') + return url_quote(s, safe) + +def urlencode(query, doseq=True): + """ + An alternate implementation of Python's stdlib `urllib.urlencode + function `_ which + accepts unicode keys and values within the ``query`` + dict/sequence; all Unicode keys and values are first converted to + UTF-8 before being used to compose the query string. + + The value of ``query`` must be a sequence of two-tuples + representing key/value pairs *or* an object (often a dictionary) + with an ``.items()`` method that returns a sequence of two-tuples + representing key/value pairs. + + For minimal calling convention backwards compatibility, this + version of urlencode accepts *but ignores* a second argument + conventionally named ``doseq``. The Python stdlib version behaves + differently when ``doseq`` is False and when a sequence is + presented as one of the values. This version always behaves in + the ``doseq=True`` mode, no matter what the value of the second + argument. + + See the Python stdlib documentation for ``urllib.urlencode`` for + more information. + """ + try: + # presumed to be a dictionary + query = query.items() + except AttributeError: + pass + + result = '' + prefix = '' + + for (k, v) in query: + if k.__class__ is unicode: + k = k.encode('utf-8') + k = quote_plus(str(k)) + if hasattr(v, '__iter__'): + for x in v: + if x.__class__ is unicode: + x = x.encode('utf-8') + x = quote_plus(str(x)) + result += '%s%s=%s' % (prefix, k, x) + prefix = '&' + else: + if v.__class__ is unicode: + v = v.encode('utf-8') + v = quote_plus(str(v)) + result += '%s%s=%s' % (prefix, k, v) + prefix = '&' + + return result diff --git a/pyramid/events.py b/pyramid/events.py new file mode 100644 index 000000000..efcbdf61f --- /dev/null +++ b/pyramid/events.py @@ -0,0 +1,163 @@ +import venusian + +from zope.interface import implements + +from repoze.bfg.interfaces import IContextFound +from repoze.bfg.interfaces import INewRequest +from repoze.bfg.interfaces import INewResponse +from repoze.bfg.interfaces import IApplicationCreated + +class subscriber(object): + """ Decorator activated via a :term:`scan` which treats the + function being decorated as an event subscriber for the set of + interfaces passed as ``*ifaces`` to the decorator constructor. + + For example: + + .. code-block:: python + + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.events import subscriber + + @subscriber(INewRequest) + def mysubscriber(event): + event.request.foo = 1 + + More than one event type can be passed as a construtor argument: + + .. code-block:: python + + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.events import subscriber + + @subscriber(INewRequest, INewResponse) + def mysubscriber(event): + print event + + When the ``subscriber`` decorator is used without passing an arguments, + the function it decorates is called for every event sent: + + .. code-block:: python + + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.events import subscriber + + @subscriber() + def mysubscriber(event): + print event + + This method will have no effect until a :term:`scan` is performed + against the package or module which contains it, ala: + + .. code-block:: python + + from repoze.bfg.configuration import Configurator + config = Configurator() + config.scan('somepackage_containing_subscribers') + + """ + venusian = venusian # for unit testing + + def __init__(self, *ifaces): + self.ifaces = ifaces + + def register(self, scanner, name, wrapped): + config = scanner.config + config.add_subscriber(wrapped, self.ifaces) + + def __call__(self, wrapped): + self.venusian.attach(wrapped, self.register, category='bfg') + return wrapped + +class NewRequest(object): + """ An instance of this class is emitted as an :term:`event` + whenever :mod:`repoze.bfg` begins to process a new request. The + even instance has an attribute, ``request``, which is a + :term:`request` object. This event class implements the + :class:`repoze.bfg.interfaces.INewRequest` interface.""" + implements(INewRequest) + def __init__(self, request): + self.request = request + +class NewResponse(object): + """ An instance of this class is emitted as an :term:`event` + whenever any :mod:`repoze.bfg` :term:`view` or :term:`exception + view` returns a :term:`response`. + + The instance has two attributes:``request``, which is the request + which caused the response, and ``response``, which is the response + object returned by a view or renderer. + + If the ``response`` was generated by an :term:`exception view`, + the request will have an attribute named ``exception``, which is + the exception object which caused the exception view to be + executed. If the response was generated by a 'normal' view, the + request will not have this attribute. + + This event will not be generated if a response cannot be created + due to an exception that is not caught by an exception view (no + response is created under this circumstace). + + This class implements the + :class:`repoze.bfg.interfaces.INewResponse` interface. + + .. note:: + + Postprocessing a response is usually better handled in a WSGI + :term:`middleware` component than in subscriber code that is + called by a :class:`repoze.bfg.interfaces.INewResponse` event. + The :class:`repoze.bfg.interfaces.INewResponse` event exists + almost purely for symmetry with the + :class:`repoze.bfg.interfaces.INewRequest` event. + """ + implements(INewResponse) + def __init__(self, request, response): + self.request = request + self.response = response + +class ContextFound(object): + """ An instance of this class is emitted as an :term:`event` after + the :mod:`repoze.bfg` :term:`router` finds a :term:`context` + object (after it performs traversal) but before any view code is + executed. The instance has an attribute, ``request``, which is + the request object generated by :mod:`repoze.bfg`. + + Notably, the request object will have an attribute named + ``context``, which is the context that will be provided to the + view which will eventually be called, as well as other attributes + attached by context-finding code. + + This class implements the + :class:`repoze.bfg.interfaces.IContextFound` interface. + + .. note:: As of :mod:`repoze.bfg` 1.3, for backwards compatibility + purposes, this event may also be imported as + :class:`repoze.bfg.events.AfterTraversal`. + """ + implements(IContextFound) + def __init__(self, request): + self.request = request + +AfterTraversal = ContextFound # b/c as of 1.3 + +class ApplicationCreated(object): + """ An instance of this class is emitted as an :term:`event` when + the :meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` is + called. The instance has an attribute, ``app``, which is an + instance of the :term:`router` that will handle WSGI requests. + This class implements the + :class:`repoze.bfg.interfaces.IApplicationCreated` interface. + + .. note:: For backwards compatibility purposes, this class can + also be imported as + :class:`repoze.bfg.events.WSGIApplicationCreatedEvent`. This + was the name of the event class before :mod:`repoze.bfg` 1.3. + + """ + implements(IApplicationCreated) + def __init__(self, app): + self.app = app + self.object = app + +WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.3) + diff --git a/pyramid/exceptions.py b/pyramid/exceptions.py new file mode 100644 index 000000000..9b885d9dc --- /dev/null +++ b/pyramid/exceptions.py @@ -0,0 +1,92 @@ +from zope.configuration.exceptions import ConfigurationError as ZCE +from zope.interface import implements + +from repoze.bfg.decorator import reify +from repoze.bfg.interfaces import IExceptionResponse +import cgi + +class ExceptionResponse(Exception): + """ Abstract class to support behaving as a WSGI response object """ + implements(IExceptionResponse) + status = None + + def __init__(self, message=''): + Exception.__init__(self, message) # B / C + self.message = message + + @reify # defer execution until asked explicitly + def app_iter(self): + return [ + """ + + %s + +

%s

+ %s + + + """ % (self.status, self.status, cgi.escape(self.message)) + ] + + @reify # defer execution until asked explicitly + def headerlist(self): + return [ + ('Content-Length', str(len(self.app_iter[0]))), + ('Content-Type', 'text/html') + ] + + +class Forbidden(ExceptionResponse): + """ + Raise this exception within :term:`view` code to immediately + return the :term:`forbidden view` to the invoking user. Usually + this is a basic ``401`` page, but the forbidden view can be + customized as necessary. See :ref:`changing_the_forbidden_view`. + + This exception's constructor accepts a single positional argument, + which should be a string. The value of this string will be placed + into the WSGI environment by the router under the + ``repoze.bfg.message`` key, for availability to the + :term:`Forbidden View`. + """ + status = '401 Unauthorized' + +class NotFound(ExceptionResponse): + """ + Raise this exception within :term:`view` code to immediately + return the :term:`Not Found view` to the invoking user. Usually + this is a basic ``404`` page, but the Not Found view can be + customized as necessary. See :ref:`changing_the_notfound_view`. + + This exception's constructor accepts a single positional argument, + which should be a string. The value of this string will be placed + into the WSGI environment by the router under the + ``repoze.bfg.message`` key, for availability to the :term:`Not Found + View`. + """ + status = '404 Not Found' + +class PredicateMismatch(NotFound): + """ + Internal exception (not an API) raised by multiviews when no + view matches. This exception subclasses the ``NotFound`` + exception only one reason: if it reaches the main exception + handler, it should be treated like a ``NotFound`` by any exception + view registrations. + """ + +class URLDecodeError(UnicodeDecodeError): + """ + This exception is raised when :mod:`repoze.bfg` cannot + successfully decode a URL or a URL path segment. This exception + it behaves just like the Python builtin + :exc:`UnicodeDecodeError`. It is a subclass of the builtin + :exc:`UnicodeDecodeError` exception only for identity purposes, + mostly so an exception view can be registered when a URL cannot be + decoded. + """ + +class ConfigurationError(ZCE): + """ Raised when inappropriate input values are supplied to an API + method of a :term:`Configurator`""" + diff --git a/pyramid/i18n.py b/pyramid/i18n.py new file mode 100644 index 000000000..c3ba7efad --- /dev/null +++ b/pyramid/i18n.py @@ -0,0 +1,322 @@ +import gettext +import os + +from translationstring import Translator +from translationstring import Pluralizer +from translationstring import TranslationString # API +from translationstring import TranslationStringFactory # API + +TranslationString = TranslationString # PyFlakes +TranslationStringFactory = TranslationStringFactory # PyFlakes + +from repoze.bfg.interfaces import ILocalizer +from repoze.bfg.interfaces import ITranslationDirectories +from repoze.bfg.interfaces import ILocaleNegotiator + +from repoze.bfg.settings import get_settings +from repoze.bfg.threadlocal import get_current_registry + +class Localizer(object): + """ + An object providing translation and pluralizations related to + the current request's locale name. A + :class:`repoze.bfg.i18n.Localizer` object is created using the + :func:`repoze.bfg.i18n.get_localizer` function. + """ + def __init__(self, locale_name, translations): + self.locale_name = locale_name + self.translations = translations + self.pluralizer = None + self.translator = None + + def translate(self, tstring, domain=None, mapping=None): + """ + Translate a :term:`translation string` to the current language + and interpolate any *replacement markers* in the result. The + ``translate`` method accepts three arguments: ``tstring`` + (required), ``domain`` (optional) and ``mapping`` (optional). + When called, it will translate the ``tstring`` translation + string to a ``unicode`` object using the current locale. If + the current locale could not be determined, the result of + interpolation of the default value is returned. The optional + ``domain`` argument can be used to specify or override the + domain of the ``tstring`` (useful when ``tstring`` is a normal + string rather than a translation string). The optional + ``mapping`` argument can specify or override the ``tstring`` + interpolation mapping, useful when the ``tstring`` argument is + a simple string instead of a translation string. + + Example:: + + from repoze.bfg.18n import TranslationString + ts = TranslationString('Add ${item}', domain='mypackage', + mapping={'item':'Item'}) + translated = localizer.translate(ts) + + Example:: + + translated = localizer.translate('Add ${item}', domain='mypackage', + mapping={'item':'Item'}) + + """ + if self.translator is None: + self.translator = Translator(self.translations) + return self.translator(tstring, domain=domain, mapping=mapping) + + def pluralize(self, singular, plural, n, domain=None, mapping=None): + """ + Return a Unicode string translation by using two + :term:`message identifier` objects as a singular/plural pair + and an ``n`` value representing the number that appears in the + message using gettext plural forms support. The ``singular`` + and ``plural`` objects passed may be translation strings or + unicode strings. ``n`` represents the number of elements. + ``domain`` is the translation domain to use to do the + pluralization, and ``mapping`` is the interpolation mapping + that should be used on the result. Note that if the objects + passed are translation strings, their domains and mappings are + ignored. The domain and mapping arguments must be used + instead. If the ``domain`` is not supplied, a default domain + is used (usually ``messages``). + + Example:: + + num = 1 + translated = localizer.pluralize('Add ${num} item', + 'Add ${num} items', + num, + mapping={'num':num}) + + + """ + if self.pluralizer is None: + self.pluralizer = Pluralizer(self.translations) + return self.pluralizer(singular, plural, n, domain=domain, + mapping=mapping) + + +def default_locale_negotiator(request): + """ The default :term:`locale negotiator`. Returns a locale name + or ``None``. + + - First, the negotiator looks for the ``_LOCALE_`` attribute of + the request object (possibly set by a view or a listener for an + :term:`event`). + + - Then it looks for the ``request.params['_LOCALE_']`` value. + + - Then it looks for the ``request.cookies['_LOCALE_']`` value. + + - Finally, the negotiator returns ``None`` if the locale could not + be determined via any of the previous checks (when a locale + negotiator returns ``None``, it signifies that the + :term:`default locale name` should be used.) + """ + name = '_LOCALE_' + locale_name = getattr(request, name, None) + if locale_name is None: + locale_name = request.params.get(name) + if locale_name is None: + locale_name = request.cookies.get(name) + return locale_name + +def negotiate_locale_name(request): + """ Negotiate and return the :term:`locale name` associated with + the current request (never cached).""" + try: + registry = request.registry + except AttributeError: + registry = get_current_registry() + negotiator = registry.queryUtility(ILocaleNegotiator, + default=default_locale_negotiator) + locale_name = negotiator(request) + + if locale_name is None: + settings = get_settings() or {} + locale_name = settings.get('default_locale_name', 'en') + + return locale_name + +def get_locale_name(request): + """ Return the :term:`locale name` associated with the current + request (possibly cached).""" + locale_name = getattr(request, 'bfg_locale_name', None) + if locale_name is None: + locale_name = negotiate_locale_name(request) + request.bfg_locale_name = locale_name + return locale_name + +def get_localizer(request): + """ Retrieve a :class:`repoze.bfg.i18n.Localizer` object + corresponding to the current request's locale name. """ + localizer = getattr(request, 'bfg_localizer', None) + + if localizer is None: + # no locale object cached on request + try: + registry = request.registry + except AttributeError: + registry = get_current_registry() + + current_locale_name = get_locale_name(request) + localizer = registry.queryUtility(ILocalizer, name=current_locale_name) + + if localizer is None: + # no localizer utility registered yet + translations = Translations() + translations._catalog = {} + tdirs = registry.queryUtility(ITranslationDirectories, default=[]) + for tdir in tdirs: + locale_dirs = [ (lname, os.path.join(tdir, lname)) for lname in + os.listdir(tdir) ] + for locale_name, locale_dir in locale_dirs: + if locale_name != current_locale_name: + continue + messages_dir = os.path.join(locale_dir, 'LC_MESSAGES') + if not os.path.isdir(os.path.realpath(messages_dir)): + continue + for mofile in os.listdir(messages_dir): + mopath = os.path.realpath(os.path.join(messages_dir, + mofile)) + if mofile.endswith('.mo') and os.path.isfile(mopath): + mofp = open(mopath, 'rb') + domain = mofile[:-3] + dtrans = Translations(mofp, domain) + translations.add(dtrans) + + localizer = Localizer(locale_name=current_locale_name, + translations=translations) + registry.registerUtility(localizer, ILocalizer, + name=current_locale_name) + request.bfg_localizer = localizer + + return localizer + +class Translations(gettext.GNUTranslations, object): + """An extended translation catalog class (ripped off from Babel) """ + + DEFAULT_DOMAIN = 'messages' + + def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN): + """Initialize the translations catalog. + + :param fileobj: the file-like object the translation should be read + from + """ + gettext.GNUTranslations.__init__(self, fp=fileobj) + self.files = filter(None, [getattr(fileobj, 'name', None)]) + self.domain = domain + self._domains = {} + + @classmethod + def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN): + """Load translations from the given directory. + + :param dirname: the directory containing the ``MO`` files + :param locales: the list of locales in order of preference (items in + this list can be either `Locale` objects or locale + strings) + :param domain: the message domain + :return: the loaded catalog, or a ``NullTranslations`` instance if no + matching translations were found + :rtype: `Translations` + """ + if locales is not None: + if not isinstance(locales, (list, tuple)): + locales = [locales] + locales = [str(l) for l in locales] + if not domain: + domain = cls.DEFAULT_DOMAIN + filename = gettext.find(domain, dirname, locales) + if not filename: + return gettext.NullTranslations() + return cls(fileobj=open(filename, 'rb'), domain=domain) + + def __repr__(self): + return '<%s: "%s">' % (type(self).__name__, + self._info.get('project-id-version')) + + def add(self, translations, merge=True): + """Add the given translations to the catalog. + + If the domain of the translations is different than that of the + current catalog, they are added as a catalog that is only accessible + by the various ``d*gettext`` functions. + + :param translations: the `Translations` instance with the messages to + add + :param merge: whether translations for message domains that have + already been added should be merged with the existing + translations + :return: the `Translations` instance (``self``) so that `merge` calls + can be easily chained + :rtype: `Translations` + """ + domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN) + if merge and domain == self.domain: + return self.merge(translations) + + existing = self._domains.get(domain) + if merge and existing is not None: + existing.merge(translations) + else: + translations.add_fallback(self) + self._domains[domain] = translations + + return self + + def merge(self, translations): + """Merge the given translations into the catalog. + + Message translations in the specified catalog override any messages + with the same identifier in the existing catalog. + + :param translations: the `Translations` instance with the messages to + merge + :return: the `Translations` instance (``self``) so that `merge` calls + can be easily chained + :rtype: `Translations` + """ + if isinstance(translations, gettext.GNUTranslations): + self._catalog.update(translations._catalog) + if isinstance(translations, Translations): + self.files.extend(translations.files) + + return self + + def dgettext(self, domain, message): + """Like ``gettext()``, but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).gettext(message) + + def ldgettext(self, domain, message): + """Like ``lgettext()``, but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).lgettext(message) + + def dugettext(self, domain, message): + """Like ``ugettext()``, but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).ugettext(message) + + def dngettext(self, domain, singular, plural, num): + """Like ``ngettext()``, but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).ngettext(singular, plural, num) + + def ldngettext(self, domain, singular, plural, num): + """Like ``lngettext()``, but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).lngettext(singular, plural, num) + + def dungettext(self, domain, singular, plural, num): + """Like ``ungettext()`` but look the message up in the specified + domain. + """ + return self._domains.get(domain, self).ungettext(singular, plural, num) + diff --git a/pyramid/includes/__init__.py b/pyramid/includes/__init__.py new file mode 100644 index 000000000..9faa798e8 --- /dev/null +++ b/pyramid/includes/__init__.py @@ -0,0 +1 @@ +# includes package: referred to by generated apps diff --git a/pyramid/includes/configure.zcml b/pyramid/includes/configure.zcml new file mode 100644 index 000000000..6a373fa5d --- /dev/null +++ b/pyramid/includes/configure.zcml @@ -0,0 +1,5 @@ + + + + + diff --git a/pyramid/includes/meta.zcml b/pyramid/includes/meta.zcml new file mode 100644 index 000000000..58ab1c782 --- /dev/null +++ b/pyramid/includes/meta.zcml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py new file mode 100644 index 000000000..d67fd2dfb --- /dev/null +++ b/pyramid/interfaces.py @@ -0,0 +1,379 @@ +from zope.interface import Attribute +from zope.interface import Interface + +# public API interfaces + +class IContextFound(Interface): + """ An event type that is emitted after :mod:`repoze.bfg` finds a + :term:`context` object but before it calls any view code. See the + documentation attached to :class:`repoze.bfg.events.ContextFound` + for more information. + + .. note:: For backwards compatibility with versions of + :mod:`repoze.bfg` before 1.3, this event interface can also be + imported as :class:`repoze.bfg.interfaces.IAfterTraversal`. + """ + request = Attribute('The request object') + +IAfterTraversal = IContextFound + +class INewRequest(Interface): + """ An event type that is emitted whenever :mod:`repoze.bfg` + begins to process a new request. See the documentation attached + to :class:`repoze.bfg.events.NewRequest` for more information.""" + request = Attribute('The request object') + +class INewResponse(Interface): + """ An event type that is emitted whenever any :mod:`repoze.bfg` + view returns a response. See the + documentation attached to :class:`repoze.bfg.events.NewResponse` + for more information.""" + request = Attribute('The request object') + response = Attribute('The response object') + +class IApplicationCreated(Interface): + """ Event issued when the + :meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` method + is called. See the documentation attached to + :class:`repoze.bfg.events.ApplicationCreated` for more + information. + + .. note:: For backwards compatibility with :mod:`repoze.bfg` + versions before 1.3, this interface can also be imported as + :class:`repoze.bfg.interfaces.IWSGIApplicationCreatedEvent`. + """ + app = Attribute(u"Created application") + +IWSGIApplicationCreatedEvent = IApplicationCreated # b /c + +class IResponse(Interface): # not an API + status = Attribute('WSGI status code of response') + headerlist = Attribute('List of response headers') + app_iter = Attribute('Iterable representing the response body') + +class IException(Interface): # not an API + """ An interface representing a generic exception """ + +class IExceptionResponse(IException, IResponse): + """ An interface representing a WSGI response which is also an + exception object. Register an exception view using this interface + as a ``context`` to apply the registered view for all exception + types raised by :mod:`repoze.bfg` internally + (:class:`repoze.bfg.exceptions.NotFound` and + :class:`repoze.bfg.exceptions.Forbidden`).""" + +# internal interfaces + +class IRequest(Interface): + """ Request type interface attached to all request objects """ + +IRequest.combined = IRequest # for exception view lookups + +class IRouteRequest(Interface): + """ *internal only* interface used as in a utility lookup to find + route-specific interfaces. Not an API.""" + +class IAuthenticationPolicy(Interface): + """ An object representing a BFG authentication policy. """ + def authenticated_userid(request): + """ Return the authenticated userid or ``None`` if no + authenticated userid can be found. """ + + def effective_principals(request): + """ Return a sequence representing the effective principals + including the userid and any groups belonged to by the current + user, including 'system' groups such as Everyone and + Authenticated. """ + + def remember(request, principal, **kw): + """ Return a set of headers suitable for 'remembering' the + principal named ``principal`` when set in a response. An + individual authentication policy and its consumers can decide + on the composition and meaning of **kw. """ + + def forget(request): + """ Return a set of headers suitable for 'forgetting' the + current user on subsequent requests. """ + +class IAuthorizationPolicy(Interface): + """ An object representing a BFG authorization policy. """ + def permits(context, principals, permission): + """ Return True if any of the principals is allowed the + permission in the current context, else return False """ + + def principals_allowed_by_permission(context, permission): + """ Return a set of principal identifiers allowed by the permission """ + +class IStaticURLInfo(Interface): + """ A policy for generating URLs to static resources """ + def add(name, spec, **extra): + """ Add a new static info registration """ + + def generate(path, request, **kw): + """ Generate a URL for the given path """ + +class IResponseFactory(Interface): + """ A utility which generates a response factory """ + def __call__(): + """ Return a response factory (e.g. a callable that returns an + object implementing IResponse, e.g. ``webob.Response``; it + should accept all the arguments that the webob.Response class + accepts)""" + +class IRequestFactory(Interface): + """ A utility which generates a request """ + def __call__(environ): + """ Return an object implementing IRequest, e.g. an instance + of ``repoze.bfg.request.Request``""" + + def blank(path): + """ Return an empty request object (see + ``webob.Request.blank``)""" + +class IViewClassifier(Interface): + """ *Internal only* marker interface for views.""" + +class IExceptionViewClassifier(Interface): + """ *Internal only* marker interface for exception views.""" + +class IView(Interface): + def __call__(context, request): + """ Must return an object that implements IResponse. May + optionally raise ``repoze.bfg.exceptions.Forbidden`` if an + authorization failure is detected during view execution or + ``repoze.bfg.exceptions.NotFound`` if the not found page is + meant to be returned.""" + +class ISecuredView(IView): + """ *Internal only* interface. Not an API. """ + def __call_permissive__(context, request): + """ Guaranteed-permissive version of __call__ """ + + def __permitted__(context, request): + """ Return True if view execution will be permitted using the + context and request, False otherwise""" + +class IMultiView(ISecuredView): + """ *internal only*. A multiview is a secured view that is a + collection of other views. Each of the views is associated with + zero or more predicates. Not an API.""" + def add(view, predicates, order, accept=None, phash=None): + """ Add a view to the multiview. """ + +class IRootFactory(Interface): + def __call__(request): + """ Return a root object based on the request """ + +class IDefaultRootFactory(Interface): + def __call__(request): + """ Return the *default* root object for an application """ + +class ITraverser(Interface): + def __call__(request): + """ Return a dictionary with (at least) the keys ``root``, + ``context``, ``view_name``, ``subpath``, ``traversed``, + ``virtual_root``, and ``virtual_root_path``. These values are + typically the result of an object graph traversal. ``root`` + is the physical root object, ``context`` will be a model + object, ``view_name`` will be the view name used (a Unicode + name), ``subpath`` will be a sequence of Unicode names that + followed the view name but were not traversed, ``traversed`` + will be a sequence of Unicode names that were traversed + (including the virtual root path, if any) ``virtual_root`` + will be a model object representing the virtual root (or the + physical root if traversal was not performed), and + ``virtual_root_path`` will be a sequence representing the + virtual root path (a sequence of Unicode names) or None if + traversal was not performed. + + Extra keys for special purpose functionality can be added as + necessary. + + All values returned in the dictionary will be made available + as attributes of the ``request`` object. + """ + +ITraverserFactory = ITraverser # b / c for 1.0 code + +class IRenderer(Interface): + def __call__(value, system): + """ Call a the renderer implementation with the result of the + view (``value``) passed in and return a result (a string or + unicode object useful as a response body). Values computed by + the system are passed by the system in the ``system`` + parameter, which is a dictionary. Keys in the dictionary + include: ``view`` (the view callable that returned the value), + ``renderer_name`` (the template name or simple name of the + renderer), ``context`` (the context object passed to the + view), and ``request`` (the request object passed to the + view).""" + +class IRendererFactory(Interface): + def __call__(name): + """ Return an object that implements ``IRenderer`` """ + +class IRendererGlobalsFactory(Interface): + def __call__(system_values): + """ Return a dictionary of global renderer values (aka + top-level template names). The ``system_values`` value passed + in will be a dictionary that includes at least a ``request`` + key, indicating the current request, and the value + ``renderer_name``, which will be the name of the renderer in + use.""" + +class ITemplateRenderer(IRenderer): + def implementation(): + """ Return the object that the underlying templating system + uses to render the template; it is typically a callable that + accepts arbitrary keyword arguments and returns a string or + unicode object """ + +class IViewPermission(Interface): + def __call__(context, request): + """ Return True if the permission allows, return False if it denies. """ + +class IRouter(Interface): + """WSGI application which routes requests to 'view' code based on + a view registry.""" + registry = Attribute( + """Component architecture registry local to this application.""") + +class ISettings(Interface): + """ Runtime settings utility for repoze.bfg; represents the + deployment settings for the application. Implements a mapping + interface.""" + +# this interface, even if it becomes unused within BFG, is imported by +# other packages (such as repoze.bfg.traversalwrapper) +class ILocation(Interface): + """Objects that have a structural location""" + __parent__ = Attribute("The parent in the location hierarchy") + __name__ = Attribute("The name within the parent") + +class IDebugLogger(Interface): + """ Interface representing a PEP 282 logger """ + +ILogger = IDebugLogger # b/c + +class IRoutePregenerator(Interface): + def __call__(request, elements, kw): + """ A pregenerator is a function associated by a developer + with a :term:`route`. The pregenerator for a route is called + by :func:`repoze.bfg.url.route_url` in order to adjust the set + of arguments passed to it by the user for special purposes, + such as Pylons 'subdomain' support. It will influence the URL + returned by ``route_url``. + + A pregenerator should return a two-tuple of ``(elements, kw)`` + after examining the originals passed to this function, which + are the arguments ``(request, elements, kw)``. The simplest + pregenerator is:: + + def pregenerator(request, elements, kw): + return elements, kw + + You can employ a pregenerator by passing a ``pregenerator`` + argument to the + :meth:`repoze.bfg.configuration.Configurator.add_route` + function. + + .. note:: This interface is new as of :mod:`repoze.bfg` 1.3. + """ + +class IRoute(Interface): + """ Interface representing the type of object returned from + ``IRoutesMapper.get_route``""" + name = Attribute('The route name') + pattern = Attribute('The route pattern') + factory = Attribute( + 'The :term:`root factory` used by the :mod:`repoze.bfg` router ' + 'when this route matches (or ``None``)') + predicates = Attribute( + 'A sequence of :term:`route predicate` objects used to ' + 'determine if a request matches this route or not or not after ' + 'basic pattern matching has been completed.') + pregenerator = Attribute('This attribute should either be ``None`` or ' + 'a callable object implementing the ' + '``IRoutePregenerator`` interface') + def match(path): + """ + If the ``path`` passed to this function can be matched by the + ``pattern`` of this route, return a dictionary (the + 'matchdict'), which will contain keys representing the dynamic + segment markers in the pattern mapped to values extracted from + the provided ``path``. + + If the ``path`` passed to this function cannot be matched by + the ``pattern`` of this route, return ``None``. + """ + def generate(kw): + """ + Generate a URL based on filling in the dynamic segment markers + in the pattern using the ``kw`` dictionary provided. + """ + +class IRoutesMapper(Interface): + """ Interface representing a Routes ``Mapper`` object """ + def get_routes(): + """ Return a sequence of Route objects registered in the mapper.""" + + def has_routes(): + """ Returns ``True`` if any route has been registered. """ + + def get_route(name): + """ Returns an ``IRoute`` object if a route with the name ``name`` + was registered, otherwise return ``None``.""" + + def connect(name, pattern, factory=None, predicates=()): + """ Add a new route. """ + + def generate(name, kw): + """ Generate a URL using the route named ``name`` with the + keywords implied by kw""" + + def __call__(request): + """ Return a dictionary containing matching information for + the request; the ``route`` key of this dictionary will either + be a Route object or ``None`` if no route matched; the + ``match`` key will be the matchdict or ``None`` if no route + matched.""" + +class IContextURL(Interface): + """ An adapter which deals with URLs related to a context. + """ + def virtual_root(): + """ Return the virtual root related to a request and the + current context""" + + def __call__(): + """ Return a URL that points to the context """ + +class IPackageOverrides(Interface): + """ Utility for pkg_resources overrides """ + +# VH_ROOT_KEY is an interface; its imported from other packages (e.g. +# traversalwrapper) +VH_ROOT_KEY = 'HTTP_X_VHM_ROOT' + +class IChameleonTranslate(Interface): + """ Internal interface representing a chameleon translate function """ + def __call__(msgid, domain=None, mapping=None, context=None, + target_language=None, default=None): + """ Translate a mess of arguments to a Unicode object """ + +class ILocalizer(Interface): + """ Localizer for a specific language """ + +class ILocaleNegotiator(Interface): + def __call__(request): + """ Return a locale name """ + +class ITranslationDirectories(Interface): + """ A list object representing all known translation directories + for an application""" + +class IDefaultPermission(Interface): + """ A string object representing the default permission to be used + for all view configurations which do not explicitly declare their + own.""" + diff --git a/pyramid/location.py b/pyramid/location.py new file mode 100644 index 000000000..7472129d2 --- /dev/null +++ b/pyramid/location.py @@ -0,0 +1,66 @@ +############################################################################## +# +# Copyright (c) 2003 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## + +def inside(model1, model2): + """Is ``model1`` 'inside' ``model2``? Return ``True`` if so, else + ``False``. + + ``model1`` is 'inside' ``model2`` if ``model2`` is a + :term:`lineage` ancestor of ``model1``. It is a lineage ancestor + if its parent (or one of its parent's parents, etc.) is an + ancestor. + """ + while model1 is not None: + if model1 is model2: + return True + model1 = model1.__parent__ + + return False + +def lineage(model): + """ + Return a generator representing the :term:`lineage` of the + :term:`model` object implied by the ``model`` argument. The + generator first returns ``model`` unconditionally. Then, if + ``model`` supplies a ``__parent__`` attribute, return the object + represented by ``model.__parent__``. If *that* object has a + ``__parent__`` attribute, return that object's parent, and so on, + until the object being inspected either has no ``__parent__`` + attribute or which has a ``__parent__`` attribute of ``None``. + For example, if the object tree is:: + + thing1 = Thing() + thing2 = Thing() + thing2.__parent__ = thing1 + + Calling ``lineage(thing2)`` will return a generator. When we turn + it into a list, we will get:: + + list(lineage(thing2)) + [ , ] + """ + while model is not None: + yield model + # The common case is that the AttributeError exception below + # is exceptional as long as the developer is a "good citizen" + # who has a root object with a __parent__ of None. Using an + # exception here instead of a getattr with a default is an + # important micro-optimization, because this function is + # called in any non-trivial application over and over again to + # generate URLs and paths. + try: + model = model.__parent__ + except AttributeError: + model = None + diff --git a/pyramid/log.py b/pyramid/log.py new file mode 100644 index 000000000..b8762e6e2 --- /dev/null +++ b/pyramid/log.py @@ -0,0 +1,16 @@ +import logging + +def make_stream_logger( + name, stream, levelname='DEBUG', fmt='%(asctime)s %(message)s'): + """ Return an object which implements + ``repoze.bfg.interfaces.IDebugLogger`` (ie. a Python PEP 282 logger + instance) with the name ``name`` using the stream (or open + filehandle) ``stream``, logging at ``levelname`` log level or + above with format ``fmt``. """ + handler = logging.StreamHandler(stream) + formatter = logging.Formatter(fmt) + handler.setFormatter(formatter) + logger = logging.Logger(name) + logger.addHandler(handler) + logger.setLevel(getattr(logging, levelname)) + return logger diff --git a/pyramid/paster.py b/pyramid/paster.py new file mode 100644 index 000000000..e8ce46ce2 --- /dev/null +++ b/pyramid/paster.py @@ -0,0 +1,111 @@ +import os +import sys +from code import interact + +from paste.deploy import loadapp +from paste.script.command import Command +from paste.script.templates import Template +from paste.util.template import paste_script_template_renderer + +from repoze.bfg.scripting import get_root + + +class StarterProjectTemplate(Template): + _template_dir = 'paster_templates/starter' + summary = 'repoze.bfg starter project' + template_renderer = staticmethod(paste_script_template_renderer) + +class ZODBProjectTemplate(Template): + _template_dir = 'paster_templates/zodb' + summary = 'repoze.bfg ZODB starter project' + template_renderer = staticmethod(paste_script_template_renderer) + +class RoutesAlchemyProjectTemplate(Template): + _template_dir = 'paster_templates/routesalchemy' + summary = 'repoze.bfg SQLAlchemy project using Routes (no traversal)' + template_renderer = staticmethod(paste_script_template_renderer) + +class AlchemyProjectTemplate(Template): + _template_dir = 'paster_templates/alchemy' + summary = 'repoze.bfg SQLAlchemy project using traversal' + template_renderer = staticmethod(paste_script_template_renderer) + +def get_app(config_file, name, loadapp=loadapp): + """ Return the WSGI application named ``name`` in the PasteDeploy + config file ``config_file``""" + config_name = 'config:%s' % config_file + here_dir = os.getcwd() + app = loadapp(config_name, name=name, relative_to=here_dir) + return app + +_marker = object() +class BFGShellCommand(Command): + """Open an interactive shell with a :mod:`repoze.bfg` app loaded. + + This command accepts two positional arguments: + + ``config_file`` -- specifies the PasteDeploy config file to use + for the interactive shell. + + ``section_name`` -- specifies the section name in the PasteDeploy + config file that represents the application. + + Example:: + + $ paster bfgshell myapp.ini main + + .. note:: You should use a ``section_name`` that refers to the + actual ``app`` section in the config file that points at + your BFG app without any middleware wrapping, or this + command will almost certainly fail. + + """ + summary = "Open an interactive shell with a repoze.bfg app loaded" + + min_args = 2 + max_args = 2 + group_name = 'bfg' + + parser = Command.standard_parser(simulate=True) + parser.add_option('-d', '--disable-ipython', + action='store_true', + dest='disable_ipython', + help="Don't use IPython even if it is available") + + interact = (interact,) # for testing + loadapp = (loadapp,) # for testing + get_app = staticmethod(get_app) # hook point + get_root = staticmethod(get_root) # hook point + verbose = 3 + + def __init__(self, *arg, **kw): + # needs to be in constructor to support Jython (used to be at class + # scope as ``usage = '\n' + __doc__``. + self.usage = '\n' + self.__doc__ + Command.__init__(self, *arg, **kw) + + def command(self, IPShell=_marker): + if IPShell is _marker: + try: #pragma no cover + from IPython.Shell import IPShell + except ImportError: #pragma no cover + IPShell = None + cprt =('Type "help" for more information. "root" is the BFG app ' + 'root object.') + banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) + config_file, section_name = self.args + self.logging_file_config(config_file) + app = self.get_app(config_file, section_name, loadapp=self.loadapp[0]) + root, closer = self.get_root(app) + if IPShell is not None and not self.options.disable_ipython: + try: + shell = IPShell(argv=[], user_ns={'root':root}) + shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner + shell.mainloop() + finally: + closer() + else: + try: + self.interact[0](banner, local={'root':root}) + finally: + closer() diff --git a/pyramid/paster_templates/alchemy/+package+/__init__.py b/pyramid/paster_templates/alchemy/+package+/__init__.py new file mode 100755 index 000000000..cbdfd3ac6 --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/__init__.py @@ -0,0 +1,2 @@ +# A package + diff --git a/pyramid/paster_templates/alchemy/+package+/configure.zcml b/pyramid/paster_templates/alchemy/+package+/configure.zcml new file mode 100644 index 000000000..521f06ba4 --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/configure.zcml @@ -0,0 +1,23 @@ + + + + + + + + + + + + diff --git a/pyramid/paster_templates/alchemy/+package+/models.py b/pyramid/paster_templates/alchemy/+package+/models.py new file mode 100755 index 000000000..336613cf9 --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/models.py @@ -0,0 +1,88 @@ +import transaction + +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker + +from sqlalchemy.ext.declarative import declarative_base + +from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm.exc import NoResultFound + +from sqlalchemy import create_engine +from sqlalchemy import Integer +from sqlalchemy import Unicode +from sqlalchemy import Column + +from zope.sqlalchemy import ZopeTransactionExtension + +DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) +Base = declarative_base() + +class MyModel(Base): + __tablename__ = 'models' + id = Column(Integer, primary_key=True) + name = Column(Unicode(255), unique=True) + value = Column(Integer) + + def __init__(self, name, value): + self.name = name + self.value = value + +class MyApp(object): + __name__ = None + __parent__ = None + + def __getitem__(self, key): + session= DBSession() + try: + id = int(key) + except (ValueError, TypeError): + raise KeyError(key) + + query = session.query(MyModel).filter_by(id=id) + + try: + item = query.one() + item.__parent__ = self + item.__name__ = key + return item + except NoResultFound: + raise KeyError(key) + + def get(self, key, default=None): + try: + item = self.__getitem__(key) + except KeyError: + item = default + return item + + def __iter__(self): + session= DBSession() + query = session.query(MyModel) + return iter(query) + +root = MyApp() + +def default_get_root(request): + return root + +def populate(): + session = DBSession() + model = MyModel(name=u'test name',value=55) + session.add(model) + session.flush() + transaction.commit() + +def initialize_sql(db_string, db_echo=False): + engine = create_engine(db_string, echo=db_echo) + DBSession.configure(bind=engine) + Base.metadata.bind = engine + Base.metadata.create_all(engine) + try: + populate() + except IntegrityError: + pass + +def appmaker(db_string, db_echo=False): + initialize_sql(db_string, db_echo) + return default_get_root diff --git a/pyramid/paster_templates/alchemy/+package+/run.py_tmpl b/pyramid/paster_templates/alchemy/+package+/run.py_tmpl new file mode 100644 index 000000000..026505f3a --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/run.py_tmpl @@ -0,0 +1,23 @@ +from repoze.bfg.configuration import Configurator +from paste.deploy.converters import asbool + +from {{package}}.models import appmaker + +def app(global_config, **settings): + """ This function returns a WSGI application. + + It is usually called by the PasteDeploy framework during + ``paster serve``. + """ + zcml_file = settings.get('configure_zcml', 'configure.zcml') + db_string = settings.get('db_string') + if db_string is None: + raise ValueError("No 'db_string' value in application configuration.") + db_echo = settings.get('db_echo', 'false') + get_root = appmaker(db_string, asbool(db_echo)) + config = Configurator(settings=settings, root_factory=get_root) + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() + diff --git a/pyramid/paster_templates/alchemy/+package+/templates/model.pt b/pyramid/paster_templates/alchemy/+package+/templates/model.pt new file mode 100644 index 000000000..e29b92836 --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/templates/model.pt @@ -0,0 +1,103 @@ + + + + +${project} Application + + + + + + + + + +
+ +
+ +
+
+

Welcome to ${project}, an + application generated by the repoze.bfg web + application framework.

+

+ Id: ${item.id}
+ Name: ${item.name}
+ Value: ${item.value}

+
+
+ + + + +
 
+
+
+ + + + + + diff --git a/pyramid/paster_templates/alchemy/+package+/templates/root.pt b/pyramid/paster_templates/alchemy/+package+/templates/root.pt new file mode 100644 index 000000000..2cac6fe6e --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/templates/root.pt @@ -0,0 +1,101 @@ + + + + +${project} Application + + + + + + + + + +
+ +
+ +
+
+

Welcome to ${project}, an + application generated by the repoze.bfg web + application framework.

+

+ ${item.name}

+
+
+ + + + +
 
+
+
+ + + + + + diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/default.css b/pyramid/paster_templates/alchemy/+package+/templates/static/default.css new file mode 100644 index 000000000..41b3debde --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/templates/static/default.css @@ -0,0 +1,380 @@ +/* +Design by Free CSS Templates +http://www.freecsstemplates.org +Released for free under a Creative Commons Attribution 2.5 License +*/ + +body { + margin: 0; + padding: 0; + background: url(images/img01.gif) repeat-x left top; + font-size: 13px; + font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; + text-align: justify; + color: #FFFFFF; +} + +h1, h2, h3 { + margin: 0; + text-transform: lowercase; + font-weight: normal; + color: #FFFFFF; +} + +h1 { + letter-spacing: -1px; + font-size: 32px; +} + +h2 { + font-size: 23px; +} + +p, ul, ol { + margin: 0 0 2em 0; + text-align: justify; + line-height: 26px; +} + +a:link { + color: #8BD80E; +} + +a:hover, a:active { + text-decoration: none; + color: #8BD80E; +} + +a:visited { + color: #8BD80E; +} + +img { + border: none; +} + +img.left { + float: left; + margin-right: 15px; +} + +img.right { + float: right; + margin-left: 15px; +} + +/* Form */ + +form { + margin: 0; + padding: 0; +} + +fieldset { + margin: 0; + padding: 0; + border: none; +} + +legend { + display: none; +} + +input, textarea, select { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 13px; + color: #333333; +} + +#wrapper { + margin: 0; + padding: 0; + background: #000000; +} + +/* Header */ + +#header { + width: 713px; + margin: 0 auto; + height: 42px; +} + +/* Menu */ + +#menu { + float: left; + width: 713px; + height: 50px; + background: url(images/img02.gif) no-repeat left top; +} + +#menu ul { + margin: 0; + padding: 0px 0 0 10px; + list-style: none; + line-height: normal; +} + +#menu li { + display: block; + float: left; +} + +#menu a { + display: block; + float: left; + background: url(images/img04.gif) no-repeat right 55%; + margin-top: 5px; + margin-right: 3px; + padding: 8px 17px; + text-decoration: none; + font-size: 13px; + color: #000000; +} + +#menu a:hover { + color: #000000; +} + +#menu .current_page_item a { + color: #000000; +} + +/** LOGO */ + +#logo { + width: 713px; + height: 80px; + margin: 0 auto; +} + +#logo h1, #logo h2 { + float: left; + margin: 0; + padding: 30px 0 0 0px; + line-height: normal; +} + +#logo h1 { + font-family: Georgia, "Times New Roman", Times, serif; + font-size:40px; +} + +#logo h1 a { + text-decoration: none; + color: #4C4C4C; +} + +#logo h1 a:hover { text-decoration: underline; } + +#logo h2 { + float: left; + padding: 45px 0 0 18px; + font: 18px Georgia, "Times New Roman", Times, serif; + color: #8BD80E; +} + +#logo p a { + text-decoration: none; + color: #8BD80E; +} + +#logo p a:hover { text-decoration: underline; } + + + +/* Page */ + +#page { + width: 663px; + margin: 0 auto; + background: #4C4C4C url(images/img03.gif) no-repeat left bottom; + padding: 0 25px; +} + +/* Content */ + +#content { + float: left; + width: 410px; + +} + +/* Post */ + +.post { + padding: 15px 0px; + margin-bottom: 20px; +} + +.post .title { + margin-bottom: 20px; + padding-bottom: 5px; +} + +.post h1 { + padding: 0px 0 0 0px; + background: url(images/img08.jpg) no-repeat left top; + font-size: 24px; + color: #FFFFFF; +} + +.post h2 { + padding: 0px 0 0 0px; + font-size: 22px; + color: #FFFFFF; +} + +.post .entry { +} + +.post .meta { + padding: 15px 15px 30px 0px; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; +} + +.post .meta p { + margin: 0; + padding-top: 15px; + line-height: normal; + color: #FFFFFF; +} + +.post .meta .byline { + float: left; +} + +.post .meta .links { + float: right; +} + +.post .meta .more { + padding: 0 10px 0 18px; +} + +.post .meta .comments { +} + +.post .meta b { + display: none; +} + + +/* Sidebar */ + +#sidebar { + width: 210px; + float: right; + margin: 0; + padding: 0; +} + +#sidebar ul { + margin: 0; + padding: 0; + list-style: none; +} + +#sidebar li { + margin-bottom: 40px; +} + +#sidebar li ul { +} + +#sidebar li li { + margin: 0; +} + +#sidebar h2 { + width: 250px; + padding: 8px 0 0 0px; + margin-bottom: 10px; + background: url(images/img07.jpg) no-repeat left top; + font-size: 20px; + color: #FFFFFF; +} + +/* Search */ + +#search { + +} + +#search h2 { + margin-bottom: 20px; +} + +#s { + width: 140px; + margin-right: 5px; + padding: 3px; + border: 1px solid #BED99C; +} + +#x { + padding: 3px; + border: none; + background: #8BD80E; + text-transform: lowercase; + font-size: 11px; + color: #FFFFFF; +} + +/* Boxes */ + +.box1 { + padding: 20px; +} + +.box2 { + color: #BABABA; +} + +.box2 h2 { + margin-bottom: 15px; + font-size: 16px; + color: #FFFFFF; +} + +.box2 ul { + margin: 0; + padding: 0; + list-style: none; +} + +.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { + color: #EDEDED; +} + +/* Footer */ +#footer-wrap { +} + +#footer { + margin: 0 auto; + padding: 20px 0 10px 0; + background: #000000; +} + +html>body #footer { + height: auto; +} + +#footer p { + font-size: 11px; +} + +#legal { + clear: both; + padding-top: 17px; + text-align: center; + color: #FFFFFF; +} + +#legal a { + font-weight: normal; + color: #FFFFFF; +} diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/images/img01.gif b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img01.gif new file mode 100644 index 000000000..5f082bd99 Binary files /dev/null and b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img01.gif differ diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/images/img02.gif b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img02.gif new file mode 100644 index 000000000..45a3ae976 Binary files /dev/null and b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img02.gif differ diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/images/img03.gif b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img03.gif new file mode 100644 index 000000000..d92ea38f9 Binary files /dev/null and b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img03.gif differ diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/images/img04.gif b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img04.gif new file mode 100644 index 000000000..950c4af9d Binary files /dev/null and b/pyramid/paster_templates/alchemy/+package+/templates/static/images/img04.gif differ diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/images/spacer.gif b/pyramid/paster_templates/alchemy/+package+/templates/static/images/spacer.gif new file mode 100644 index 000000000..5bfd67a2d Binary files /dev/null and b/pyramid/paster_templates/alchemy/+package+/templates/static/images/spacer.gif differ diff --git a/pyramid/paster_templates/alchemy/+package+/templates/static/templatelicense.txt b/pyramid/paster_templates/alchemy/+package+/templates/static/templatelicense.txt new file mode 100644 index 000000000..ccb6b06ab --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/templates/static/templatelicense.txt @@ -0,0 +1,243 @@ +Creative Commons + +Creative Commons Legal Code + +*Attribution 2.5* + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE +LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN +ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION +ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE +INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM +ITS USE. + +/License/ + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE +RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS +AND CONDITIONS. + +*1. Definitions* + + 1. *"Collective Work"* means a work, such as a periodical issue, + anthology or encyclopedia, in which the Work in its entirety in + unmodified form, along with a number of other contributions, + constituting separate and independent works in themselves, are + assembled into a collective whole. A work that constitutes a + Collective Work will not be considered a Derivative Work (as + defined below) for the purposes of this License. + 2. *"Derivative Work"* means a work based upon the Work or upon the + Work and other pre-existing works, such as a translation, musical + arrangement, dramatization, fictionalization, motion picture + version, sound recording, art reproduction, abridgment, + condensation, or any other form in which the Work may be recast, + transformed, or adapted, except that a work that constitutes a + Collective Work will not be considered a Derivative Work for the + purpose of this License. For the avoidance of doubt, where the + Work is a musical composition or sound recording, the + synchronization of the Work in timed-relation with a moving image + ("synching") will be considered a Derivative Work for the purpose + of this License. + 3. *"Licensor"* means the individual or entity that offers the Work + under the terms of this License. + 4. *"Original Author"* means the individual or entity who created the + Work. + 5. *"Work"* means the copyrightable work of authorship offered under + the terms of this License. + 6. *"You"* means an individual or entity exercising rights under this + License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission + from the Licensor to exercise rights under this License despite a + previous violation. + +*2. Fair Use Rights.* Nothing in this license is intended to reduce, +limit, or restrict any rights arising from fair use, first sale or other +limitations on the exclusive rights of the copyright owner under +copyright law or other applicable laws. + +*3. License Grant.* Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + 1. to reproduce the Work, to incorporate the Work into one or more + Collective Works, and to reproduce the Work as incorporated in the + Collective Works; + 2. to create and reproduce Derivative Works; + 3. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission the Work including as incorporated in Collective Works; + 4. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission Derivative Works. + 5. + + For the avoidance of doubt, where the work is a musical composition: + + 1. *Performance Royalties Under Blanket Licenses*. Licensor + waives the exclusive right to collect, whether individually + or via a performance rights society (e.g. ASCAP, BMI, + SESAC), royalties for the public performance or public + digital performance (e.g. webcast) of the Work. + 2. *Mechanical Rights and Statutory Royalties*. Licensor waives + the exclusive right to collect, whether individually or via + a music rights agency or designated agent (e.g. Harry Fox + Agency), royalties for any phonorecord You create from the + Work ("cover version") and distribute, subject to the + compulsory license created by 17 USC Section 115 of the US + Copyright Act (or the equivalent in other jurisdictions). + 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of + doubt, where the Work is a sound recording, Licensor waives the + exclusive right to collect, whether individually or via a + performance-rights society (e.g. SoundExchange), royalties for the + public digital performance (e.g. webcast) of the Work, subject to + the compulsory license created by 17 USC Section 114 of the US + Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights +in other media and formats. All rights not expressly granted by Licensor +are hereby reserved. + +*4. Restrictions.*The license granted in Section 3 above is expressly +made subject to and limited by the following restrictions: + + 1. You may distribute, publicly display, publicly perform, or + publicly digitally perform the Work only under the terms of this + License, and You must include a copy of, or the Uniform Resource + Identifier for, this License with every copy or phonorecord of the + Work You distribute, publicly display, publicly perform, or + publicly digitally perform. You may not offer or impose any terms + on the Work that alter or restrict the terms of this License or + the recipients' exercise of the rights granted hereunder. You may + not sublicense the Work. You must keep intact all notices that + refer to this License and to the disclaimer of warranties. You may + not distribute, publicly display, publicly perform, or publicly + digitally perform the Work with any technological measures that + control access or use of the Work in a manner inconsistent with + the terms of this License Agreement. The above applies to the Work + as incorporated in a Collective Work, but this does not require + the Collective Work apart from the Work itself to be made subject + to the terms of this License. If You create a Collective Work, + upon notice from any Licensor You must, to the extent practicable, + remove from the Collective Work any credit as required by clause + 4(b), as requested. If You create a Derivative Work, upon notice + from any Licensor You must, to the extent practicable, remove from + the Derivative Work any credit as required by clause 4(b), as + requested. + 2. If you distribute, publicly display, publicly perform, or publicly + digitally perform the Work or any Derivative Works or Collective + Works, You must keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) + the name of the Original Author (or pseudonym, if applicable) if + supplied, and/or (ii) if the Original Author and/or Licensor + designate another party or parties (e.g. a sponsor institute, + publishing entity, journal) for attribution in Licensor's + copyright notice, terms of service or by other reasonable means, + the name of such party or parties; the title of the Work if + supplied; to the extent reasonably practicable, the Uniform + Resource Identifier, if any, that Licensor specifies to be + associated with the Work, unless such URI does not refer to the + copyright notice or licensing information for the Work; and in the + case of a Derivative Work, a credit identifying the use of the + Work in the Derivative Work (e.g., "French translation of the Work + by Original Author," or "Screenplay based on original Work by + Original Author"). Such credit may be implemented in any + reasonable manner; provided, however, that in the case of a + Derivative Work or Collective Work, at a minimum such credit will + appear where any other comparable authorship credit appears and in + a manner at least as prominent as such other comparable authorship + credit. + +*5. Representations, Warranties and Disclaimer* + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE +EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY +APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL +THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY +DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF +LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +*7. Termination* + + 1. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Derivative Works or + Collective Works from You under this License, however, will not + have their licenses terminated provided such individuals or + entities remain in full compliance with those licenses. Sections + 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted + here is perpetual (for the duration of the applicable copyright in + the Work). Notwithstanding the above, Licensor reserves the right + to release the Work under different license terms or to stop + distributing the Work at any time; provided, however that any such + election will not serve to withdraw this License (or any other + license that has been, or is required to be, granted under the + terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +*8. Miscellaneous* + + 1. Each time You distribute or publicly digitally perform the Work or + a Collective Work, the Licensor offers to the recipient a license + to the Work on the same terms and conditions as the license + granted to You under this License. + 2. Each time You distribute or publicly digitally perform a + Derivative Work, Licensor offers to the recipient a license to the + original Work on the same terms and conditions as the license + granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this License, and without further + action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision + valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in + writing and signed by the party to be charged with such waiver or + consent. + 5. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no + understandings, agreements or representations with respect to the + Work not specified here. Licensor shall not be bound by any + additional provisions that may appear in any communication from + You. This License may not be modified without the mutual written + agreement of the Licensor and You. + +Creative Commons is not a party to this License, and makes no warranty +whatsoever in connection with the Work. Creative Commons will not be +liable to You or any party on any legal theory for any damages +whatsoever, including without limitation any general, special, +incidental or consequential damages arising in connection to this +license. Notwithstanding the foregoing two (2) sentences, if Creative +Commons has expressly identified itself as the Licensor hereunder, it +shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work +is licensed under the CCPL, neither party will use the trademark +"Creative Commons" or any related trademark or logo of Creative Commons +without the prior written consent of Creative Commons. Any permitted use +will be in compliance with Creative Commons' then-current trademark +usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. + +Creative Commons may be contacted at http://creativecommons.org/ +. + +« Back to Commons Deed <./> diff --git a/pyramid/paster_templates/alchemy/+package+/views.py_tmpl b/pyramid/paster_templates/alchemy/+package+/views.py_tmpl new file mode 100644 index 000000000..12bce138e --- /dev/null +++ b/pyramid/paster_templates/alchemy/+package+/views.py_tmpl @@ -0,0 +1,5 @@ +def view_root(context, request): + return {'items':list(context), 'project':'{{project}}'} + +def view_model(context, request): + return {'item':context, 'project':'{{project}}'} diff --git a/pyramid/paster_templates/alchemy/+project+.ini_tmpl b/pyramid/paster_templates/alchemy/+project+.ini_tmpl new file mode 100644 index 000000000..8260104d5 --- /dev/null +++ b/pyramid/paster_templates/alchemy/+project+.ini_tmpl @@ -0,0 +1,22 @@ +[DEFAULT] +debug = true + +[app:sqlalchemy] +use = egg:{{package}}#app +reload_templates = true +debug_authorization = false +debug_notfound = false +debug_templates = true +default_locale_name = en +db_string = sqlite:///%(here)s/{{package}}.db +db_echo = false + +[pipeline:main] +pipeline = + egg:repoze.tm2#tm + sqlalchemy + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 6543 diff --git a/pyramid/paster_templates/alchemy/CHANGES.txt_tmpl b/pyramid/paster_templates/alchemy/CHANGES.txt_tmpl new file mode 100644 index 000000000..5b34f7803 --- /dev/null +++ b/pyramid/paster_templates/alchemy/CHANGES.txt_tmpl @@ -0,0 +1,4 @@ +0.1 +--- + +- Initial version diff --git a/pyramid/paster_templates/alchemy/README.txt_tmpl b/pyramid/paster_templates/alchemy/README.txt_tmpl new file mode 100644 index 000000000..0ddebfc3e --- /dev/null +++ b/pyramid/paster_templates/alchemy/README.txt_tmpl @@ -0,0 +1,4 @@ +{{project}} README + + + diff --git a/pyramid/paster_templates/alchemy/setup.cfg_tmpl b/pyramid/paster_templates/alchemy/setup.cfg_tmpl new file mode 100644 index 000000000..5bec29823 --- /dev/null +++ b/pyramid/paster_templates/alchemy/setup.cfg_tmpl @@ -0,0 +1,27 @@ +[nosetests] +match=^test +nocapture=1 +cover-package={{package}} +with-coverage=1 +cover-erase=1 + +[compile_catalog] +directory = {{package}}/locale +domain = {{project}} +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = {{package}}/locale/{{project}}.pot +width = 80 + +[init_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale + +[update_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale +previous = true diff --git a/pyramid/paster_templates/alchemy/setup.py_tmpl b/pyramid/paster_templates/alchemy/setup.py_tmpl new file mode 100644 index 000000000..9446964e5 --- /dev/null +++ b/pyramid/paster_templates/alchemy/setup.py_tmpl @@ -0,0 +1,45 @@ +import os +import sys + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, 'README.txt')).read() +CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() + +requires = [ + 'repoze.bfg', + 'repoze.tm2', + 'sqlalchemy', + 'zope.sqlalchemy', + ] + +if sys.version_info[:3] < (2,5,0): + requires.append('pysqlite') + +setup(name='{{project}}', + version='0.0', + description='{{project}}', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: BFG", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web wsgi bfg', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires = requires, + tests_require = requires, + test_suite="{{package}}", + entry_points = """\ + [paste.app_factory] + app = {{package}}.run:app + """ + ) + diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py b/pyramid/paster_templates/routesalchemy/+package+/__init__.py new file mode 100644 index 000000000..cbdfd3ac6 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py @@ -0,0 +1,2 @@ +# A package + diff --git a/pyramid/paster_templates/routesalchemy/+package+/configure.zcml b/pyramid/paster_templates/routesalchemy/+package+/configure.zcml new file mode 100644 index 000000000..6d16bd089 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/configure.zcml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/pyramid/paster_templates/routesalchemy/+package+/models.py b/pyramid/paster_templates/routesalchemy/+package+/models.py new file mode 100644 index 000000000..a1726ebf4 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/models.py @@ -0,0 +1,44 @@ +import transaction + +from sqlalchemy import create_engine +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import Unicode + +from sqlalchemy.exc import IntegrityError +from sqlalchemy.ext.declarative import declarative_base + +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker + +from zope.sqlalchemy import ZopeTransactionExtension + +DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) +Base = declarative_base() + +class MyModel(Base): + __tablename__ = 'models' + id = Column(Integer, primary_key=True) + name = Column(Unicode(255), unique=True) + value = Column(Integer) + + def __init__(self, name, value): + self.name = name + self.value = value + +def populate(): + session = DBSession() + model = MyModel(name=u'root',value=55) + session.add(model) + session.flush() + transaction.commit() + +def initialize_sql(db_string, db_echo=False): + engine = create_engine(db_string, echo=db_echo) + DBSession.configure(bind=engine) + Base.metadata.bind = engine + Base.metadata.create_all(engine) + try: + populate() + except IntegrityError: + pass diff --git a/pyramid/paster_templates/routesalchemy/+package+/run.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/run.py_tmpl new file mode 100644 index 000000000..b85f57556 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/run.py_tmpl @@ -0,0 +1,23 @@ +from repoze.bfg.configuration import Configurator +from paste.deploy.converters import asbool + +from {{package}}.models import initialize_sql + +def app(global_config, **settings): + """ This function returns a WSGI application. + + It is usually called by the PasteDeploy framework during + ``paster serve``. + """ + zcml_file = settings.get('configure_zcml', 'configure.zcml') + db_string = settings.get('db_string') + if db_string is None: + raise ValueError("No 'db_string' value in application configuration.") + db_echo = settings.get('db_echo', 'false') + initialize_sql(db_string, asbool(db_echo)) + config = Configurator(settings=settings) + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() + diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt b/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt new file mode 100644 index 000000000..2aedcad9f --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/templates/mytemplate.pt @@ -0,0 +1,99 @@ + + + + +${project} Application + + + + + + + + + +
+ +
+ +
+
+

Welcome to ${project}, an + application generated by the repoze.bfg web + application framework.

+
+
+ + + + +
 
+
+
+ + + + + + diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/default.css b/pyramid/paster_templates/routesalchemy/+package+/templates/static/default.css new file mode 100644 index 000000000..41b3debde --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/templates/static/default.css @@ -0,0 +1,380 @@ +/* +Design by Free CSS Templates +http://www.freecsstemplates.org +Released for free under a Creative Commons Attribution 2.5 License +*/ + +body { + margin: 0; + padding: 0; + background: url(images/img01.gif) repeat-x left top; + font-size: 13px; + font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; + text-align: justify; + color: #FFFFFF; +} + +h1, h2, h3 { + margin: 0; + text-transform: lowercase; + font-weight: normal; + color: #FFFFFF; +} + +h1 { + letter-spacing: -1px; + font-size: 32px; +} + +h2 { + font-size: 23px; +} + +p, ul, ol { + margin: 0 0 2em 0; + text-align: justify; + line-height: 26px; +} + +a:link { + color: #8BD80E; +} + +a:hover, a:active { + text-decoration: none; + color: #8BD80E; +} + +a:visited { + color: #8BD80E; +} + +img { + border: none; +} + +img.left { + float: left; + margin-right: 15px; +} + +img.right { + float: right; + margin-left: 15px; +} + +/* Form */ + +form { + margin: 0; + padding: 0; +} + +fieldset { + margin: 0; + padding: 0; + border: none; +} + +legend { + display: none; +} + +input, textarea, select { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 13px; + color: #333333; +} + +#wrapper { + margin: 0; + padding: 0; + background: #000000; +} + +/* Header */ + +#header { + width: 713px; + margin: 0 auto; + height: 42px; +} + +/* Menu */ + +#menu { + float: left; + width: 713px; + height: 50px; + background: url(images/img02.gif) no-repeat left top; +} + +#menu ul { + margin: 0; + padding: 0px 0 0 10px; + list-style: none; + line-height: normal; +} + +#menu li { + display: block; + float: left; +} + +#menu a { + display: block; + float: left; + background: url(images/img04.gif) no-repeat right 55%; + margin-top: 5px; + margin-right: 3px; + padding: 8px 17px; + text-decoration: none; + font-size: 13px; + color: #000000; +} + +#menu a:hover { + color: #000000; +} + +#menu .current_page_item a { + color: #000000; +} + +/** LOGO */ + +#logo { + width: 713px; + height: 80px; + margin: 0 auto; +} + +#logo h1, #logo h2 { + float: left; + margin: 0; + padding: 30px 0 0 0px; + line-height: normal; +} + +#logo h1 { + font-family: Georgia, "Times New Roman", Times, serif; + font-size:40px; +} + +#logo h1 a { + text-decoration: none; + color: #4C4C4C; +} + +#logo h1 a:hover { text-decoration: underline; } + +#logo h2 { + float: left; + padding: 45px 0 0 18px; + font: 18px Georgia, "Times New Roman", Times, serif; + color: #8BD80E; +} + +#logo p a { + text-decoration: none; + color: #8BD80E; +} + +#logo p a:hover { text-decoration: underline; } + + + +/* Page */ + +#page { + width: 663px; + margin: 0 auto; + background: #4C4C4C url(images/img03.gif) no-repeat left bottom; + padding: 0 25px; +} + +/* Content */ + +#content { + float: left; + width: 410px; + +} + +/* Post */ + +.post { + padding: 15px 0px; + margin-bottom: 20px; +} + +.post .title { + margin-bottom: 20px; + padding-bottom: 5px; +} + +.post h1 { + padding: 0px 0 0 0px; + background: url(images/img08.jpg) no-repeat left top; + font-size: 24px; + color: #FFFFFF; +} + +.post h2 { + padding: 0px 0 0 0px; + font-size: 22px; + color: #FFFFFF; +} + +.post .entry { +} + +.post .meta { + padding: 15px 15px 30px 0px; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; +} + +.post .meta p { + margin: 0; + padding-top: 15px; + line-height: normal; + color: #FFFFFF; +} + +.post .meta .byline { + float: left; +} + +.post .meta .links { + float: right; +} + +.post .meta .more { + padding: 0 10px 0 18px; +} + +.post .meta .comments { +} + +.post .meta b { + display: none; +} + + +/* Sidebar */ + +#sidebar { + width: 210px; + float: right; + margin: 0; + padding: 0; +} + +#sidebar ul { + margin: 0; + padding: 0; + list-style: none; +} + +#sidebar li { + margin-bottom: 40px; +} + +#sidebar li ul { +} + +#sidebar li li { + margin: 0; +} + +#sidebar h2 { + width: 250px; + padding: 8px 0 0 0px; + margin-bottom: 10px; + background: url(images/img07.jpg) no-repeat left top; + font-size: 20px; + color: #FFFFFF; +} + +/* Search */ + +#search { + +} + +#search h2 { + margin-bottom: 20px; +} + +#s { + width: 140px; + margin-right: 5px; + padding: 3px; + border: 1px solid #BED99C; +} + +#x { + padding: 3px; + border: none; + background: #8BD80E; + text-transform: lowercase; + font-size: 11px; + color: #FFFFFF; +} + +/* Boxes */ + +.box1 { + padding: 20px; +} + +.box2 { + color: #BABABA; +} + +.box2 h2 { + margin-bottom: 15px; + font-size: 16px; + color: #FFFFFF; +} + +.box2 ul { + margin: 0; + padding: 0; + list-style: none; +} + +.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { + color: #EDEDED; +} + +/* Footer */ +#footer-wrap { +} + +#footer { + margin: 0 auto; + padding: 20px 0 10px 0; + background: #000000; +} + +html>body #footer { + height: auto; +} + +#footer p { + font-size: 11px; +} + +#legal { + clear: both; + padding-top: 17px; + text-align: center; + color: #FFFFFF; +} + +#legal a { + font-weight: normal; + color: #FFFFFF; +} diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif new file mode 100644 index 000000000..5f082bd99 Binary files /dev/null and b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif differ diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif new file mode 100644 index 000000000..45a3ae976 Binary files /dev/null and b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif differ diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif new file mode 100644 index 000000000..d92ea38f9 Binary files /dev/null and b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif differ diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif new file mode 100644 index 000000000..950c4af9d Binary files /dev/null and b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif differ diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif new file mode 100644 index 000000000..5bfd67a2d Binary files /dev/null and b/pyramid/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif differ diff --git a/pyramid/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt b/pyramid/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt new file mode 100644 index 000000000..ccb6b06ab --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt @@ -0,0 +1,243 @@ +Creative Commons + +Creative Commons Legal Code + +*Attribution 2.5* + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE +LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN +ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION +ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE +INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM +ITS USE. + +/License/ + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE +RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS +AND CONDITIONS. + +*1. Definitions* + + 1. *"Collective Work"* means a work, such as a periodical issue, + anthology or encyclopedia, in which the Work in its entirety in + unmodified form, along with a number of other contributions, + constituting separate and independent works in themselves, are + assembled into a collective whole. A work that constitutes a + Collective Work will not be considered a Derivative Work (as + defined below) for the purposes of this License. + 2. *"Derivative Work"* means a work based upon the Work or upon the + Work and other pre-existing works, such as a translation, musical + arrangement, dramatization, fictionalization, motion picture + version, sound recording, art reproduction, abridgment, + condensation, or any other form in which the Work may be recast, + transformed, or adapted, except that a work that constitutes a + Collective Work will not be considered a Derivative Work for the + purpose of this License. For the avoidance of doubt, where the + Work is a musical composition or sound recording, the + synchronization of the Work in timed-relation with a moving image + ("synching") will be considered a Derivative Work for the purpose + of this License. + 3. *"Licensor"* means the individual or entity that offers the Work + under the terms of this License. + 4. *"Original Author"* means the individual or entity who created the + Work. + 5. *"Work"* means the copyrightable work of authorship offered under + the terms of this License. + 6. *"You"* means an individual or entity exercising rights under this + License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission + from the Licensor to exercise rights under this License despite a + previous violation. + +*2. Fair Use Rights.* Nothing in this license is intended to reduce, +limit, or restrict any rights arising from fair use, first sale or other +limitations on the exclusive rights of the copyright owner under +copyright law or other applicable laws. + +*3. License Grant.* Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + 1. to reproduce the Work, to incorporate the Work into one or more + Collective Works, and to reproduce the Work as incorporated in the + Collective Works; + 2. to create and reproduce Derivative Works; + 3. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission the Work including as incorporated in Collective Works; + 4. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission Derivative Works. + 5. + + For the avoidance of doubt, where the work is a musical composition: + + 1. *Performance Royalties Under Blanket Licenses*. Licensor + waives the exclusive right to collect, whether individually + or via a performance rights society (e.g. ASCAP, BMI, + SESAC), royalties for the public performance or public + digital performance (e.g. webcast) of the Work. + 2. *Mechanical Rights and Statutory Royalties*. Licensor waives + the exclusive right to collect, whether individually or via + a music rights agency or designated agent (e.g. Harry Fox + Agency), royalties for any phonorecord You create from the + Work ("cover version") and distribute, subject to the + compulsory license created by 17 USC Section 115 of the US + Copyright Act (or the equivalent in other jurisdictions). + 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of + doubt, where the Work is a sound recording, Licensor waives the + exclusive right to collect, whether individually or via a + performance-rights society (e.g. SoundExchange), royalties for the + public digital performance (e.g. webcast) of the Work, subject to + the compulsory license created by 17 USC Section 114 of the US + Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights +in other media and formats. All rights not expressly granted by Licensor +are hereby reserved. + +*4. Restrictions.*The license granted in Section 3 above is expressly +made subject to and limited by the following restrictions: + + 1. You may distribute, publicly display, publicly perform, or + publicly digitally perform the Work only under the terms of this + License, and You must include a copy of, or the Uniform Resource + Identifier for, this License with every copy or phonorecord of the + Work You distribute, publicly display, publicly perform, or + publicly digitally perform. You may not offer or impose any terms + on the Work that alter or restrict the terms of this License or + the recipients' exercise of the rights granted hereunder. You may + not sublicense the Work. You must keep intact all notices that + refer to this License and to the disclaimer of warranties. You may + not distribute, publicly display, publicly perform, or publicly + digitally perform the Work with any technological measures that + control access or use of the Work in a manner inconsistent with + the terms of this License Agreement. The above applies to the Work + as incorporated in a Collective Work, but this does not require + the Collective Work apart from the Work itself to be made subject + to the terms of this License. If You create a Collective Work, + upon notice from any Licensor You must, to the extent practicable, + remove from the Collective Work any credit as required by clause + 4(b), as requested. If You create a Derivative Work, upon notice + from any Licensor You must, to the extent practicable, remove from + the Derivative Work any credit as required by clause 4(b), as + requested. + 2. If you distribute, publicly display, publicly perform, or publicly + digitally perform the Work or any Derivative Works or Collective + Works, You must keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) + the name of the Original Author (or pseudonym, if applicable) if + supplied, and/or (ii) if the Original Author and/or Licensor + designate another party or parties (e.g. a sponsor institute, + publishing entity, journal) for attribution in Licensor's + copyright notice, terms of service or by other reasonable means, + the name of such party or parties; the title of the Work if + supplied; to the extent reasonably practicable, the Uniform + Resource Identifier, if any, that Licensor specifies to be + associated with the Work, unless such URI does not refer to the + copyright notice or licensing information for the Work; and in the + case of a Derivative Work, a credit identifying the use of the + Work in the Derivative Work (e.g., "French translation of the Work + by Original Author," or "Screenplay based on original Work by + Original Author"). Such credit may be implemented in any + reasonable manner; provided, however, that in the case of a + Derivative Work or Collective Work, at a minimum such credit will + appear where any other comparable authorship credit appears and in + a manner at least as prominent as such other comparable authorship + credit. + +*5. Representations, Warranties and Disclaimer* + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE +EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY +APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL +THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY +DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF +LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +*7. Termination* + + 1. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Derivative Works or + Collective Works from You under this License, however, will not + have their licenses terminated provided such individuals or + entities remain in full compliance with those licenses. Sections + 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted + here is perpetual (for the duration of the applicable copyright in + the Work). Notwithstanding the above, Licensor reserves the right + to release the Work under different license terms or to stop + distributing the Work at any time; provided, however that any such + election will not serve to withdraw this License (or any other + license that has been, or is required to be, granted under the + terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +*8. Miscellaneous* + + 1. Each time You distribute or publicly digitally perform the Work or + a Collective Work, the Licensor offers to the recipient a license + to the Work on the same terms and conditions as the license + granted to You under this License. + 2. Each time You distribute or publicly digitally perform a + Derivative Work, Licensor offers to the recipient a license to the + original Work on the same terms and conditions as the license + granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this License, and without further + action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision + valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in + writing and signed by the party to be charged with such waiver or + consent. + 5. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no + understandings, agreements or representations with respect to the + Work not specified here. Licensor shall not be bound by any + additional provisions that may appear in any communication from + You. This License may not be modified without the mutual written + agreement of the Licensor and You. + +Creative Commons is not a party to this License, and makes no warranty +whatsoever in connection with the Work. Creative Commons will not be +liable to You or any party on any legal theory for any damages +whatsoever, including without limitation any general, special, +incidental or consequential damages arising in connection to this +license. Notwithstanding the foregoing two (2) sentences, if Creative +Commons has expressly identified itself as the Licensor hereunder, it +shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work +is licensed under the CCPL, neither party will use the trademark +"Creative Commons" or any related trademark or logo of Creative Commons +without the prior written consent of Creative Commons. Any permitted use +will be in compliance with Creative Commons' then-current trademark +usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. + +Creative Commons may be contacted at http://creativecommons.org/ +. + +« Back to Commons Deed <./> diff --git a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl new file mode 100644 index 000000000..ed7f1280b --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl @@ -0,0 +1,24 @@ +import unittest +from repoze.bfg.configuration import Configurator +from repoze.bfg import testing + +def _initTestingDB(): + from {{package}}.models import initialize_sql + session = initialize_sql('sqlite://') + return session + +class TestMyView(unittest.TestCase): + def setUp(self): + self.config = Configurator() + self.config.begin() + _initTestingDB() + + def tearDown(self): + self.config.end() + + def test_it(self): + from {{package}}.views import my_view + request = testing.DummyRequest() + info = my_view(request) + self.assertEqual(info['root'].name, 'root') + self.assertEqual(info['project'], '{{package}}') diff --git a/pyramid/paster_templates/routesalchemy/+package+/views.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/views.py_tmpl new file mode 100644 index 000000000..86cc02e41 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+package+/views.py_tmpl @@ -0,0 +1,7 @@ +from {{package}}.models import DBSession +from {{package}}.models import MyModel + +def my_view(request): + dbsession = DBSession() + root = dbsession.query(MyModel).filter(MyModel.name==u'root').first() + return {'root':root, 'project':'{{project}}'} diff --git a/pyramid/paster_templates/routesalchemy/+project+.ini_tmpl b/pyramid/paster_templates/routesalchemy/+project+.ini_tmpl new file mode 100644 index 000000000..8260104d5 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/+project+.ini_tmpl @@ -0,0 +1,22 @@ +[DEFAULT] +debug = true + +[app:sqlalchemy] +use = egg:{{package}}#app +reload_templates = true +debug_authorization = false +debug_notfound = false +debug_templates = true +default_locale_name = en +db_string = sqlite:///%(here)s/{{package}}.db +db_echo = false + +[pipeline:main] +pipeline = + egg:repoze.tm2#tm + sqlalchemy + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 6543 diff --git a/pyramid/paster_templates/routesalchemy/CHANGES.txt_tmpl b/pyramid/paster_templates/routesalchemy/CHANGES.txt_tmpl new file mode 100644 index 000000000..35a34f332 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/CHANGES.txt_tmpl @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/pyramid/paster_templates/routesalchemy/README.txt_tmpl b/pyramid/paster_templates/routesalchemy/README.txt_tmpl new file mode 100644 index 000000000..0ddebfc3e --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/README.txt_tmpl @@ -0,0 +1,4 @@ +{{project}} README + + + diff --git a/pyramid/paster_templates/routesalchemy/setup.cfg_tmpl b/pyramid/paster_templates/routesalchemy/setup.cfg_tmpl new file mode 100644 index 000000000..5bec29823 --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/setup.cfg_tmpl @@ -0,0 +1,27 @@ +[nosetests] +match=^test +nocapture=1 +cover-package={{package}} +with-coverage=1 +cover-erase=1 + +[compile_catalog] +directory = {{package}}/locale +domain = {{project}} +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = {{package}}/locale/{{project}}.pot +width = 80 + +[init_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale + +[update_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale +previous = true diff --git a/pyramid/paster_templates/routesalchemy/setup.py_tmpl b/pyramid/paster_templates/routesalchemy/setup.py_tmpl new file mode 100644 index 000000000..0ce1b8bad --- /dev/null +++ b/pyramid/paster_templates/routesalchemy/setup.py_tmpl @@ -0,0 +1,45 @@ +import os +import sys + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, 'README.txt')).read() +CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() + +requires = [ + 'repoze.bfg', + 'SQLAlchemy', + 'transaction', + 'repoze.tm2', + 'zope.sqlalchemy', + ] + +if sys.version_info[:3] < (2,5,0): + requires.append('pysqlite') + +setup(name='{{project}}', + version='0.0', + description='{{project}}', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: BFG", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web wsgi bfg', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + test_suite='{{package}}', + install_requires = requires, + entry_points = """\ + [paste.app_factory] + app = {{package}}.run:app + """ + ) + diff --git a/pyramid/paster_templates/starter/+package+/__init__.py b/pyramid/paster_templates/starter/+package+/__init__.py new file mode 100644 index 000000000..cbdfd3ac6 --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/__init__.py @@ -0,0 +1,2 @@ +# A package + diff --git a/pyramid/paster_templates/starter/+package+/configure.zcml b/pyramid/paster_templates/starter/+package+/configure.zcml new file mode 100644 index 000000000..e83dd3933 --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/configure.zcml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/pyramid/paster_templates/starter/+package+/models.py b/pyramid/paster_templates/starter/+package+/models.py new file mode 100644 index 000000000..75dec7505 --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/models.py @@ -0,0 +1,7 @@ +class MyModel(object): + pass + +root = MyModel() + +def get_root(request): + return root diff --git a/pyramid/paster_templates/starter/+package+/run.py_tmpl b/pyramid/paster_templates/starter/+package+/run.py_tmpl new file mode 100644 index 000000000..74876f3f4 --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/run.py_tmpl @@ -0,0 +1,15 @@ +from repoze.bfg.configuration import Configurator +from {{package}}.models import get_root + +def app(global_config, **settings): + """ This function returns a WSGI application. + + It is usually called by the PasteDeploy framework during + ``paster serve``. + """ + zcml_file = settings.get('configure_zcml', 'configure.zcml') + config = Configurator(root_factory=get_root, settings=settings) + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() diff --git a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt new file mode 100644 index 000000000..2aedcad9f --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt @@ -0,0 +1,99 @@ + + + + +${project} Application + + + + + + + + + +
+ +
+ +
+
+

Welcome to ${project}, an + application generated by the repoze.bfg web + application framework.

+
+
+ + + + +
 
+
+
+ + + + + + diff --git a/pyramid/paster_templates/starter/+package+/templates/static/default.css b/pyramid/paster_templates/starter/+package+/templates/static/default.css new file mode 100644 index 000000000..41b3debde --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/templates/static/default.css @@ -0,0 +1,380 @@ +/* +Design by Free CSS Templates +http://www.freecsstemplates.org +Released for free under a Creative Commons Attribution 2.5 License +*/ + +body { + margin: 0; + padding: 0; + background: url(images/img01.gif) repeat-x left top; + font-size: 13px; + font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; + text-align: justify; + color: #FFFFFF; +} + +h1, h2, h3 { + margin: 0; + text-transform: lowercase; + font-weight: normal; + color: #FFFFFF; +} + +h1 { + letter-spacing: -1px; + font-size: 32px; +} + +h2 { + font-size: 23px; +} + +p, ul, ol { + margin: 0 0 2em 0; + text-align: justify; + line-height: 26px; +} + +a:link { + color: #8BD80E; +} + +a:hover, a:active { + text-decoration: none; + color: #8BD80E; +} + +a:visited { + color: #8BD80E; +} + +img { + border: none; +} + +img.left { + float: left; + margin-right: 15px; +} + +img.right { + float: right; + margin-left: 15px; +} + +/* Form */ + +form { + margin: 0; + padding: 0; +} + +fieldset { + margin: 0; + padding: 0; + border: none; +} + +legend { + display: none; +} + +input, textarea, select { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 13px; + color: #333333; +} + +#wrapper { + margin: 0; + padding: 0; + background: #000000; +} + +/* Header */ + +#header { + width: 713px; + margin: 0 auto; + height: 42px; +} + +/* Menu */ + +#menu { + float: left; + width: 713px; + height: 50px; + background: url(images/img02.gif) no-repeat left top; +} + +#menu ul { + margin: 0; + padding: 0px 0 0 10px; + list-style: none; + line-height: normal; +} + +#menu li { + display: block; + float: left; +} + +#menu a { + display: block; + float: left; + background: url(images/img04.gif) no-repeat right 55%; + margin-top: 5px; + margin-right: 3px; + padding: 8px 17px; + text-decoration: none; + font-size: 13px; + color: #000000; +} + +#menu a:hover { + color: #000000; +} + +#menu .current_page_item a { + color: #000000; +} + +/** LOGO */ + +#logo { + width: 713px; + height: 80px; + margin: 0 auto; +} + +#logo h1, #logo h2 { + float: left; + margin: 0; + padding: 30px 0 0 0px; + line-height: normal; +} + +#logo h1 { + font-family: Georgia, "Times New Roman", Times, serif; + font-size:40px; +} + +#logo h1 a { + text-decoration: none; + color: #4C4C4C; +} + +#logo h1 a:hover { text-decoration: underline; } + +#logo h2 { + float: left; + padding: 45px 0 0 18px; + font: 18px Georgia, "Times New Roman", Times, serif; + color: #8BD80E; +} + +#logo p a { + text-decoration: none; + color: #8BD80E; +} + +#logo p a:hover { text-decoration: underline; } + + + +/* Page */ + +#page { + width: 663px; + margin: 0 auto; + background: #4C4C4C url(images/img03.gif) no-repeat left bottom; + padding: 0 25px; +} + +/* Content */ + +#content { + float: left; + width: 410px; + +} + +/* Post */ + +.post { + padding: 15px 0px; + margin-bottom: 20px; +} + +.post .title { + margin-bottom: 20px; + padding-bottom: 5px; +} + +.post h1 { + padding: 0px 0 0 0px; + background: url(images/img08.jpg) no-repeat left top; + font-size: 24px; + color: #FFFFFF; +} + +.post h2 { + padding: 0px 0 0 0px; + font-size: 22px; + color: #FFFFFF; +} + +.post .entry { +} + +.post .meta { + padding: 15px 15px 30px 0px; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; +} + +.post .meta p { + margin: 0; + padding-top: 15px; + line-height: normal; + color: #FFFFFF; +} + +.post .meta .byline { + float: left; +} + +.post .meta .links { + float: right; +} + +.post .meta .more { + padding: 0 10px 0 18px; +} + +.post .meta .comments { +} + +.post .meta b { + display: none; +} + + +/* Sidebar */ + +#sidebar { + width: 210px; + float: right; + margin: 0; + padding: 0; +} + +#sidebar ul { + margin: 0; + padding: 0; + list-style: none; +} + +#sidebar li { + margin-bottom: 40px; +} + +#sidebar li ul { +} + +#sidebar li li { + margin: 0; +} + +#sidebar h2 { + width: 250px; + padding: 8px 0 0 0px; + margin-bottom: 10px; + background: url(images/img07.jpg) no-repeat left top; + font-size: 20px; + color: #FFFFFF; +} + +/* Search */ + +#search { + +} + +#search h2 { + margin-bottom: 20px; +} + +#s { + width: 140px; + margin-right: 5px; + padding: 3px; + border: 1px solid #BED99C; +} + +#x { + padding: 3px; + border: none; + background: #8BD80E; + text-transform: lowercase; + font-size: 11px; + color: #FFFFFF; +} + +/* Boxes */ + +.box1 { + padding: 20px; +} + +.box2 { + color: #BABABA; +} + +.box2 h2 { + margin-bottom: 15px; + font-size: 16px; + color: #FFFFFF; +} + +.box2 ul { + margin: 0; + padding: 0; + list-style: none; +} + +.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { + color: #EDEDED; +} + +/* Footer */ +#footer-wrap { +} + +#footer { + margin: 0 auto; + padding: 20px 0 10px 0; + background: #000000; +} + +html>body #footer { + height: auto; +} + +#footer p { + font-size: 11px; +} + +#legal { + clear: both; + padding-top: 17px; + text-align: center; + color: #FFFFFF; +} + +#legal a { + font-weight: normal; + color: #FFFFFF; +} diff --git a/pyramid/paster_templates/starter/+package+/templates/static/images/img01.gif b/pyramid/paster_templates/starter/+package+/templates/static/images/img01.gif new file mode 100644 index 000000000..5f082bd99 Binary files /dev/null and b/pyramid/paster_templates/starter/+package+/templates/static/images/img01.gif differ diff --git a/pyramid/paster_templates/starter/+package+/templates/static/images/img02.gif b/pyramid/paster_templates/starter/+package+/templates/static/images/img02.gif new file mode 100644 index 000000000..45a3ae976 Binary files /dev/null and b/pyramid/paster_templates/starter/+package+/templates/static/images/img02.gif differ diff --git a/pyramid/paster_templates/starter/+package+/templates/static/images/img03.gif b/pyramid/paster_templates/starter/+package+/templates/static/images/img03.gif new file mode 100644 index 000000000..d92ea38f9 Binary files /dev/null and b/pyramid/paster_templates/starter/+package+/templates/static/images/img03.gif differ diff --git a/pyramid/paster_templates/starter/+package+/templates/static/images/img04.gif b/pyramid/paster_templates/starter/+package+/templates/static/images/img04.gif new file mode 100644 index 000000000..950c4af9d Binary files /dev/null and b/pyramid/paster_templates/starter/+package+/templates/static/images/img04.gif differ diff --git a/pyramid/paster_templates/starter/+package+/templates/static/images/spacer.gif b/pyramid/paster_templates/starter/+package+/templates/static/images/spacer.gif new file mode 100644 index 000000000..5bfd67a2d Binary files /dev/null and b/pyramid/paster_templates/starter/+package+/templates/static/images/spacer.gif differ diff --git a/pyramid/paster_templates/starter/+package+/templates/static/templatelicense.txt b/pyramid/paster_templates/starter/+package+/templates/static/templatelicense.txt new file mode 100644 index 000000000..ccb6b06ab --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/templates/static/templatelicense.txt @@ -0,0 +1,243 @@ +Creative Commons + +Creative Commons Legal Code + +*Attribution 2.5* + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE +LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN +ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION +ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE +INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM +ITS USE. + +/License/ + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE +RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS +AND CONDITIONS. + +*1. Definitions* + + 1. *"Collective Work"* means a work, such as a periodical issue, + anthology or encyclopedia, in which the Work in its entirety in + unmodified form, along with a number of other contributions, + constituting separate and independent works in themselves, are + assembled into a collective whole. A work that constitutes a + Collective Work will not be considered a Derivative Work (as + defined below) for the purposes of this License. + 2. *"Derivative Work"* means a work based upon the Work or upon the + Work and other pre-existing works, such as a translation, musical + arrangement, dramatization, fictionalization, motion picture + version, sound recording, art reproduction, abridgment, + condensation, or any other form in which the Work may be recast, + transformed, or adapted, except that a work that constitutes a + Collective Work will not be considered a Derivative Work for the + purpose of this License. For the avoidance of doubt, where the + Work is a musical composition or sound recording, the + synchronization of the Work in timed-relation with a moving image + ("synching") will be considered a Derivative Work for the purpose + of this License. + 3. *"Licensor"* means the individual or entity that offers the Work + under the terms of this License. + 4. *"Original Author"* means the individual or entity who created the + Work. + 5. *"Work"* means the copyrightable work of authorship offered under + the terms of this License. + 6. *"You"* means an individual or entity exercising rights under this + License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission + from the Licensor to exercise rights under this License despite a + previous violation. + +*2. Fair Use Rights.* Nothing in this license is intended to reduce, +limit, or restrict any rights arising from fair use, first sale or other +limitations on the exclusive rights of the copyright owner under +copyright law or other applicable laws. + +*3. License Grant.* Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + 1. to reproduce the Work, to incorporate the Work into one or more + Collective Works, and to reproduce the Work as incorporated in the + Collective Works; + 2. to create and reproduce Derivative Works; + 3. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission the Work including as incorporated in Collective Works; + 4. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission Derivative Works. + 5. + + For the avoidance of doubt, where the work is a musical composition: + + 1. *Performance Royalties Under Blanket Licenses*. Licensor + waives the exclusive right to collect, whether individually + or via a performance rights society (e.g. ASCAP, BMI, + SESAC), royalties for the public performance or public + digital performance (e.g. webcast) of the Work. + 2. *Mechanical Rights and Statutory Royalties*. Licensor waives + the exclusive right to collect, whether individually or via + a music rights agency or designated agent (e.g. Harry Fox + Agency), royalties for any phonorecord You create from the + Work ("cover version") and distribute, subject to the + compulsory license created by 17 USC Section 115 of the US + Copyright Act (or the equivalent in other jurisdictions). + 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of + doubt, where the Work is a sound recording, Licensor waives the + exclusive right to collect, whether individually or via a + performance-rights society (e.g. SoundExchange), royalties for the + public digital performance (e.g. webcast) of the Work, subject to + the compulsory license created by 17 USC Section 114 of the US + Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights +in other media and formats. All rights not expressly granted by Licensor +are hereby reserved. + +*4. Restrictions.*The license granted in Section 3 above is expressly +made subject to and limited by the following restrictions: + + 1. You may distribute, publicly display, publicly perform, or + publicly digitally perform the Work only under the terms of this + License, and You must include a copy of, or the Uniform Resource + Identifier for, this License with every copy or phonorecord of the + Work You distribute, publicly display, publicly perform, or + publicly digitally perform. You may not offer or impose any terms + on the Work that alter or restrict the terms of this License or + the recipients' exercise of the rights granted hereunder. You may + not sublicense the Work. You must keep intact all notices that + refer to this License and to the disclaimer of warranties. You may + not distribute, publicly display, publicly perform, or publicly + digitally perform the Work with any technological measures that + control access or use of the Work in a manner inconsistent with + the terms of this License Agreement. The above applies to the Work + as incorporated in a Collective Work, but this does not require + the Collective Work apart from the Work itself to be made subject + to the terms of this License. If You create a Collective Work, + upon notice from any Licensor You must, to the extent practicable, + remove from the Collective Work any credit as required by clause + 4(b), as requested. If You create a Derivative Work, upon notice + from any Licensor You must, to the extent practicable, remove from + the Derivative Work any credit as required by clause 4(b), as + requested. + 2. If you distribute, publicly display, publicly perform, or publicly + digitally perform the Work or any Derivative Works or Collective + Works, You must keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) + the name of the Original Author (or pseudonym, if applicable) if + supplied, and/or (ii) if the Original Author and/or Licensor + designate another party or parties (e.g. a sponsor institute, + publishing entity, journal) for attribution in Licensor's + copyright notice, terms of service or by other reasonable means, + the name of such party or parties; the title of the Work if + supplied; to the extent reasonably practicable, the Uniform + Resource Identifier, if any, that Licensor specifies to be + associated with the Work, unless such URI does not refer to the + copyright notice or licensing information for the Work; and in the + case of a Derivative Work, a credit identifying the use of the + Work in the Derivative Work (e.g., "French translation of the Work + by Original Author," or "Screenplay based on original Work by + Original Author"). Such credit may be implemented in any + reasonable manner; provided, however, that in the case of a + Derivative Work or Collective Work, at a minimum such credit will + appear where any other comparable authorship credit appears and in + a manner at least as prominent as such other comparable authorship + credit. + +*5. Representations, Warranties and Disclaimer* + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE +EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY +APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL +THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY +DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF +LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +*7. Termination* + + 1. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Derivative Works or + Collective Works from You under this License, however, will not + have their licenses terminated provided such individuals or + entities remain in full compliance with those licenses. Sections + 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted + here is perpetual (for the duration of the applicable copyright in + the Work). Notwithstanding the above, Licensor reserves the right + to release the Work under different license terms or to stop + distributing the Work at any time; provided, however that any such + election will not serve to withdraw this License (or any other + license that has been, or is required to be, granted under the + terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +*8. Miscellaneous* + + 1. Each time You distribute or publicly digitally perform the Work or + a Collective Work, the Licensor offers to the recipient a license + to the Work on the same terms and conditions as the license + granted to You under this License. + 2. Each time You distribute or publicly digitally perform a + Derivative Work, Licensor offers to the recipient a license to the + original Work on the same terms and conditions as the license + granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this License, and without further + action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision + valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in + writing and signed by the party to be charged with such waiver or + consent. + 5. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no + understandings, agreements or representations with respect to the + Work not specified here. Licensor shall not be bound by any + additional provisions that may appear in any communication from + You. This License may not be modified without the mutual written + agreement of the Licensor and You. + +Creative Commons is not a party to this License, and makes no warranty +whatsoever in connection with the Work. Creative Commons will not be +liable to You or any party on any legal theory for any damages +whatsoever, including without limitation any general, special, +incidental or consequential damages arising in connection to this +license. Notwithstanding the foregoing two (2) sentences, if Creative +Commons has expressly identified itself as the Licensor hereunder, it +shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work +is licensed under the CCPL, neither party will use the trademark +"Creative Commons" or any related trademark or logo of Creative Commons +without the prior written consent of Creative Commons. Any permitted use +will be in compliance with Creative Commons' then-current trademark +usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. + +Creative Commons may be contacted at http://creativecommons.org/ +. + +« Back to Commons Deed <./> diff --git a/pyramid/paster_templates/starter/+package+/tests.py_tmpl b/pyramid/paster_templates/starter/+package+/tests.py_tmpl new file mode 100644 index 000000000..578a58d35 --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/tests.py_tmpl @@ -0,0 +1,20 @@ +import unittest + +from repoze.bfg.configuration import Configurator +from repoze.bfg import testing + +class ViewTests(unittest.TestCase): + def setUp(self): + self.config = Configurator() + self.config.begin() + + def tearDown(self): + self.config.end() + + def test_my_view(self): + from {{package}}.views import my_view + request = testing.DummyRequest() + info = my_view(request) + self.assertEqual(info['project'], '{{project}}') + + diff --git a/pyramid/paster_templates/starter/+package+/views.py_tmpl b/pyramid/paster_templates/starter/+package+/views.py_tmpl new file mode 100644 index 000000000..12ed8832d --- /dev/null +++ b/pyramid/paster_templates/starter/+package+/views.py_tmpl @@ -0,0 +1,2 @@ +def my_view(request): + return {'project':'{{project}}'} diff --git a/pyramid/paster_templates/starter/+project+.ini_tmpl b/pyramid/paster_templates/starter/+project+.ini_tmpl new file mode 100644 index 000000000..9bdeec1ae --- /dev/null +++ b/pyramid/paster_templates/starter/+project+.ini_tmpl @@ -0,0 +1,15 @@ +[DEFAULT] +debug = true + +[app:main] +use = egg:{{project}}#app +reload_templates = true +debug_authorization = false +debug_notfound = false +debug_templates = true +default_locale_name = en + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 6543 diff --git a/pyramid/paster_templates/starter/CHANGES.txt_tmpl b/pyramid/paster_templates/starter/CHANGES.txt_tmpl new file mode 100644 index 000000000..35a34f332 --- /dev/null +++ b/pyramid/paster_templates/starter/CHANGES.txt_tmpl @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/pyramid/paster_templates/starter/README.txt_tmpl b/pyramid/paster_templates/starter/README.txt_tmpl new file mode 100644 index 000000000..0ddebfc3e --- /dev/null +++ b/pyramid/paster_templates/starter/README.txt_tmpl @@ -0,0 +1,4 @@ +{{project}} README + + + diff --git a/pyramid/paster_templates/starter/setup.cfg_tmpl b/pyramid/paster_templates/starter/setup.cfg_tmpl new file mode 100644 index 000000000..5bec29823 --- /dev/null +++ b/pyramid/paster_templates/starter/setup.cfg_tmpl @@ -0,0 +1,27 @@ +[nosetests] +match=^test +nocapture=1 +cover-package={{package}} +with-coverage=1 +cover-erase=1 + +[compile_catalog] +directory = {{package}}/locale +domain = {{project}} +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = {{package}}/locale/{{project}}.pot +width = 80 + +[init_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale + +[update_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale +previous = true diff --git a/pyramid/paster_templates/starter/setup.py_tmpl b/pyramid/paster_templates/starter/setup.py_tmpl new file mode 100644 index 000000000..df1206c1e --- /dev/null +++ b/pyramid/paster_templates/starter/setup.py_tmpl @@ -0,0 +1,36 @@ +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, 'README.txt')).read() +CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() + +requires = ['repoze.bfg'] + +setup(name='{{project}}', + version='0.0', + description='{{project}}', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: BFG", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web wsgi bfg', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=requires, + tests_require=requires, + test_suite="{{package}}", + entry_points = """\ + [paste.app_factory] + app = {{package}}.run:app + """ + ) + diff --git a/pyramid/paster_templates/zodb/+package+/__init__.py b/pyramid/paster_templates/zodb/+package+/__init__.py new file mode 100644 index 000000000..cbdfd3ac6 --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/__init__.py @@ -0,0 +1,2 @@ +# A package + diff --git a/pyramid/paster_templates/zodb/+package+/configure.zcml b/pyramid/paster_templates/zodb/+package+/configure.zcml new file mode 100644 index 000000000..e83dd3933 --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/configure.zcml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/pyramid/paster_templates/zodb/+package+/models.py b/pyramid/paster_templates/zodb/+package+/models.py new file mode 100644 index 000000000..8dd0f5a49 --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/models.py @@ -0,0 +1,12 @@ +from persistent.mapping import PersistentMapping + +class MyModel(PersistentMapping): + __parent__ = __name__ = None + +def appmaker(zodb_root): + if not 'app_root' in zodb_root: + app_root = MyModel() + zodb_root['app_root'] = app_root + import transaction + transaction.commit() + return zodb_root['app_root'] diff --git a/pyramid/paster_templates/zodb/+package+/run.py_tmpl b/pyramid/paster_templates/zodb/+package+/run.py_tmpl new file mode 100644 index 000000000..c0a245089 --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/run.py_tmpl @@ -0,0 +1,23 @@ +from repoze.bfg.configuration import Configurator +from repoze.zodbconn.finder import PersistentApplicationFinder +from {{package}}.models import appmaker + +def app(global_config, **settings): + """ This function returns a WSGI application. + + It is usually called by the PasteDeploy framework during + ``paster serve``. + """ + zodb_uri = settings.get('zodb_uri') + zcml_file = settings.get('configure_zcml', 'configure.zcml') + if zodb_uri is None: + raise ValueError("No 'zodb_uri' in application configuration.") + + finder = PersistentApplicationFinder(zodb_uri, appmaker) + def get_root(request): + return finder(request.environ) + config = Configurator(root_factory=get_root, settings=settings) + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() diff --git a/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt b/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt new file mode 100644 index 000000000..2aedcad9f --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/templates/mytemplate.pt @@ -0,0 +1,99 @@ + + + + +${project} Application + + + + + + + + + +
+ +
+ +
+
+

Welcome to ${project}, an + application generated by the repoze.bfg web + application framework.

+
+
+ + + + +
 
+
+
+ + + + + + diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/default.css b/pyramid/paster_templates/zodb/+package+/templates/static/default.css new file mode 100644 index 000000000..41b3debde --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/templates/static/default.css @@ -0,0 +1,380 @@ +/* +Design by Free CSS Templates +http://www.freecsstemplates.org +Released for free under a Creative Commons Attribution 2.5 License +*/ + +body { + margin: 0; + padding: 0; + background: url(images/img01.gif) repeat-x left top; + font-size: 13px; + font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; + text-align: justify; + color: #FFFFFF; +} + +h1, h2, h3 { + margin: 0; + text-transform: lowercase; + font-weight: normal; + color: #FFFFFF; +} + +h1 { + letter-spacing: -1px; + font-size: 32px; +} + +h2 { + font-size: 23px; +} + +p, ul, ol { + margin: 0 0 2em 0; + text-align: justify; + line-height: 26px; +} + +a:link { + color: #8BD80E; +} + +a:hover, a:active { + text-decoration: none; + color: #8BD80E; +} + +a:visited { + color: #8BD80E; +} + +img { + border: none; +} + +img.left { + float: left; + margin-right: 15px; +} + +img.right { + float: right; + margin-left: 15px; +} + +/* Form */ + +form { + margin: 0; + padding: 0; +} + +fieldset { + margin: 0; + padding: 0; + border: none; +} + +legend { + display: none; +} + +input, textarea, select { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 13px; + color: #333333; +} + +#wrapper { + margin: 0; + padding: 0; + background: #000000; +} + +/* Header */ + +#header { + width: 713px; + margin: 0 auto; + height: 42px; +} + +/* Menu */ + +#menu { + float: left; + width: 713px; + height: 50px; + background: url(images/img02.gif) no-repeat left top; +} + +#menu ul { + margin: 0; + padding: 0px 0 0 10px; + list-style: none; + line-height: normal; +} + +#menu li { + display: block; + float: left; +} + +#menu a { + display: block; + float: left; + background: url(images/img04.gif) no-repeat right 55%; + margin-top: 5px; + margin-right: 3px; + padding: 8px 17px; + text-decoration: none; + font-size: 13px; + color: #000000; +} + +#menu a:hover { + color: #000000; +} + +#menu .current_page_item a { + color: #000000; +} + +/** LOGO */ + +#logo { + width: 713px; + height: 80px; + margin: 0 auto; +} + +#logo h1, #logo h2 { + float: left; + margin: 0; + padding: 30px 0 0 0px; + line-height: normal; +} + +#logo h1 { + font-family: Georgia, "Times New Roman", Times, serif; + font-size:40px; +} + +#logo h1 a { + text-decoration: none; + color: #4C4C4C; +} + +#logo h1 a:hover { text-decoration: underline; } + +#logo h2 { + float: left; + padding: 45px 0 0 18px; + font: 18px Georgia, "Times New Roman", Times, serif; + color: #8BD80E; +} + +#logo p a { + text-decoration: none; + color: #8BD80E; +} + +#logo p a:hover { text-decoration: underline; } + + + +/* Page */ + +#page { + width: 663px; + margin: 0 auto; + background: #4C4C4C url(images/img03.gif) no-repeat left bottom; + padding: 0 25px; +} + +/* Content */ + +#content { + float: left; + width: 410px; + +} + +/* Post */ + +.post { + padding: 15px 0px; + margin-bottom: 20px; +} + +.post .title { + margin-bottom: 20px; + padding-bottom: 5px; +} + +.post h1 { + padding: 0px 0 0 0px; + background: url(images/img08.jpg) no-repeat left top; + font-size: 24px; + color: #FFFFFF; +} + +.post h2 { + padding: 0px 0 0 0px; + font-size: 22px; + color: #FFFFFF; +} + +.post .entry { +} + +.post .meta { + padding: 15px 15px 30px 0px; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; +} + +.post .meta p { + margin: 0; + padding-top: 15px; + line-height: normal; + color: #FFFFFF; +} + +.post .meta .byline { + float: left; +} + +.post .meta .links { + float: right; +} + +.post .meta .more { + padding: 0 10px 0 18px; +} + +.post .meta .comments { +} + +.post .meta b { + display: none; +} + + +/* Sidebar */ + +#sidebar { + width: 210px; + float: right; + margin: 0; + padding: 0; +} + +#sidebar ul { + margin: 0; + padding: 0; + list-style: none; +} + +#sidebar li { + margin-bottom: 40px; +} + +#sidebar li ul { +} + +#sidebar li li { + margin: 0; +} + +#sidebar h2 { + width: 250px; + padding: 8px 0 0 0px; + margin-bottom: 10px; + background: url(images/img07.jpg) no-repeat left top; + font-size: 20px; + color: #FFFFFF; +} + +/* Search */ + +#search { + +} + +#search h2 { + margin-bottom: 20px; +} + +#s { + width: 140px; + margin-right: 5px; + padding: 3px; + border: 1px solid #BED99C; +} + +#x { + padding: 3px; + border: none; + background: #8BD80E; + text-transform: lowercase; + font-size: 11px; + color: #FFFFFF; +} + +/* Boxes */ + +.box1 { + padding: 20px; +} + +.box2 { + color: #BABABA; +} + +.box2 h2 { + margin-bottom: 15px; + font-size: 16px; + color: #FFFFFF; +} + +.box2 ul { + margin: 0; + padding: 0; + list-style: none; +} + +.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { + color: #EDEDED; +} + +/* Footer */ +#footer-wrap { +} + +#footer { + margin: 0 auto; + padding: 20px 0 10px 0; + background: #000000; +} + +html>body #footer { + height: auto; +} + +#footer p { + font-size: 11px; +} + +#legal { + clear: both; + padding-top: 17px; + text-align: center; + color: #FFFFFF; +} + +#legal a { + font-weight: normal; + color: #FFFFFF; +} diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/images/img01.gif b/pyramid/paster_templates/zodb/+package+/templates/static/images/img01.gif new file mode 100644 index 000000000..5f082bd99 Binary files /dev/null and b/pyramid/paster_templates/zodb/+package+/templates/static/images/img01.gif differ diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/images/img02.gif b/pyramid/paster_templates/zodb/+package+/templates/static/images/img02.gif new file mode 100644 index 000000000..45a3ae976 Binary files /dev/null and b/pyramid/paster_templates/zodb/+package+/templates/static/images/img02.gif differ diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/images/img03.gif b/pyramid/paster_templates/zodb/+package+/templates/static/images/img03.gif new file mode 100644 index 000000000..d92ea38f9 Binary files /dev/null and b/pyramid/paster_templates/zodb/+package+/templates/static/images/img03.gif differ diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/images/img04.gif b/pyramid/paster_templates/zodb/+package+/templates/static/images/img04.gif new file mode 100644 index 000000000..950c4af9d Binary files /dev/null and b/pyramid/paster_templates/zodb/+package+/templates/static/images/img04.gif differ diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/images/spacer.gif b/pyramid/paster_templates/zodb/+package+/templates/static/images/spacer.gif new file mode 100644 index 000000000..5bfd67a2d Binary files /dev/null and b/pyramid/paster_templates/zodb/+package+/templates/static/images/spacer.gif differ diff --git a/pyramid/paster_templates/zodb/+package+/templates/static/templatelicense.txt b/pyramid/paster_templates/zodb/+package+/templates/static/templatelicense.txt new file mode 100644 index 000000000..ccb6b06ab --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/templates/static/templatelicense.txt @@ -0,0 +1,243 @@ +Creative Commons + +Creative Commons Legal Code + +*Attribution 2.5* + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE +LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN +ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION +ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE +INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM +ITS USE. + +/License/ + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE +RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS +AND CONDITIONS. + +*1. Definitions* + + 1. *"Collective Work"* means a work, such as a periodical issue, + anthology or encyclopedia, in which the Work in its entirety in + unmodified form, along with a number of other contributions, + constituting separate and independent works in themselves, are + assembled into a collective whole. A work that constitutes a + Collective Work will not be considered a Derivative Work (as + defined below) for the purposes of this License. + 2. *"Derivative Work"* means a work based upon the Work or upon the + Work and other pre-existing works, such as a translation, musical + arrangement, dramatization, fictionalization, motion picture + version, sound recording, art reproduction, abridgment, + condensation, or any other form in which the Work may be recast, + transformed, or adapted, except that a work that constitutes a + Collective Work will not be considered a Derivative Work for the + purpose of this License. For the avoidance of doubt, where the + Work is a musical composition or sound recording, the + synchronization of the Work in timed-relation with a moving image + ("synching") will be considered a Derivative Work for the purpose + of this License. + 3. *"Licensor"* means the individual or entity that offers the Work + under the terms of this License. + 4. *"Original Author"* means the individual or entity who created the + Work. + 5. *"Work"* means the copyrightable work of authorship offered under + the terms of this License. + 6. *"You"* means an individual or entity exercising rights under this + License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission + from the Licensor to exercise rights under this License despite a + previous violation. + +*2. Fair Use Rights.* Nothing in this license is intended to reduce, +limit, or restrict any rights arising from fair use, first sale or other +limitations on the exclusive rights of the copyright owner under +copyright law or other applicable laws. + +*3. License Grant.* Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + 1. to reproduce the Work, to incorporate the Work into one or more + Collective Works, and to reproduce the Work as incorporated in the + Collective Works; + 2. to create and reproduce Derivative Works; + 3. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission the Work including as incorporated in Collective Works; + 4. to distribute copies or phonorecords of, display publicly, perform + publicly, and perform publicly by means of a digital audio + transmission Derivative Works. + 5. + + For the avoidance of doubt, where the work is a musical composition: + + 1. *Performance Royalties Under Blanket Licenses*. Licensor + waives the exclusive right to collect, whether individually + or via a performance rights society (e.g. ASCAP, BMI, + SESAC), royalties for the public performance or public + digital performance (e.g. webcast) of the Work. + 2. *Mechanical Rights and Statutory Royalties*. Licensor waives + the exclusive right to collect, whether individually or via + a music rights agency or designated agent (e.g. Harry Fox + Agency), royalties for any phonorecord You create from the + Work ("cover version") and distribute, subject to the + compulsory license created by 17 USC Section 115 of the US + Copyright Act (or the equivalent in other jurisdictions). + 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of + doubt, where the Work is a sound recording, Licensor waives the + exclusive right to collect, whether individually or via a + performance-rights society (e.g. SoundExchange), royalties for the + public digital performance (e.g. webcast) of the Work, subject to + the compulsory license created by 17 USC Section 114 of the US + Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights +in other media and formats. All rights not expressly granted by Licensor +are hereby reserved. + +*4. Restrictions.*The license granted in Section 3 above is expressly +made subject to and limited by the following restrictions: + + 1. You may distribute, publicly display, publicly perform, or + publicly digitally perform the Work only under the terms of this + License, and You must include a copy of, or the Uniform Resource + Identifier for, this License with every copy or phonorecord of the + Work You distribute, publicly display, publicly perform, or + publicly digitally perform. You may not offer or impose any terms + on the Work that alter or restrict the terms of this License or + the recipients' exercise of the rights granted hereunder. You may + not sublicense the Work. You must keep intact all notices that + refer to this License and to the disclaimer of warranties. You may + not distribute, publicly display, publicly perform, or publicly + digitally perform the Work with any technological measures that + control access or use of the Work in a manner inconsistent with + the terms of this License Agreement. The above applies to the Work + as incorporated in a Collective Work, but this does not require + the Collective Work apart from the Work itself to be made subject + to the terms of this License. If You create a Collective Work, + upon notice from any Licensor You must, to the extent practicable, + remove from the Collective Work any credit as required by clause + 4(b), as requested. If You create a Derivative Work, upon notice + from any Licensor You must, to the extent practicable, remove from + the Derivative Work any credit as required by clause 4(b), as + requested. + 2. If you distribute, publicly display, publicly perform, or publicly + digitally perform the Work or any Derivative Works or Collective + Works, You must keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) + the name of the Original Author (or pseudonym, if applicable) if + supplied, and/or (ii) if the Original Author and/or Licensor + designate another party or parties (e.g. a sponsor institute, + publishing entity, journal) for attribution in Licensor's + copyright notice, terms of service or by other reasonable means, + the name of such party or parties; the title of the Work if + supplied; to the extent reasonably practicable, the Uniform + Resource Identifier, if any, that Licensor specifies to be + associated with the Work, unless such URI does not refer to the + copyright notice or licensing information for the Work; and in the + case of a Derivative Work, a credit identifying the use of the + Work in the Derivative Work (e.g., "French translation of the Work + by Original Author," or "Screenplay based on original Work by + Original Author"). Such credit may be implemented in any + reasonable manner; provided, however, that in the case of a + Derivative Work or Collective Work, at a minimum such credit will + appear where any other comparable authorship credit appears and in + a manner at least as prominent as such other comparable authorship + credit. + +*5. Representations, Warranties and Disclaimer* + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE +EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY +APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL +THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY +DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF +LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +*7. Termination* + + 1. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Derivative Works or + Collective Works from You under this License, however, will not + have their licenses terminated provided such individuals or + entities remain in full compliance with those licenses. Sections + 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted + here is perpetual (for the duration of the applicable copyright in + the Work). Notwithstanding the above, Licensor reserves the right + to release the Work under different license terms or to stop + distributing the Work at any time; provided, however that any such + election will not serve to withdraw this License (or any other + license that has been, or is required to be, granted under the + terms of this License), and this License will continue in full + force and effect unless terminated as stated above. + +*8. Miscellaneous* + + 1. Each time You distribute or publicly digitally perform the Work or + a Collective Work, the Licensor offers to the recipient a license + to the Work on the same terms and conditions as the license + granted to You under this License. + 2. Each time You distribute or publicly digitally perform a + Derivative Work, Licensor offers to the recipient a license to the + original Work on the same terms and conditions as the license + granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this License, and without further + action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision + valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in + writing and signed by the party to be charged with such waiver or + consent. + 5. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no + understandings, agreements or representations with respect to the + Work not specified here. Licensor shall not be bound by any + additional provisions that may appear in any communication from + You. This License may not be modified without the mutual written + agreement of the Licensor and You. + +Creative Commons is not a party to this License, and makes no warranty +whatsoever in connection with the Work. Creative Commons will not be +liable to You or any party on any legal theory for any damages +whatsoever, including without limitation any general, special, +incidental or consequential damages arising in connection to this +license. Notwithstanding the foregoing two (2) sentences, if Creative +Commons has expressly identified itself as the Licensor hereunder, it +shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work +is licensed under the CCPL, neither party will use the trademark +"Creative Commons" or any related trademark or logo of Creative Commons +without the prior written consent of Creative Commons. Any permitted use +will be in compliance with Creative Commons' then-current trademark +usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. + +Creative Commons may be contacted at http://creativecommons.org/ +. + +« Back to Commons Deed <./> diff --git a/pyramid/paster_templates/zodb/+package+/tests.py_tmpl b/pyramid/paster_templates/zodb/+package+/tests.py_tmpl new file mode 100644 index 000000000..30da5c9b3 --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/tests.py_tmpl @@ -0,0 +1,19 @@ +import unittest + +from repoze.bfg.configuration import Configurator +from repoze.bfg import testing + +class ViewTests(unittest.TestCase): + def setUp(self): + self.config = Configurator() + self.config.begin() + + def tearDown(self): + self.config.end() + + def test_my_view(self): + from {{package}}.views import my_view + request = testing.DummyRequest() + info = my_view(request) + self.assertEqual(info['project'], '{{project}}') + diff --git a/pyramid/paster_templates/zodb/+package+/views.py_tmpl b/pyramid/paster_templates/zodb/+package+/views.py_tmpl new file mode 100644 index 000000000..12ed8832d --- /dev/null +++ b/pyramid/paster_templates/zodb/+package+/views.py_tmpl @@ -0,0 +1,2 @@ +def my_view(request): + return {'project':'{{project}}'} diff --git a/pyramid/paster_templates/zodb/+project+.ini_tmpl b/pyramid/paster_templates/zodb/+project+.ini_tmpl new file mode 100644 index 000000000..993cec596 --- /dev/null +++ b/pyramid/paster_templates/zodb/+project+.ini_tmpl @@ -0,0 +1,22 @@ +[DEFAULT] +debug = true + +[app:zodb] +use = egg:{{project}}#app +reload_templates = true +debug_authorization = false +debug_notfound = false +debug_templates = true +default_locale_name = en +zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000 + +[pipeline:main] +pipeline = + egg:repoze.zodbconn#closer + egg:repoze.tm#tm + zodb + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 6543 diff --git a/pyramid/paster_templates/zodb/CHANGES.txt_tmpl b/pyramid/paster_templates/zodb/CHANGES.txt_tmpl new file mode 100644 index 000000000..35a34f332 --- /dev/null +++ b/pyramid/paster_templates/zodb/CHANGES.txt_tmpl @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/pyramid/paster_templates/zodb/README.txt_tmpl b/pyramid/paster_templates/zodb/README.txt_tmpl new file mode 100644 index 000000000..0ddebfc3e --- /dev/null +++ b/pyramid/paster_templates/zodb/README.txt_tmpl @@ -0,0 +1,4 @@ +{{project}} README + + + diff --git a/pyramid/paster_templates/zodb/setup.cfg_tmpl b/pyramid/paster_templates/zodb/setup.cfg_tmpl new file mode 100644 index 000000000..5bec29823 --- /dev/null +++ b/pyramid/paster_templates/zodb/setup.cfg_tmpl @@ -0,0 +1,27 @@ +[nosetests] +match=^test +nocapture=1 +cover-package={{package}} +with-coverage=1 +cover-erase=1 + +[compile_catalog] +directory = {{package}}/locale +domain = {{project}} +statistics = true + +[extract_messages] +add_comments = TRANSLATORS: +output_file = {{package}}/locale/{{project}}.pot +width = 80 + +[init_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale + +[update_catalog] +domain = {{project}} +input_file = {{package}}/locale/{{project}}.pot +output_dir = {{package}}/locale +previous = true diff --git a/pyramid/paster_templates/zodb/setup.py_tmpl b/pyramid/paster_templates/zodb/setup.py_tmpl new file mode 100644 index 000000000..dba0e4ac7 --- /dev/null +++ b/pyramid/paster_templates/zodb/setup.py_tmpl @@ -0,0 +1,41 @@ +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, 'README.txt')).read() +CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() + +requires = [ + 'repoze.bfg', + 'repoze.zodbconn', + 'repoze.tm', + 'ZODB3', + ] + +setup(name='{{project}}', + version='0.0', + description='{{project}}', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: BFG", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web wsgi bfg', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires = requires, + tests_require= requires, + test_suite="{{package}}", + entry_points = """\ + [paste.app_factory] + app = {{package}}.run:app + """ + ) + diff --git a/pyramid/path.py b/pyramid/path.py new file mode 100644 index 000000000..b5850968f --- /dev/null +++ b/pyramid/path.py @@ -0,0 +1,69 @@ +import os +import pkg_resources +import sys +import imp + +ignore_types = [ imp.C_EXTENSION, imp.C_BUILTIN ] +init_names = [ '__init__%s' % x[0] for x in imp.get_suffixes() if + x[0] and x[2] not in ignore_types ] + +def caller_path(path, level=2): + if not os.path.isabs(path): + module = caller_module(level+1) + prefix = package_path(module) + path = os.path.join(prefix, path) + return path + +def caller_module(level=2): + module_globals = sys._getframe(level).f_globals + module_name = module_globals['__name__'] + module = sys.modules[module_name] + return module + +def package_name(pkg_or_module): + """ If this function is passed a module, return the dotted Python + package name of the package in which the module lives. If this + function is passed a package, return the dotted Python package + name of the package itself.""" + if pkg_or_module is None: + return '__main__' + pkg_filename = pkg_or_module.__file__ + pkg_name = pkg_or_module.__name__ + splitted = os.path.split(pkg_filename) + if splitted[-1] in init_names: + # it's a package + return pkg_name + return pkg_name.rsplit('.', 1)[0] + +def package_of(pkg_or_module): + """ Return the package of a module or return the package itself """ + pkg_name = package_name(pkg_or_module) + __import__(pkg_name) + return sys.modules[pkg_name] + +def caller_package(level=2, caller_module=caller_module): + # caller_module in arglist for tests + module = caller_module(level+1) + if '__init__.py' in getattr(module, '__file__', ''): # empty at >>> + # Module is a package + return module + # Go up one level to get package + package_name = module.__name__.rsplit('.', 1)[0] + return sys.modules[package_name] + +def package_path(package): + # computing the abspath is actually kinda expensive so we memoize + # the result + prefix = getattr(package, '__bfg_abspath__', None) + if prefix is None: + prefix = pkg_resources.resource_filename(package.__name__, '') + # pkg_resources doesn't care whether we feed it a package + # name or a module name within the package, the result + # will be the same: a directory name to the package itself + try: + package.__bfg_abspath__ = prefix + except: + # this is only an optimization, ignore any error + pass + return prefix + diff --git a/pyramid/registry.py b/pyramid/registry.py new file mode 100644 index 000000000..e935ac165 --- /dev/null +++ b/pyramid/registry.py @@ -0,0 +1,24 @@ +from zope.component.registry import Components + +class Registry(Components, dict): + + # for optimization purposes, if no listeners are listening, don't try + # to notify them + has_listeners = False + + def registerSubscriptionAdapter(self, *arg, **kw): + result = Components.registerSubscriptionAdapter(self, *arg, **kw) + self.has_listeners = True + return result + + def registerHandler(self, *arg, **kw): + result = Components.registerHandler(self, *arg, **kw) + self.has_listeners = True + return result + + def notify(self, *events): + if self.has_listeners: + # iterating over subscribers assures they get executed + [ _ for _ in self.subscribers(events, None) ] + +global_registry = Registry('global') diff --git a/pyramid/renderers.py b/pyramid/renderers.py new file mode 100644 index 000000000..00413e3fe --- /dev/null +++ b/pyramid/renderers.py @@ -0,0 +1,320 @@ +import os +import pkg_resources +import threading + +from webob import Response + +from zope.deprecation import deprecated + +from repoze.bfg.interfaces import IRendererGlobalsFactory +from repoze.bfg.interfaces import IRendererFactory +from repoze.bfg.interfaces import IResponseFactory +from repoze.bfg.interfaces import ITemplateRenderer + +from repoze.bfg.compat import json +from repoze.bfg.path import caller_package +from repoze.bfg.settings import get_settings +from repoze.bfg.threadlocal import get_current_registry +from repoze.bfg.resource import resolve_resource_spec +from repoze.bfg.decorator import reify + +# API + +def render(renderer_name, value, request=None, package=None): + """ Using the renderer specified as ``renderer_name`` (a template + or a static renderer) render the value (or set of values) present + in ``value``. Return the result of the renderer's ``__call__`` + method (usually a string or Unicode). + + If the renderer name refers to a file on disk (such as when the + renderer is a template), it's usually best to supply the name as a + :term:`resource specification` + (e.g. ``packagename:path/to/template.pt``). + + You may supply a relative resource spec as ``renderer_name``. If + the ``package`` argument is supplied, a relative renderer path + will be converted to an absolute resource specification by + combining the package supplied as ``package`` with the relative + resource specification supplied as ``renderer_name``. If you do + not supply a ``package`` (or ``package`` is ``None``) the package + name of the *caller* of this function will be used as the package. + + The ``value`` provided will be supplied as the input to the + renderer. Usually, for template renderings, this should be a + dictionary. For other renderers, this will need to be whatever + sort of value the renderer expects. + + The 'system' values supplied to the renderer will include a basic + set of top-level system names, such as ``request``, ``context``, + and ``renderer_name``. If :term:`renderer globals` have been + specified, these will also be used to agument the value. + + Supply a ``request`` parameter in order to provide the renderer + with the most correct 'system' values (``request`` and ``context`` + in particular). + + .. note:: This API is new in :mod:`repoze.bfg` 1.3. + + """ + try: + registry = request.registry + except AttributeError: + registry = None + if package is None: + package = caller_package() + renderer = RendererHelper(renderer_name, package=package, registry=registry) + return renderer.render(value, None, request=request) + +def render_to_response(renderer_name, value, request=None, package=None): + """ Using the renderer specified as ``renderer_name`` (a template + or a static renderer) render the value (or set of values) using + the result of the renderer's ``__call__`` method (usually a string + or Unicode) as the response body. + + If the renderer name refers to a file on disk (such as when the + renderer is a template), it's usually best to supply the name as a + :term:`resource specification`. + + You may supply a relative resource spec as ``renderer_name``. If + the ``package`` argument is supplied, a relative renderer name + will be converted to an absolute resource specification by + combining the package supplied as ``package`` with the relative + resource specification supplied as ``renderer_name``. If you do + not supply a ``package`` (or ``package`` is ``None``) the package + name of the *caller* of this function will be used as the package. + + The ``value`` provided will be supplied as the input to the + renderer. Usually, for template renderings, this should be a + dictionary. For other renderers, this will need to be whatever + sort of value the renderer expects. + + The 'system' values supplied to the renderer will include a basic + set of top-level system names, such as ``request``, ``context``, + and ``renderer_name``. If :term:`renderer globals` have been + specified, these will also be used to agument the value. + + Supply a ``request`` parameter in order to provide the renderer + with the most correct 'system' values (``request`` and ``context`` + in particular). + + .. note:: This API is new in :mod:`repoze.bfg` 1.3. + + """ + try: + registry = request.registry + except AttributeError: + registry = None + if package is None: + package = caller_package() + renderer = RendererHelper(renderer_name, package=package, registry=registry) + return renderer.render_to_response(value, None, request=request) + +def get_renderer(renderer_name, package=None): + """ Return the renderer object for the renderer named as + ``renderer_name``. + + You may supply a relative resource spec as ``renderer_name``. If + the ``package`` argument is supplied, a relative renderer name + will be converted to an absolute resource specification by + combining the package supplied as ``package`` with the relative + resource specification supplied as ``renderer_name``. If you do + not supply a ``package`` (or ``package`` is ``None``) the package + name of the *caller* of this function will be used as the package. + """ + if package is None: + package = caller_package() + renderer = RendererHelper(renderer_name, package=package) + return renderer.get_renderer() + + +# concrete renderer factory implementations (also API) + +def json_renderer_factory(name): + def _render(value, system): + request = system.get('request') + if request is not None: + if not hasattr(request, 'response_content_type'): + request.response_content_type = 'application/json' + return json.dumps(value) + return _render + +def string_renderer_factory(name): + def _render(value, system): + if not isinstance(value, basestring): + value = str(value) + request = system.get('request') + if request is not None: + if not hasattr(request, 'response_content_type'): + request.response_content_type = 'text/plain' + return value + return _render + +# utility functions, not API + +# Lock to prevent simultaneous registry writes; used in +# template_renderer_factory. template_renderer_factory may be called +# at runtime, from more than a single thread. +registry_lock = threading.Lock() + +def template_renderer_factory(spec, impl, lock=registry_lock): + reg = get_current_registry() + if os.path.isabs(spec): + # 'spec' is an absolute filename + if not os.path.exists(spec): + raise ValueError('Missing template file: %s' % spec) + renderer = reg.queryUtility(ITemplateRenderer, name=spec) + if renderer is None: + renderer = impl(spec) + # cache the template + try: + lock.acquire() + reg.registerUtility(renderer, ITemplateRenderer, name=spec) + finally: + lock.release() + else: + # spec is a package:relpath resource spec + renderer = reg.queryUtility(ITemplateRenderer, name=spec) + if renderer is None: + try: + package_name, filename = spec.split(':', 1) + except ValueError: # pragma: no cover + # somehow we were passed a relative pathname; this + # should die + package_name = caller_package(4).__name__ + filename = spec + abspath = pkg_resources.resource_filename(package_name, filename) + if not pkg_resources.resource_exists(package_name, filename): + raise ValueError( + 'Missing template resource: %s (%s)' % (spec, abspath)) + renderer = impl(abspath) + if not _reload_resources(): + # cache the template + try: + lock.acquire() + reg.registerUtility(renderer, ITemplateRenderer, name=spec) + finally: + lock.release() + + return renderer + +def _reload_resources(): + settings = get_settings() + return settings and settings.get('reload_resources') + +def renderer_from_name(path, package=None): + return RendererHelper(path, package=package).get_renderer() + +def rendered_response(renderer, result, view, context, request, renderer_name): + # XXX: deprecated, left here only to not break old code; use + # render_to_response instead + if ( hasattr(result, 'app_iter') and hasattr(result, 'headerlist') + and hasattr(result, 'status') ): + return result + + system = {'view':view, 'renderer_name':renderer_name, + 'context':context, 'request':request} + + helper = RendererHelper(renderer_name) + helper.renderer = renderer + return helper.render_to_response(result, system, request=request) + +deprecated('rendered_response', + "('repoze.bfg.renderers.rendered_response' is not a public API; it is " + "officially deprecated as of repoze.bfg 1.3; " + "use repoze.bfg.renderers.render_to_response instead')", + ) + +class RendererHelper(object): + def __init__(self, renderer_name, registry=None, package=None): + if registry is None: + registry = get_current_registry() + self.registry = registry + self.package = package + if renderer_name is None: + factory = registry.queryUtility(IRendererFactory) + renderer_type = None + else: + if '.' in renderer_name: + renderer_type = os.path.splitext(renderer_name)[1] + renderer_name = self.resolve_spec(renderer_name) + else: + renderer_type = renderer_name + renderer_name = renderer_name + factory = registry.queryUtility(IRendererFactory, + name=renderer_type) + self.renderer_name = renderer_name + self.renderer_type = renderer_type + self.factory = factory + + @reify + def renderer(self): + if self.factory is None: + raise ValueError('No such renderer factory %s' % str(self.renderer_type)) + return self.factory(self.renderer_name) + + def resolve_spec(self, path_or_spec): + if path_or_spec is None: + return path_or_spec + + package, filename = resolve_resource_spec(path_or_spec, + self.package) + if package is None: + return filename # absolute filename + return '%s:%s' % (package, filename) + + def get_renderer(self): + return self.renderer + + def render(self, value, system_values, request=None): + renderer = self.renderer + if system_values is None: + system_values = { + 'view':None, + 'renderer_name':self.renderer_name, + 'context':getattr(request, 'context', None), + 'request':request, + } + + registry = self.registry + globals_factory = registry.queryUtility(IRendererGlobalsFactory) + + if globals_factory is not None: + renderer_globals = globals_factory(system_values) + if renderer_globals: + system_values.update(renderer_globals) + + result = renderer(value, system_values) + return result + + def render_to_response(self, value, system_values, request=None): + result = self.render(value, system_values, request=request) + return self._make_response(result, request) + + def _make_response(self, result, request): + registry = self.registry + response_factory = registry.queryUtility(IResponseFactory, + default=Response) + + response = response_factory(result) + + if request is not None: + attrs = request.__dict__ + content_type = attrs.get('response_content_type', None) + if content_type is not None: + response.content_type = content_type + headerlist = attrs.get('response_headerlist', None) + if headerlist is not None: + for k, v in headerlist: + response.headers.add(k, v) + status = attrs.get('response_status', None) + if status is not None: + response.status = status + charset = attrs.get('response_charset', None) + if charset is not None: + response.charset = charset + cache_for = attrs.get('response_cache_for', None) + if cache_for is not None: + response.cache_expires = cache_for + + return response + diff --git a/pyramid/request.py b/pyramid/request.py new file mode 100644 index 000000000..f32a7a40c --- /dev/null +++ b/pyramid/request.py @@ -0,0 +1,229 @@ +from zope.deprecation import deprecated +from zope.interface import implements +from zope.interface.interface import InterfaceClass + +from webob import Request as WebobRequest + +from repoze.bfg.interfaces import IRequest + +class Request(WebobRequest): + """ + A subclass of the :term:`WebOb` Request class. An instance of + this class is created by the :term:`router` and is provided to a + view callable (and to other subsystems) as the ``request`` + argument. + + The documentation below (save for the ``add_response_callback`` + and ''add_finished_callback`` methods, which are defined in this + subclass itself, and the attributes ``context``, ``registry``, + ``root``, ``subpath``, ``traversed``, ``view_name``, + ``virtual_root`` , and ``virtual_root_path``, each of which is + added to the request at by the :term:`router` at request ingress + time) are autogenerated from the WebOb source code used when this + documentation was generated. + + Due to technical constraints, we can't yet display the WebOb + version number from which this documentation is autogenerated, but + it will be the 'prevailing WebOb version' at the time of the + release of this :mod:`repoze.bfg` version. See + http://http://pythonpaste.org/webob/ for further information. + """ + implements(IRequest) + response_callbacks = () + finished_callbacks = () + exception = None + + def add_response_callback(self, callback): + """ + Add a callback to the set of callbacks to be called by the + :term:`router` at a point after a :term:`response` object is + successfully created. :mod:`repoze.bfg` does not have a + global response object: this functionality allows an + application to register an action to be performed against the + response once one is created. + + A 'callback' is a callable which accepts two positional + parameters: ``request`` and ``response``. For example: + + .. code-block:: python + :linenos: + + def cache_callback(request, response): + 'Set the cache_control max_age for the response' + response.cache_control.max_age = 360 + request.add_response_callback(cache_callback) + + Response callbacks are called in the order they're added + (first-to-most-recently-added). No response callback is + called if an exception happens in application code, or if the + response object returned by :term:`view` code is invalid. + + All response callbacks are called *after* the + :class:`repoze.bfg.interfaces.INewResponse` event is sent. + + Errors raised by callbacks are not handled specially. They + will be propagated to the caller of the :mod:`repoze.bfg` + router application. + + .. note: ``add_response_callback`` is new in :mod:`repoze.bfg` + 1.3. + + See also: :ref:`using_response_callbacks`. + """ + + callbacks = self.response_callbacks + if not callbacks: + callbacks = [] + callbacks.append(callback) + self.response_callbacks = callbacks + + def _process_response_callbacks(self, response): + callbacks = self.response_callbacks + while callbacks: + callback = callbacks.pop(0) + callback(self, response) + + def add_finished_callback(self, callback): + """ + Add a callback to the set of callbacks to be called + unconditionally by the :term:`router` at the very end of + request processing. + + ``callback`` is a callable which accepts a single positional + parameter: ``request``. For example: + + .. code-block:: python + :linenos: + + import transaction + + def commit_callback(request): + '''commit or abort the transaction associated with request''' + if request.exception is not None: + transaction.abort() + else: + transaction.commit() + request.add_finished_callback(commit_callback) + + Finished callbacks are called in the order they're added ( + first- to most-recently- added). Finished callbacks (unlike + response callbacks) are *always* called, even if an exception + happens in application code that prevents a response from + being generated. + + The set of finished callbacks associated with a request are + called *very late* in the processing of that request; they are + essentially the last thing called by the :term:`router`. They + are called after response processing has already occurred in a + top-level ``finally:`` block within the router request + processing code. As a result, mutations performed to the + ``request`` provided to a finished callback will have no + meaningful effect, because response processing will have + already occurred, and the request's scope will expire almost + immediately after all finished callbacks have been processed. + + Errors raised by finished callbacks are not handled specially. + They will be propagated to the caller of the :mod:`repoze.bfg` + router application. + + .. note: ``add_finished_callback`` is new in :mod:`repoze.bfg` + 1.3. + + See also: :ref:`using_finished_callbacks`. + """ + + callbacks = self.finished_callbacks + if not callbacks: + callbacks = [] + callbacks.append(callback) + self.finished_callbacks = callbacks + + def _process_finished_callbacks(self): + callbacks = self.finished_callbacks + while callbacks: + callback = callbacks.pop(0) + callback(self) + + # override default WebOb "environ['adhoc_attr']" mutation behavior + __getattr__ = object.__getattribute__ + __setattr__ = object.__setattr__ + __delattr__ = object.__delattr__ + + # b/c dict interface for "root factory" code that expects a bare + # environ. Explicitly omitted dict methods: clear (unnecessary), + # copy (implemented by WebOb), fromkeys (unnecessary) + + def __contains__(self, k): + return self.environ.__contains__(k) + + def __delitem__(self, k): + return self.environ.__delitem__(k) + + def __getitem__(self, k): + return self.environ.__getitem__(k) + + def __iter__(self): + return iter(self.environ) + + def __setitem__(self, k, v): + self.environ[k] = v + + def get(self, k, default=None): + return self.environ.get(k, default) + + def has_key(self, k): + return self.environ.has_key(k) + + def items(self): + return self.environ.items() + + def iteritems(self): + return self.environ.iteritems() + + def iterkeys(self): + return self.environ.iterkeys() + + def itervalues(self): + return self.environ.itervalues() + + def keys(self): + return self.environ.keys() + + def pop(self, k): + return self.environ.pop(k) + + def popitem(self): + return self.environ.popitem() + + def setdefault(self, v, default): + return self.environ.setdefault(v, default) + + def update(self, v, **kw): + return self.environ.update(v, **kw) + + def values(self): + return self.environ.values() + +def route_request_iface(name, bases=()): + iface = InterfaceClass('%s_IRequest' % name, bases=bases) + # for exception view lookups + iface.combined = InterfaceClass('%s_combined_IRequest' % name, + bases=(iface, IRequest)) + return iface + +def add_global_response_headers(request, headerlist): + def add_headers(request, response): + for k, v in headerlist: + response.headerlist.append((k, v)) + request.add_response_callback(add_headers) + +from repoze.bfg.threadlocal import get_current_request as get_request # b/c + +get_request # prevent PyFlakes complaints + +deprecated('get_request', + 'As of repoze.bfg 1.0, any import of get_request from' + '``repoze.bfg.request`` is ' + 'deprecated. Use ``from repoze.bfg.threadlocal import ' + 'get_current_request instead.') + diff --git a/pyramid/resource.py b/pyramid/resource.py new file mode 100644 index 000000000..da49f16ec --- /dev/null +++ b/pyramid/resource.py @@ -0,0 +1,201 @@ +import os +import pkg_resources + +from zope.interface import implements + +from repoze.bfg.interfaces import IPackageOverrides + +from repoze.bfg.path import package_path +from repoze.bfg.path import package_name +from repoze.bfg.threadlocal import get_current_registry + +class OverrideProvider(pkg_resources.DefaultProvider): + def __init__(self, module): + pkg_resources.DefaultProvider.__init__(self, module) + self.module_name = module.__name__ + + def _get_overrides(self): + reg = get_current_registry() + overrides = reg.queryUtility(IPackageOverrides, self.module_name) + return overrides + + def get_resource_filename(self, manager, resource_name): + """ Return a true filesystem path for resource_name, + co-ordinating the extraction with manager, if the resource + must be unpacked to the filesystem. + """ + overrides = self._get_overrides() + if overrides is not None: + filename = overrides.get_filename(resource_name) + if filename is not None: + return filename + return pkg_resources.DefaultProvider.get_resource_filename( + self, manager, resource_name) + + def get_resource_stream(self, manager, resource_name): + """ Return a readable file-like object for resource_name.""" + overrides = self._get_overrides() + if overrides is not None: + stream = overrides.get_stream(resource_name) + if stream is not None: + return stream + return pkg_resources.DefaultProvider.get_resource_stream( + self, manager, resource_name) + + def get_resource_string(self, manager, resource_name): + """ Return a string containing the contents of resource_name.""" + overrides = self._get_overrides() + if overrides is not None: + string = overrides.get_string(resource_name) + if string is not None: + return string + return pkg_resources.DefaultProvider.get_resource_string( + self, manager, resource_name) + + def has_resource(self, resource_name): + overrides = self._get_overrides() + if overrides is not None: + result = overrides.has_resource(resource_name) + if result is not None: + return result + return pkg_resources.DefaultProvider.has_resource( + self, resource_name) + + def resource_isdir(self, resource_name): + overrides = self._get_overrides() + if overrides is not None: + result = overrides.isdir(resource_name) + if result is not None: + return result + return pkg_resources.DefaultProvider.resource_isdir( + self, resource_name) + + def resource_listdir(self, resource_name): + overrides = self._get_overrides() + if overrides is not None: + result = overrides.listdir(resource_name) + if result is not None: + return result + return pkg_resources.DefaultProvider.resource_listdir( + self, resource_name) + +class PackageOverrides: + implements(IPackageOverrides) + # pkg_resources arg in kw args below for testing + def __init__(self, package, pkg_resources=pkg_resources): + if hasattr(package, '__loader__') and not isinstance(package.__loader__, + self.__class__): + raise TypeError('Package %s already has a non-%s __loader__ ' + '(probably a module in a zipped egg)' % + (package, self.__class__)) + # We register ourselves as a __loader__ *only* to support the + # setuptools _find_adapter adapter lookup; this class doesn't + # actually support the PEP 302 loader "API". This is + # excusable due to the following statement in the spec: + # ... Loader objects are not + # required to offer any useful functionality (any such functionality, + # such as the zipimport get_data() method mentioned above, is + # optional)... + # A __loader__ attribute is basically metadata, and setuptools + # uses it as such. + package.__loader__ = self + # we call register_loader_type for every instantiation of this + # class; that's OK, it's idempotent to do it more than once. + pkg_resources.register_loader_type(self.__class__, OverrideProvider) + self.overrides = [] + self.overridden_package_name = package.__name__ + + def insert(self, path, package, prefix): + if not path or path.endswith('/'): + override = DirectoryOverride(path, package, prefix) + else: + override = FileOverride(path, package, prefix) + self.overrides.insert(0, override) + return override + + def search_path(self, resource_name): + for override in self.overrides: + o = override(resource_name) + if o is not None: + package, name = o + yield package, name + + def get_filename(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return pkg_resources.resource_filename(package, rname) + + def get_stream(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return pkg_resources.resource_stream(package, rname) + + def get_string(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return pkg_resources.resource_string(package, rname) + + def has_resource(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return True + + def isdir(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return pkg_resources.resource_isdir(package, rname) + + def listdir(self, resource_name): + for package, rname in self.search_path(resource_name): + if pkg_resources.resource_exists(package, rname): + return pkg_resources.resource_listdir(package, rname) + + +class DirectoryOverride: + def __init__(self, path, package, prefix): + self.path = path + self.package = package + self.prefix = prefix + self.pathlen = len(self.path) + + def __call__(self, resource_name): + if resource_name.startswith(self.path): + name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) + return self.package, name + +class FileOverride: + def __init__(self, path, package, prefix): + self.path = path + self.package = package + self.prefix = prefix + + def __call__(self, resource_name): + if resource_name == self.path: + return self.package, self.prefix + +def resolve_resource_spec(spec, pname='__main__'): + if pname and not isinstance(pname, basestring): + pname = pname.__name__ # as package + if os.path.isabs(spec): + return None, spec + filename = spec + if ':' in spec: + pname, filename = spec.split(':', 1) + elif pname is None: + pname, filename = None, spec + return pname, filename + +def resource_spec_from_abspath(abspath, package): + """ Try to convert an absolute path to a resource in a package to + a resource specification if possible; otherwise return the + absolute path. """ + if getattr(package, '__name__', None) == '__main__': + return abspath + pp = package_path(package) + os.path.sep + if abspath.startswith(pp): + relpath = abspath[len(pp):] + return '%s:%s' % (package_name(package), + relpath.replace(os.path.sep, '/')) + return abspath + + diff --git a/pyramid/router.py b/pyramid/router.py new file mode 100644 index 000000000..7b3bcb75e --- /dev/null +++ b/pyramid/router.py @@ -0,0 +1,189 @@ +from zope.interface import implements +from zope.interface import providedBy + +from repoze.bfg.interfaces import IDebugLogger +from repoze.bfg.interfaces import IExceptionViewClassifier +from repoze.bfg.interfaces import IRequest +from repoze.bfg.interfaces import IRootFactory +from repoze.bfg.interfaces import IRouteRequest +from repoze.bfg.interfaces import IRouter +from repoze.bfg.interfaces import IRequestFactory +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import ISettings +from repoze.bfg.interfaces import ITraverser +from repoze.bfg.interfaces import IView +from repoze.bfg.interfaces import IViewClassifier + +from repoze.bfg.configuration import make_app # b/c import +from repoze.bfg.events import ContextFound +from repoze.bfg.events import NewRequest +from repoze.bfg.events import NewResponse +from repoze.bfg.exceptions import NotFound +from repoze.bfg.request import Request +from repoze.bfg.threadlocal import manager +from repoze.bfg.traversal import DefaultRootFactory +from repoze.bfg.traversal import ModelGraphTraverser + +make_app # prevent pyflakes from complaining + +class Router(object): + implements(IRouter) + + debug_notfound = False + threadlocal_manager = manager + + def __init__(self, registry): + q = registry.queryUtility + self.logger = q(IDebugLogger) + self.root_factory = q(IRootFactory, default=DefaultRootFactory) + self.routes_mapper = q(IRoutesMapper) + self.request_factory = q(IRequestFactory, default=Request) + self.root_policy = self.root_factory # b/w compat + self.registry = registry + settings = q(ISettings) + if settings is not None: + self.debug_notfound = settings['debug_notfound'] + + def __call__(self, environ, start_response): + """ + Accept ``environ`` and ``start_response``; create a + :term:`request` and route the request to a :mod:`repoze.bfg` + view based on introspection of :term:`view configuration` + within the application registry; call ``start_response`` and + return an iterable. + """ + registry = self.registry + adapters = registry.adapters + has_listeners = registry.has_listeners + logger = self.logger + manager = self.threadlocal_manager + request = None + threadlocals = {'registry':registry, 'request':request} + manager.push(threadlocals) + + try: # matches finally: manager.pop() + + try: # matches finally: ... call request finished callbacks ... + + # create the request + request = self.request_factory(environ) + context = None + threadlocals['request'] = request + attrs = request.__dict__ + attrs['registry'] = registry + has_listeners and registry.notify(NewRequest(request)) + request_iface = IRequest + + try: + # find the root object + root_factory = self.root_factory + if self.routes_mapper is not None: + info = self.routes_mapper(request) + match, route = info['match'], info['route'] + if route is not None: + # TODO: kill off bfg.routes.* environ keys + # when traverser requires request arg, and + # cant cope with environ anymore (likely 1.4+) + environ['bfg.routes.route'] = route + environ['bfg.routes.matchdict'] = match + attrs['matchdict'] = match + attrs['matched_route'] = route + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + root_factory = route.factory or self.root_factory + + root = root_factory(request) + attrs['root'] = root + + # find a context + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ModelGraphTraverser(root) + tdict = traverser(request) + context, view_name, subpath, traversed, vroot, vroot_path =( + tdict['context'], tdict['view_name'], tdict['subpath'], + tdict['traversed'], tdict['virtual_root'], + tdict['virtual_root_path']) + attrs.update(tdict) + has_listeners and registry.notify(ContextFound(request)) + + # find a view callable + context_iface = providedBy(context) + view_callable = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name=view_name, default=None) + + # invoke the view callable + if view_callable is None: + if self.debug_notfound: + msg = ( + 'debug_notfound of url %s; path_info: %r, ' + 'context: %r, view_name: %r, subpath: %r, ' + 'traversed: %r, root: %r, vroot: %r, ' + 'vroot_path: %r' % ( + request.url, request.path_info, context, + view_name, + subpath, traversed, root, vroot, vroot_path) + ) + logger and logger.debug(msg) + else: + msg = request.path_info + # XXX repoze.bfg.message should be deprecated + environ['repoze.bfg.message'] = msg + raise NotFound(msg) + else: + response = view_callable(context, request) + + # handle exceptions raised during root finding and view-exec + except Exception, why: + attrs['exception'] = why + + for_ = (IExceptionViewClassifier, + request_iface.combined, + providedBy(why)) + view_callable = adapters.lookup(for_, IView, default=None) + + if view_callable is None: + raise + + # XXX r.b.message should be deprecated + try: + msg = why[0] + except: + msg = '' + environ['repoze.bfg.message'] = msg + + response = view_callable(why, request) + + # process the response + + has_listeners and registry.notify(NewResponse(request,response)) + + if request.response_callbacks: + request._process_response_callbacks(response) + + try: + headers = response.headerlist + app_iter = response.app_iter + status = response.status + except AttributeError: + raise ValueError( + 'Non-response object returned from view named %s ' + '(and no renderer): %r' % (view_name, response)) + + finally: + if request is not None and request.finished_callbacks: + request._process_finished_callbacks() + + start_response(status, headers) + return app_iter + + finally: + manager.pop() + + + + + diff --git a/pyramid/scripting.py b/pyramid/scripting.py new file mode 100644 index 000000000..ca0bea597 --- /dev/null +++ b/pyramid/scripting.py @@ -0,0 +1,25 @@ +from repoze.bfg.request import Request +from repoze.bfg.interfaces import IRequestFactory + +def get_root(app, request=None): + """ Return a tuple composed of ``(root, closer)`` when provided a + :term:`router` instance as the ``app`` argument. The ``root`` + returned is the application root object. The ``closer`` returned + is a callable (accepting no arguments) that should be called when + your scripting application is finished using the root. If + ``request`` is not None, it is used as the request passed to the + :mod:`repoze.bfg` application root factory. A request is + constructed and passed to the root factory if ``request`` is None.""" + registry = app.registry + if request is None: + request_factory = registry.queryUtility( + IRequestFactory, default=Request) + request = request_factory.blank('/') + request.registry = registry + threadlocals = {'registry':registry, 'request':request} + app.threadlocal_manager.push(threadlocals) + def closer(request=request): # keep request alive via this function default + app.threadlocal_manager.pop() + root = app.root_factory(request) + return root, closer + diff --git a/pyramid/security.py b/pyramid/security.py new file mode 100644 index 000000000..cd1bae9a5 --- /dev/null +++ b/pyramid/security.py @@ -0,0 +1,270 @@ +from zope.interface import providedBy + +from zope.deprecation import deprecated + +from repoze.bfg.interfaces import IAuthenticationPolicy +from repoze.bfg.interfaces import IAuthorizationPolicy +from repoze.bfg.interfaces import ISecuredView +from repoze.bfg.interfaces import IViewClassifier + +from repoze.bfg.exceptions import Forbidden as Unauthorized # b/c import +from repoze.bfg.threadlocal import get_current_registry + +Unauthorized # prevent PyFlakes from complaining + +deprecated('Unauthorized', + "('from repoze.bfg.security import Unauthorized' was " + "deprecated as of repoze.bfg 1.1; instead use 'from " + "repoze.bfg.exceptions import Forbidden')", + ) + +Everyone = 'system.Everyone' +Authenticated = 'system.Authenticated' +Allow = 'Allow' +Deny = 'Deny' + +class AllPermissionsList(object): + """ Stand in 'permission list' to represent all permissions """ + def __iter__(self): + return () + def __contains__(self, other): + return True + def __eq__(self, other): + return isinstance(other, self.__class__) + +ALL_PERMISSIONS = AllPermissionsList() +DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) + +def has_permission(permission, context, request): + """ Provided a permission (a string or unicode object), a context + (a :term:`model` instance) and a request object, return an + instance of :data:`repoze.bfg.security.Allowed` if the permission + is granted in this context to the user implied by the + request. Return an instance of :mod:`repoze.bfg.security.Denied` + if this permission is not granted in this context to this user. + This function delegates to the current authentication and + authorization policies. Return + :data:`repoze.bfg.security.Allowed` unconditionally if no + authentication policy has been configured in this application.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + authn_policy = reg.queryUtility(IAuthenticationPolicy) + if authn_policy is None: + return Allowed('No authentication policy in use.') + + authz_policy = reg.queryUtility(IAuthorizationPolicy) + if authz_policy is None: + raise ValueError('Authentication policy registered without ' + 'authorization policy') # should never happen + principals = authn_policy.effective_principals(request) + return authz_policy.permits(context, principals, permission) + +def authenticated_userid(request): + """ Return the userid of the currently authenticated user or + ``None`` if there is no :term:`authentication policy` in effect or + there is no currently authenticated user.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + + policy = reg.queryUtility(IAuthenticationPolicy) + if policy is None: + return None + return policy.authenticated_userid(request) + +def effective_principals(request): + """ Return the list of 'effective' :term:`principal` identifiers + for the ``request``. This will include the userid of the + currently authenticated user if a user is currently + authenticated. If no :term:`authentication policy` is in effect, + this will return an empty sequence.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + + policy = reg.queryUtility(IAuthenticationPolicy) + if policy is None: + return [] + return policy.effective_principals(request) + +def principals_allowed_by_permission(context, permission): + """ Provided a ``context`` (a model object), and a ``permission`` + (a string or unicode object), if a :term:`authorization policy` is + in effect, return a sequence of :term:`principal` ids that possess + the permission in the ``context``. If no authorization policy is + in effect, this will return a sequence with the single value + :mod:`repoze.bfg.security.Everyone` (the special principal + identifier representing all principals). + + .. note:: even if an :term:`authorization policy` is in effect, + some (exotic) authorization policies may not implement the + required machinery for this function; those will cause a + :exc:`NotImplementedError` exception to be raised when this + function is invoked. + """ + reg = get_current_registry() + policy = reg.queryUtility(IAuthorizationPolicy) + if policy is None: + return [Everyone] + return policy.principals_allowed_by_permission(context, permission) + +def view_execution_permitted(context, request, name=''): + """ If the view specified by ``context`` and ``name`` is protected + by a :term:`permission`, check the permission associated with the + view using the effective authentication/authorization policies and + the ``request``. Return a boolean result. If no + :term:`authorization policy` is in effect, or if the view is not + protected by a permission, return ``True``.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + provides = [IViewClassifier] + map(providedBy, (request, context)) + view = reg.adapters.lookup(provides, ISecuredView, name=name) + if view is None: + return Allowed( + 'Allowed: view name %r in context %r (no permission defined)' % + (name, context)) + return view.__permitted__(context, request) + +def remember(request, principal, **kw): + """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', + 'foo=abc')]``) suitable for 'remembering' a set of credentials + implied by the data passed as ``principal`` and ``*kw`` using the + current :term:`authentication policy`. Common usage might look + like so within the body of a view function (``response`` is + assumed to be an :term:`WebOb` -style :term:`response` object + computed previously by the view code):: + + from repoze.bfg.security import remember + headers = remember(request, 'chrism', password='123', max_age='86400') + response.headerlist.extend(headers) + return response + + If no :term:`authentication policy` is in use, this function will + always return an empty sequence. If used, the composition and + meaning of ``**kw`` must be agreed upon by the calling code and + the effective authentication policy.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + policy = reg.queryUtility(IAuthenticationPolicy) + if policy is None: + return [] + else: + return policy.remember(request, principal, **kw) + +def forget(request): + """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', + 'foo=abc')]``) suitable for 'forgetting' the set of credentials + possessed by the currently authenticated user. A common usage + might look like so within the body of a view function + (``response`` is assumed to be an :term:`WebOb` -style + :term:`response` object computed previously by the view code):: + + from repoze.bfg.security import forget + headers = forget(request) + response.headerlist.extend(headers) + return response + + If no :term:`authentication policy` is in use, this function will + always return an empty sequence.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + policy = reg.queryUtility(IAuthenticationPolicy) + if policy is None: + return [] + else: + return policy.forget(request) + +class PermitsResult(int): + def __new__(cls, s, *args): + inst = int.__new__(cls, cls.boolval) + inst.s = s + inst.args = args + return inst + + @property + def msg(self): + return self.s % self.args + + def __str__(self): + return self.msg + + def __repr__(self): + return '<%s instance at %s with msg %r>' % (self.__class__.__name__, + id(self), + self.msg) + +class Denied(PermitsResult): + """ An instance of ``Denied`` is returned when a security-related + API or other :mod:`repoze.bfg` code denies an action unrelated to + an ACL check. It evaluates equal to all boolean false types. It + has an attribute named ``msg`` describing the circumstances for + the deny.""" + boolval = 0 + +class Allowed(PermitsResult): + """ An instance of ``Allowed`` is returned when a security-related + API or other :mod:`repoze.bfg` code allows an action unrelated to + an ACL check. It evaluates equal to all boolean true types. It + has an attribute named ``msg`` describing the circumstances for + the allow.""" + boolval = 1 + +class ACLPermitsResult(int): + def __new__(cls, ace, acl, permission, principals, context): + inst = int.__new__(cls, cls.boolval) + inst.permission = permission + inst.ace = ace + inst.acl = acl + inst.principals = principals + inst.context = context + return inst + + @property + def msg(self): + s = ('%s permission %r via ACE %r in ACL %r on context %r for ' + 'principals %r') + return s % (self.__class__.__name__, + self.permission, + self.ace, + self.acl, + self.context, + self.principals) + + def __str__(self): + return self.msg + + def __repr__(self): + return '<%s instance at %s with msg %r>' % (self.__class__.__name__, + id(self), + self.msg) + +class ACLDenied(ACLPermitsResult): + """ An instance of ``ACLDenied`` represents that a security check + made explicitly against ACL was denied. It evaluates equal to all + boolean false types. It also has attributes which indicate which + acl, ace, permission, principals, and context were involved in the + request. Its __str__ method prints a summary of these attributes + for debugging purposes. The same summary is available as the + ``msg`` attribute.""" + boolval = 0 + +class ACLAllowed(ACLPermitsResult): + """ An instance of ``ACLAllowed`` represents that a security check + made explicitly against ACL was allowed. It evaluates equal to + all boolean true types. It also has attributes which indicate + which acl, ace, permission, principals, and context were involved + in the request. Its __str__ method prints a summary of these + attributes for debugging purposes. The same summary is available + as the ``msg`` attribute.""" + boolval = 1 + diff --git a/pyramid/settings.py b/pyramid/settings.py new file mode 100644 index 000000000..5e1181809 --- /dev/null +++ b/pyramid/settings.py @@ -0,0 +1,85 @@ +import os + +from zope.interface import implements + +from repoze.bfg.interfaces import ISettings + +from repoze.bfg.threadlocal import get_current_registry + +class Settings(dict): + """ Deployment settings. Update application settings (usually + from PasteDeploy keywords) with framework-specific key/value pairs + (e.g. find ``BFG_DEBUG_AUTHORIZATION`` in os.environ and jam into + keyword args).""" + implements(ISettings) + # _environ_ is dep inj for testing + def __init__(self, d=None, _environ_=os.environ, **kw): + if d is None: + d = {} + dict.__init__(self, d, **kw) + eget = _environ_.get + config_debug_all = self.get('debug_all', '') + eff_debug_all = asbool(eget('BFG_DEBUG_ALL', config_debug_all)) + config_reload_all = self.get('reload_all', '') + eff_reload_all = asbool(eget('BFG_RELOAD_ALL',config_reload_all)) + config_debug_auth = self.get('debug_authorization', '') + eff_debug_auth = asbool(eget('BFG_DEBUG_AUTHORIZATION', + config_debug_auth)) + config_debug_notfound = self.get('debug_notfound', '') + eff_debug_notfound = asbool(eget('BFG_DEBUG_NOTFOUND', + config_debug_notfound)) + config_debug_templates = self.get('debug_templates', '') + eff_debug_templates = asbool(eget('BFG_DEBUG_TEMPLATES', + config_debug_templates)) + config_reload_templates = self.get('reload_templates', '') + eff_reload_templates = asbool(eget('BFG_RELOAD_TEMPLATES', + config_reload_templates)) + config_reload_resources = self.get('reload_resources', '') + eff_reload_resources = asbool(eget('BFG_RELOAD_RESOURCES', + config_reload_resources)) + configure_zcml = self.get('configure_zcml', '') + eff_configure_zcml = eget('BFG_CONFIGURE_ZCML', configure_zcml) + locale_name = self.get('default_locale_name', 'en') + eff_locale_name = eget('BFG_DEFAULT_LOCALE_NAME', locale_name) + + update = { + 'debug_authorization': eff_debug_all or eff_debug_auth, + 'debug_notfound': eff_debug_all or eff_debug_notfound, + 'debug_templates': eff_debug_all or eff_debug_templates, + 'reload_templates': eff_reload_all or eff_reload_templates, + 'reload_resources':eff_reload_all or eff_reload_resources, + 'configure_zcml':eff_configure_zcml, + 'default_locale_name':eff_locale_name, + } + + self.update(update) + + def __getattr__(self, name): + # backwards compatibility + try: + return self[name] + except KeyError: + raise AttributeError(name) + +def get_settings(): + """ + Return a 'settings' object for the current application. A + 'settings' object is a dictionary-like object that contains + key/value pairs based on the dictionary passed as the ``settings`` + argument to the :class:`repoze.bfg.configuration.Configurator` + constructor or the :func:`repoze.bfg.router.make_app` API. + + .. note:: For backwards compatibility, dictionary keys can also be + looked up as attributes of the settings object. + + .. note:: the + :class:`repoze.bfg.configuration.Configurator.get_settings` method + performs the same duty. + """ + reg = get_current_registry() + return reg.queryUtility(ISettings) + +def asbool(s): + s = str(s).strip() + return s.lower() in ('t', 'true', 'y', 'yes', 'on', '1') + diff --git a/pyramid/static.py b/pyramid/static.py new file mode 100644 index 000000000..71db132b7 --- /dev/null +++ b/pyramid/static.py @@ -0,0 +1,213 @@ +import os +import pkg_resources +from urlparse import urljoin +from urlparse import urlparse + +from paste import httpexceptions +from paste import request +from paste.httpheaders import ETAG +from paste.urlparser import StaticURLParser + +from zope.interface import implements + +from repoze.bfg.interfaces import IStaticURLInfo +from repoze.bfg.path import caller_package +from repoze.bfg.resource import resolve_resource_spec +from repoze.bfg.url import route_url + +class PackageURLParser(StaticURLParser): + """ This probably won't work with zipimported resources """ + def __init__(self, package_name, resource_name, root_resource=None, + cache_max_age=None): + self.package_name = package_name + self.resource_name = os.path.normpath(resource_name) + if root_resource is None: + root_resource = self.resource_name + self.root_resource = root_resource + self.cache_max_age = cache_max_age + + def __call__(self, environ, start_response): + path_info = environ.get('PATH_INFO', '') + if not path_info: + return self.add_slash(environ, start_response) + if path_info == '/': + # @@: This should obviously be configurable + filename = 'index.html' + else: + filename = request.path_info_pop(environ) + resource = os.path.normcase(os.path.normpath( + self.resource_name + '/' + filename)) + if ( (self.root_resource is not None) and + (not resource.startswith(self.root_resource)) ): + # Out of bounds + return self.not_found(environ, start_response) + if not pkg_resources.resource_exists(self.package_name, resource): + return self.not_found(environ, start_response) + if pkg_resources.resource_isdir(self.package_name, resource): + # @@: Cache? + child_root = (self.root_resource is not None and + self.root_resource or self.resource_name) + return self.__class__( + self.package_name, resource, root_resource=child_root, + cache_max_age=self.cache_max_age)(environ, start_response) + if (environ.get('PATH_INFO') + and environ.get('PATH_INFO') != '/'): # pragma: no cover + return self.error_extra_path(environ, start_response) + full = pkg_resources.resource_filename(self.package_name, resource) + if_none_match = environ.get('HTTP_IF_NONE_MATCH') + if if_none_match: + mytime = os.stat(full).st_mtime + if str(mytime) == if_none_match: + headers = [] + ETAG.update(headers, mytime) + start_response('304 Not Modified', headers) + return [''] # empty body + + fa = self.make_app(full) + if self.cache_max_age: + fa.cache_control(max_age=self.cache_max_age) + return fa(environ, start_response) + + def not_found(self, environ, start_response, debug_message=None): + comment=('SCRIPT_NAME=%r; PATH_INFO=%r; looking in package %s; ' + 'subdir %s ;debug: %s' % (environ.get('SCRIPT_NAME'), + environ.get('PATH_INFO'), + self.package_name, + self.resource_name, + debug_message or '(none)')) + exc = httpexceptions.HTTPNotFound( + 'The resource at %s could not be found' + % request.construct_url(environ), + comment=comment) + return exc.wsgi_application(environ, start_response) + + def __repr__(self): + return '<%s %s:%s at %s>' % (self.__class__.__name__, self.package_name, + self.root_resource, id(self)) + +class StaticURLInfo(object): + implements(IStaticURLInfo) + + route_url = staticmethod(route_url) # for testing only + + def __init__(self, config): + self.config = config + self.registrations = [] + + def generate(self, path, request, **kw): + for (name, spec, is_url) in self.registrations: + if path.startswith(spec): + subpath = path[len(spec):] + if is_url: + return urljoin(name, subpath) + else: + kw['subpath'] = subpath + return self.route_url(name, request, **kw) + + raise ValueError('No static URL definition matching %s' % path) + + def add(self, name, spec, **extra): + # This feature only allows for the serving of a directory and + # the files contained within, not of a single resource; + # appending a slash here if the spec doesn't have one is + # required for proper prefix matching done in ``generate`` + # (``subpath = path[len(spec):]``). + if not spec.endswith('/'): + spec = spec + '/' + + # we also make sure the name ends with a slash, purely as a + # convenience: a name that is a url is required to end in a + # slash, so that ``urljoin(name, subpath))`` will work above + # when the name is a URL, and it doesn't hurt things for it to + # have a name that ends in a slash if it's used as a route + # name instead of a URL. + if not name.endswith('/'): + # make sure it ends with a slash + name = name + '/' + + names = [ t[0] for t in self.registrations ] + + if name in names: + idx = names.index(name) + self.registrations.pop(idx) + + if urlparse(name)[0]: + # it's a URL + self.registrations.append((name, spec, True)) + else: + # it's a view name + _info = extra.pop('_info', None) + cache_max_age = extra.pop('cache_max_age', None) + view = static_view(spec, cache_max_age=cache_max_age) + # register a route using this view + self.config.add_route( + name, + "%s*subpath" % name, # name already ends with slash + view=view, + view_for=self.__class__, + factory=lambda *x: self, + _info=_info + ) + self.registrations.append((name, spec, False)) + +class static_view(object): + """ An instance of this class is a callable which can act as a + :mod:`repoze.bfg` :term:`view callable`; this view will serve + static files from a directory on disk based on the ``root_dir`` + you provide to its constructor. + + The directory may contain subdirectories (recursively); the static + view implementation will descend into these directories as + necessary based on the components of the URL in order to resolve a + path into a response. + + You may pass an absolute or relative filesystem path or a + :term:`resource specification` representing the directory + containing static files as the ``root_dir`` argument to this + class' constructor. + + If the ``root_dir`` path is relative, and the ``package_name`` + argument is ``None``, ``root_dir`` will be considered relative to + the directory in which the Python file which *calls* ``static`` + resides. If the ``package_name`` name argument is provided, and a + relative ``root_dir`` is provided, the ``root_dir`` will be + considered relative to the Python :term:`package` specified by + ``package_name`` (a dotted path to a Python package). + + ``cache_max_age`` influences the ``Expires`` and ``Max-Age`` + response headers returned by the view (default is 3600 seconds or + five minutes). + + .. note:: If the ``root_dir`` is relative to a :term:`package`, or + is a :term:`resource specification` the :mod:`repoze.bfg` + ``resource`` ZCML directive or + :class:`repoze.bfg.configuration.Configurator` method can be + used to override resources within the named ``root_dir`` + package-relative directory. However, if the ``root_dir`` is + absolute, the ``resource`` directive will not be able to + override the resources it contains. """ + + def __init__(self, root_dir, cache_max_age=3600, package_name=None): + # package_name is for bw compat; it is preferred to pass in a + # package-relative path as root_dir + # (e.g. ``anotherpackage:foo/static``). + caller_package_name = caller_package().__name__ + package_name = package_name or caller_package_name + package_name, root_dir = resolve_resource_spec(root_dir, package_name) + if package_name is None: + app = StaticURLParser(root_dir, cache_max_age=cache_max_age) + else: + app = PackageURLParser( + package_name, root_dir, cache_max_age=cache_max_age) + self.app = app + + def __call__(self, context, request): + subpath = '/'.join(request.subpath) + request_copy = request.copy() + # Fix up PATH_INFO to get rid of everything but the "subpath" + # (the actual path to the file relative to the root dir). + request_copy.environ['PATH_INFO'] = '/' + subpath + # Zero out SCRIPT_NAME for good measure. + request_copy.environ['SCRIPT_NAME'] = '' + return request_copy.get_response(self.app) + diff --git a/pyramid/testing.py b/pyramid/testing.py new file mode 100644 index 000000000..c99ba8706 --- /dev/null +++ b/pyramid/testing.py @@ -0,0 +1,818 @@ +import copy +import types + +from webob import Response + +from zope.configuration.xmlconfig import _clearContext + +from zope.deprecation import deprecated + +from zope.interface import implements +from zope.interface import Interface +from zope.interface import alsoProvides + +from repoze.bfg.interfaces import IRequest +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import ISecuredView +from repoze.bfg.interfaces import IView +from repoze.bfg.interfaces import IViewClassifier +from repoze.bfg.interfaces import IViewPermission + +from repoze.bfg.configuration import Configurator +from repoze.bfg.exceptions import Forbidden +from repoze.bfg.registry import Registry +from repoze.bfg.security import Allowed +from repoze.bfg.security import Authenticated +from repoze.bfg.security import Denied +from repoze.bfg.security import Everyone +from repoze.bfg.security import has_permission +from repoze.bfg.threadlocal import get_current_registry +from repoze.bfg.threadlocal import manager +from repoze.bfg.urldispatch import RoutesMapper +from repoze.bfg.zcml import zcml_configure # API + +zcml_configure # prevent pyflakes from complaining + +_marker = object() + +def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): + """ Registers a pair of faux :mod:`repoze.bfg` security policies: + a :term:`authentication policy` and a :term:`authorization + policy`. + + The behavior of the registered :term:`authorization policy` + depends on the ``permissive`` argument. If ``permissive`` is + true, a permissive :term:`authorization policy` is registered; + this policy allows all access. If ``permissive`` is false, a + nonpermissive :term:`authorization policy` is registered; this + policy denies all access. + + The behavior of the registered :term:`authentication policy` + depends on the values provided for the ``userid`` and ``groupids`` + argument. The authentication policy will return the userid + identifier implied by the ``userid`` argument and the group ids + implied by the ``groupids`` argument when the + :func:`repoze.bfg.security.authenticated_userid` or + :func:`repoze.bfg.security.effective_principals` APIs are used. + + This function is most useful when testing code that uses the APIs + named :func:`repoze.bfg.security.has_permission`, + :func:`repoze.bfg.security.authenticated_userid`, + :func:`repoze.bfg.security.effective_principals`, and + :func:`repoze.bfg.security.principals_allowed_by_permission`. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.testing_securitypolicy` + method in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_securitypolicy(userid=userid, groupids=groupids, + permissive=permissive) + +def registerModels(models): + """ Registers a dictionary of :term:`model` objects that can be + resolved via the :func:`repoze.bfg.traversal.find_model` API. + + The :func:`repoze.bfg.traversal.find_model` API is called with a + path as one of its arguments. If the dictionary you register when + calling this method contains that path as a string key + (e.g. ``/foo/bar`` or ``foo/bar``), the corresponding value will + be returned to ``find_model`` (and thus to your code) when + :func:`repoze.bfg.traversal.find_model` is called with an + equivalent path string or tuple. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.testing_models` + method in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_models(models) + +def registerEventListener(event_iface=None): + """ Registers an :term:`event` listener (aka :term:`subscriber`) + listening for events of the type ``event_iface``. This method + returns a list object which is appended to by the subscriber + whenever an event is captured. + + When an event is dispatched that matches ``event_iface``, that + event will be appended to the list. You can then compare the + values in the list to expected event notifications. This method + is useful when testing code that wants to call + :meth:`repoze.bfg.registry.Registry.notify`, + :func:`zope.component.event.dispatch` or + :func:`zope.component.event.objectEventNotify`. + + The default value of ``event_iface`` (``None``) implies a + subscriber registered for *any* kind of event. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.testing_add_subscriber` + method in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_add_subscriber(event_iface) + +def registerTemplateRenderer(path, renderer=None): + """ Register a template renderer at ``path`` (usually a relative + filename ala ``templates/foo.pt``) and return the renderer object. + If the ``renderer`` argument is None, a 'dummy' renderer will be + used. This function is useful when testing code that calls the + :func:`repoze.bfg.renderers.render` function or + :func:`repoze.bfg.renderers.render_to_response` function or any + other ``render_*`` or ``get_*`` API of the + :mod:`repoze.bfg.renderers` module. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.testing_add_template`` + method in your unit and integration tests. + + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_add_template(path, renderer) + +# registerDummyRenderer is a deprecated alias that should never be removed +# (too much usage in the wild) +registerDummyRenderer = registerTemplateRenderer + +def registerView(name, result='', view=None, for_=(Interface, Interface), + permission=None): + """ Registers a :mod:`repoze.bfg` :term:`view callable` under the + name implied by the ``name`` argument. The view will return a + :term:`WebOb` :term:`Response` object with the value implied by + the ``result`` argument as its ``body`` attribute. To gain more + control, if you pass in a non-``None`` ``view`` argument, this + value will be used as a view callable instead of an automatically + generated view callable (and ``result`` is not used). + + To protect the view using a :term:`permission`, pass in a + non-``None`` value as ``permission``. This permission will be + checked by any active :term:`authorization policy` when view + execution is attempted. + + This function is useful when testing code which calls + :func:`repoze.bfg.view.render_view_to_response`. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.add_view`` + method in your unit and integration tests. + """ + for_ = (IViewClassifier, ) + for_ + if view is None: + def view(context, request): + return Response(result) + if permission is None: + return registerAdapter(view, for_, IView, name) + else: + def _secure(context, request): + if not has_permission(permission, context, request): + raise Forbidden('no permission') + else: + return view(context, request) + _secure.__call_permissive__ = view + def permitted(context, request): + return has_permission(permission, context, request) + _secure.__permitted__ = permitted + return registerAdapter(_secure, for_, ISecuredView, name) + +def registerViewPermission(name, result=True, viewpermission=None, + for_=(Interface, Interface)): + """ Registers a :mod:`repoze.bfg` 'view permission' object under a + name implied by the ``name`` argument. + + .. warning:: This function was deprecated in repoze.bfg 1.1; it + has no real effect in 1.2+. + """ + if result is True: + result = Allowed('message') + else: + result = Denied('message') + if viewpermission is None: + def viewpermission(context, request): + return result + return registerAdapter(viewpermission, for_, IViewPermission, name) + +deprecated('registerViewPermission', + 'registerViewPermission has been deprecated. As of repoze.bfg ' + 'version 1.1, view functions are now responsible for protecting ' + 'their own execution. A call to this function won\'t prevent a ' + 'view from being executed by the repoze.bfg router, nor ' + 'will the ``repoze.bfg.security.view_execution_permitted`` function ' + 'use the permission registered with this function. Instead,' + 'to register a view permission during testing, use the ' + '``repoze.bfg.testing.registerView`` directive with a ' + '``permission`` argument.') + +def registerUtility(impl, iface=Interface, name=''): + """ Register a ZCA utility component. + + The ``impl`` argument specifies the implementation of the utility. + The ``iface`` argument specifies the :term:`interface` which will + be later required to look up the utility + (:class:`zope.interface.Interface`, by default). The ``name`` + argument implies the utility name; it is the empty string by + default. + + See `The ZCA book `_ for + more information about ZCA utilities. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the :meth:`repoze.bfg.Registry.registerUtility` + method. The ``registry`` attribute of a :term:`Configurator` + in your unit and integration tests is an instance of the + :class:`repoze.bfg.Registry` class. + """ + reg = get_current_registry() + reg.registerUtility(impl, iface, name=name) + return impl + +def registerAdapter(impl, for_=Interface, provides=Interface, name=''): + """ Register a ZCA adapter component. + + The ``impl`` argument specifies the implementation of the + component (often a class). The ``for_`` argument implies the + ``for`` interface type used for this registration; it is + :class:`zope.interface.Interface` by default. If ``for`` is not a + tuple or list, it will be converted to a one-tuple before being + passed to underlying :meth:`repoze.bfg.registry.registerAdapter` + API. + + The ``provides`` argument specifies the ZCA 'provides' interface, + :class:`zope.interface.Interface` by default. + + The ``name`` argument is the empty string by default; it implies + the name under which the adapter is registered. + + See `The ZCA book `_ for + more information about ZCA adapters. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the :meth:`repoze.bfg.Registry.registerAdapter` + method. The ``registry`` attribute of a :term:`Configurator` + in your unit and integration tests is an instance of the + :class:`repoze.bfg.Registry` class. + """ + reg = get_current_registry() + if not isinstance(for_, (tuple, list)): + for_ = (for_,) + reg.registerAdapter(impl, for_, provides, name=name) + return impl + +def registerSubscriber(subscriber, iface=Interface): + """ Register a ZCA subscriber component. + + The ``subscriber`` argument specifies the implementation of the + subscriber component (often a function). + + The ``iface`` argument is the interface type for which the + subscriber will be registered (:class:`zope.interface.Interface` + by default). If ``iface`` is not a tuple or list, it will be + converted to a one-tuple before being passed to the underlying ZCA + :meth:`repoze.bfg.registry.registerHandler` method. + + See `The ZCA book `_ for + more information about ZCA subscribers. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.add_subscriber` + method in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry) + return config.add_subscriber(subscriber, iface=iface) + +def registerRoute(pattern, name, factory=None): + """ Register a new :term:`route` using a pattern + (e.g. ``:pagename``), a name (e.g. ``home``), and an optional root + factory. + + The ``pattern`` argument implies the route pattern. The ``name`` + argument implies the route name. The ``factory`` argument implies + a :term:`root factory` associated with the route. + + This API is useful for testing code that calls + e.g. :func:`repoze.bfg.url.route_url`. + + .. note:: This API was added in :mod:`repoze.bfg` version 1.1. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.add_route` + method in your unit and integration tests. + """ + reg = get_current_registry() + config = Configurator(registry=reg) + return config.add_route(name, pattern, factory=factory) + +def registerRoutesMapper(root_factory=None): + """ Register a routes 'mapper' object. + + .. note:: This API was added in :mod:`repoze.bfg` version 1.1. + + .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.2: + a route mapper is no longer required to be present for + successful system operation. + """ + mapper = RoutesMapper() + reg = get_current_registry() + reg.registerUtility(mapper, IRoutesMapper) + return mapper + +deprecated('registerRoutesMapper', + 'registerRoutesMapper has been deprecated. As of repoze.bfg ' + 'version 1.2, a route mapper is not required to be registered ' + 'for normal system operation; if you actually do want a route to ' + 'be registered, use the ' + '``repoze.bfg.configuration.Configurator.add_route`` ' + 'method; this will cause a route mapper to be registered also.') + +def registerSettings(dictarg=None, **kw): + """Register one or more 'setting' key/value pairs. A setting is + a single key/value pair in the dictionary-ish object returned from + the API :func:`repoze.bfg.settings.get_settings`. + + You may pass a dictionary:: + + registerSettings({'external_uri':'http://example.com'}) + + Or a set of key/value pairs:: + + registerSettings(external_uri='http://example.com') + + Use of this function is required when you need to test code that + calls the :func:`repoze.bfg.settings.get_settings` API and which + uses return values from that API. + + .. note:: This API is new as of :mod:`repoze.bfg` 1.1. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the + :meth:`repoze.bfg.configuration.Configurator.add_settings` + method in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry=registry) + config.add_settings(dictarg, **kw) + +class DummyRootFactory(object): + __parent__ = None + __name__ = None + def __init__(self, request): + if 'bfg.routes.matchdict' in request: + self.__dict__.update(request['bfg.routes.matchdict']) + +class DummySecurityPolicy(object): + """ A standin for both an IAuthentication and IAuthorization policy """ + def __init__(self, userid=None, groupids=(), permissive=True): + self.userid = userid + self.groupids = groupids + self.permissive = permissive + + def authenticated_userid(self, request): + return self.userid + + def effective_principals(self, request): + effective_principals = [Everyone] + if self.userid: + effective_principals.append(Authenticated) + effective_principals.append(self.userid) + effective_principals.extend(self.groupids) + return effective_principals + + def remember(self, request, principal, **kw): + return [] + + def forget(self, request): + return [] + + def permits(self, context, principals, permission): + return self.permissive + + def principals_allowed_by_permission(self, context, permission): + return self.effective_principals(None) + +class DummyTemplateRenderer(object): + """ + An instance of this class is returned from + :func:`repoze.bfg.testing.registerTemplateRenderer`. It has a + helper function (``assert_``) that makes it possible to make an + assertion which compares data passed to the renderer by the view + function against expected key/value pairs. + """ + + def __init__(self, string_response=''): + self._received = {} + self._string_response = string_response + self._implementation = MockTemplate(string_response) + + # For in-the-wild test code that doesn't create its own renderer, + # but mutates our internals instead. When all you read is the + # source code, *everything* is an API! + def _get_string_response(self): + return self._string_response + def _set_string_response(self, response): + self._string_response = response + self._implementation.response = response + string_response = property(_get_string_response, _set_string_response) + + def implementation(self): + return self._implementation + + def __call__(self, kw, system=None): + if system: + self._received.update(system) + self._received.update(kw) + return self.string_response + + def __getattr__(self, k): + """ Backwards compatibility """ + val = self._received.get(k, _marker) + if val is _marker: + val = self._implementation._received.get(k, _marker) + if val is _marker: + raise AttributeError(k) + return val + + def assert_(self, **kw): + """ Accept an arbitrary set of assertion key/value pairs. For + each assertion key/value pair assert that the renderer + (eg. :func:`repoze.bfg.renderer.render_to_response`) + received the key with a value that equals the asserted + value. If the renderer did not receive the key at all, or the + value received by the renderer doesn't match the assertion + value, raise an :exc:`AssertionError`.""" + for k, v in kw.items(): + myval = self._received.get(k, _marker) + if myval is _marker: + myval = self._implementation._received.get(k, _marker) + if myval is _marker: + raise AssertionError( + 'A value for key "%s" was not passed to the renderer' + % k) + + if myval != v: + raise AssertionError( + '\nasserted value for %s: %r\nactual value: %r' % ( + v, k, myval)) + return True + +class DummyModel: + """ A dummy :mod:`repoze.bfg` :term:`model` object.""" + def __init__(self, __name__=None, __parent__=None, __provides__=None, + **kw): + """ The model's ``__name__`` attribute will be set to the + value of the ``__name__`` argument, and the model's + ``__parent__`` attribute will be set to the value of the + ``__parent__`` argument. If ``__provides__`` is specified, it + should be an interface object or tuple of interface objects + that will be attached to the resulting model via + :func:`zope.interface.alsoProvides`. Any extra keywords passed + in the ``kw`` argumnent will be set as direct attributes of + the model object.""" + self.__name__ = __name__ + self.__parent__ = __parent__ + if __provides__ is not None: + alsoProvides(self, __provides__) + self.kw = kw + self.__dict__.update(**kw) + self.subs = {} + + def __setitem__(self, name, val): + """ When the ``__setitem__`` method is called, the object + passed in as ``val`` will be decorated with a ``__parent__`` + attribute pointing at the dummy model and a ``__name__`` + attribute that is the value of ``name``. The value will then + be returned when dummy model's ``__getitem__`` is called with + the name ``name```.""" + val.__name__ = name + val.__parent__ = self + self.subs[name] = val + + def __getitem__(self, name): + """ Return a named subobject (see ``__setitem__``)""" + ob = self.subs[name] + return ob + + def __delitem__(self, name): + del self.subs[name] + + def get(self, name, default=None): + return self.subs.get(name, default) + + def values(self): + """ Return the values set by __setitem__ """ + return self.subs.values() + + def items(self): + """ Return the items set by __setitem__ """ + return self.subs.items() + + def keys(self): + """ Return the keys set by __setitem__ """ + return self.subs.keys() + + __iter__ = keys + + def __nonzero__(self): + return True + + def __len__(self): + return len(self.subs) + + def __contains__(self, name): + return name in self.subs + + def clone(self, __name__=_marker, __parent__=_marker, **kw): + """ Create a clone of the model object. If ``__name__`` or + ``__parent__`` arguments are passed, use these values to + override the existing ``__name__`` or ``__parent__`` of the + model. If any extra keyword args are passed in via the ``kw`` + argument, use these keywords to add to or override existing + model keywords (attributes).""" + oldkw = self.kw.copy() + oldkw.update(kw) + inst = self.__class__(self.__name__, self.__parent__, **oldkw) + inst.subs = copy.deepcopy(self.subs) + if __name__ is not _marker: + inst.__name__ = __name__ + if __parent__ is not _marker: + inst.__parent__ = __parent__ + return inst + +class DummyRequest(object): + """ A dummy request object (imitates a :term:`request` object). + + The ``params``, ``environ``, ``headers``, ``path``, and + ``cookies`` arguments correspond to their :term`WebOb` + equivalents. + + The ``post`` argument, if passed, populates the request's + ``POST`` attribute, but *not* ``params``, in order to allow testing + that the app accepts data for a given view only from POST requests. + This argument also sets ``self.method`` to "POST". + + Extra keyword arguments are assigned as attributes of the request + itself. + """ + implements(IRequest) + method = 'GET' + application_url = 'http://example.com' + host = 'example.com:80' + content_length = 0 + response_callbacks = () + def __init__(self, params=None, environ=None, headers=None, path='/', + cookies=None, post=None, **kw): + if environ is None: + environ = {} + if params is None: + params = {} + if headers is None: + headers = {} + if cookies is None: + cookies = {} + self.environ = environ + self.headers = headers + self.params = params + self.cookies = cookies + self.matchdict = {} + self.GET = params + if post is not None: + self.method = 'POST' + self.POST = post + else: + self.POST = params + self.host_url = self.application_url + self.path_url = self.application_url + self.url = self.application_url + self.path = path + self.path_info = path + self.script_name = '' + self.path_qs = '' + self.body = '' + self.view_name = '' + self.subpath = () + self.traversed = () + self.virtual_root_path = () + self.context = None + self.root = None + self.virtual_root = None + self.marshalled = params # repoze.monty + self.registry = get_current_registry() + self.__dict__.update(kw) + + def add_response_callback(self, callback): + if not self.response_callbacks: + self.response_callbacks = [] + self.response_callbacks.append(callback) + +def setUp(registry=None, request=None, hook_zca=True): + """ + Set :mod:`repoze.bfg` registry and request thread locals for the + duration of a single unit test. + + .. note:: The ``setUp`` function is new as of :mod:`repoze.bfg` + 1.1. + + Use this function in the ``setUp`` method of a unittest test case + which directly or indirectly uses: + + - any of the ``register*`` functions in :mod:`repoze.bfg.testing` + (such as :func:`repoze.bfg.testing.registerModels`) + + - any method of the :class:`repoze.bfg.configuration.Configurator` + object returned by this function. + + - the :func:`repoze.bfg.threadlocal.get_current_registry` or + :func:`repoze.bfg.threadlocal.get_current_request` functions. + + If you use the ``testing.register*`` APIs, or the + ``get_current_*`` functions (or call :mod:`repoze.bfg` code that + uses these functions) without calling ``setUp``, + :func:`repoze.bfg.threadlocal.get_current_registry` will return a + *global* :term:`application registry`, which may cause unit tests + to not be isolated with respect to registrations they perform. + + If the ``registry`` argument is ``None``, a new empty + :term:`application registry` will be created (an instance of the + :class:`repoze.bfg.registry.Registry` class). If the ``registry`` + argument is not ``None``, the value passed in should be an + instance of the :class:`repoze.bfg.registry.Registry` class or a + suitable testing analogue. + + After ``setUp`` is finished, the registry returned by the + :func:`repoze.bfg.threadlocal.get_current_request` function will + be the passed (or constructed) registry until + :func:`repoze.bfg.testing.tearDown` is called (or + :func:`repoze.bfg.testing.setUp` is called again) . + + .. note:: The ``registry`` argument is new as of :mod:`repoze.bfg` + 1.2. + + If the ``hook_zca`` argument is ``True``, ``setUp`` will attempt + to perform the operation ``zope.component.getSiteManager.sethook( + repoze.bfg.threadlocal.get_current_registry)``, which will cause + the :term:`Zope Component Architecture` global API + (e.g. :func:`zope.component.getSiteManager`, + :func:`zope.component.getAdapter`, and so on) to use the registry + constructed by ``setUp`` as the value it returns from + :func:`zope.component.getSiteManager`. If the + :mod:`zope.component` package cannot be imported, or if + ``hook_zca`` is ``False``, the hook will not be set. + + This function returns an instance of the + :class:`repoze.bfg.configuration.Configurator` class, which can be + used for further configuration to set up an environment suitable + for a unit or integration test. The ``registry`` attribute + attached to the Configurator instance represents the 'current' + :term:`application registry`; the same registry will be returned + by :func:`repoze.bfg.threadlocal.get_current_registry` during the + execution of the test. + + .. note:: The ``hook_zca`` argument is new as of :mod:`repoze.bfg` + 1.2. + + .. note:: The return value (a ``Configurator`` instance) is new as + of :mod:`repoze.bfg` 1.2 (previous versions used to return + ``None``) + + .. warning:: Although this method of setting up a test registry + will never disappear, after :mod:`repoze.bfg` 1.2a6, + using the ``begin`` and ``end`` methods of a + ``Configurator`` are preferred to using + ``repoze.bfg.testing.setUp`` and + ``repoze.bfg.testing.tearDown``. See + :ref:`unittesting_chapter` for more information. + """ + manager.clear() + if registry is None: + registry = Registry('testing') + config = Configurator(registry=registry) + if hasattr(registry, 'registerUtility'): + # Sometimes nose calls us with a non-registry object because + # it thinks this function is module test setup. Likewise, + # someone may be passing us an esoteric "dummy" registry, and + # the below won't succeed if it doesn't have a registerUtility + # method. + from repoze.bfg.configuration import DEFAULT_RENDERERS + for name, renderer in DEFAULT_RENDERERS: + # Cause the default renderers to be registered because + # in-the-wild test code relies on being able to call + # e.g. ``repoze.bfg.chameleon_zpt.render_template`` + # without registering a .pt renderer, expecting the "real" + # template to be rendered. This is a holdover from when + # individual template system renderers weren't indirected + # by the ``repoze.bfg.renderers`` machinery, and + # ``render_template`` and friends went behind the back of + # any existing renderer factory lookup system. + config.add_renderer(name, renderer) + hook_zca and config.hook_zca() + config.begin(request=request) + return config + +def tearDown(unhook_zca=True): + """Undo the effects :func:`repoze.bfg.testing.setUp`. Use this + function in the ``tearDown`` method of a unit test that uses + :func:`repoze.bfg.testing.setUp` in its ``setUp`` method. + + .. note:: This function is new as of :mod:`repoze.bfg` 1.1. + + If the ``unhook_zca`` argument is ``True`` (the default), call + :func:`zope.component.getSiteManager.reset`. This undoes the + action of :func:`repoze.bfg.testing.setUp` called with the + argument ``hook_zca=True``. If :mod:`zope.component` cannot be + imported, ignore the argument. + + .. note:: The ``unhook_zca`` argument is new as of + :mod:`repoze.bfg` 1.2. + + .. warning:: Although this method of tearing a test setup down + will never disappear, after :mod:`repoze.bfg` 1.2a6, + using the ``begin`` and ``end`` methods of a + ``Configurator`` are preferred to using + ``repoze.bfg.testing.setUp`` and + ``repoze.bfg.testing.tearDown``. See + :ref:`unittesting_chapter` for more information. + + """ + if unhook_zca: + try: + from zope.component import getSiteManager + getSiteManager.reset() + except ImportError: # pragma: no cover + pass + info = manager.pop() + manager.clear() + if info is not None: + registry = info['registry'] + if hasattr(registry, '__init__') and hasattr(registry, '__name__'): + try: + registry.__init__(registry.__name__) + except TypeError: + # calling __init__ is largely for the benefit of + # people who want to use the global ZCA registry; + # however maybe somebody's using a registry we don't + # understand, let's not blow up + pass + _clearContext() # XXX why? + +def cleanUp(*arg, **kw): + """ :func:`repoze.bfg.testing.cleanUp` is an alias for + :func:`repoze.bfg.testing.setUp`. Although this function is + effectively deprecated as of :mod:`repoze.bfg` 1.1, due to its + extensive production usage, it will never be removed.""" + return setUp(*arg, **kw) + +class DummyRendererFactory(object): + """ Registered by + ``repoze.bfg.configuration.Configurator.testing_add_renderer`` as + a dummy renderer factory. The indecision about what to use as a + key (a spec vs. a relative name) is caused by test suites in the + wild believing they can register either. The ``factory`` argument + passed to this constructor is usually the *real* template renderer + factory, found when ``testing_add_renderer`` is called.""" + def __init__(self, name, factory): + self.name = name + self.factory = factory # the "real" renderer factory reg'd previously + self.renderers = {} + + def add(self, spec, renderer): + self.renderers[spec] = renderer + if ':' in spec: + package, relative = spec.split(':', 1) + self.renderers[relative] = renderer + + def __call__(self, spec): + renderer = self.renderers.get(spec) + if renderer is None: + if ':' in spec: + package, relative = spec.split(':', 1) + renderer = self.renderers.get(relative) + if renderer is None: + if self.factory: + renderer = self.factory(spec) + else: + raise KeyError('No testing renderer registered for %r' % + spec) + return renderer + + +class MockTemplate(object): + def __init__(self, response): + self._received = {} + self.response = response + def __getattr__(self, attrname): + return self + def __getitem__(self, attrname): + return self + def __call__(self, *arg, **kw): + self._received.update(kw) + return self.response diff --git a/pyramid/tests/__init__.py b/pyramid/tests/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/ccbugapp/__init__.py b/pyramid/tests/ccbugapp/__init__.py new file mode 100644 index 000000000..546616b2c --- /dev/null +++ b/pyramid/tests/ccbugapp/__init__.py @@ -0,0 +1 @@ +# fixture application diff --git a/pyramid/tests/ccbugapp/configure.zcml b/pyramid/tests/ccbugapp/configure.zcml new file mode 100644 index 000000000..cb9ca6f1d --- /dev/null +++ b/pyramid/tests/ccbugapp/configure.zcml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/pyramid/tests/ccbugapp/views.py b/pyramid/tests/ccbugapp/views.py new file mode 100644 index 000000000..4a6939ac2 --- /dev/null +++ b/pyramid/tests/ccbugapp/views.py @@ -0,0 +1,10 @@ +from webob import Response + +def rdf_view(request): + """ """ + return Response('rdf') + +def juri_view(request): + """ """ + return Response('juri') + diff --git a/pyramid/tests/exceptionviewapp/__init__.py b/pyramid/tests/exceptionviewapp/__init__.py new file mode 100644 index 000000000..ef5fe8b12 --- /dev/null +++ b/pyramid/tests/exceptionviewapp/__init__.py @@ -0,0 +1 @@ +# a package diff --git a/pyramid/tests/exceptionviewapp/configure.zcml b/pyramid/tests/exceptionviewapp/configure.zcml new file mode 100644 index 000000000..680e065a6 --- /dev/null +++ b/pyramid/tests/exceptionviewapp/configure.zcml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyramid/tests/exceptionviewapp/models.py b/pyramid/tests/exceptionviewapp/models.py new file mode 100644 index 000000000..fe407badc --- /dev/null +++ b/pyramid/tests/exceptionviewapp/models.py @@ -0,0 +1,18 @@ + +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/pyramid/tests/exceptionviewapp/views.py b/pyramid/tests/exceptionviewapp/views.py new file mode 100644 index 000000000..1432618cf --- /dev/null +++ b/pyramid/tests/exceptionviewapp/views.py @@ -0,0 +1,17 @@ +from webob import Response +from models import AnException + +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() diff --git a/pyramid/tests/fixtureapp/__init__.py b/pyramid/tests/fixtureapp/__init__.py new file mode 100644 index 000000000..546616b2c --- /dev/null +++ b/pyramid/tests/fixtureapp/__init__.py @@ -0,0 +1 @@ +# fixture application diff --git a/pyramid/tests/fixtureapp/another.zcml b/pyramid/tests/fixtureapp/another.zcml new file mode 100644 index 000000000..f8678bad7 --- /dev/null +++ b/pyramid/tests/fixtureapp/another.zcml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/pyramid/tests/fixtureapp/configure.zcml b/pyramid/tests/fixtureapp/configure.zcml new file mode 100644 index 000000000..e3470d47a --- /dev/null +++ b/pyramid/tests/fixtureapp/configure.zcml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + diff --git a/pyramid/tests/fixtureapp/models.py b/pyramid/tests/fixtureapp/models.py new file mode 100644 index 000000000..d80d14bb3 --- /dev/null +++ b/pyramid/tests/fixtureapp/models.py @@ -0,0 +1,8 @@ +from zope.interface import Interface + +class IFixture(Interface): + pass + +def fixture(): + """ """ + diff --git a/pyramid/tests/fixtureapp/subpackage/__init__.py b/pyramid/tests/fixtureapp/subpackage/__init__.py new file mode 100644 index 000000000..d3173e636 --- /dev/null +++ b/pyramid/tests/fixtureapp/subpackage/__init__.py @@ -0,0 +1 @@ +#package diff --git a/pyramid/tests/fixtureapp/subpackage/templates/bar.pt b/pyramid/tests/fixtureapp/subpackage/templates/bar.pt new file mode 100644 index 000000000..90531a4b3 --- /dev/null +++ b/pyramid/tests/fixtureapp/subpackage/templates/bar.pt @@ -0,0 +1,2 @@ + + diff --git a/pyramid/tests/fixtureapp/subpackage/yetanother.zcml b/pyramid/tests/fixtureapp/subpackage/yetanother.zcml new file mode 100644 index 000000000..464163477 --- /dev/null +++ b/pyramid/tests/fixtureapp/subpackage/yetanother.zcml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pyramid/tests/fixtureapp/templates/fixture.pt b/pyramid/tests/fixtureapp/templates/fixture.pt new file mode 100644 index 000000000..06dd4e2b1 --- /dev/null +++ b/pyramid/tests/fixtureapp/templates/fixture.pt @@ -0,0 +1,6 @@ + + + + + diff --git a/pyramid/tests/fixtureapp/views.py b/pyramid/tests/fixtureapp/views.py new file mode 100644 index 000000000..862046d43 --- /dev/null +++ b/pyramid/tests/fixtureapp/views.py @@ -0,0 +1,22 @@ +from zope.interface import Interface +from webob import Response +from repoze.bfg.exceptions import Forbidden + +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 Forbidden() + +class IDummy(Interface): + pass diff --git a/pyramid/tests/fixtures/minimal.pt b/pyramid/tests/fixtures/minimal.pt new file mode 100644 index 000000000..693d155ef --- /dev/null +++ b/pyramid/tests/fixtures/minimal.pt @@ -0,0 +1,3 @@ +
+
diff --git a/pyramid/tests/fixtures/minimal.txt b/pyramid/tests/fixtures/minimal.txt new file mode 100644 index 000000000..18832d351 --- /dev/null +++ b/pyramid/tests/fixtures/minimal.txt @@ -0,0 +1 @@ +Hello. diff --git a/pyramid/tests/fixtures/nonminimal.txt b/pyramid/tests/fixtures/nonminimal.txt new file mode 100644 index 000000000..9de95ec92 --- /dev/null +++ b/pyramid/tests/fixtures/nonminimal.txt @@ -0,0 +1 @@ +Hello, ${name}! diff --git a/pyramid/tests/fixtures/pp.pt b/pyramid/tests/fixtures/pp.pt new file mode 100644 index 000000000..9df7d22da --- /dev/null +++ b/pyramid/tests/fixtures/pp.pt @@ -0,0 +1,3 @@ +

WRAPPED

diff --git a/pyramid/tests/fixtures/static/index.html b/pyramid/tests/fixtures/static/index.html new file mode 100644 index 000000000..6498787a5 --- /dev/null +++ b/pyramid/tests/fixtures/static/index.html @@ -0,0 +1 @@ +static diff --git a/pyramid/tests/fixtures/static/subdir/index.html b/pyramid/tests/fixtures/static/subdir/index.html new file mode 100644 index 000000000..bb84fad04 --- /dev/null +++ b/pyramid/tests/fixtures/static/subdir/index.html @@ -0,0 +1 @@ +subdir diff --git a/pyramid/tests/grokkedapp/__init__.py b/pyramid/tests/grokkedapp/__init__.py new file mode 100644 index 000000000..5d2843885 --- /dev/null +++ b/pyramid/tests/grokkedapp/__init__.py @@ -0,0 +1,90 @@ +from repoze.bfg.view import bfg_view + +@bfg_view() +def grokked(context, request): + return 'grokked' + +@bfg_view(request_method='POST') +def grokked_post(context, request): + return 'grokked_post' + +@bfg_view(name='stacked2') +@bfg_view(name='stacked1') +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 = bfg_view(name='stacked_class1')(stacked_class) +stacked_class = bfg_view(name='stacked_class2')(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 = bfg_view(name='oldstyle_grokked_class')( + 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 = bfg_view(name='grokked_class')(grokked_class) + +class Foo(object): + def __call__(self, context, request): + return 'grokked_instance' + +grokked_instance = Foo() +grokked_instance = bfg_view(name='grokked_instance')(grokked_instance) + +class Base(object): + @bfg_view(name='basemethod') + def basemethod(self): + """ """ + +class MethodViews(Base): + def __init__(self, context, request): + self.context = context + self.request = request + + @bfg_view(name='method1') + def method1(self): + return 'method1' + + @bfg_view(name='method2') + def method2(self): + return 'method2' + + @bfg_view(name='stacked_method2') + @bfg_view(name='stacked_method1') + def stacked(self): + return 'stacked_method' + +# ungrokkable + +A = 1 +B = {} + +def stuff(): + """ """ + +class Whatever(object): + pass + +class Whatever2: + pass diff --git a/pyramid/tests/grokkedapp/another.py b/pyramid/tests/grokkedapp/another.py new file mode 100644 index 000000000..7dda1d579 --- /dev/null +++ b/pyramid/tests/grokkedapp/another.py @@ -0,0 +1,62 @@ +from repoze.bfg.view import bfg_view + +@bfg_view(name='another') +def grokked(context, request): + return 'another_grokked' + +@bfg_view(request_method='POST', name='another') +def grokked_post(context, request): + return 'another_grokked_post' + +@bfg_view(name='another_stacked2') +@bfg_view(name='another_stacked1') +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 = bfg_view(name='another_stacked_class1')(stacked_class) +stacked_class = bfg_view(name='another_stacked_class2')(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 = bfg_view(name='another_oldstyle_grokked_class')( + 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 = bfg_view(name='another_grokked_class')(grokked_class) + +class Foo(object): + def __call__(self, context, request): + return 'another_grokked_instance' + +grokked_instance = Foo() +grokked_instance = bfg_view(name='another_grokked_instance')(grokked_instance) + +# ungrokkable + +A = 1 +B = {} + +def stuff(): + """ """ + diff --git a/pyramid/tests/grokkedapp/configure.zcml b/pyramid/tests/grokkedapp/configure.zcml new file mode 100644 index 000000000..6867046df --- /dev/null +++ b/pyramid/tests/grokkedapp/configure.zcml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pyramid/tests/grokkedapp/pod/notinit.py b/pyramid/tests/grokkedapp/pod/notinit.py new file mode 100644 index 000000000..ca0538123 --- /dev/null +++ b/pyramid/tests/grokkedapp/pod/notinit.py @@ -0,0 +1,5 @@ +from repoze.bfg.view import bfg_view + +@bfg_view(name='pod_notinit') +def subpackage_notinit(context, request): + return 'pod_notinit' diff --git a/pyramid/tests/grokkedapp/subpackage/__init__.py b/pyramid/tests/grokkedapp/subpackage/__init__.py new file mode 100644 index 000000000..bdbe54e13 --- /dev/null +++ b/pyramid/tests/grokkedapp/subpackage/__init__.py @@ -0,0 +1,5 @@ +from repoze.bfg.view import bfg_view + +@bfg_view(name='subpackage_init') +def subpackage_init(context, request): + return 'subpackage_init' diff --git a/pyramid/tests/grokkedapp/subpackage/notinit.py b/pyramid/tests/grokkedapp/subpackage/notinit.py new file mode 100644 index 000000000..84de6503d --- /dev/null +++ b/pyramid/tests/grokkedapp/subpackage/notinit.py @@ -0,0 +1,5 @@ +from repoze.bfg.view import bfg_view + +@bfg_view(name='subpackage_notinit') +def subpackage_notinit(context, request): + return 'subpackage_notinit' diff --git a/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py b/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py new file mode 100644 index 000000000..a83be850b --- /dev/null +++ b/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py @@ -0,0 +1,5 @@ +from repoze.bfg.view import bfg_view + +@bfg_view(name='subsubpackage_init') +def subpackage_init(context, request): + return 'subsubpackage_init' diff --git a/pyramid/tests/hybridapp/__init__.py b/pyramid/tests/hybridapp/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/hybridapp/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/hybridapp/configure.zcml b/pyramid/tests/hybridapp/configure.zcml new file mode 100644 index 000000000..a94409e26 --- /dev/null +++ b/pyramid/tests/hybridapp/configure.zcml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyramid/tests/hybridapp/views.py b/pyramid/tests/hybridapp/views.py new file mode 100644 index 000000000..135ef8290 --- /dev/null +++ b/pyramid/tests/hybridapp/views.py @@ -0,0 +1,39 @@ +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/pyramid/tests/localeapp/__init__.py b/pyramid/tests/localeapp/__init__.py new file mode 100644 index 000000000..1a35cdb4a --- /dev/null +++ b/pyramid/tests/localeapp/__init__.py @@ -0,0 +1 @@ +# a file diff --git a/pyramid/tests/localeapp/locale/GARBAGE b/pyramid/tests/localeapp/locale/GARBAGE new file mode 100644 index 000000000..032c55584 --- /dev/null +++ b/pyramid/tests/localeapp/locale/GARBAGE @@ -0,0 +1 @@ +Garbage file. diff --git a/pyramid/tests/localeapp/locale/be/LC_MESSAGES b/pyramid/tests/localeapp/locale/be/LC_MESSAGES new file mode 100644 index 000000000..909cf6a3b --- /dev/null +++ b/pyramid/tests/localeapp/locale/be/LC_MESSAGES @@ -0,0 +1 @@ +busted. diff --git a/pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo b/pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo new file mode 100644 index 000000000..2924a5eb5 Binary files /dev/null and b/pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo differ diff --git a/pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po b/pyramid/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/pyramid/tests/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 , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME \n" +"Language-Team: de \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo b/pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo new file mode 100644 index 000000000..2924a5eb5 Binary files /dev/null and b/pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo differ diff --git a/pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po b/pyramid/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po new file mode 100644 index 000000000..17f87bc19 --- /dev/null +++ b/pyramid/tests/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 , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: deformsite 0.0\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-04-22 14:17+0400\n" +"PO-Revision-Date: 2010-04-22 14:17-0400\n" +"Last-Translator: FULL NAME \n" +"Language-Team: de \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.5\n" + +#: deformsite/__init__.py:458 +msgid "Approve" +msgstr "Genehmigen" + +#: deformsite/__init__.py:459 +msgid "Show approval" +msgstr "Zeigen Genehmigung" + +#: deformsite/__init__.py:466 +msgid "Submit" +msgstr "Beugen" + diff --git a/pyramid/tests/restbugapp/__init__.py b/pyramid/tests/restbugapp/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/restbugapp/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/restbugapp/configure.zcml b/pyramid/tests/restbugapp/configure.zcml new file mode 100644 index 000000000..67954b892 --- /dev/null +++ b/pyramid/tests/restbugapp/configure.zcml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/pyramid/tests/restbugapp/views.py b/pyramid/tests/restbugapp/views.py new file mode 100644 index 000000000..b94851099 --- /dev/null +++ b/pyramid/tests/restbugapp/views.py @@ -0,0 +1,15 @@ +from webob 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/pyramid/tests/routesapp/__init__.py b/pyramid/tests/routesapp/__init__.py new file mode 100644 index 000000000..546616b2c --- /dev/null +++ b/pyramid/tests/routesapp/__init__.py @@ -0,0 +1 @@ +# fixture application diff --git a/pyramid/tests/routesapp/configure.zcml b/pyramid/tests/routesapp/configure.zcml new file mode 100644 index 000000000..01062b6d4 --- /dev/null +++ b/pyramid/tests/routesapp/configure.zcml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/pyramid/tests/routesapp/models.py b/pyramid/tests/routesapp/models.py new file mode 100644 index 000000000..a57b06308 --- /dev/null +++ b/pyramid/tests/routesapp/models.py @@ -0,0 +1,5 @@ +from zope.interface import Interface + +class IFixture(Interface): + pass + diff --git a/pyramid/tests/routesapp/templates/fixture.pt b/pyramid/tests/routesapp/templates/fixture.pt new file mode 100644 index 000000000..06dd4e2b1 --- /dev/null +++ b/pyramid/tests/routesapp/templates/fixture.pt @@ -0,0 +1,6 @@ + + + + + diff --git a/pyramid/tests/routesapp/views.py b/pyramid/tests/routesapp/views.py new file mode 100644 index 000000000..f805b88c9 --- /dev/null +++ b/pyramid/tests/routesapp/views.py @@ -0,0 +1,8 @@ +from zope.interface import Interface + +def fixture_view(context, request): + """ """ + +class IDummy(Interface): + pass + diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py new file mode 100644 index 000000000..d020a11a6 --- /dev/null +++ b/pyramid/tests/test_authentication.py @@ -0,0 +1,664 @@ +import unittest + +class TestRepozeWho1AuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import IAuthenticationPolicy + verifyObject(IAuthenticationPolicy, self._makeOne()) + + 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_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_effective_principals_None(self): + from repoze.bfg.security import Everyone + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals_userid_only(self): + from repoze.bfg.security import Everyone + from repoze.bfg.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 repoze.bfg.security import Everyone + from repoze.bfg.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 repoze.bfg.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_remember_no_plugins(self): + request = DummyRequest({}) + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(result, []) + + def test_remember(self): + authtkt = DummyWhoPlugin() + request = DummyRequest( + {'repoze.who.plugins':{'auth_tkt':authtkt}}) + policy = self._makeOne() + result = policy.remember(request, 'fred') + self.assertEqual(result[0], request.environ) + self.assertEqual(result[1], {'repoze.who.userid':'fred'}) + + def test_forget_no_plugins(self): + request = DummyRequest({}) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(result, []) + + def test_forget(self): + authtkt = DummyWhoPlugin() + request = DummyRequest( + {'repoze.who.plugins':{'auth_tkt':authtkt}, + 'repoze.who.identity':{'repoze.who.userid':'fred'}, + }) + policy = self._makeOne() + result = policy.forget(request) + self.assertEqual(result[0], request.environ) + self.assertEqual(result[1], request.environ['repoze.who.identity']) + +class TestRemoteUserAuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import IAuthenticationPolicy + verifyObject(IAuthenticationPolicy, self._makeOne()) + + 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 repoze.bfg.security import Everyone + request = DummyRequest({}) + policy = self._makeOne() + self.assertEqual(policy.effective_principals(request), [Everyone]) + + def test_effective_principals(self): + from repoze.bfg.security import Everyone + from repoze.bfg.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 TestAutkTktAuthenticationPolicy(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.authentication import AuthTktAuthenticationPolicy + return AuthTktAuthenticationPolicy + + def _makeOne(self, callback, cookieidentity, **kw): + inst = self._getTargetClass()('secret', callback, **kw) + inst.cookie = DummyCookieHelper(cookieidentity) + return inst + + 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, + ) + self.assertEqual(inst.callback, None) + + def test_class_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import IAuthenticationPolicy + verifyClass(IAuthenticationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthenticationPolicy(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import IAuthenticationPolicy + verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) + + 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 repoze.bfg.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 repoze.bfg.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 repoze.bfg.security import Everyone + from repoze.bfg.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, []) + +class TestAuthTktCookieHelper(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.authentication import AuthTktCookieHelper + return AuthTktCookieHelper + + def _makeOne(self, *arg, **kw): + plugin = self._getTargetClass()(*arg, **kw) + plugin.auth_tkt = DummyAuthTktModule() + return plugin + + def _makeRequest(self, kw=None): + environ = {'wsgi.version': (1,0)} + if kw is not None: + environ.update(kw) + environ['REMOTE_ADDR'] = '1.1.1.1' + environ['SERVER_NAME'] = 'localhost' + return DummyRequest(environ) + + def _cookieValue(self, cookie): + return eval(cookie.value) + + def _parseHeaders(self, headers): + return [ self._parseHeader(header) for header in headers ] + + def _parseHeader(self, header): + cookie = self._parseCookie(header[1]) + return cookie + + def _parseCookie(self, cookie): + from Cookie import SimpleCookie + cookies = SimpleCookie() + cookies.load(cookie) + return cookies.get('auth_tkt') + + def test_identify_nocookie(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.identify(request) + self.assertEqual(result, None) + + def test_identify_good_cookie_include_ip(self): + plugin = self._makeOne('secret', include_ip=True) + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.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(plugin.auth_tkt.value, 'ticket') + self.assertEqual(plugin.auth_tkt.remote_addr, '1.1.1.1') + self.assertEqual(plugin.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): + plugin = self._makeOne('secret', include_ip=False) + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.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(plugin.auth_tkt.value, 'ticket') + self.assertEqual(plugin.auth_tkt.remote_addr, '0.0.0.0') + self.assertEqual(plugin.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): + plugin = self._makeOne('secret', include_ip=False) + plugin.auth_tkt.userid = '1' + plugin.auth_tkt.user_data = 'userid_type:int' + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.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_good_cookie_unknown_useridtype(self): + plugin = self._makeOne('secret', include_ip=False) + plugin.auth_tkt.userid = 'abc' + plugin.auth_tkt.user_data = 'userid_type:unknown' + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.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): + plugin = self._makeOne('secret', include_ip=False) + plugin.auth_tkt.userid = 'encoded'.encode('base64').strip() + plugin.auth_tkt.user_data = 'userid_type:b64str' + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], '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): + plugin = self._makeOne('secret', include_ip=False) + plugin.auth_tkt.userid = '\xc3\xa9ncoded'.encode('base64').strip() + plugin.auth_tkt.user_data = 'userid_type:b64unicode' + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) + result = plugin.identify(request) + self.assertEqual(len(result), 4) + self.assertEqual(result['tokens'], ()) + self.assertEqual(result['userid'], unicode('\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): + plugin = self._makeOne('secret', include_ip=True) + plugin.auth_tkt.parse_raise = True + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) + result = plugin.identify(request) + self.assertEqual(result, None) + + def test_identify_cookie_timed_out(self): + plugin = self._makeOne('secret', timeout=1) + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) + result = plugin.identify(request) + self.assertEqual(result, None) + + def test_identify_cookie_reissue(self): + import time + plugin = self._makeOne('secret', timeout=5, reissue_time=0) + plugin.auth_tkt.timestamp = time.time() + request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) + result = plugin.identify(request) + self.failUnless(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') + + def test_remember(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.remember(request, 'userid') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.failUnless(result[0][1].endswith('; Path=/')) + self.failUnless(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.failUnless(result[1][1].endswith('; Path=/; Domain=localhost')) + self.failUnless(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.failUnless(result[2][1].endswith('; Path=/; Domain=.localhost')) + self.failUnless(result[2][1].startswith('auth_tkt=')) + + def test_remember_include_ip(self): + plugin = self._makeOne('secret', include_ip=True) + request = self._makeRequest() + result = plugin.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.failUnless(result[0][1].endswith('; Path=/')) + self.failUnless(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.failUnless(result[1][1].endswith('; Path=/; Domain=localhost')) + self.failUnless(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.failUnless(result[2][1].endswith('; Path=/; Domain=.localhost')) + self.failUnless(result[2][1].startswith('auth_tkt=')) + + def test_remember_path(self): + plugin = self._makeOne('secret', include_ip=True, + path="/cgi-bin/bfg.cgi/") + request = self._makeRequest() + result = plugin.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.failUnless(result[0][1].endswith('; Path=/cgi-bin/bfg.cgi/')) + self.failUnless(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.failUnless(result[1][1].endswith( + '; Path=/cgi-bin/bfg.cgi/; Domain=localhost')) + self.failUnless(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.failUnless(result[2][1].endswith( + '; Path=/cgi-bin/bfg.cgi/; Domain=.localhost')) + self.failUnless(result[2][1].startswith('auth_tkt=')) + + def test_remember_http_only(self): + plugin = self._makeOne('secret', include_ip=True, http_only=True) + request = self._makeRequest() + result = plugin.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.failUnless(result[0][1].endswith('; HttpOnly')) + self.failUnless(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.failUnless(result[1][1].endswith('; HttpOnly')) + self.failUnless(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.failUnless(result[2][1].endswith('; HttpOnly')) + self.failUnless(result[2][1].startswith('auth_tkt=')) + + def test_remember_secure(self): + plugin = self._makeOne('secret', include_ip=True, secure=True) + request = self._makeRequest() + result = plugin.remember(request, 'other') + self.assertEqual(len(result), 3) + + self.assertEqual(result[0][0], 'Set-Cookie') + self.failUnless('; Secure' in result[0][1]) + self.failUnless(result[0][1].startswith('auth_tkt=')) + + self.assertEqual(result[1][0], 'Set-Cookie') + self.failUnless('; Secure' in result[1][1]) + self.failUnless(result[1][1].startswith('auth_tkt=')) + + self.assertEqual(result[2][0], 'Set-Cookie') + self.failUnless('; Secure' in result[2][1]) + self.failUnless(result[2][1].startswith('auth_tkt=')) + + def test_remember_string_userid(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.remember(request, 'userid') + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + val = self._cookieValue(values[0]) + self.assertEqual(val['userid'], 'userid'.encode('base64').strip()) + self.assertEqual(val['user_data'], 'userid_type:b64str') + + def test_remember_int_userid(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.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): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.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): + plugin = self._makeOne('secret') + request = self._makeRequest() + userid = unicode('\xc2\xa9', 'utf-8') + result = plugin.remember(request, userid) + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + val = self._cookieValue(values[0]) + self.assertEqual(val['userid'], + userid.encode('utf-8').encode('base64').strip()) + self.assertEqual(val['user_data'], 'userid_type:b64unicode') + + def test_remember_max_age(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + result = plugin.remember(request, 'userid', max_age='500') + values = self._parseHeaders(result) + self.assertEqual(len(result), 3) + + self.assertEqual(values[0]['max-age'], '500') + self.failUnless(values[0]['expires']) + + def test_forget(self): + plugin = self._makeOne('secret') + request = self._makeRequest() + headers = plugin.forget(request) + self.assertEqual(len(headers), 3) + name, value = headers[0] + self.assertEqual(name, 'Set-Cookie') + self.assertEqual(value, + 'auth_tkt=""; Path=/; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT') + name, value = headers[1] + self.assertEqual(name, 'Set-Cookie') + self.assertEqual(value, + 'auth_tkt=""; Path=/; Domain=localhost; Max-Age=0; ' + 'Expires=Wed, 31-Dec-97 23:59:59 GMT') + name, value = headers[2] + self.assertEqual(name, 'Set-Cookie') + self.assertEqual(value, + 'auth_tkt=""; Path=/; Domain=.localhost; Max-Age=0; ' + 'Expires=Wed, 31-Dec-97 23:59:59 GMT') + + def test_timeout_lower_than_reissue(self): + self.assertRaises(ValueError, self._makeOne, 'userid', timeout=1, + reissue_time=2) + +class DummyContext: + pass + +class DummyRequest: + def __init__(self, environ): + self.environ = environ + self.callbacks = [] + + 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): + self.timestamp = timestamp + self.userid = userid + self.tokens = tokens + self.user_data = user_data + self.parse_raise = parse_raise + def parse_ticket(secret, value, remote_addr): + self.secret = secret + self.value = value + self.remote_addr = remote_addr + if self.parse_raise: + raise self.BadTicket() + return self.timestamp, self.userid, self.tokens, self.user_data + self.parse_ticket = parse_ticket + + class AuthTicket(object): + def __init__(self, secret, userid, remote_addr, **kw): + self.secret = secret + self.userid = userid + self.remote_addr = remote_addr + self.kw = kw + + def cookie_value(self): + result = {'secret':self.secret, 'userid':self.userid, + 'remote_addr':self.remote_addr} + result.update(self.kw) + result = repr(result) + return result + self.AuthTicket = AuthTicket + + class BadTicket(Exception): + pass + +class DummyResponse: + def __init__(self): + self.headerlist = [] + diff --git a/pyramid/tests/test_authorization.py b/pyramid/tests/test_authorization.py new file mode 100644 index 000000000..6b8c8293a --- /dev/null +++ b/pyramid/tests/test_authorization.py @@ -0,0 +1,189 @@ +import unittest + +from repoze.bfg.testing import cleanUp + +class TestACLAuthorizationPolicy(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from repoze.bfg.authorization import ACLAuthorizationPolicy + return ACLAuthorizationPolicy + + def _makeOne(self): + return self._getTargetClass()() + + def test_class_implements_IAuthorizationPolicy(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import IAuthorizationPolicy + verifyClass(IAuthorizationPolicy, self._getTargetClass()) + + def test_instance_implements_IAuthorizationPolicy(self): + from zope.interface.verify import verifyObject + from repoze.bfg.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 repoze.bfg.security import Deny + from repoze.bfg.security import Allow + from repoze.bfg.security import Everyone + from repoze.bfg.security import Authenticated + from repoze.bfg.security import ALL_PERMISSIONS + from repoze.bfg.security import DENY_ALL + root = DummyContext() + community = DummyContext(__name__='community', __parent__=root) + blog = DummyContext(__name__='blog', __parent__=community) + root.__acl__ = [ + (Allow, Authenticated, VIEW), + ] + community.__acl__ = [ + (Allow, 'fred', ALL_PERMISSIONS), + (Allow, 'wilma', VIEW), + DENY_ALL, + ] + blog.__acl__ = [ + (Allow, 'barney', MEMBER_PERMS), + (Allow, 'wilma', VIEW), + ] + + policy = self._makeOne() + + result = policy.permits(blog, [Everyone, Authenticated, 'wilma'], + 'view') + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'wilma', VIEW)) + self.assertEqual(result.acl, blog.__acl__) + + result = policy.permits(blog, [Everyone, Authenticated, 'wilma'], + 'delete') + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits(blog, [Everyone, Authenticated, 'fred'], 'view') + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + result = policy.permits(blog, [Everyone, Authenticated, 'fred'], + 'doesntevenexistyet') + self.assertEqual(result, True) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits(blog, [Everyone, Authenticated, 'barney'], + 'view') + self.assertEqual(result, True) + self.assertEqual(result.context, blog) + self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS)) + result = policy.permits(blog, [Everyone, Authenticated, 'barney'], + 'administer') + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits(root, [Everyone, Authenticated, 'someguy'], + 'view') + self.assertEqual(result, True) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, (Allow, Authenticated, VIEW)) + result = policy.permits(blog, + [Everyone, Authenticated, 'someguy'], 'view') + self.assertEqual(result, False) + self.assertEqual(result.context, community) + self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) + self.assertEqual(result.acl, community.__acl__) + + result = policy.permits(root, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.context, root) + self.assertEqual(result.ace, '') + self.assertEqual(result.acl, root.__acl__) + + context = DummyContext() + result = policy.permits(context, [Everyone], 'view') + self.assertEqual(result, False) + self.assertEqual(result.ace, '') + self.assertEqual( + result.acl, + '') + + def test_principals_allowed_by_permission_direct(self): + from repoze.bfg.security import Allow + from repoze.bfg.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(self): + from repoze.bfg.security import Allow + from repoze.bfg.security import Deny + from repoze.bfg.security import DENY_ALL + from repoze.bfg.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, []) + +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/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py new file mode 100644 index 000000000..d9cefbd67 --- /dev/null +++ b/pyramid/tests/test_chameleon_text.py @@ -0,0 +1,198 @@ +import unittest + +from repoze.bfg.testing import cleanUp + +class Base: + def setUp(self): + cleanUp() + import os + try: + # avoid spew from chameleon logger? + os.unlink(self._getTemplatePath('minimal.txt.py')) + except: + pass + + def tearDown(self): + cleanUp() + + def _getTemplatePath(self, name): + import os + here = os.path.abspath(os.path.dirname(__file__)) + return os.path.join(here, 'fixtures', name) + + def _registerUtility(self, utility, iface, name=''): + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + reg.registerUtility(utility, iface, name=name) + return reg + + +class TextTemplateRendererTests(Base, unittest.TestCase): + def setUp(self): + from repoze.bfg.configuration import Configurator + from repoze.bfg.registry import Registry + registry = Registry() + self.config = Configurator(registry=registry) + self.config.begin() + + def tearDown(self): + self.config.end() + + def _getTargetClass(self): + from repoze.bfg.chameleon_text import TextTemplateRenderer + return TextTemplateRenderer + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_instance_implements_ITemplate(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import ITemplateRenderer + path = self._getTemplatePath('minimal.txt') + verifyObject(ITemplateRenderer, self._makeOne(path)) + + def test_class_implements_ITemplate(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import ITemplateRenderer + verifyClass(ITemplateRenderer, self._getTargetClass()) + + def test_template_reified(self): + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template, instance.__dict__['template']) + + def test_template_with_ichameleon_translate(self): + from repoze.bfg.interfaces import IChameleonTranslate + def ct(): pass + self.config.registry.registerUtility(ct, IChameleonTranslate) + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.translate, ct) + + def test_template_with_debug_templates(self): + self.config.add_settings({'debug_templates':True}) + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.debug, True) + + def test_template_with_reload_templates(self): + self.config.add_settings({'reload_templates':True}) + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.auto_reload, True) + + def test_template_with_emptydict(self): + from repoze.bfg.interfaces import ISettings + self.config.registry.registerUtility({}, ISettings) + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.auto_reload, False) + self.assertEqual(template.debug, False) + + def test_call(self): + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + result = instance({}, {}) + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + + def test_call_with_nondict_value(self): + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.assertRaises(ValueError, instance, None, {}) + + def test_call_nonminimal(self): + nonminimal = self._getTemplatePath('nonminimal.txt') + instance = self._makeOne(nonminimal) + result = instance({'name':'Chris'}, {}) + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello, Chris!\n') + + def test_implementation(self): + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + result = instance.implementation()() + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + +class RenderTemplateTests(Base, unittest.TestCase): + def _callFUT(self, name, **kw): + from repoze.bfg.chameleon_text import render_template + return render_template(name, **kw) + + def test_it(self): + minimal = self._getTemplatePath('minimal.txt') + result = self._callFUT(minimal) + self.failUnless(isinstance(result, str)) + self.assertEqual(result, 'Hello.\n') + +class RenderTemplateToResponseTests(Base, unittest.TestCase): + def _callFUT(self, name, **kw): + from repoze.bfg.chameleon_text import render_template_to_response + return render_template_to_response(name, **kw) + + def test_minimal(self): + minimal = self._getTemplatePath('minimal.txt') + result = self._callFUT(minimal) + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, ['Hello.\n']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + + def test_iresponsefactory_override(self): + from webob import Response + class Response2(Response): + pass + from repoze.bfg.interfaces import IResponseFactory + self._registerUtility(Response2, IResponseFactory) + minimal = self._getTemplatePath('minimal.txt') + result = self._callFUT(minimal) + self.failUnless(isinstance(result, Response2)) + +class GetRendererTests(Base, unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.chameleon_text import get_renderer + return get_renderer(name) + + def test_it(self): + from repoze.bfg.interfaces import IRendererFactory + class Dummy: + template = object() + def implementation(self): pass + renderer = Dummy() + def rf(spec): + return renderer + self._registerUtility(rf, IRendererFactory, name='foo') + result = self._callFUT('foo') + self.failUnless(result is renderer) + +class GetTemplateTests(Base, unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.chameleon_text import get_template + return get_template(name) + + def test_it(self): + from repoze.bfg.interfaces import IRendererFactory + class Dummy: + template = object() + def implementation(self): + return self.template + renderer = Dummy() + def rf(spec): + return renderer + self._registerUtility(rf, IRendererFactory, name='foo') + result = self._callFUT('foo') + self.failUnless(result is renderer.template) + diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py new file mode 100644 index 000000000..a0f01701a --- /dev/null +++ b/pyramid/tests/test_chameleon_zpt.py @@ -0,0 +1,194 @@ +import unittest + +from repoze.bfg.testing import cleanUp + +class Base(object): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTemplatePath(self, name): + import os + here = os.path.abspath(os.path.dirname(__file__)) + return os.path.join(here, 'fixtures', name) + + def _registerUtility(self, utility, iface, name=''): + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + reg.registerUtility(utility, iface, name=name) + return reg + +class ZPTTemplateRendererTests(Base, unittest.TestCase): + def setUp(self): + from repoze.bfg.configuration import Configurator + from repoze.bfg.registry import Registry + registry = Registry() + self.config = Configurator(registry=registry) + self.config.begin() + + def tearDown(self): + self.config.end() + + def _getTargetClass(self): + from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer + return ZPTTemplateRenderer + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_instance_implements_ITemplate(self): + from zope.interface.verify import verifyObject + from repoze.bfg.interfaces import ITemplateRenderer + path = self._getTemplatePath('minimal.pt') + verifyObject(ITemplateRenderer, self._makeOne(path)) + + def test_class_implements_ITemplate(self): + from zope.interface.verify import verifyClass + from repoze.bfg.interfaces import ITemplateRenderer + verifyClass(ITemplateRenderer, self._getTargetClass()) + + def test_call(self): + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + result = instance({}, {}) + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, + '
\n
') + + def test_template_reified(self): + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template, instance.__dict__['template']) + + def test_template_with_ichameleon_translate(self): + from repoze.bfg.interfaces import IChameleonTranslate + def ct(): pass + self.config.registry.registerUtility(ct, IChameleonTranslate) + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.translate, ct) + + def test_template_with_debug_templates(self): + self.config.add_settings({'debug_templates':True}) + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.debug, True) + + def test_template_with_reload_templates(self): + self.config.add_settings({'reload_templates':True}) + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.auto_reload, True) + + def test_template_with_emptydict(self): + from repoze.bfg.interfaces import ISettings + self.config.registry.registerUtility({}, ISettings) + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template.auto_reload, False) + self.assertEqual(template.debug, False) + + def test_call_with_nondict_value(self): + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.assertRaises(ValueError, instance, None, {}) + + def test_implementation(self): + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + result = instance.implementation()() + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, + '
\n
') + + +class RenderTemplateTests(Base, unittest.TestCase): + def _callFUT(self, name, **kw): + from repoze.bfg.chameleon_zpt import render_template + return render_template(name, **kw) + + def test_it(self): + minimal = self._getTemplatePath('minimal.pt') + result = self._callFUT(minimal) + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, + '
\n
') + +class RenderTemplateToResponseTests(Base, unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, name, **kw): + from repoze.bfg.chameleon_zpt import render_template_to_response + return render_template_to_response(name, **kw) + + def test_it(self): + minimal = self._getTemplatePath('minimal.pt') + result = self._callFUT(minimal) + from webob import Response + self.failUnless(isinstance(result, Response)) + self.assertEqual(result.app_iter, + ['
\n
']) + self.assertEqual(result.status, '200 OK') + self.assertEqual(len(result.headerlist), 2) + + def test_iresponsefactory_override(self): + from webob import Response + class Response2(Response): + pass + from repoze.bfg.interfaces import IResponseFactory + self._registerUtility(Response2, IResponseFactory) + minimal = self._getTemplatePath('minimal.pt') + result = self._callFUT(minimal) + self.failUnless(isinstance(result, Response2)) + +class GetRendererTests(Base, unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.chameleon_zpt import get_renderer + return get_renderer(name) + + def test_it(self): + from repoze.bfg.interfaces import IRendererFactory + class Dummy: + template = object() + def implementation(self): pass + renderer = Dummy() + def rf(spec): + return renderer + self._registerUtility(rf, IRendererFactory, name='foo') + result = self._callFUT('foo') + self.failUnless(result is renderer) + +class GetTemplateTests(Base, unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.chameleon_zpt import get_template + return get_template(name) + + def test_it(self): + from repoze.bfg.interfaces import IRendererFactory + class Dummy: + template = object() + def implementation(self): + return self.template + renderer = Dummy() + def rf(spec): + return renderer + self._registerUtility(rf, IRendererFactory, name='foo') + result = self._callFUT('foo') + self.failUnless(result is renderer.template) diff --git a/pyramid/tests/test_compat.py b/pyramid/tests/test_compat.py new file mode 100644 index 000000000..66ea61860 --- /dev/null +++ b/pyramid/tests/test_compat.py @@ -0,0 +1,9 @@ +import unittest + +class TestAliases(unittest.TestCase): + def test_all(self): + from repoze.bfg.compat import all + self.assertEqual(all([True, True]), True) + self.assertEqual(all([False, False]), False) + self.assertEqual(all([False, True]), False) + diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py new file mode 100644 index 000000000..47649d6ba --- /dev/null +++ b/pyramid/tests/test_configuration.py @@ -0,0 +1,4335 @@ +import unittest + +from repoze.bfg import testing + +class ConfiguratorTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from repoze.bfg.configuration import Configurator + return Configurator(*arg, **kw) + + def _registerRenderer(self, config, name='.txt'): + from repoze.bfg.interfaces import IRendererFactory + from repoze.bfg.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + def __init__(self, path): + self.__class__.path = path + def __call__(self, *arg): + return 'Hello!' + config.registry.registerUtility(Renderer, IRendererFactory, name=name) + return Renderer + + def _getViewCallable(self, config, ctx_iface=None, request_iface=None, + name='', exception_view=False): + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + if exception_view: + classifier = IExceptionViewClassifier + else: + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), IView, name=name, + default=None) + + def _getRouteRequestIface(self, config, name): + from repoze.bfg.interfaces import IRouteRequest + iface = config.registry.getUtility(IRouteRequest, name) + return iface + + def _assertNotFound(self, wrapper, *arg): + from repoze.bfg.exceptions import NotFound + self.assertRaises(NotFound, wrapper, *arg) + + 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 _registerLogger(self, config): + from repoze.bfg.interfaces import IDebugLogger + logger = DummyLogger() + config.registry.registerUtility(logger, IDebugLogger) + return logger + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def _registerSecurityPolicy(self, config, permissive): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + policy = DummySecurityPolicy(permissive) + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + + def _registerSettings(self, config, **settings): + from repoze.bfg.interfaces import ISettings + config.registry.registerUtility(settings, ISettings) + + def test_ctor_no_registry(self): + import sys + from repoze.bfg.interfaces import ISettings + from repoze.bfg.configuration import Configurator + from repoze.bfg.interfaces import IRendererFactory + config = Configurator() + this_pkg = sys.modules['repoze.bfg.tests'] + self.failUnless(config.registry.getUtility(ISettings)) + self.assertEqual(config.package, this_pkg) + self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) + self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.txt')) + + def test_begin(self): + from repoze.bfg.configuration 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 repoze.bfg.configuration import Configurator + config = Configurator() + request = object() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin(request=request) + self.assertEqual(manager.pushed, + {'registry':config.registry, 'request':request}) + self.assertEqual(manager.popped, False) + + def test_end(self): + from repoze.bfg.configuration import Configurator + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.end() + self.assertEqual(manager.pushed, None) + self.assertEqual(manager.popped, True) + + def test_ctor_with_package_registry(self): + import sys + from repoze.bfg.configuration import Configurator + bfg_pkg = sys.modules['repoze.bfg'] + config = Configurator(package=bfg_pkg) + self.assertEqual(config.package, bfg_pkg) + + def test_ctor_noreg_custom_settings(self): + from repoze.bfg.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 repoze.bfg.interfaces import IDebugLogger + config = self._makeOne() + logger = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'repoze.bfg.debug') + + def test_ctor_noreg_debug_logger_non_None(self): + from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy + policy = object() + config = self._makeOne(authentication_policy=policy) + result = config.registry.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_ctor_authorization_policy_only(self): + from repoze.bfg.exceptions import ConfigurationError + policy = object() + self.assertRaises(ConfigurationError, + self._makeOne, authorization_policy=policy) + + def test_ctor_no_root_factory(self): + from repoze.bfg.interfaces import IRootFactory + config = self._makeOne() + self.failUnless(config.registry.getUtility(IRootFactory)) + + def test_ctor_alternate_renderers(self): + from repoze.bfg.interfaces import IRendererFactory + renderer = object() + config = self._makeOne(renderers=[('yeah', renderer)]) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_ctor_default_permission(self): + from repoze.bfg.interfaces import IDefaultPermission + config = self._makeOne(default_permission='view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') + + def test_with_package_module(self): + from repoze.bfg.tests import test_configuration + import repoze.bfg.tests + config = self._makeOne() + newconfig = config.with_package(test_configuration) + self.assertEqual(newconfig.package, repoze.bfg.tests) + + def test_with_package_package(self): + import repoze.bfg.tests + config = self._makeOne() + newconfig = config.with_package(repoze.bfg.tests) + self.assertEqual(newconfig.package, repoze.bfg.tests) + + def test_maybe_dotted_string_success(self): + import repoze.bfg.tests + config = self._makeOne() + result = config.maybe_dotted('repoze.bfg.tests') + self.assertEqual(result, repoze.bfg.tests) + + def test_maybe_dotted_string_fail(self): + from repoze.bfg.configuration import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.maybe_dotted, 'cant.be.found') + + def test_maybe_dotted_notstring_success(self): + import repoze.bfg.tests + config = self._makeOne() + result = config.maybe_dotted(repoze.bfg.tests) + self.assertEqual(result, repoze.bfg.tests) + + def test_absolute_resource_spec_already_absolute(self): + import repoze.bfg.tests + config = self._makeOne(package=repoze.bfg.tests) + result = config.absolute_resource_spec('already:absolute') + self.assertEqual(result, 'already:absolute') + + def test_absolute_resource_spec_notastring(self): + import repoze.bfg.tests + config = self._makeOne(package=repoze.bfg.tests) + result = config.absolute_resource_spec(None) + self.assertEqual(result, None) + + def test_absolute_resource_spec_relative(self): + import repoze.bfg.tests + config = self._makeOne(package=repoze.bfg.tests) + result = config.absolute_resource_spec('templates') + self.assertEqual(result, 'repoze.bfg.tests:templates') + + def test_setup_registry_fixed(self): + class DummyRegistry(object): + def subscribers(self, events, name): + self.events = events + return events + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + config.add_view = lambda *arg, **kw: False + config.setup_registry() + self.assertEqual(reg.has_listeners, True) + self.assertEqual(reg.notify(1), None) + self.assertEqual(reg.events, (1,)) + + def test_setup_registry_registers_default_exceptionresponse_view(self): + from repoze.bfg.interfaces import IExceptionResponse + from repoze.bfg.view import default_exceptionresponse_view + class DummyRegistry(object): + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + views = [] + config.add_view = lambda *arg, **kw: views.append((arg, kw)) + config.setup_registry() + self.assertEqual(views[0], ((default_exceptionresponse_view,), + {'context':IExceptionResponse})) + + def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.exceptions import NotFound + from repoze.bfg.registry import Registry + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() # registers IExceptionResponse default view + def myview(context, request): + return 'OK' + config.add_view(myview, context=NotFound) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_setup_registry_custom_settings(self): + from repoze.bfg.registry import Registry + from repoze.bfg.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 repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + logger = reg.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'repoze.bfg.debug') + + def test_setup_registry_debug_logger_non_None(self): + from repoze.bfg.registry import Registry + from repoze.bfg.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_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger='repoze.bfg.tests') + result = reg.getUtility(IDebugLogger) + import repoze.bfg.tests + self.assertEqual(result, repoze.bfg.tests) + + def test_setup_registry_authentication_policy(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IAuthenticationPolicy + policy = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy=policy) + result = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_setup_registry_authentication_policy_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IAuthenticationPolicy + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy='repoze.bfg.tests') + result = reg.getUtility(IAuthenticationPolicy) + import repoze.bfg.tests + self.assertEqual(result, repoze.bfg.tests) + + def test_setup_registry_authorization_policy_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IAuthorizationPolicy + reg = Registry() + config = self._makeOne(reg) + dummy = object() + config.setup_registry(authentication_policy=dummy, + authorization_policy='repoze.bfg.tests') + result = reg.getUtility(IAuthorizationPolicy) + import repoze.bfg.tests + self.assertEqual(result, repoze.bfg.tests) + + def test_setup_registry_authorization_policy_only(self): + from repoze.bfg.registry import Registry + from repoze.bfg.exceptions import ConfigurationError + policy = object() + reg = Registry() + config = self._makeOne(reg) + config = self.assertRaises(ConfigurationError, + config.setup_registry, + authorization_policy=policy) + + def test_setup_registry_default_root_factory(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + self.failUnless(reg.getUtility(IRootFactory)) + + def test_setup_registry_dottedname_root_factory(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + import repoze.bfg.tests + config.setup_registry(root_factory='repoze.bfg.tests') + self.assertEqual(reg.getUtility(IRootFactory), repoze.bfg.tests) + + def test_setup_registry_locale_negotiator_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + import repoze.bfg.tests + config.setup_registry(locale_negotiator='repoze.bfg.tests') + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, repoze.bfg.tests) + + def test_setup_registry_locale_negotiator(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + negotiator = object() + config.setup_registry(locale_negotiator=negotiator) + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, negotiator) + + def test_setup_registry_request_factory(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(request_factory=factory) + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_request_factory_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + import repoze.bfg.tests + config.setup_registry(request_factory='repoze.bfg.tests') + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, repoze.bfg.tests) + + def test_setup_registry_renderer_globals_factory(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(renderer_globals_factory=factory) + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_renderer_globals_factory_dottedname(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + import repoze.bfg.tests + config.setup_registry(renderer_globals_factory='repoze.bfg.tests') + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, repoze.bfg.tests) + + def test_setup_registry_alternate_renderers(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IRendererFactory + renderer = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(renderers=[('yeah', renderer)]) + self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_setup_registry_default_permission(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import IDefaultPermission + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(default_permission='view') + self.assertEqual(reg.getUtility(IDefaultPermission), 'view') + + def test_get_settings_nosettings(self): + from repoze.bfg.registry import Registry + reg = Registry() + config = self._makeOne(reg) + self.assertEqual(config.get_settings(), None) + + def test_get_settings_withsettings(self): + from repoze.bfg.interfaces import ISettings + settings = {'a':1} + config = self._makeOne() + config.registry.registerUtility(settings, ISettings) + self.assertEqual(config.get_settings(), settings) + + def test_add_settings_settings_already_registered(self): + from repoze.bfg.registry import Registry + from repoze.bfg.interfaces import ISettings + reg = Registry() + config = self._makeOne(reg) + config._set_settings({'a':1}) + config.add_settings({'b':2}) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['a'], 1) + self.assertEqual(settings['b'], 2) + + def test_add_settings_settings_not_yet_registered(self): + from repoze.bfg.registry import Registry + from repoze.bfg.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_subscriber_defaults(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne() + 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 implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne() + 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 repoze.bfg.tests + from repoze.bfg.interfaces import INewRequest + config = self._makeOne() + config.add_subscriber('repoze.bfg.tests', + 'repoze.bfg.interfaces.INewRequest') + handlers = list(config.registry.registeredHandlers()) + self.assertEqual(len(handlers), 1) + handler = handlers[0] + self.assertEqual(handler.handler, repoze.bfg.tests) + self.assertEqual(handler.required, (INewRequest,)) + + def test_add_object_event_subscriber(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + object = 'foo' + implements(IEvent) + event = Event() + L = [] + def subscriber(object, event): + L.append(event) + config = self._makeOne() + 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_make_wsgi_app(self): + from repoze.bfg.router import Router + from repoze.bfg.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.failUnless(manager.popped) + self.assertEqual(len(subscriber), 1) + self.failUnless(IApplicationCreated.providedBy(subscriber[0])) + + def test_load_zcml_default(self): + import repoze.bfg.tests.fixtureapp + config = self._makeOne(package=repoze.bfg.tests.fixtureapp) + registry = config.load_zcml() + from repoze.bfg.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_routesapp(self): + from repoze.bfg.interfaces import IRoutesMapper + config = self._makeOne() + config.load_zcml('repoze.bfg.tests.routesapp:configure.zcml') + self.failUnless(config.registry.getUtility(IRoutesMapper)) + + def test_load_zcml_fixtureapp(self): + from repoze.bfg.tests.fixtureapp.models import IFixture + config = self._makeOne() + config.load_zcml('repoze.bfg.tests.fixtureapp:configure.zcml') + self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_relative_filename(self): + import repoze.bfg.tests.fixtureapp + config = self._makeOne(package=repoze.bfg.tests.fixtureapp) + registry = config.load_zcml('configure.zcml') + from repoze.bfg.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_absolute_filename(self): + import os + import repoze.bfg.tests.fixtureapp + config = self._makeOne(package=repoze.bfg.tests.fixtureapp) + dn = os.path.dirname(repoze.bfg.tests.fixtureapp.__file__) + c_z = os.path.join(dn, 'configure.zcml') + registry = config.load_zcml(c_z) + from repoze.bfg.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_lock_and_unlock(self): + config = self._makeOne() + dummylock = DummyLock() + config.load_zcml( + 'repoze.bfg.tests.fixtureapp:configure.zcml', + lock=dummylock) + self.assertEqual(dummylock.acquired, True) + self.assertEqual(dummylock.released, True) + + def test_add_view_view_callable_None_no_renderer(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_view) + + def test_add_view_with_request_type_and_route_name(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + view = lambda *arg: 'OK' + self.assertRaises(ConfigurationError, config.add_view, view, '', None, + None, True, True) + + def test_add_view_with_request_type_methodname_string(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, request_type='GET') + wrapper = self._getViewCallable(config) + request = DummyRequest() + request.method = 'POST' + self._assertNotFound(wrapper, None, request) + request = DummyRequest() + request.method = 'GET' + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_with_request_type(self): + from zope.interface import directlyProvides + from repoze.bfg.interfaces import IRequest + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, + request_type='repoze.bfg.interfaces.IRequest') + 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() + self._registerRenderer(config, name='dummy') + config.add_view(renderer='dummy') + view = self._getViewCallable(config) + self.failUnless('Hello!' in view(None, None).body) + + def test_add_view_wrapped_view_is_decorated(self): + def view(request): # request-only wrapper + """ """ + config = self._makeOne() + 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__) + + def test_add_view_view_callable_dottedname(self): + config = self._makeOne() + config.add_view(view='repoze.bfg.tests.test_configuration.dummy_view') + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_function_callable(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_function_callable_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance(self): + class AView: + def __call__(self, context, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance_requestonly(self): + class AView: + def __call__(self, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class(self): + class view: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne() + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_context_as_class(self): + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne() + config.add_view(context=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_iface(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(context=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(context='repoze.bfg.tests.test_configuration.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for__as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(for_='repoze.bfg.tests.test_configuration.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_class(self): + # ``for_`` is older spelling for ``context`` + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne() + config.add_view(for_=Foo, view=view) + 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`` + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(for_=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_trumps_for(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne() + class Foo: + pass + config.add_view(context=IDummy, for_=Foo, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_register_secured_view(self): + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import ISecuredView + from repoze.bfg.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne() + config.add_view(view=view) + 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 zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne() + config.add_view(view=view, context=RuntimeError) + 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 repoze.bfg.compat import md5 + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, xhr=True) + wrapper = self._getViewCallable(config) + self.failIf(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 repoze.bfg.compat import md5 + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne() + 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) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(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 zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(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 zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne() + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(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 repoze.bfg.configuration import DEFAULT_PHASH + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(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 repoze.bfg.configuration import DEFAULT_PHASH + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne() + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(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 zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne() + 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) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_multiview_replaces_existing_securedview(self): + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import ISecuredView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + ISecuredView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_securedview(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import ISecuredView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne() + 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) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_accept_multiview_replaces_existing_view(self): + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2, accept='text/html') + wrapper = self._getViewCallable(config) + self.failUnless(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_exc_with_accept_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne() + 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) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(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 zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.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() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(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 zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.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() + 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) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(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 zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + view = DummyMultiView() + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_exc_multiview_replaces_multiview(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + view = DummyMultiView() + config = self._makeOne() + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_multiview_context_superclass_then_subclass(self): + from zope.interface import Interface + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + class ISuper(Interface): + pass + class ISub(ISuper): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne() + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, ISuper), IView, name='') + config.add_view(view=view2, for_=ISub) + wrapper = self._getViewCallable(config, ISuper, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + wrapper = self._getViewCallable(config, ISub, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK2') + + def test_add_view_multiview_exception_superclass_then_subclass(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IMultiView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + class Super(Exception): + pass + class Sub(Super): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne() + 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) + wrapper = self._getViewCallable( + config, implementedBy(Super), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Super), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + wrapper = self._getViewCallable( + config, implementedBy(Sub), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Sub), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK2') + + def test_add_view_multiview_call_ordering(self): + 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() + config.add_view(view=view1) + config.add_view(view=view2, request_method='POST') + config.add_view(view=view3,request_param='param') + config.add_view(view=view4, containment=IDummy) + config.add_view(view=view5, request_method='POST',request_param='param') + config.add_view(view=view6, request_method='POST', containment=IDummy) + config.add_view(view=view7, request_param='param', containment=IDummy) + config.add_view(view=view8, request_method='POST',request_param='param', + containment=IDummy) + + wrapper = self._getViewCallable(config) + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view1') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view2') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {'param':'1'} + request.method = 'GET' + self.assertEqual(wrapper(ctx, request), 'view3') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view4') + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view5') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view6') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view7') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view8') + + def test_add_view_with_template_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() + renderer = self._registerRenderer(config) + fixture = 'repoze.bfg.tests:fixtures/minimal.txt' + config.add_view(view=view, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + self.assertEqual(renderer.path, 'repoze.bfg.tests:fixtures/minimal.txt') + + def test_add_view_with_template_renderer_no_callable(self): + config = self._makeOne() + renderer = self._registerRenderer(config) + fixture = 'repoze.bfg.tests:fixtures/minimal.txt' + config.add_view(view=None, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + self.assertEqual(renderer.path, 'repoze.bfg.tests:fixtures/minimal.txt') + + def test_add_view_with_request_type_as_iface(self): + from zope.interface import directlyProvides + def view(context, request): + return 'OK' + config = self._makeOne() + config.add_view(request_type=IDummy, view=view) + 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): + from repoze.bfg.exceptions import ConfigurationError + 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 zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, route_name='foo') + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper = self._getViewCallable(config, None) + self.assertEqual(wrapper, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper = self._getViewCallable(config, request_iface=request_iface) + self.failIfEqual(wrapper, None) + self.assertEqual(wrapper(None, None), 'OK') + + def test_deferred_route_views_retains_custom_predicates(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, route_name='foo', custom_predicates=('123',)) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['custom_predicates'], ('123',)) + + def test_add_view_with_route_name_exception(self): + from zope.interface import implementedBy + from zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, route_name='foo', context=RuntimeError) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + exception_view=True) + self.assertEqual(wrapper_exc_view, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface, exception_view=True) + self.failIfEqual(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): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, request_method='POST') + 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() + 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_param_noval_true(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, request_param='abc') + 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() + 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): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, request_param='abc=123') + 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() + 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): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, xhr=True) + 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() + 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): + from repoze.bfg.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, header='Host:a\\') + + def test_add_view_with_header_noval_match(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, header='Host') + 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() + 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): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, header=r'Host:\d') + 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() + 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 repoze.bfg.exceptions import NotFound + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NoHost':'1'} + self.assertRaises(NotFound, wrapper, None, request) + + def test_add_view_with_accept_match(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_accept_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/html'] + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_containment_true(self): + from zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, containment=IDummy) + 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() + 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 zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view( + view=view, + containment='repoze.bfg.tests.test_configuration.IDummy') + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_path_info_badregex(self): + from repoze.bfg.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, path_info='\\') + + def test_add_view_with_path_info_match(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_path_info_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne() + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_custom_predicates_match(self): + view = lambda *arg: 'OK' + config = self._makeOne() + def pred1(context, request): + return True + def pred2(context, request): + return True + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_custom_predicates_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne() + def pred1(context, request): + return True + def pred2(context, request): + return False + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self._assertNotFound(wrapper, None, request) + + def test_add_view_custom_predicate_bests_standard_predicate(self): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne() + def pred1(context, request): + return True + config.add_view(view=view, custom_predicates=(pred1,)) + config.add_view(view=view2, request_method='GET') + 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): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne() + config.add_view(view=view, request_method='GET', xhr=True) + config.add_view(view=view2, request_method='GET') + 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) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'second') + + def test_add_view_with_permission(self): + 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) + config.add_view(view=view1, permission='view') + 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): + 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') + config.add_view(view=view1) + 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): + view1 = lambda *arg: 'OK' + class DummyPolicy(object): pass # wont be called + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy) + config.add_view(view=view1) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def _assertRoute(self, config, name, path, num_predicates=0): + from repoze.bfg.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_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 repoze.bfg.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() + route = config.add_route('name', 'path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_with_factory(self): + config = self._makeOne() + factory = object() + route = config.add_route('name', 'path', factory=factory) + self.assertEqual(route.factory, factory) + + def test_add_route_with_factory_dottedname(self): + config = self._makeOne() + route = config.add_route( + 'name', 'path', + factory='repoze.bfg.tests.test_configuration.dummyfactory') + self.assertEqual(route.factory, dummyfactory) + + def test_add_route_with_xhr(self): + config = self._makeOne() + 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() + 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() + config.add_route('name', 'path', path_info='/foo') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.path_info = '/' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_param(self): + config = self._makeOne() + config.add_route('name', 'path', request_param='abc') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.params = {'abc':'123'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.params = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_custom_predicates(self): + config = self._makeOne() + def pred1(context, request): pass + def pred2(context, request): pass + config.add_route('name', 'path', custom_predicates=(pred1, pred2)) + route = self._assertRoute(config, 'name', 'path', 2) + self.assertEqual(route.predicates, [pred1, pred2]) + + def test_add_route_with_header(self): + config = self._makeOne() + 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() + config.add_route('name', 'path', accept='text/xml') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.accept = ['text/html'] + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_view(self): + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + + def test_add_route_with_view_context(self): + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_exception(self): + from zope.interface import implementedBy + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=RuntimeError) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable( + config, ctx_iface=IOther, + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_for(self): + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_for=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_for_(self): + config = self._makeOne() + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, for_=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_renderer(self): + config = self._makeOne() + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + view_renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_attr(self): + config = self._makeOne() + self._registerRenderer(config) + class View(object): + def __init__(self, context, request): + pass + def alt(self): + return 'OK' + config.add_route('name', 'path', view=View, view_attr='alt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_route_with_view_renderer_alias(self): + config = self._makeOne() + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_permission(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + config = self._makeOne() + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.failUnless(hasattr(wrapper, '__call_permissive__')) + + def test_add_route_with_view_permission_alias(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + config = self._makeOne() + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.failUnless(hasattr(wrapper, '__call_permissive__')) + + def test_add_route_no_pattern_with_path(self): + config = self._makeOne() + route = config.add_route('name', path='path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_no_path_no_pattern(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_route, 'name') + + def test_add_route_with_pregenerator(self): + config = self._makeOne() + route = config.add_route('name', 'pattern', pregenerator='123') + self.assertEqual(route.pregenerator, '123') + + def test__override_not_yet_registered(self): + from repoze.bfg.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + config = self._makeOne() + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + overrides = config.registry.queryUtility(IPackageOverrides, + name='package') + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test__override_already_registered(self): + from repoze.bfg.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + overrides = DummyOverrides(package) + config = self._makeOne() + config.registry.registerUtility(overrides, IPackageOverrides, + name='package') + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test_add_static_here_no_utility_registered(self): + from repoze.bfg.static import PackageURLParser + from zope.interface import implementedBy + from repoze.bfg.static import StaticURLInfo + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + config = self._makeOne() + config.add_static_view('static', 'fixtures/static') + request_type = self._getRouteRequestIface(config, 'static/') + route = self._assertRoute(config, 'static/', 'static/*subpath') + self.assertEqual(route.factory.__class__, type(lambda x: x)) + iface = implementedBy(StaticURLInfo) + wrapped = config.registry.adapters.lookup( + (IViewClassifier, request_type, iface), IView, name='') + request = self._makeRequest(config) + self.assertEqual(wrapped(None, request).__class__, PackageURLParser) + + def test_add_static_view_package_relative(self): + from repoze.bfg.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne() + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'repoze.bfg.tests:fixtures/static') + self.assertEqual(info.added, + [('static', 'repoze.bfg.tests:fixtures/static', {})]) + + def test_add_static_view_package_here_relative(self): + from repoze.bfg.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne() + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'fixtures/static') + self.assertEqual(info.added, + [('static', 'repoze.bfg.tests:fixtures/static', {})]) + + def test_add_static_view_absolute(self): + import os + from repoze.bfg.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne() + config.registry.registerUtility(info, IStaticURLInfo) + here = os.path.dirname(__file__) + static_path = os.path.join(here, 'fixtures', 'static') + config.add_static_view('static', static_path) + self.assertEqual(info.added, + [('static', static_path, {})]) + + def test_set_notfound_view(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.exceptions import NotFound + config = self._makeOne() + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, (None, request)) + + def test_set_notfound_view_request_has_context(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.exceptions import NotFound + config = self._makeOne() + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test_set_forbidden_view(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.exceptions import Forbidden + config = self._makeOne() + view = lambda *arg: 'OK' + config.set_forbidden_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_set_forbidden_view_request_has_context(self): + from zope.interface import implementedBy + from repoze.bfg.interfaces import IRequest + from repoze.bfg.exceptions import Forbidden + config = self._makeOne() + view = lambda *arg: arg + config.set_forbidden_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test__set_authentication_policy(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + config = self._makeOne() + policy = object() + config._set_authentication_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthenticationPolicy), policy) + + def test__set_authorization_policy(self): + from repoze.bfg.interfaces import IAuthorizationPolicy + config = self._makeOne() + policy = object() + config._set_authorization_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), policy) + + def test_set_locale_negotiator(self): + from repoze.bfg.interfaces import ILocaleNegotiator + config = self._makeOne() + def negotiator(request): pass + config.set_locale_negotiator(negotiator) + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + negotiator) + + def test_set_locale_negotiator_dottedname(self): + from repoze.bfg.interfaces import ILocaleNegotiator + config = self._makeOne() + config.set_locale_negotiator( + 'repoze.bfg.tests.test_configuration.dummyfactory') + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + dummyfactory) + + def test_set_request_factory(self): + from repoze.bfg.interfaces import IRequestFactory + config = self._makeOne() + factory = object() + config.set_request_factory(factory) + self.assertEqual(config.registry.getUtility(IRequestFactory), factory) + + def test_set_request_factory_dottedname(self): + from repoze.bfg.interfaces import IRequestFactory + config = self._makeOne() + config.set_request_factory( + 'repoze.bfg.tests.test_configuration.dummyfactory') + self.assertEqual(config.registry.getUtility(IRequestFactory), + dummyfactory) + + def test_set_renderer_globals_factory(self): + from repoze.bfg.interfaces import IRendererGlobalsFactory + config = self._makeOne() + factory = object() + config.set_renderer_globals_factory(factory) + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + factory) + + def test_set_renderer_globals_factory_dottedname(self): + from repoze.bfg.interfaces import IRendererGlobalsFactory + config = self._makeOne() + config.set_renderer_globals_factory( + 'repoze.bfg.tests.test_configuration.dummyfactory') + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + dummyfactory) + + def test_set_default_permission(self): + from repoze.bfg.interfaces import IDefaultPermission + config = self._makeOne() + config.set_default_permission('view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), + 'view') + + def test_add_translation_dirs_missing_dir(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_translation_dirs, + '/wont/exist/on/my/system') + + def test_add_translation_dirs_resource_spec(self): + import os + from repoze.bfg.interfaces import ITranslationDirectories + config = self._makeOne() + config.add_translation_dirs('repoze.bfg.tests.localeapp:locale') + here = os.path.dirname(__file__) + locale = os.path.join(here, 'localeapp', 'locale') + self.assertEqual(config.registry.getUtility(ITranslationDirectories), + [locale]) + + def test_add_translation_dirs_registers_chameleon_translate(self): + from repoze.bfg.interfaces import IChameleonTranslate + from repoze.bfg.threadlocal import manager + request = DummyRequest() + config = self._makeOne() + manager.push({'request':request, 'registry':config.registry}) + try: + config.add_translation_dirs('repoze.bfg.tests.localeapp:locale') + translate = config.registry.getUtility(IChameleonTranslate) + self.assertEqual(translate('Approve'), u'Approve') + finally: + manager.pop() + + def test_add_translation_dirs_abspath(self): + import os + from repoze.bfg.interfaces import ITranslationDirectories + config = self._makeOne() + here = os.path.dirname(__file__) + locale = os.path.join(here, 'localeapp', 'locale') + config.add_translation_dirs(locale) + self.assertEqual(config.registry.getUtility(ITranslationDirectories), + [locale]) + + def test_derive_view_function(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_dottedname(self): + config = self._makeOne() + result = config.derive_view( + 'repoze.bfg.tests.test_configuration.dummy_view') + self.failIf(result is dummy_view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_with_renderer(self): + def view(request): + return 'OK' + config = self._makeOne() + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer('moo', moo) + result = config.derive_view(view, renderer='moo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_class_without_attr(self): + class View(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_class_with_attr(self): + class View(object): + def __init__(self, request): + pass + def another(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View, attr='another') + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(view(None, None), 'OK') + + def test__derive_view_as_function_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_with_debug_authorization_no_authpol(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + result = 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.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_with_debug_authorization_no_permission(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerSecurityPolicy(config, True) + logger = self._registerLogger(config) + result = config._derive_view(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_debug_auth_permission_authpol_permitted(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, debug_authorization=True, + reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, True) + result = 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__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + 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__derive_view_debug_auth_permission_authpol_denied(self): + from repoze.bfg.exceptions import Forbidden + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = 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__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertRaises(Forbidden, 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__derive_view_debug_auth_permission_authpol_denied2(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = 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(config) + request.view_name = 'view_name' + request.url = 'url' + permitted = result.__permitted__(None, None) + self.assertEqual(permitted, False) + + def test__derive_view_with_predicates_all(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result(None, None) + self.assertEqual(next, 'OK') + self.assertEqual(predicates, [True, True]) + + def test__derive_view_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 + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result.__predicated__(None, None) + self.assertEqual(next, True) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_predicates_notall(self): + from repoze.bfg.exceptions import NotFound + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return False + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + self.assertRaises(NotFound, result, None, None) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_wrapper_viewname(self): + from webob import Response + from repoze.bfg.interfaces import IView + from repoze.bfg.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, inner_view) + return Response('outer ' + request.wrapped_body) + config = self._makeOne() + config.registry.registerAdapter( + outer_view, (IViewClassifier, None, None), IView, 'owrap') + result = config._derive_view(inner_view, viewname='inner', + wrapper_viewname='owrap') + self.failIf(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest(config) + request.registry = config.registry + response = result(None, request) + self.assertEqual(response.body, 'outer OK') + + def test__derive_view_with_wrapper_viewname_notfound(self): + from webob import Response + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + config = self._makeOne() + request = self._makeRequest(config) + request.registry = config.registry + wrapped = config._derive_view( + inner_view, viewname='inner', wrapper_viewname='owrap') + self.assertRaises(ValueError, wrapped, None, request) + + def test_override_resource_samename(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') + + def test_override_resource_directory_with_file(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo/', 'a:foo.pt') + + def test_override_resource_file_with_directory(self): + from repoze.bfg.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo.pt', 'a:foo/') + + def test_override_resource_success(self): + config = self._makeOne() + override = DummyUnderOverride() + config.override_resource( + 'repoze.bfg.tests.fixtureapp:templates/foo.pt', + 'repoze.bfg.tests.fixtureapp.subpackage:templates/bar.pt', + _override=override) + from repoze.bfg.tests import fixtureapp + from repoze.bfg.tests.fixtureapp import subpackage + self.assertEqual(override.package, fixtureapp) + self.assertEqual(override.path, 'templates/foo.pt') + self.assertEqual(override.override_package, subpackage) + self.assertEqual(override.override_prefix, 'templates/bar.pt') + + def test_add_renderer(self): + from repoze.bfg.interfaces import IRendererFactory + config = self._makeOne() + renderer = object() + config.add_renderer('name', renderer) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + renderer) + + def test_add_renderer_dottedname_factory(self): + from repoze.bfg.interfaces import IRendererFactory + config = self._makeOne() + import repoze.bfg.tests + config.add_renderer('name', 'repoze.bfg.tests') + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + repoze.bfg.tests) + + def test_scan_integration(self): + from zope.interface import alsoProvides + from repoze.bfg.interfaces import IRequest + from repoze.bfg.view import render_view_to_response + import repoze.bfg.tests.grokkedapp as package + config = self._makeOne() + 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') + + 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_dottedname_package(self): + from zope.interface import alsoProvides + from repoze.bfg.interfaces import IRequest + from repoze.bfg.view import render_view_to_response + config = self._makeOne() + config.scan('repoze.bfg.tests.grokkedapp') + + 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_testing_securitypolicy(self): + from repoze.bfg.testing import DummySecurityPolicy + config = self._makeOne() + config.testing_securitypolicy('user', ('group1', 'group2'), + permissive=False) + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + ut = config.registry.getUtility(IAuthenticationPolicy) + self.failUnless(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_models(self): + from repoze.bfg.traversal import find_model + from repoze.bfg.interfaces import ITraverser + ob1 = object() + ob2 = object() + models = {'/ob1':ob1, '/ob2':ob2} + config = self._makeOne() + config.testing_models(models) + adapter = config.registry.getAdapter(None, ITraverser) + result = adapter({'PATH_INFO':'/ob1'}) + self.assertEqual(result['context'], ob1) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob1',)) + self.assertEqual(result['virtual_root'], ob1) + self.assertEqual(result['virtual_root_path'], ()) + result = adapter({'PATH_INFO':'/ob2'}) + self.assertEqual(result['context'], ob2) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob2',)) + self.assertEqual(result['virtual_root'], ob2) + self.assertEqual(result['virtual_root_path'], ()) + self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) + try: + config.begin() + self.assertEqual(find_model(None, '/ob1'), ob1) + finally: + config.end() + + def test_testing_add_subscriber_single(self): + config = self._makeOne() + 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() + L = config.testing_add_subscriber( + 'repoze.bfg.tests.test_configuration.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): + config = self._makeOne() + 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() + 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_hook_zca(self): + from repoze.bfg.threadlocal import get_current_registry + gsm = DummyGetSiteManager() + config = self._makeOne() + config.hook_zca(getSiteManager=gsm) + self.assertEqual(gsm.hook, get_current_registry) + + def test_unhook_zca(self): + gsm = DummyGetSiteManager() + config = self._makeOne() + config.unhook_zca(getSiteManager=gsm) + self.assertEqual(gsm.unhooked, True) + + def test_testing_add_renderer(self): + config = self._makeOne() + renderer = config.testing_add_renderer('templates/foo.pt') + from repoze.bfg.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, DummyTemplateRenderer)) + from repoze.bfg.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_explicitrenderer(self): + config = self._makeOne() + 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 repoze.bfg.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() + renderer = config.testing_add_template('templates/foo.pt') + from repoze.bfg.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, DummyTemplateRenderer)) + from repoze.bfg.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) + +class Test__map_view(unittest.TestCase): + def setUp(self): + from repoze.bfg.registry import Registry + self.registry = Registry() + testing.setUp(registry=self.registry) + + def tearDown(self): + del self.registry + testing.tearDown() + + def _registerRenderer(self, typ='.txt'): + from repoze.bfg.interfaces import IRendererFactory + from repoze.bfg.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + spec = 'abc' + typ + def __init__(self, path): + self.__class__.path = path + def __call__(self, *arg): + return 'Hello!' + self.registry.registerUtility(Renderer, IRendererFactory, name=typ) + return Renderer + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.registry + return request + + def _callFUT(self, *arg, **kw): + from repoze.bfg.configuration import _map_view + return _map_view(*arg, **kw) + + def test__map_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_with_attr(self): + def view(context, request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_with_attr_and_renderer(self): + renderer = self._registerRenderer() + view = lambda *arg: 'OK' + result = self._callFUT(view, attr='__name__', + renderer_name=renderer.spec) + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_requestonly(self): + def view(request): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_requestonly_with_attr(self): + def view(request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_with_attr(self): + class view(object): + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( + self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_with_attr(self): + class view(object): + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): + class view: + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_with_attr(self): + class view: + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_and_attr(self): + class View: + def index(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, context, request): + return {'a':'1'} + view = View() + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr(self): + class View: + def index(self, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, request): + return {'a':'1'} + view = View() + result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_rendereronly(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + result = self._callFUT(view, renderer_name=renderer.spec) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_with_registry(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + result = self._callFUT(view, renderer_name=renderer.spec, + registry=self.registry) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_with_package(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + result = self._callFUT(view, renderer_name=renderer.spec, + package='repoze.bfg') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(renderer.path, 'repoze.bfg:abc.txt') + +class Test_decorate_view(unittest.TestCase): + def _callFUT(self, wrapped, original): + from repoze.bfg.configuration import decorate_view + return decorate_view(wrapped, original) + + def test_it_same(self): + def view(context, request): + """ """ + result = self._callFUT(view, view) + self.assertEqual(result, False) + + def test_it_different(self): + class DummyView1: + """ 1 """ + __name__ = '1' + __module__ = '1' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + class DummyView2: + """ 2 """ + __name__ = '2' + __module__ = '2' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + view1 = DummyView1() + view2 = DummyView2() + result = self._callFUT(view1, view2) + self.assertEqual(result, True) + self.failUnless(view1.__doc__ is view2.__doc__) + self.failUnless(view1.__module__ is view2.__module__) + self.failUnless(view1.__name__ is view2.__name__) + self.failUnless(view1.__call_permissive__.im_func is + view2.__call_permissive__.im_func) + self.failUnless(view1.__permitted__.im_func is + view2.__permitted__.im_func) + self.failUnless(view1.__predicated__.im_func is + view2.__predicated__.im_func) + +class Test__make_predicates(unittest.TestCase): + def _callFUT(self, **kw): + from repoze.bfg.configuration import _make_predicates + return _make_predicates(**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.failUnless(order1 < order2) + + def test_ordering_number_of_predicates(self): + order1, _, _ = 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=('a',) + ) + order2, _, _ = 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=('a',) + ) + order3, _, _ = 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', + ) + order4, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + ) + order5, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + ) + order6, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + ) + order7, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + ) + order8, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order9, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order10, _, _ = self._callFUT( + xhr='xhr', + ) + order11, _, _ = self._callFUT( + ) + self.assertEqual(order1, order2) + self.failUnless(order3 > order2) + self.failUnless(order4 > order3) + self.failUnless(order5 > order4) + self.failUnless(order6 > order5) + self.failUnless(order7 > order6) + self.failUnless(order8 > order7) + self.failUnless(order9 > order8) + self.failUnless(order10 > order9) + self.failUnless(order11 > order10) + + def test_ordering_importance_of_predicates(self): + 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( + custom=('a',), + ) + self.failUnless(order1 > order2) + self.failUnless(order2 > order3) + self.failUnless(order3 > order4) + self.failUnless(order4 > order5) + self.failUnless(order5 > order6) + self.failUnless(order6 > order7) + self.failUnless(order7 > order8) + self.failUnless(order8 > order9) + + def test_ordering_importance_and_number(self): + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + custom=('a',), + ) + self.failUnless(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(order1 > order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(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=('a',), + ) + self.failUnless(order1 > order2) + + def test_different_custom_predicates_with_same_hash(self): + class PredicateWithHash(object): + def __hash__(self): + return 1 + a = PredicateWithHash() + b = PredicateWithHash() + _, _, a_phash = self._callFUT(custom=(a,)) + _, _, b_phash = self._callFUT(custom=(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')}}) + +class TestMultiView(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.configuration 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 repoze.bfg.interfaces import ISecuredView + verifyClass(ISecuredView, self._getTargetClass()) + + def test_instance_implements_ISecuredView(self): + from zope.interface.verify import verifyObject + from repoze.bfg.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, 'text/html') + self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)]) + mv.add('view4', 99, 'text/html') + self.assertEqual(mv.media_views['text/html'], + [(99, 'view4', None), (100, 'view3', None)]) + mv.add('view5', 100, '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)]) + mv.add('view6', 98, 'text/*') + self.assertEqual(mv.views, [(98, 'view6', None), + (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_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 repoze.bfg.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv.match, context, request) + + def test_match_predicate_fails(self): + from repoze.bfg.exceptions import NotFound + mv = self._makeOne() + def view(context, request): + """ """ + view.__predicated__ = lambda *arg: False + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv, context, request) + + def test___call__intermediate_not_found(self): + from repoze.bfg.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 repoze.bfg.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + def view1(context, request): + raise NotFound + def view2(context, request): + """ """ + mv.views = [(100, view1, None), (99, view2, None)] + self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, 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 TestRequestOnly(unittest.TestCase): + def _callFUT(self, arg): + from repoze.bfg.configuration import requestonly + return requestonly(arg) + + def test_newstyle_class_no_init(self): + class foo(object): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_toomanyargs(self): + class foo(object): + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_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)) + + 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), 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_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)) + + 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), True) + + def test_instance_nocall(self): + class Foo: pass + foo = Foo() + self.assertFalse(self._callFUT(foo)) + +class TestMakeApp(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.configuration import make_app + return make_app(*arg, **kw) + + def test_it(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + self.assertEqual(app.zca_hooked, True) + + def test_it_options_means_settings(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, options=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + + def test_it_with_package(self): + package = object() + rootfactory = object() + app = self._callFUT(rootfactory, package=package, + Configurator=DummyConfigurator) + self.assertEqual(app.package, package) + + def test_it_with_custom_configure_zcml(self): + rootfactory = object() + settings = {'configure_zcml':'2.zcml'} + app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.zcml_file, '2.zcml') + +class TestDottedNameResolver(unittest.TestCase): + def _makeOne(self, package=None): + from repoze.bfg.configuration import DottedNameResolver + return DottedNameResolver(package) + + def config_exc(self, func, *arg, **kw): + from repoze.bfg.exceptions import ConfigurationError + try: + func(*arg, **kw) + except ConfigurationError, e: + return e + else: + raise AssertionError('Invalid not raised') # pragma: no cover + + def test_zope_dottedname_style_resolve_absolute(self): + typ = self._makeOne() + result = typ._zope_dottedname_style( + 'repoze.bfg.tests.test_configuration.TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test_zope_dottedname_style_irrresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises(ImportError, typ._zope_dottedname_style, + 'repoze.bfg.test_configuration.nonexisting_name') + + def test__zope_dottedname_style_resolve_relative(self): + import repoze.bfg.tests + typ = self._makeOne(package=repoze.bfg.tests) + result = typ._zope_dottedname_style( + '.test_configuration.TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test__zope_dottedname_style_resolve_relative_leading_dots(self): + import repoze.bfg.tests.test_configuration + typ = self._makeOne(package=repoze.bfg.tests) + result = typ._zope_dottedname_style( + '..tests.test_configuration.TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test__zope_dottedname_style_resolve_relative_is_dot(self): + import repoze.bfg.tests + typ = self._makeOne(package=repoze.bfg.tests) + result = typ._zope_dottedname_style('.') + self.assertEqual(result, repoze.bfg.tests) + + def test__zope_dottedname_style_irresolveable_relative_is_dot(self): + typ = self._makeOne() + e = self.config_exc(typ._zope_dottedname_style, '.') + 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') + self.assertEqual( + e.args[0], + "relative name '.whatever' irresolveable without package") + + def test_zope_dottedname_style_irrresolveable_relative(self): + import repoze.bfg.tests + typ = self._makeOne(package=repoze.bfg.tests) + self.assertRaises(ImportError, typ._zope_dottedname_style, + '.notexisting') + + def test__zope_dottedname_style_resolveable_relative(self): + import repoze.bfg + typ = self._makeOne(package=repoze.bfg) + result = typ._zope_dottedname_style('.tests') + from repoze.bfg import tests + self.assertEqual(result, tests) + + def test__zope_dottedname_style_irresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises( + ImportError, + typ._zope_dottedname_style, 'repoze.bfg.fudge.bar') + + def test__zope_dottedname_style_resolveable_absolute(self): + typ = self._makeOne() + result = typ._zope_dottedname_style( + 'repoze.bfg.tests.test_configuration.TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_resolve_absolute(self): + typ = self._makeOne() + result = typ._pkg_resources_style( + 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_irrresolveable_absolute(self): + typ = self._makeOne() + self.assertRaises(ImportError, typ._pkg_resources_style, + 'repoze.bfg.tests:nonexisting') + + def test__pkg_resources_style_resolve_relative(self): + import repoze.bfg.tests + typ = self._makeOne(package=repoze.bfg.tests) + result = typ._pkg_resources_style( + '.test_configuration:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test__pkg_resources_style_resolve_relative_is_dot(self): + import repoze.bfg.tests + typ = self._makeOne(package=repoze.bfg.tests) + result = typ._pkg_resources_style('.') + self.assertEqual(result, repoze.bfg.tests) + + def test__pkg_resources_style_resolve_relative_nocurrentpackage(self): + typ = self._makeOne() + from repoze.bfg.exceptions import ConfigurationError + self.assertRaises(ConfigurationError, typ._pkg_resources_style, + '.whatever') + + def test__pkg_resources_style_irrresolveable_relative(self): + import repoze.bfg + typ = self._makeOne(package=repoze.bfg) + self.assertRaises(ImportError, typ._pkg_resources_style, + ':notexisting') + + 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( + 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test_resolve_using_zope_dottedname_style(self): + typ = self._makeOne() + result = typ.resolve( + 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') + self.assertEqual(result, self.__class__) + + def test_resolve_missing_raises(self): + typ = self._makeOne() + e = self.config_exc(typ.resolve, 'cant.be.found') + self.assertEqual(e.args[0], + "The dotted name 'cant.be.found' cannot be imported") + + def test_ctor_string_module_resolveable(self): + import repoze.bfg.tests + typ = self._makeOne('repoze.bfg.tests.test_configuration') + self.assertEqual(typ.package, repoze.bfg.tests) + self.assertEqual(typ.package_name, 'repoze.bfg.tests') + + def test_ctor_string_package_resolveable(self): + import repoze.bfg.tests + typ = self._makeOne('repoze.bfg.tests') + self.assertEqual(typ.package, repoze.bfg.tests) + self.assertEqual(typ.package_name, 'repoze.bfg.tests') + + def test_ctor_string_irresolveable(self): + from repoze.bfg.configuration import ConfigurationError + self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found') + + def test_ctor_module(self): + import repoze.bfg.tests + import repoze.bfg.tests.test_configuration + typ = self._makeOne(repoze.bfg.tests.test_configuration) + self.assertEqual(typ.package, repoze.bfg.tests) + self.assertEqual(typ.package_name, 'repoze.bfg.tests') + + def test_ctor_package(self): + import repoze.bfg.tests + typ = self._makeOne(repoze.bfg.tests) + self.assertEqual(typ.package, repoze.bfg.tests) + self.assertEqual(typ.package_name, 'repoze.bfg.tests') + + def test_ctor_None(self): + typ = self._makeOne(None) + self.assertEqual(typ.package, None) + self.assertEqual(typ.package_name, None) + +class Test_isexception(unittest.TestCase): + def _callFUT(self, ob): + from repoze.bfg.configuration 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 repoze.bfg.interfaces import IException + self.assertEqual(self._callFUT(IException), True) + + def test_is_IException_subinterface(self): + from repoze.bfg.interfaces import IException + class ISubException(IException): + pass + self.assertEqual(self._callFUT(ISubException), True) + +class DummyRequest: + subpath = () + def __init__(self): + self.environ = {'PATH_INFO':'/static'} + self.params = {} + self.cookies = {} + def copy(self): + return self + def get_response(self, app): + return app + +class DummyContext: + pass + +class DummyLock: + def acquire(self): + self.acquired = True + + def release(self): + self.released = True + +class DummyPackage: + def __init__(self, name): + self.__name__ = name + +class DummyOverrides: + def __init__(self, package): + self.package = package + self.inserted = [] + + def insert(self, path, package, prefix): + self.inserted.append((path, package, prefix)) + +class DummyUnderOverride: + def __call__(self, package, path, override_package, override_prefix, + _info=u''): + self.package = package + self.path = path + self.override_package = override_package + self.override_prefix = override_prefix + +from zope.interface import Interface +class IDummy(Interface): + pass + +class IOther(Interface): + pass + +class DummyResponse: + status = '200 OK' + headerlist = () + app_iter = () + body = '' + +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 DummyConfigurator(object): + def __init__(self, registry=None, package=None, root_factory=None, + settings=None): + self.root_factory = root_factory + self.package = package + self.settings = settings + + def begin(self, request=None): + self.begun = True + self.request = request + + def end(self): + self.ended = True + + def load_zcml(self, filename): + self.zcml_file = filename + + def make_wsgi_app(self): + return self + + def hook_zca(self): + self.zca_hooked = True + +class DummyAccept(object): + def __init__(self, *matches): + self.matches = list(matches) + + def best_match(self, offered): + if self.matches: + for match in self.matches: + if match in offered: + self.matches.remove(match) + return match + def __contains__(self, val): + return val in self.matches + +from zope.interface import implements +from repoze.bfg.interfaces import IMultiView +class DummyMultiView: + implements(IMultiView) + def __init__(self): + self.views = [] + self.name = 'name' + def add(self, view, order, accept=None, phash=None): + self.views.append((view, accept, phash)) + def __call__(self, context, request): + return 'OK1' + def __permitted__(self, context, request): + """ """ + +class DummyGetSiteManager(object): + def sethook(self, hook): + self.hook = hook + def reset(self): + self.unhooked = True + +class DummyThreadLocalManager(object): + pushed = None + popped = False + def push(self, d): + self.pushed = d + def pop(self): + self.popped = True + +class IFactory(Interface): + pass + +class DummyFactory(object): + implements(IFactory) + def __call__(self): + """ """ + +class DummyEvent: + implements(IDummy) + +class DummyStaticURLInfo: + def __init__(self): + self.added = [] + + def add(self, name, spec, **kw): + self.added.append((name, spec, kw)) + +def dummy_view(request): + return 'OK' + +def dummyfactory(request): + """ """ diff --git a/pyramid/tests/test_decorator.py b/pyramid/tests/test_decorator.py new file mode 100644 index 000000000..d41c62c65 --- /dev/null +++ b/pyramid/tests/test_decorator.py @@ -0,0 +1,29 @@ +import unittest + +class TestReify(unittest.TestCase): + def _makeOne(self, wrapped): + from repoze.bfg.decorator import reify + return reify(wrapped) + + def test___get__withinst(self): + def wrapped(inst): + return 'a' + decorator = self._makeOne(wrapped) + inst = Dummy() + result = decorator.__get__(inst) + self.assertEqual(result, 'a') + self.assertEqual(inst.__dict__['wrapped'], 'a') + + def test___get__noinst(self): + decorator = self._makeOne(None) + result = decorator.__get__(None) + self.assertEqual(result, decorator) + + def test___doc__copied(self): + def wrapped(inst): + """My doc""" + decorator = self._makeOne(wrapped) + self.assertEqual(decorator.__doc__, "My doc") + +class Dummy(object): + pass diff --git a/pyramid/tests/test_docs.py b/pyramid/tests/test_docs.py new file mode 100644 index 000000000..a00171842 --- /dev/null +++ b/pyramid/tests/test_docs.py @@ -0,0 +1,35 @@ +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('repoze.bfg').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/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py new file mode 100644 index 000000000..364247fb3 --- /dev/null +++ b/pyramid/tests/test_encode.py @@ -0,0 +1,61 @@ +import unittest + +class UrlEncodeTests(unittest.TestCase): + def _callFUT(self, query, doseq=False): + from repoze.bfg.encode import urlencode + return urlencode(query, doseq) + + def test_ascii_only(self): + result = self._callFUT([('a',1), ('b',2)]) + self.assertEqual(result, 'a=1&b=2') + + def test_unicode_key(self): + la = unicode('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 = unicode('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 = [unicode('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_dict(self): + result = self._callFUT({'a':1}) + self.assertEqual(result, 'a=1') + +class URLQuoteTests(unittest.TestCase): + def _callFUT(self, val, safe=''): + from repoze.bfg.encode import url_quote + return url_quote(val, safe) + + def test_it_default(self): + la = 'La/Pe\xc3\xb1a' + result = self._callFUT(la) + self.assertEqual(result, 'La%2FPe%C3%B1a') + + def test_it_with_safe(self): + la = 'La/Pe\xc3\xb1a' + result = self._callFUT(la, '/') + self.assertEqual(result, 'La/Pe%C3%B1a') + +class TestQuotePlus(unittest.TestCase): + def _callFUT(self, val, safe=''): + from repoze.bfg.encode import quote_plus + return quote_plus(val, safe) + + def test_it_default(self): + la = 'La Pe\xc3\xb1a' + result = self._callFUT(la) + self.assertEqual(result, 'La+Pe%C3%B1a') + + def test_it_with_safe(self): + la = 'La /Pe\xc3\xb1a' + result = self._callFUT(la, '/') + self.assertEqual(result, 'La+/Pe%C3%B1a') + + diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py new file mode 100644 index 000000000..d097ca0f4 --- /dev/null +++ b/pyramid/tests/test_events.py @@ -0,0 +1,181 @@ +import unittest + +class NewRequestEventTests(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.events import NewRequest + return NewRequest + + def _makeOne(self, request): + return self._getTargetClass()(request) + + def test_class_implements(self): + from repoze.bfg.interfaces import INewRequest + from zope.interface.verify import verifyClass + klass = self._getTargetClass() + verifyClass(INewRequest, klass) + + def test_instance_implements(self): + from repoze.bfg.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 repoze.bfg.events import NewResponse + return NewResponse + + def _makeOne(self, request, response): + return self._getTargetClass()(request, response) + + def test_class_implements(self): + from repoze.bfg.interfaces import INewResponse + from zope.interface.verify import verifyClass + klass = self._getTargetClass() + verifyClass(INewResponse, klass) + + def test_instance_implements(self): + from repoze.bfg.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 test_alias_object_implements(self): + from repoze.bfg.events import WSGIApplicationCreatedEvent + event = WSGIApplicationCreatedEvent(object()) + from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent + from repoze.bfg.interfaces import IApplicationCreated + from zope.interface.verify import verifyObject + verifyObject(IWSGIApplicationCreatedEvent, event) + verifyObject(IApplicationCreated, event) + + def test_alias_class_implements(self): + from repoze.bfg.events import WSGIApplicationCreatedEvent + from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent + from repoze.bfg.interfaces import IApplicationCreated + from zope.interface.verify import verifyClass + verifyClass(IWSGIApplicationCreatedEvent, WSGIApplicationCreatedEvent) + verifyClass(IApplicationCreated, WSGIApplicationCreatedEvent) + + def test_object_implements(self): + from repoze.bfg.events import ApplicationCreated + event = ApplicationCreated(object()) + from repoze.bfg.interfaces import IApplicationCreated + from zope.interface.verify import verifyObject + verifyObject(IApplicationCreated, event) + + def test_class_implements(self): + from repoze.bfg.events import ApplicationCreated + from repoze.bfg.interfaces import IApplicationCreated + from zope.interface.verify import verifyClass + verifyClass(IApplicationCreated, ApplicationCreated) + +class ContextFoundEventTests(unittest.TestCase): + def test_alias_class_implements(self): + from zope.interface.verify import verifyClass + from repoze.bfg.events import AfterTraversal + from repoze.bfg.interfaces import IAfterTraversal + from repoze.bfg.interfaces import IContextFound + verifyClass(IAfterTraversal, AfterTraversal) + verifyClass(IContextFound, AfterTraversal) + + def test_alias_instance_implements(self): + from zope.interface.verify import verifyObject + from repoze.bfg.events import AfterTraversal + from repoze.bfg.interfaces import IAfterTraversal + from repoze.bfg.interfaces import IContextFound + request = DummyRequest() + inst = AfterTraversal(request) + verifyObject(IAfterTraversal, inst) + verifyObject(IContextFound, inst) + + def test_class_implements(self): + from zope.interface.verify import verifyClass + from repoze.bfg.events import ContextFound + from repoze.bfg.interfaces import IContextFound + verifyClass(IContextFound, ContextFound) + + def test_instance_implements(self): + from zope.interface.verify import verifyObject + from repoze.bfg.events import ContextFound + from repoze.bfg.interfaces import IContextFound + request = DummyRequest() + inst = ContextFound(request) + verifyObject(IContextFound, inst) + +class TestSubscriber(unittest.TestCase): + def setUp(self): + registry = DummyRegistry() + from repoze.bfg.configuration import Configurator + self.config = Configurator(registry) + self.config.begin() + + def tearDown(self): + self.config.end() + + def _makeOne(self, *ifaces): + from repoze.bfg.events import subscriber + return subscriber(*ifaces) + + def test_register(self): + from zope.interface import Interface + class IFoo(Interface): pass + class IBar(Interface): pass + dec = self._makeOne(IFoo, IBar) + def foo(): pass + config = DummyConfigurator() + scanner = Dummy() + scanner.config = config + dec.register(scanner, None, foo) + self.assertEqual(config.subscribed, [(foo, (IFoo, IBar))]) + + def test___call__(self): + dec = self._makeOne() + dummy_venusian = DummyVenusian() + dec.venusian = dummy_venusian + def foo(): pass + dec(foo) + self.assertEqual(dummy_venusian.attached, [(foo, dec.register, 'bfg')]) + +class DummyConfigurator(object): + def __init__(self): + self.subscribed = [] + + def add_subscriber(self, wrapped, ifaces): + self.subscribed.append((wrapped, ifaces)) + +class DummyRegistry(object): + pass + +class DummyVenusian(object): + def __init__(self): + self.attached = [] + + def attach(self, wrapped, fn, category=None): + self.attached.append((wrapped, fn, category)) + +class Dummy: + pass + +class DummyRequest: + pass + +class DummyResponse: + pass + diff --git a/pyramid/tests/test_exceptions.py b/pyramid/tests/test_exceptions.py new file mode 100644 index 000000000..4091eb941 --- /dev/null +++ b/pyramid/tests/test_exceptions.py @@ -0,0 +1,45 @@ +import unittest + +class TestExceptionResponse(unittest.TestCase): + def _makeOne(self, message): + from repoze.bfg.exceptions import ExceptionResponse + return ExceptionResponse(message) + + def test_app_iter(self): + exc = self._makeOne('') + self.failUnless('' in exc.app_iter[0]) + + def test_headerlist(self): + exc = self._makeOne('') + headerlist = exc.headerlist + headerlist.sort() + app_iter = exc.app_iter + clen = str(len(app_iter[0])) + self.assertEqual(headerlist[0], ('Content-Length', clen)) + self.assertEqual(headerlist[1], ('Content-Type', 'text/html')) + + def test_withmessage(self): + exc = self._makeOne('abc&123') + self.failUnless('abc&123' in exc.app_iter[0]) + +class TestNotFound(unittest.TestCase): + def _makeOne(self, message): + from repoze.bfg.exceptions import NotFound + return NotFound(message) + + def test_it(self): + from repoze.bfg.exceptions import ExceptionResponse + e = self._makeOne('notfound') + self.failUnless(isinstance(e, ExceptionResponse)) + self.assertEqual(e.status, '404 Not Found') + +class TestForbidden(unittest.TestCase): + def _makeOne(self, message): + from repoze.bfg.exceptions import Forbidden + return Forbidden(message) + + def test_it(self): + from repoze.bfg.exceptions import ExceptionResponse + e = self._makeOne('unauthorized') + self.failUnless(isinstance(e, ExceptionResponse)) + self.assertEqual(e.status, '401 Unauthorized') diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py new file mode 100644 index 000000000..95017807f --- /dev/null +++ b/pyramid/tests/test_i18n.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +# + +import unittest +from repoze.bfg.testing import cleanUp + +class TestTranslationString(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from repoze.bfg.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 repoze.bfg.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 repoze.bfg.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.failUnless(localizer.translator) + + def test_pluralize(self): + translations = DummyTranslations() + localizer = self._makeOne(None, translations) + self.assertEqual(localizer.pluralize('singular', 'plural', 1, + domain='1', mapping={}), + 'singular') + self.failUnless(localizer.pluralizer) + +class Test_negotiate_locale_name(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.i18n import negotiate_locale_name + return negotiate_locale_name(request) + + def _registerImpl(self, impl): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + from repoze.bfg.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 repoze.bfg.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 repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + settings = {'default_locale_name':'settings'} + from repoze.bfg.interfaces import ISettings + registry.registerUtility(settings, ISettings) + request = DummyRequest() + request.registry = registry + result = self._callFUT(request) + self.assertEqual(result, 'settings') + + def test_use_default_locale_negotiator(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + request = DummyRequest() + request.registry = registry + request._LOCALE_ = 'locale' + result = self._callFUT(request) + self.assertEqual(result, 'locale') + + def test_default_default(self): + request = DummyRequest() + result = self._callFUT(request) + self.assertEqual(result, 'en') + +class Test_get_locale_name(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.i18n import get_locale_name + return get_locale_name(request) + + def _registerImpl(self, impl): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + from repoze.bfg.interfaces import ILocaleNegotiator + registry.registerUtility(impl, ILocaleNegotiator) + + def test_name_on_request(self): + request = DummyRequest() + request.bfg_locale_name = 'ie' + result = self._callFUT(request) + self.assertEqual(result, 'ie') + + def test_name_not_on_request(self): + self._registerImpl(dummy_negotiator) + request = DummyRequest() + result = self._callFUT(request) + self.assertEqual(result, 'bogus') + self.assertEqual(request.bfg_locale_name, 'bogus') + +class Test_get_localizer(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.i18n import get_localizer + return get_localizer(request) + + def test_no_registry_on_request(self): + request = DummyRequest() + request.bfg_localizer = '123' + result = self._callFUT(request) + self.assertEqual(result, '123') + + def test_with_registry_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + request = DummyRequest() + request.bfg_localizer = '123' + request.registry = registry + result = self._callFUT(request) + self.assertEqual(result, '123') + + def test_locale_on_request(self): + request = DummyRequest() + request.bfg_localizer = 'abc' + result = self._callFUT(request) + self.assertEqual(result, 'abc') + + def test_locale_from_registry(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ILocalizer + registry = get_current_registry() + locale = 'abc' + registry.registerUtility(locale, ILocalizer, name='en') + request = DummyRequest() + request.bfg_locale_name = 'en' + result = self._callFUT(request) + self.assertEqual(result, 'abc') + + def test_locale_from_mo(self): + import os + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ITranslationDirectories + from repoze.bfg.i18n import Localizer + registry = get_current_registry() + here = os.path.dirname(__file__) + localedir = os.path.join(here, 'localeapp', 'locale') + localedirs = [localedir] + registry.registerUtility(localedirs, ITranslationDirectories) + request = DummyRequest() + request.bfg_locale_name = 'de' + result = self._callFUT(request) + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.translate('Approve', 'deformsite'), + 'Genehmigen') + self.assertEqual(result.translate('Approve'), 'Approve') + self.failUnless(hasattr(result, 'pluralize')) + + def test_locale_from_mo_bad_mo(self): + import os + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ITranslationDirectories + from repoze.bfg.i18n import Localizer + registry = get_current_registry() + here = os.path.dirname(__file__) + localedir = os.path.join(here, 'localeapp', 'locale') + localedirs = [localedir] + registry.registerUtility(localedirs, ITranslationDirectories) + request = DummyRequest() + request.bfg_locale_name = 'be' + result = self._callFUT(request) + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.translate('Approve', 'deformsite'), + 'Approve') + +class Test_default_locale_negotiator(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.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 repoze.bfg.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_domain_None(self): + import gettext + import os + here = os.path.dirname(__file__) + localedir = os.path.join(here, 'localeapp', 'locale') + 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): + import os + here = os.path.dirname(__file__) + localedir = os.path.join(here, 'localeapp', 'locale') + 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): + import os + here = os.path.dirname(__file__) + localedir = os.path.join(here, 'localeapp', 'locale') + locales = 'de' + klass = self._getTargetClass() + result = klass.load(localedir, locales, domain='deformsite') + self.assertEqual(result.__class__, klass) + + def test___repr__(self): + inst = self._makeOne() + result = repr(inst) + self.assertEqual(result, '') + + def test_merge_not_gnutranslations(self): + inst = self._makeOne() + self.assertEqual(inst.merge(None), inst) + + def test_merge_gnutranslations(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2._catalog['a'] = 'b' + inst.merge(inst2) + self.assertEqual(inst._catalog['a'], 'b') + + def test_add_different_domain_merge_true_notexisting(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2.domain = 'domain2' + inst.add(inst2) + self.assertEqual(inst._domains['domain2'], inst2) + + def test_add_different_domain_merge_true_existing(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst3 = self._makeOne() + inst2.domain = 'domain2' + inst2._catalog['a'] = 'b' + inst3.domain = 'domain2' + inst._domains['domain2'] = inst3 + inst.add(inst2) + self.assertEqual(inst._domains['domain2'], inst3) + self.assertEqual(inst3._catalog['a'], 'b') + + def test_add_same_domain_merge_true(self): + inst = self._makeOne() + inst2 = self._makeOne() + inst2._catalog['a'] = 'b' + inst.add(inst2) + self.assertEqual(inst._catalog['a'], 'b') + + def test_dgettext(self): + t = self._makeOne() + self.assertEqual(t.dgettext('messages', 'foo'), 'Voh') + self.assertEqual(t.dgettext('messages1', 'foo'), 'VohD') + + def test_ldgettext(self): + t = self._makeOne() + self.assertEqual(t.ldgettext('messages', 'foo'), 'Voh') + self.assertEqual(t.ldgettext('messages1', 'foo'), '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), 'Voh1') + self.assertEqual(t.ldngettext('messages1', 'foo1', 'foos1', 1), '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') + + +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 + + def ungettext(self, singular, plural, n): + return singular diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py new file mode 100644 index 000000000..b6eb860ee --- /dev/null +++ b/pyramid/tests/test_integration.py @@ -0,0 +1,236 @@ +import os +import unittest + +from repoze.bfg.wsgi import wsgiapp +from repoze.bfg.view import bfg_view +from repoze.bfg.view import static + +from zope.interface import Interface + +from repoze.bfg import testing + +class INothing(Interface): + pass + +@bfg_view(for_=INothing) +@wsgiapp +def wsgiapptest(environ, start_response): + """ """ + return '123' + +class WGSIAppPlusBFGViewTests(unittest.TestCase): + def test_it(self): + from venusian import ATTACH_ATTR + import types + self.failUnless(getattr(wsgiapptest, ATTACH_ATTR)) + self.failUnless(type(wsgiapptest) is types.FunctionType) + context = DummyContext() + request = DummyRequest() + result = wsgiapptest(context, request) + self.assertEqual(result, '123') + + def test_scanned(self): + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.configuration import Configurator + from repoze.bfg.tests import test_integration + config = Configurator() + config.scan(test_integration) + reg = config.registry + view = reg.adapters.lookup( + (IViewClassifier, IRequest, INothing), IView, name='') + self.assertEqual(view, wsgiapptest) + +here = os.path.dirname(__file__) +staticapp = static(os.path.join(here, 'fixtures')) + +class TestStaticApp(unittest.TestCase): + def test_it(self): + from webob import Request + context = DummyContext() + from StringIO import StringIO + request = Request({'PATH_INFO':'', + 'SCRIPT_NAME':'', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'80', + 'REQUEST_METHOD':'GET', + 'wsgi.version':(1,0), + 'wsgi.url_scheme':'http', + 'wsgi.input':StringIO()}) + request.subpath = ['minimal.pt'] + result = staticapp(context, request) + self.assertEqual(result.status, '200 OK') + self.assertEqual( + result.body, + open(os.path.join(here, 'fixtures/minimal.pt'), 'r').read()) + +class TwillBase(unittest.TestCase): + root_factory = None + def setUp(self): + import sys + import twill + from repoze.bfg.configuration import Configurator + config = Configurator(root_factory=self.root_factory) + config.load_zcml(self.config) + twill.add_wsgi_intercept('localhost', 6543, config.make_wsgi_app) + if sys.platform is 'win32': # pragma: no cover + out = open('nul:', 'wb') + else: + out = open('/dev/null', 'wb') + twill.set_output(out) + testing.setUp(registry=config.registry) + + def tearDown(self): + import twill + import twill.commands + twill.commands.reset_browser() + twill.remove_wsgi_intercept('localhost', 6543) + twill.set_output(None) + testing.tearDown() + +class TestFixtureApp(TwillBase): + config = 'repoze.bfg.tests.fixtureapp:configure.zcml' + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/another.html') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'fixture') + browser.go('http://localhost:6543') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'fixture') + browser.go('http://localhost:6543/dummyskin.html') + self.assertEqual(browser.get_code(), 404) + browser.go('http://localhost:6543/error.html') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'supressed') + browser.go('http://localhost:6543/protected.html') + self.assertEqual(browser.get_code(), 401) + +class TestCCBug(TwillBase): + # "unordered" as reported in IRC by author of + # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ + config = 'repoze.bfg.tests.ccbugapp:configure.zcml' + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/licenses/1/v1/rdf') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'rdf') + browser.go('http://localhost:6543/licenses/1/v1/juri') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'juri') + +class TestHybridApp(TwillBase): + # 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. + config = 'repoze.bfg.tests.hybridapp:configure.zcml' + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'global') + browser.go('http://localhost:6543/abc') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'route') + browser.go('http://localhost:6543/def') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'route2') + browser.go('http://localhost:6543/ghi') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'global') + browser.go('http://localhost:6543/jkl') + self.assertEqual(browser.get_code(), 404) + browser.go('http://localhost:6543/mno/global2') + self.assertEqual(browser.get_code(), 404) + browser.go('http://localhost:6543/pqr/global2') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'global2') + browser.go('http://localhost:6543/error') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'supressed') + browser.go('http://localhost:6543/error2') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'supressed2') + browser.go('http://localhost:6543/error_sub') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'supressed2') + +class TestRestBugApp(TwillBase): + # test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515) + config = 'repoze.bfg.tests.restbugapp:configure.zcml' + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/pet') + self.assertEqual(browser.get_code(), 200) + self.assertEqual(browser.get_html(), 'gotten') + +class TestViewDecoratorApp(TwillBase): + config = 'repoze.bfg.tests.viewdecoratorapp:configure.zcml' + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/first') + self.assertEqual(browser.get_code(), 200) + self.failUnless('OK' in browser.get_html()) + + browser.go('http://localhost:6543/second') + self.assertEqual(browser.get_code(), 200) + self.failUnless('OK2' in browser.get_html()) + + browser.go('http://localhost:6543/third') + self.assertEqual(browser.get_code(), 200) + self.failUnless('OK3' in browser.get_html()) + +from repoze.bfg.tests.exceptionviewapp.models import AnException, NotAnException +excroot = {'anexception':AnException(), + 'notanexception':NotAnException()} + +class TestExceptionViewsApp(TwillBase): + config = 'repoze.bfg.tests.exceptionviewapp:configure.zcml' + root_factory = lambda *arg: excroot + def test_it(self): + import twill.commands + browser = twill.commands.get_browser() + browser.go('http://localhost:6543/') + self.assertEqual(browser.get_code(), 200) + self.failUnless('maybe' in browser.get_html()) + + browser.go('http://localhost:6543/notanexception') + self.assertEqual(browser.get_code(), 200) + self.failUnless('no' in browser.get_html()) + + browser.go('http://localhost:6543/anexception') + self.assertEqual(browser.get_code(), 200) + self.failUnless('yes' in browser.get_html()) + + browser.go('http://localhost:6543/route_raise_exception') + self.assertEqual(browser.get_code(), 200) + self.failUnless('yes' in browser.get_html()) + + browser.go('http://localhost:6543/route_raise_exception2') + self.assertEqual(browser.get_code(), 200) + self.failUnless('yes' in browser.get_html()) + + browser.go('http://localhost:6543/route_raise_exception3') + self.assertEqual(browser.get_code(), 200) + self.failUnless('whoa' in browser.get_html()) + + browser.go('http://localhost:6543/route_raise_exception4') + self.assertEqual(browser.get_code(), 200) + self.failUnless('whoa' in browser.get_html()) + +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) + diff --git a/pyramid/tests/test_location.py b/pyramid/tests/test_location.py new file mode 100644 index 000000000..9b8360f8b --- /dev/null +++ b/pyramid/tests/test_location.py @@ -0,0 +1,40 @@ +import unittest + +class TestInside(unittest.TestCase): + def _callFUT(self, one, two): + from repoze.bfg.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 repoze.bfg.location import lineage + return lineage(context) + + def test_lineage(self): + o1 = Location() + o2 = Location(); o2.__parent__ = o1 + o3 = Location(); o3.__parent__ = o2 + o4 = Location(); o4.__parent__ = o3 + result = list(self._callFUT(o3)) + self.assertEqual(result, [o3, o2, o1]) + result = list(self._callFUT(o1)) + self.assertEqual(result, [o1]) + +from repoze.bfg.interfaces import ILocation +from zope.interface import implements +class Location(object): + implements(ILocation) + __name__ = __parent__ = None diff --git a/pyramid/tests/test_log.py b/pyramid/tests/test_log.py new file mode 100644 index 000000000..4cc8d12a0 --- /dev/null +++ b/pyramid/tests/test_log.py @@ -0,0 +1,16 @@ +import unittest + +class TestFunctions(unittest.TestCase): + def test_make_stream_logger(self): + from repoze.bfg.log import make_stream_logger + import logging + import sys + logger = make_stream_logger('foo', sys.stderr, levelname='DEBUG', + fmt='%(message)s') + self.assertEqual(logger.name, 'foo') + self.assertEqual(logger.handlers[0].stream, sys.stderr) + self.assertEqual(logger.handlers[0].formatter._fmt, '%(message)s') + self.assertEqual(logger.level, logging.DEBUG) + + + diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py new file mode 100644 index 000000000..43837b5f0 --- /dev/null +++ b/pyramid/tests/test_paster.py @@ -0,0 +1,186 @@ +import unittest + +class TestBFGShellCommand(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.paster import BFGShellCommand + return BFGShellCommand + + def _makeOne(self): + return self._getTargetClass()('bfgshell') + + def test_command_ipython_disabled(self): + command = self._makeOne() + interact = DummyInteractor() + app = DummyApp() + loadapp = DummyLoadApp(app) + command.interact = (interact,) + command.loadapp = (loadapp,) + command.args = ('/foo/bar/myapp.ini', 'myapp') + class Options(object): pass + command.options = Options() + command.options.disable_ipython =True + command.command(IPShell=None) + self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') + self.assertEqual(loadapp.section_name, 'myapp') + self.failUnless(loadapp.relative_to) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['registry'], dummy_registry) + self.assertEqual(pushed['request'].registry, dummy_registry) + self.assertEqual(interact.local, {'root':dummy_root}) + self.failUnless(interact.banner) + self.assertEqual(len(app.threadlocal_manager.popped), 1) + + def test_command_ipython_enabled(self): + command = self._makeOne() + app = DummyApp() + loadapp = DummyLoadApp(app) + command.loadapp = (loadapp,) + dummy_shell_factory = DummyIPShellFactory() + command.args = ('/foo/bar/myapp.ini', 'myapp') + class Options(object): pass + command.options = Options() + command.options.disable_ipython = False + command.command(IPShell=dummy_shell_factory) + self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') + self.assertEqual(loadapp.section_name, 'myapp') + self.failUnless(loadapp.relative_to) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['registry'], dummy_registry) + self.assertEqual(pushed['request'].registry, dummy_registry) + self.assertEqual(dummy_shell_factory.shell.local_ns,{'root':dummy_root}) + self.assertEqual(dummy_shell_factory.shell.global_ns, {}) + self.failUnless('\n\n' in dummy_shell_factory.shell.IP.BANNER) + self.assertEqual(len(app.threadlocal_manager.popped), 1) + + def test_command_get_app_hookable(self): + from paste.deploy import loadapp + command = self._makeOne() + app = DummyApp() + apped = [] + def get_app(*arg, **kw): + apped.append((arg, kw)) + return app + command.get_app = get_app + interact = DummyInteractor() + app = DummyApp() + command.interact = (interact,) + command.args = ('/foo/bar/myapp.ini', 'myapp') + class Options(object): pass + command.options = Options() + command.options.disable_ipython =True + command.command(IPShell=None) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['registry'], dummy_registry) + self.assertEqual(pushed['request'].registry, dummy_registry) + self.assertEqual(interact.local, {'root':dummy_root}) + self.failUnless(interact.banner) + self.assertEqual(len(app.threadlocal_manager.popped), 1) + self.assertEqual(apped, [(('/foo/bar/myapp.ini', 'myapp'), + {'loadapp': loadapp})]) + + def test_command_get_root_hookable(self): + command = self._makeOne() + interact = DummyInteractor() + app = DummyApp() + loadapp = DummyLoadApp(app) + command.interact = (interact,) + command.loadapp = (loadapp,) + root = Dummy() + apps = [] + def get_root(app): + apps.append(app) + return root, lambda *arg: None + command.get_root =get_root + command.args = ('/foo/bar/myapp.ini', 'myapp') + class Options(object): pass + command.options = Options() + command.options.disable_ipython =True + command.command(IPShell=None) + self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') + self.assertEqual(loadapp.section_name, 'myapp') + self.failUnless(loadapp.relative_to) + self.assertEqual(len(app.threadlocal_manager.pushed), 0) + self.assertEqual(interact.local, {'root':root}) + self.failUnless(interact.banner) + self.assertEqual(apps, [app]) + +class TestGetApp(unittest.TestCase): + def _callFUT(self, config_file, section_name, loadapp): + from repoze.bfg.paster import get_app + return get_app(config_file, section_name, loadapp) + + def test_it(self): + import os + app = DummyApp() + loadapp = DummyLoadApp(app) + result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp) + self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') + self.assertEqual(loadapp.section_name, 'myapp') + self.assertEqual(loadapp.relative_to, os.getcwd()) + self.assertEqual(result, app) + +class Dummy: + pass + +class DummyIPShellFactory(object): + def __call__(self, argv, user_ns=None): + shell = DummyIPShell() + shell(user_ns, {}) + self.shell = shell + return shell + +class DummyIPShell(object): + IP = Dummy() + IP.BANNER = 'foo' + def __call__(self, local_ns, global_ns): + self.local_ns = local_ns + self.global_ns = global_ns + + def mainloop(self): + pass + +dummy_root = Dummy() + +class DummyRegistry(object): + def queryUtility(self, iface, default=None): + return default + +dummy_registry = DummyRegistry() + +class DummyInteractor: + def __call__(self, banner, local): + self.banner = banner + self.local = local + +class DummyLoadApp: + def __init__(self, app): + self.app = app + + def __call__(self, config_name, name=None, relative_to=None): + self.config_name = config_name + self.section_name = name + self.relative_to = relative_to + return self.app + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + self.threadlocal_manager = DummyThreadLocalManager() + + def root_factory(self, environ): + return dummy_root + +class DummyThreadLocalManager: + def __init__(self): + self.pushed = [] + self.popped = [] + + def push(self, item): + self.pushed.append(item) + + def pop(self): + self.popped.append(True) + diff --git a/pyramid/tests/test_path.py b/pyramid/tests/test_path.py new file mode 100644 index 000000000..8ee0474f9 --- /dev/null +++ b/pyramid/tests/test_path.py @@ -0,0 +1,172 @@ +import unittest + +class TestCallerPath(unittest.TestCase): + def tearDown(self): + from repoze.bfg.tests import test_path + if hasattr(test_path, '__bfg_abspath__'): + del test_path.__bfg_abspath__ + + def _callFUT(self, path, level=2): + from repoze.bfg.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 + here = os.path.abspath(os.path.dirname(__file__)) + result = self._callFUT('a/b/c') + self.assertEqual(result, os.path.join(here, 'a/b/c')) + + def test_memoization_has_bfg_abspath(self): + import os + from repoze.bfg.tests import test_path + test_path.__bfg_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 + here = os.path.abspath(os.path.dirname(__file__)) + from repoze.bfg.tests import test_path + result = self._callFUT('a/b/c') + self.assertEqual(result, os.path.join(here, 'a/b/c')) + self.assertEqual(test_path.__bfg_abspath__, here) + +class TestCallerModule(unittest.TestCase): + def _callFUT(self, level=2): + from repoze.bfg.path import caller_module + return caller_module(level) + + def test_it_level_1(self): + from repoze.bfg.tests import test_path + result = self._callFUT(1) + self.assertEqual(result, test_path) + + def test_it_level_2(self): + from repoze.bfg.tests import test_path + result = self._callFUT(2) + self.assertEqual(result, test_path) + + def test_it_level_3(self): + from repoze.bfg.tests import test_path + result = self._callFUT(3) + self.failIfEqual(result, test_path) + +class TestCallerPackage(unittest.TestCase): + def _callFUT(self, *arg, **kw): + from repoze.bfg.path import caller_package + return caller_package(*arg, **kw) + + def test_it_level_1(self): + from repoze.bfg import tests + result = self._callFUT(1) + self.assertEqual(result, tests) + + def test_it_level_2(self): + from repoze.bfg 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 repoze.bfg.tests + def dummy_caller_module(*arg): + return repoze.bfg.tests + result = self._callFUT(1, caller_module=dummy_caller_module) + self.assertEqual(result, repoze.bfg.tests) + +class TestPackagePath(unittest.TestCase): + def _callFUT(self, package): + from repoze.bfg.path import package_path + return package_path(package) + + def test_it_package(self): + from repoze.bfg import tests + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, package.package_path) + + def test_it_module(self): + from repoze.bfg.tests import test_path + module = DummyPackageOrModule(test_path) + result = self._callFUT(module) + self.assertEqual(result, module.package_path) + + def test_memoization_success(self): + from repoze.bfg.tests import test_path + module = DummyPackageOrModule(test_path) + self._callFUT(module) + self.assertEqual(module.__bfg_abspath__, module.package_path) + + def test_memoization_fail(self): + from repoze.bfg.tests import test_path + module = DummyPackageOrModule(test_path, raise_exc=TypeError) + result = self._callFUT(module) + self.failIf(hasattr(module, '__bfg_abspath__')) + self.assertEqual(result, module.package_path) + +class TestPackageOf(unittest.TestCase): + def _callFUT(self, package): + from repoze.bfg.path import package_of + return package_of(package) + + def test_it_package(self): + from repoze.bfg import tests + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, tests) + + def test_it_module(self): + import repoze.bfg.tests.test_configuration + from repoze.bfg import tests + package = DummyPackageOrModule(repoze.bfg.tests.test_configuration) + result = self._callFUT(package) + self.assertEqual(result, tests) + +class TestPackageName(unittest.TestCase): + def _callFUT(self, package): + from repoze.bfg.path import package_name + return package_name(package) + + def test_it_package(self): + from repoze.bfg import tests + package = DummyPackageOrModule(tests) + result = self._callFUT(package) + self.assertEqual(result, 'repoze.bfg.tests') + + def test_it_module(self): + from repoze.bfg.tests import test_path + module = DummyPackageOrModule(test_path) + result = self._callFUT(module) + self.assertEqual(result, 'repoze.bfg.tests') + + def test_it_None(self): + result = self._callFUT(None) + self.assertEqual(result, '__main__') + +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 + + + + + + diff --git a/pyramid/tests/test_registry.py b/pyramid/tests/test_registry.py new file mode 100644 index 000000000..b6185964a --- /dev/null +++ b/pyramid/tests/test_registry.py @@ -0,0 +1,42 @@ +import unittest + +class TestRegistry(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.registry import Registry + return Registry + + def _makeOne(self): + return self._getTargetClass()() + + 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) + +class DummyModule: + __path__ = "foo" + __name__ = "dummy" + __file__ = '' + +from zope.interface import Interface +from zope.interface import implements +class IDummyEvent(Interface): + pass + +class DummyEvent(object): + implements(IDummyEvent) diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py new file mode 100644 index 000000000..264b4a547 --- /dev/null +++ b/pyramid/tests/test_renderers.py @@ -0,0 +1,499 @@ +import unittest + +from repoze.bfg.testing import cleanUp +from repoze.bfg import testing + +class TestTemplateRendererFactory(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, path, factory): + from repoze.bfg.renderers import template_renderer_factory + return template_renderer_factory(path, factory) + + def test_abspath_notfound(self): + from repoze.bfg.interfaces import ITemplateRenderer + abspath = '/wont/exist' + testing.registerUtility({}, ITemplateRenderer, name=abspath) + self.assertRaises(ValueError, self._callFUT, abspath, None) + + def test_abspath_alreadyregistered(self): + from repoze.bfg.interfaces import ITemplateRenderer + import os + abspath = os.path.abspath(__file__) + renderer = {} + testing.registerUtility(renderer, ITemplateRenderer, name=abspath) + result = self._callFUT(abspath, None) + self.failUnless(result is renderer) + + def test_abspath_notyetregistered(self): + from repoze.bfg.interfaces import ITemplateRenderer + import os + abspath = os.path.abspath(__file__) + renderer = {} + testing.registerUtility(renderer, ITemplateRenderer, name=abspath) + result = self._callFUT(abspath, None) + self.failUnless(result is renderer) + + def test_relpath_path_registered(self): + renderer = {} + from repoze.bfg.interfaces import ITemplateRenderer + testing.registerUtility(renderer, ITemplateRenderer, name='foo/bar') + result = self._callFUT('foo/bar', None) + self.failUnless(renderer is result) + + def test_relpath_is_package_registered(self): + renderer = {} + from repoze.bfg.interfaces import ITemplateRenderer + testing.registerUtility(renderer, ITemplateRenderer, name='foo:bar/baz') + result = self._callFUT('foo:bar/baz', None) + self.failUnless(renderer is result) + + def test_spec_notfound(self): + self.assertRaises(ValueError, self._callFUT, + 'repoze.bfg.tests:wont/exist', None) + + def test_spec_alreadyregistered(self): + from repoze.bfg.interfaces import ITemplateRenderer + from repoze.bfg import tests + module_name = tests.__name__ + relpath = 'test_renderers.py' + spec = '%s:%s' % (module_name, relpath) + renderer = {} + testing.registerUtility(renderer, ITemplateRenderer, name=spec) + result = self._callFUT(spec, None) + self.failUnless(result is renderer) + + def test_spec_notyetregistered(self): + import os + from repoze.bfg import tests + module_name = tests.__name__ + relpath = 'test_renderers.py' + renderer = {} + factory = DummyFactory(renderer) + spec = '%s:%s' % (module_name, relpath) + result = self._callFUT(spec, factory) + self.failUnless(result is renderer) + path = os.path.abspath(__file__) + if path.endswith('pyc'): # pragma: no cover + path = path[:-1] + self.assertEqual(factory.path, path) + self.assertEqual(factory.kw, {}) + + def test_reload_resources_true(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ISettings + from repoze.bfg.interfaces import ITemplateRenderer + settings = {'reload_resources':True} + testing.registerUtility(settings, ISettings) + renderer = {} + factory = DummyFactory(renderer) + result = self._callFUT('repoze.bfg.tests:test_renderers.py', factory) + self.failUnless(result is renderer) + spec = '%s:%s' % ('repoze.bfg.tests', 'test_renderers.py') + reg = get_current_registry() + self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec), + None) + + def test_reload_resources_false(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ISettings + from repoze.bfg.interfaces import ITemplateRenderer + settings = {'reload_resources':False} + testing.registerUtility(settings, ISettings) + renderer = {} + factory = DummyFactory(renderer) + result = self._callFUT('repoze.bfg.tests:test_renderers.py', factory) + self.failUnless(result is renderer) + spec = '%s:%s' % ('repoze.bfg.tests', 'test_renderers.py') + reg = get_current_registry() + self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec), + None) + +class TestRendererFromName(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, path, package=None): + from repoze.bfg.renderers import renderer_from_name + return renderer_from_name(path, package) + + def test_it(self): + from repoze.bfg.interfaces import IRendererFactory + import os + here = os.path.dirname(os.path.abspath(__file__)) + fixture = os.path.join(here, 'fixtures/minimal.pt') + def factory(path, **kw): + return path + testing.registerUtility(factory, IRendererFactory, name='.pt') + result = self._callFUT(fixture) + self.assertEqual(result, fixture) + + def test_with_package(self): + from repoze.bfg.interfaces import IRendererFactory + def factory(path, **kw): + return path + testing.registerUtility(factory, IRendererFactory, name='.pt') + import repoze.bfg.tests + result = self._callFUT('fixtures/minimal.pt', repoze.bfg.tests) + self.assertEqual(result, 'repoze.bfg.tests:fixtures/minimal.pt') + + def test_it_no_renderer(self): + self.assertRaises(ValueError, self._callFUT, 'foo') + + +class Test_json_renderer_factory(unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.renderers import json_renderer_factory + return json_renderer_factory(name) + + def test_it(self): + renderer = self._callFUT(None) + result = renderer({'a':1}, {}) + self.assertEqual(result, '{"a": 1}') + + def test_with_request_content_type_notset(self): + request = testing.DummyRequest() + renderer = self._callFUT(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._callFUT(None) + renderer({'a':1}, {'request':request}) + self.assertEqual(request.response_content_type, 'text/mishmash') + +class Test_string_renderer_factory(unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.renderers import string_renderer_factory + return string_renderer_factory(name) + + def test_it_unicode(self): + renderer = self._callFUT(None) + value = unicode('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(None, {'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(None, {'request':request}) + self.assertEqual(request.response_content_type, 'text/mishmash') + +class Test_rendered_response(unittest.TestCase): + def setUp(self): + testing.setUp() + from zope.deprecation import __show__ + __show__.off() + + def tearDown(self): + testing.tearDown() + from zope.deprecation import __show__ + __show__.on() + + def _callFUT(self, renderer, response, view=None, + context=None, request=None, renderer_name=None): + from repoze.bfg.renderers import rendered_response + if request is None: + request = testing.DummyRequest() + return rendered_response(renderer, response, view, + context, request, renderer_name) + + def _makeRenderer(self): + def renderer(*arg): + return 'Hello!' + return renderer + + def test_is_response(self): + renderer = self._makeRenderer() + response = DummyResponse() + result = self._callFUT(renderer, response) + self.assertEqual(result, response) + + def test_calls_renderer(self): + renderer = self._makeRenderer() + response = {'a':'1'} + result = self._callFUT(renderer, response) + self.assertEqual(result.body, 'Hello!') + + +class TestRendererHelper(unittest.TestCase): + def setUp(self): + self.config = cleanUp() + + def tearDown(self): + cleanUp() + + def _makeOne(self, *arg, **kw): + from repoze.bfg.renderers import RendererHelper + return RendererHelper(*arg, **kw) + + def _registerRendererFactory(self): + from repoze.bfg.interfaces import IRendererFactory + def renderer(*arg): + def respond(*arg): + return arg + return respond + self.config.registry.registerUtility(renderer, IRendererFactory, + name='.foo') + return renderer + + def test_resolve_spec_path_is_None(self): + helper = self._makeOne('loo.foo') + result = helper.resolve_spec(None) + self.assertEqual(result, None) + + def test_resolve_spec_package_is_None(self): + helper = self._makeOne('loo.foo') + result = helper.resolve_spec('/foo/bar') + self.assertEqual(result, '/foo/bar') + + def test_resolve_spec_absolute(self): + helper = self._makeOne('loo.foo') + result = helper.resolve_spec('repoze.bfg:flub') + self.assertEqual(result, 'repoze.bfg:flub') + + def test_resolve_spec_relative(self): + helper = self._makeOne('loo.foo', package='repoze.bfg') + result = helper.resolve_spec('flub') + self.assertEqual(result, 'repoze.bfg:flub') + + def test_render_to_response(self): + self._registerRendererFactory() + request = Dummy() + helper = self._makeOne('loo.foo') + response = helper.render_to_response('values', 'system_values', + request=request) + self.assertEqual(response.body, ('values', 'system_values')) + + def test_render_explicit_registry(self): + factory = self._registerRendererFactory() + class DummyRegistry(object): + def __init__(self): + self.responses = [factory, lambda *arg: {}] + def queryUtility(self, iface, name=None): + self.queried = True + return self.responses.pop(0) + reg = DummyRegistry() + helper = self._makeOne('loo.foo', registry=reg) + result = helper.render('value', {}) + self.assertEqual(result, ('value', {})) + self.failUnless(reg.queried) + + def test_render_system_values_is_None(self): + self._registerRendererFactory() + request = Dummy() + context = Dummy() + request.context = context + helper = self._makeOne('loo.foo') + result = helper.render('values', None, request=request) + system = {'request':request, 'context':context, + 'renderer_name':'loo.foo', 'view':None} + self.assertEqual(result, ('values', system)) + + def test_render_renderer_globals_factory_active(self): + self._registerRendererFactory() + from repoze.bfg.interfaces import IRendererGlobalsFactory + def rg(system): + return {'a':1} + self.config.registry.registerUtility(rg, IRendererGlobalsFactory) + helper = self._makeOne('loo.foo') + result = helper.render('values', None) + self.assertEqual(result[1]['a'], 1) + + def test__make_response_with_content_type(self): + request = testing.DummyRequest() + attrs = {'response_content_type':'text/nonsense'} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.content_type, 'text/nonsense') + self.assertEqual(response.body, 'abc') + + def test__make_response_with_headerlist(self): + request = testing.DummyRequest() + attrs = {'response_headerlist':[('a', '1'), ('b', '2')]} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.headerlist, + [('Content-Type', 'text/html; charset=UTF-8'), + ('Content-Length', '3'), + ('a', '1'), + ('b', '2')]) + self.assertEqual(response.body, 'abc') + + def test__make_response_with_status(self): + request = testing.DummyRequest() + attrs = {'response_status':'406 You Lose'} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.status, '406 You Lose') + self.assertEqual(response.body, 'abc') + + def test__make_response_with_charset(self): + request = testing.DummyRequest() + attrs = {'response_charset':'UTF-16'} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.charset, 'UTF-16') + + def test__make_response_with_cache_for(self): + request = testing.DummyRequest() + attrs = {'response_cache_for':100} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.cache_control.max_age, 100) + + def test_with_alternate_response_factory(self): + from repoze.bfg.interfaces import IResponseFactory + class ResponseFactory(object): + def __init__(self, result): + self.result = result + self.config.registry.registerUtility(ResponseFactory, IResponseFactory) + request = testing.DummyRequest() + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.__class__, ResponseFactory) + self.assertEqual(response.result, 'abc') + + def test__make_response_with_real_request(self): + # functional + from repoze.bfg.request import Request + request = Request({}) + attrs = {'response_status':'406 You Lose'} + request.__dict__.update(attrs) + helper = self._makeOne('loo.foo') + response = helper._make_response('abc', request) + self.assertEqual(response.status, '406 You Lose') + self.assertEqual(response.body, 'abc') + +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 repoze.bfg.renderers import render + return render(renderer_name, value, request=request, package=package) + + def test_it_no_request(self): + renderer = self.config.testing_add_renderer( + 'repoze.bfg.tests:abc/def.pt') + renderer.string_response = 'abc' + result = self._callFUT('abc/def.pt', dict(a=1)) + self.assertEqual(result, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=None) + + def test_it_with_request(self): + renderer = self.config.testing_add_renderer( + 'repoze.bfg.tests:abc/def.pt') + renderer.string_response = 'abc' + request = testing.DummyRequest() + result = self._callFUT('abc/def.pt', + dict(a=1), request=request) + self.assertEqual(result, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + +class Test_render_to_response(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, renderer_name, value, request=None, package=None): + from repoze.bfg.renderers import render_to_response + return render_to_response(renderer_name, value, request=request, + package=package) + + def test_it_no_request(self): + renderer = self.config.testing_add_renderer( + 'repoze.bfg.tests:abc/def.pt') + renderer.string_response = 'abc' + response = self._callFUT('abc/def.pt', dict(a=1)) + self.assertEqual(response.body, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=None) + + def test_it_with_request(self): + renderer = self.config.testing_add_renderer( + 'repoze.bfg.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, 'abc') + renderer.assert_(a=1) + renderer.assert_(request=request) + +class Test_get_renderer(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, renderer_name, **kw): + from repoze.bfg.renderers import get_renderer + return get_renderer(renderer_name) + + def test_it(self): + renderer = self.config.testing_add_renderer( + 'repoze.bfg.tests:abc/def.pt') + result = self._callFUT('abc/def.pt') + self.assertEqual(result, renderer) + +class Dummy: + pass + +class DummyResponse: + status = '200 OK' + headerlist = () + app_iter = () + body = '' + +class DummyFactory: + def __init__(self, renderer): + self.renderer = renderer + + def __call__(self, path, **kw): + self.path = path + self.kw = kw + return self.renderer + + diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py new file mode 100644 index 000000000..f1874d60a --- /dev/null +++ b/pyramid/tests/test_request.py @@ -0,0 +1,231 @@ +import unittest + +class TestRequest(unittest.TestCase): + def _makeOne(self, environ): + return self._getTargetClass()(environ) + + def _getTargetClass(self): + from repoze.bfg.request import Request + return Request + + 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_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'], u'La Pe\xf1a') + + def test_class_implements(self): + from repoze.bfg.interfaces import IRequest + klass = self._getTargetClass() + self.assertTrue(IRequest.implementedBy(klass)) + + def test_instance_provides(self): + from repoze.bfg.interfaces import IRequest + inst = self._makeOne({}) + self.assertTrue(IRequest.providedBy(inst)) + + def test_setattr_and_getattr_dotnotation(self): + inst = self._makeOne({}) + inst.foo = 1 + self.assertEqual(inst.foo, 1) + + def test_setattr_and_getattr(self): + inst = self._makeOne({}) + setattr(inst, 'bar', 1) + self.assertEqual(getattr(inst, 'bar'), 1) + + def test___contains__(self): + environ ={'zooma':1} + inst = self._makeOne(environ) + self.failUnless('zooma' in inst) + + def test___delitem__(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + del inst['zooma'] + self.failIf('zooma' in environ) + + def test___getitem__(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(inst['zooma'], 1) + + def test___iter__(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + iterator = iter(inst) + self.assertEqual(list(iterator), list(iter(environ))) + + def test___setitem__(self): + environ = {} + inst = self._makeOne(environ) + inst['zooma'] = 1 + self.assertEqual(environ, {'zooma':1}) + + def test_get(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(inst.get('zooma'), 1) + + def test_has_key(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(inst.has_key('zooma'), True) + + def test_items(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(inst.items(), environ.items()) + + def test_iteritems(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(list(inst.iteritems()), list(environ.iteritems())) + + def test_iterkeys(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(list(inst.iterkeys()), list(environ.iterkeys())) + + def test_itervalues(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(list(inst.itervalues()), list(environ.itervalues())) + + def test_keys(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + self.assertEqual(inst.keys(), environ.keys()) + + def test_pop(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + popped = inst.pop('zooma') + self.assertEqual(environ, {}) + self.assertEqual(popped, 1) + + def test_popitem(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + popped = inst.popitem() + self.assertEqual(environ, {}) + self.assertEqual(popped, ('zooma', 1)) + + def test_setdefault(self): + environ = {} + inst = self._makeOne(environ) + marker = [] + result = inst.setdefault('a', marker) + self.assertEqual(environ, {'a':marker}) + self.assertEqual(result, marker) + + def test_update(self): + environ = {} + inst = self._makeOne(environ) + inst.update({'a':1}, b=2) + self.assertEqual(environ, {'a':1, 'b':2}) + + def test_values(self): + environ = {'zooma':1} + inst = self._makeOne(environ) + result = inst.values() + self.assertEqual(result, environ.values()) + + def test_add_response_callback(self): + inst = self._makeOne({}) + self.assertEqual(inst.response_callbacks, ()) + def callback(request, response): + """ """ + inst.add_response_callback(callback) + self.assertEqual(inst.response_callbacks, [callback]) + inst.add_response_callback(callback) + self.assertEqual(inst.response_callbacks, [callback, callback]) + + def test__process_response_callbacks(self): + inst = self._makeOne({}) + def callback1(request, response): + request.called1 = True + response.called1 = True + def callback2(request, response): + request.called2 = True + response.called2 = True + inst.response_callbacks = [callback1, callback2] + response = DummyResponse() + inst._process_response_callbacks(response) + self.assertEqual(inst.called1, True) + self.assertEqual(inst.called2, True) + self.assertEqual(response.called1, True) + self.assertEqual(response.called2, True) + self.assertEqual(inst.response_callbacks, []) + + def test_add_finished_callback(self): + inst = self._makeOne({}) + self.assertEqual(inst.finished_callbacks, ()) + def callback(request): + """ """ + inst.add_finished_callback(callback) + self.assertEqual(inst.finished_callbacks, [callback]) + inst.add_finished_callback(callback) + self.assertEqual(inst.finished_callbacks, [callback, callback]) + + def test__process_finished_callbacks(self): + inst = self._makeOne({}) + def callback1(request): + request.called1 = True + def callback2(request): + request.called2 = True + inst.finished_callbacks = [callback1, callback2] + inst._process_finished_callbacks() + self.assertEqual(inst.called1, True) + self.assertEqual(inst.called2, True) + self.assertEqual(inst.finished_callbacks, []) + +class Test_route_request_iface(unittest.TestCase): + def _callFUT(self, name): + from repoze.bfg.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') + +class Test_add_global_response_headers(unittest.TestCase): + def _callFUT(self, request, headerlist): + from repoze.bfg.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 DummyRequest: + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + + def add_response_callback(self, callback): + self.response_callbacks = [callback] + +class DummyResponse: + def __init__(self): + self.headerlist = [] + + diff --git a/pyramid/tests/test_resource.py b/pyramid/tests/test_resource.py new file mode 100644 index 000000000..191d72c8b --- /dev/null +++ b/pyramid/tests/test_resource.py @@ -0,0 +1,431 @@ +import unittest +from repoze.bfg.testing import cleanUp + +class TestOverrideProvider(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from repoze.bfg.resource import OverrideProvider + return OverrideProvider + + def _makeOne(self, module): + klass = self._getTargetClass() + return klass(module) + + def _registerOverrides(self, overrides, name='repoze.bfg.tests'): + from repoze.bfg.interfaces import IPackageOverrides + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + reg.registerUtility(overrides, IPackageOverrides, name=name) + + def test_get_resource_filename_no_overrides(self): + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = os.path.join(here, resource_name) + result = provider.get_resource_filename(None, resource_name) + self.assertEqual(result, expected) + + def test_get_resource_stream_no_overrides(self): + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = open(os.path.join(here, resource_name)).read() + result = provider.get_resource_stream(None, resource_name) + self.assertEqual(result.read(), expected) + + def test_get_resource_string_no_overrides(self): + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = open(os.path.join(here, resource_name)).read() + result = provider.get_resource_string(None, resource_name) + self.assertEqual(result, expected) + + def test_has_resource_no_overrides(self): + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + result = provider.has_resource(resource_name) + self.assertEqual(result, True) + + def test_resource_isdir_no_overrides(self): + file_resource_name = 'test_resource.py' + directory_resource_name = 'fixtures' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + 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 = 'fixtures' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + result = provider.resource_isdir(resource_name) + self.failUnless(result) + + def test_get_resource_filename_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = os.path.join(here, resource_name) + result = provider.get_resource_filename(None, resource_name) + self.assertEqual(result, expected) + + def test_get_resource_stream_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = os.path.join(here, resource_name) + result = provider.get_resource_filename(None, resource_name) + self.assertEqual(result, expected) + + def test_get_resource_string_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + import os + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + here = os.path.dirname(os.path.abspath(__file__)) + expected = os.path.join(here, resource_name) + result = provider.get_resource_filename(None, resource_name) + self.assertEqual(result, expected) + + def test_has_resource_override_returns_None(self): + overrides = DummyOverrides(None) + self._registerOverrides(overrides) + resource_name = 'test_resource.py' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + 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 = 'fixtures' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + 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 = 'fixtures' + import repoze.bfg.tests + provider = self._makeOne(repoze.bfg.tests) + result = provider.resource_listdir(resource_name) + self.failUnless(result) + + def test_get_resource_filename_override_returns_value(self): + overrides = DummyOverrides('value') + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.get_resource_filename(None, 'test_resource.py') + self.assertEqual(result, 'value') + + def test_get_resource_stream_override_returns_value(self): + overrides = DummyOverrides('value') + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.get_resource_stream(None, 'test_resource.py') + self.assertEqual(result, 'value') + + def test_get_resource_string_override_returns_value(self): + overrides = DummyOverrides('value') + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.get_resource_string(None, 'test_resource.py') + self.assertEqual(result, 'value') + + def test_has_resource_override_returns_True(self): + overrides = DummyOverrides(True) + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.has_resource('test_resource.py') + self.assertEqual(result, True) + + def test_resource_isdir_override_returns_False(self): + overrides = DummyOverrides(False) + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.resource_isdir('fixtures') + self.assertEqual(result, False) + + def test_resource_listdir_override_returns_values(self): + overrides = DummyOverrides(['a']) + import repoze.bfg.tests + self._registerOverrides(overrides) + provider = self._makeOne(repoze.bfg.tests) + result = provider.resource_listdir('fixtures') + self.assertEqual(result, ['a']) + +class TestPackageOverrides(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.resource import PackageOverrides + return PackageOverrides + + def _makeOne(self, package, pkg_resources=None): + klass = self._getTargetClass() + if pkg_resources is None: + pkg_resources = DummyPkgResources() + return klass(package, pkg_resources=pkg_resources) + + def test_ctor_package_already_has_loader_of_different_type(self): + package = DummyPackage('package') + package.__loader__ = True + self.assertRaises(TypeError, self._makeOne, package) + + 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 repoze.bfg.resource 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 repoze.bfg.resource import DirectoryOverride + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= [None] + po.insert('foo/', 'package', 'bar/') + self.assertEqual(len(po.overrides), 2) + override = po.overrides[0] + self.assertEqual(override.__class__, DirectoryOverride) + + def test_insert_file(self): + from repoze.bfg.resource import FileOverride + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= [None] + po.insert('foo.pt', 'package', 'bar.pt') + self.assertEqual(len(po.overrides), 2) + override = po.overrides[0] + self.assertEqual(override.__class__, FileOverride) + + def test_search_path(self): + overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + self.assertEqual(list(po.search_path('whatever')), + [('package', 'name')]) + + def test_get_filename(self): + import os + overrides = [ DummyOverride(None), DummyOverride( + ('repoze.bfg.tests', 'test_resource.py'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + here = os.path.dirname(os.path.abspath(__file__)) + expected = os.path.join(here, 'test_resource.py') + self.assertEqual(po.get_filename('whatever'), expected) + + def test_get_stream(self): + import os + overrides = [ DummyOverride(None), DummyOverride( + ('repoze.bfg.tests', 'test_resource.py'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + here = os.path.dirname(os.path.abspath(__file__)) + expected = open(os.path.join(here, 'test_resource.py')).read() + self.assertEqual(po.get_stream('whatever').read(), expected) + + def test_get_string(self): + import os + overrides = [ DummyOverride(None), DummyOverride( + ('repoze.bfg.tests', 'test_resource.py'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + here = os.path.dirname(os.path.abspath(__file__)) + expected = open(os.path.join(here, 'test_resource.py')).read() + self.assertEqual(po.get_string('whatever'), expected) + + def test_has_resource(self): + overrides = [ DummyOverride(None), DummyOverride( + ('repoze.bfg.tests', 'test_resource.py'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + self.assertEqual(po.has_resource('whatever'), True) + + def test_isdir_false(self): + overrides = [ DummyOverride( + ('repoze.bfg.tests', 'test_resource.py'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + self.assertEqual(po.isdir('whatever'), False) + + def test_isdir_true(self): + overrides = [ DummyOverride( + ('repoze.bfg.tests', 'fixtures'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + self.assertEqual(po.isdir('whatever'), True) + + def test_listdir(self): + overrides = [ DummyOverride( + ('repoze.bfg.tests', 'fixtures'))] + package = DummyPackage('package') + po = self._makeOne(package) + po.overrides= overrides + self.failUnless(po.listdir('whatever')) + +class TestDirectoryOverride(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.resource import DirectoryOverride + return DirectoryOverride + + def _makeOne(self, path, package, prefix): + klass = self._getTargetClass() + return klass(path, package, prefix) + + def test_it_match(self): + o = self._makeOne('foo/', 'package', 'bar/') + result = o('foo/something.pt') + self.assertEqual(result, ('package', 'bar/something.pt')) + + def test_it_no_match(self): + o = self._makeOne('foo/', 'package', 'bar/') + result = o('baz/notfound.pt') + self.assertEqual(result, None) + +class Test_resolve_resource_spec(unittest.TestCase): + def _callFUT(self, spec, package_name='__main__'): + from repoze.bfg.resource import resolve_resource_spec + return resolve_resource_spec(spec, package_name) + + def test_abspath(self): + import os + here = os.path.dirname(__file__) + path = os.path.abspath(here) + package_name, filename = self._callFUT(path, 'apackage') + self.assertEqual(filename, path) + self.assertEqual(package_name, None) + + def test_rel_spec(self): + pkg = 'repoze.bfg.tests' + path = 'test_resource.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, 'repoze.bfg.tests') + self.assertEqual(filename, 'test_resource.py') + + def test_abs_spec(self): + pkg = 'repoze.bfg.tests' + path = 'repoze.bfg.nottests:test_resource.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, 'repoze.bfg.nottests') + self.assertEqual(filename, 'test_resource.py') + + def test_package_name_is_None(self): + pkg = None + path = 'test_resource.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, None) + self.assertEqual(filename, 'test_resource.py') + + def test_package_name_is_package_object(self): + import repoze.bfg.tests + pkg = repoze.bfg.tests + path = 'test_resource.py' + package_name, filename = self._callFUT(path, pkg) + self.assertEqual(package_name, 'repoze.bfg.tests') + self.assertEqual(filename, 'test_resource.py') + + +class TestFileOverride(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.resource import FileOverride + return FileOverride + + def _makeOne(self, path, package, prefix): + klass = self._getTargetClass() + return klass(path, package, prefix) + + def test_it_match(self): + o = self._makeOne('foo.pt', 'package', 'bar.pt') + result = o('foo.pt') + self.assertEqual(result, ('package', 'bar.pt')) + + def test_it_no_match(self): + o = self._makeOne('foo.pt', 'package', 'bar.pt') + result = o('notfound.pt') + self.assertEqual(result, None) + +class DummyOverride: + def __init__(self, result): + self.result = result + + def __call__(self, resource_name): + return self.result + +class DummyOverrides: + def __init__(self, result): + self.result = result + + def get_filename(self, resource_name): + return self.result + + listdir = isdir = has_resource = get_stream = get_string = get_filename + +class 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 + diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py new file mode 100644 index 000000000..fade0679b --- /dev/null +++ b/pyramid/tests/test_router.py @@ -0,0 +1,1034 @@ +import unittest + +from repoze.bfg import testing + +class TestRouter(unittest.TestCase): + def setUp(self): + testing.setUp() + from repoze.bfg.threadlocal import get_current_registry + self.registry = get_current_registry() + + def tearDown(self): + testing.tearDown() + + def _registerRouteRequest(self, name): + from repoze.bfg.interfaces import IRouteRequest + from repoze.bfg.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 repoze.bfg.interfaces import IRoutesMapper + from repoze.bfg.urldispatch import RoutesMapper + mapper = self.registry.queryUtility(IRoutesMapper) + if mapper is None: + mapper = RoutesMapper() + self.registry.registerUtility(mapper, IRoutesMapper) + mapper.connect(name, path, factory) + + def _registerLogger(self): + from repoze.bfg.interfaces import IDebugLogger + logger = DummyLogger() + self.registry.registerUtility(logger, IDebugLogger) + return logger + + def _registerSettings(self, **kw): + from repoze.bfg.interfaces import ISettings + settings = {'debug_authorization':False, 'debug_notfound':False} + settings.update(kw) + self.registry.registerUtility(settings, ISettings) + + def _registerTraverserFactory(self, context, view_name='', subpath=None, + traversed=None, virtual_root=None, + virtual_root_path=None, raise_error=None, + **kw): + from repoze.bfg.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 repoze.bfg.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 repoze.bfg.interfaces import IRootFactory + self.registry.registerUtility(rootfactory, IRootFactory) + return rootfactory + + def _getTargetClass(self): + from repoze.bfg.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_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 repoze.bfg.interfaces import IRequestFactory + class DummyRequestFactory(object): + pass + self.registry.registerUtility(DummyRequestFactory, IRequestFactory) + router = self._makeOne() + self.assertEqual(router.request_factory, DummyRequestFactory) + + def test_call_traverser_default(self): + from repoze.bfg.exceptions import NotFound + environ = self._makeEnviron() + logger = self._registerLogger() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(NotFound, router, environ, start_response) + self.failUnless('/' in why[0], why) + self.failIf('debug_notfound' in why[0]) + self.assertEqual(len(logger.messages), 0) + + def test_traverser_raises_notfound_class(self): + from repoze.bfg.exceptions import NotFound + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=NotFound) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(NotFound, router, environ, start_response) + + def test_traverser_raises_notfound_instance(self): + from repoze.bfg.exceptions import NotFound + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=NotFound('foo')) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(NotFound, router, environ, start_response) + self.failUnless('foo' in why[0], why) + + def test_traverser_raises_forbidden_class(self): + from repoze.bfg.exceptions import Forbidden + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=Forbidden) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(Forbidden, router, environ, start_response) + + def test_traverser_raises_forbidden_instance(self): + from repoze.bfg.exceptions import Forbidden + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context, raise_error=Forbidden('foo')) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(Forbidden, router, environ, start_response) + self.failUnless('foo' in why[0], why) + + def test_call_no_view_registered_no_isettings(self): + from repoze.bfg.exceptions import NotFound + environ = self._makeEnviron() + context = DummyContext() + self._registerTraverserFactory(context) + logger = self._registerLogger() + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(NotFound, router, environ, start_response) + self.failUnless('/' in why[0], why) + self.failIf('debug_notfound' in why[0]) + self.assertEqual(len(logger.messages), 0) + + def test_call_no_view_registered_debug_notfound_false(self): + from repoze.bfg.exceptions import NotFound + 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(NotFound, router, environ, start_response) + self.failUnless('/' in why[0], why) + self.failIf('debug_notfound' in why[0]) + self.assertEqual(len(logger.messages), 0) + + def test_call_no_view_registered_debug_notfound_true(self): + from repoze.bfg.exceptions import NotFound + 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(NotFound, router, environ, start_response) + self.failUnless( + "debug_notfound of url http://localhost:8080/; path_info: '/', " + "context:" in why[0]) + self.failUnless("view_name: '', subpath: []" in why[0]) + self.failUnless('http://localhost:8080' in why[0], why) + + self.assertEqual(len(logger.messages), 1) + message = logger.messages[0] + self.failUnless('of url http://localhost:8080' in message) + self.failUnless("path_info: '/'" in message) + self.failUnless('DummyContext instance at' in message) + self.failUnless("view_name: ''" in message) + self.failUnless("subpath: []" in message) + + def test_call_view_returns_nonresponse(self): + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + self._registerTraverserFactory(context) + environ = self._makeEnviron() + view = DummyView('abc') + self._registerView(view, '', IViewClassifier, None, None) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(ValueError, router, environ, start_response) + + def test_call_view_registered_nonspecific_default_path(self): + from repoze.bfg.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) + 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 repoze.bfg.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 repoze.bfg.interfaces import IRequest + from repoze.bfg.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 repoze.bfg.exceptions import NotFound + from repoze.bfg.interfaces import IViewClassifier + class IContext(Interface): + pass + class INotContext(Interface): + pass + from repoze.bfg.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(NotFound, router, environ, start_response) + + def test_call_view_raises_forbidden(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from repoze.bfg.exceptions import Forbidden + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView(response, raise_exception=Forbidden("unauthorized")) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(Forbidden, router, environ, start_response) + self.assertEqual(why[0], 'unauthorized') + + def test_call_view_raises_notfound(self): + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.exceptions import NotFound + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView(response, raise_exception=NotFound("notfound")) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + why = exc_raised(NotFound, router, environ, start_response) + self.assertEqual(why[0], 'notfound') + + def test_call_request_has_response_callbacks(self): + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse('200 OK') + def view(context, request): + def callback(request, response): + response.called_back = True + request.response_callbacks = [callback] + return response + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + router(environ, start_response) + self.assertEqual(response.called_back, True) + + def test_call_request_has_finished_callbacks_when_view_succeeds(self): + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse('200 OK') + def view(context, request): + def callback(request): + request.environ['called_back'] = True + request.finished_callbacks = [callback] + return response + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + router(environ, start_response) + self.assertEqual(environ['called_back'], True) + + def test_call_request_has_finished_callbacks_when_view_raises(self): + from zope.interface import Interface + from zope.interface import directlyProvides + class IContext(Interface): + pass + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + def view(context, request): + def callback(request): + request.environ['called_back'] = True + request.finished_callbacks = [callback] + raise NotImplementedError + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + exc_raised(NotImplementedError, router, environ, start_response) + self.assertEqual(environ['called_back'], True) + + def test_call_request_factory_raises(self): + # making sure finally doesnt barf when a request cannot be created + environ = self._makeEnviron() + router = self._makeOne() + def dummy_request_factory(environ): + raise NotImplementedError + router.request_factory = dummy_request_factory + start_response = DummyStartResponse() + exc_raised(NotImplementedError, router, environ, start_response) + + def test_call_eventsends(self): + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.interfaces import INewResponse + from repoze.bfg.interfaces import IContextFound + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, None, None) + request_events = self._registerEventListener(INewRequest) + aftertraversal_events = self._registerEventListener(IContextFound) + response_events = self._registerEventListener(INewResponse) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(len(request_events), 1) + self.assertEqual(request_events[0].request.environ, environ) + self.assertEqual(len(aftertraversal_events), 1) + self.assertEqual(aftertraversal_events[0].request.environ, environ) + self.assertEqual(len(response_events), 1) + self.assertEqual(response_events[0].response, response) + self.assertEqual(result, response.app_iter) + + def test_call_pushes_and_pops_threadlocal_manager(self): + from repoze.bfg.interfaces import IViewClassifier + context = DummyContext() + self._registerTraverserFactory(context) + response = DummyResponse() + response.app_iter = ['Hello world'] + view = DummyView(response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, None, None) + router = self._makeOne() + start_response = DummyStartResponse() + router.threadlocal_manager = DummyThreadLocalManager() + router(environ, start_response) + self.assertEqual(len(router.threadlocal_manager.pushed), 1) + self.assertEqual(len(router.threadlocal_manager.popped), 1) + + def test_call_route_matches_and_has_factory(self): + from repoze.bfg.interfaces import IViewClassifier + self._registerRouteRequest('foo') + 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(environ['bfg.routes.matchdict'], matchdict) + self.assertEqual(environ['bfg.routes.route'].name, 'foo') + self.assertEqual(request.matchdict, matchdict) + self.assertEqual(request.matched_route.name, 'foo') + + def test_call_route_matches_doesnt_overwrite_subscriber_iface(self): + from repoze.bfg.interfaces import INewRequest + from repoze.bfg.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(environ['bfg.routes.matchdict'], matchdict) + self.assertEqual(environ['bfg.routes.route'].name, 'foo') + self.assertEqual(request.matchdict, matchdict) + self.assertEqual(request.matched_route.name, 'foo') + self.failUnless(IFoo.providedBy(request)) + + def test_root_factory_raises_notfound(self): + from repoze.bfg.interfaces import IRootFactory + from repoze.bfg.exceptions import NotFound + from zope.interface import Interface + from zope.interface import directlyProvides + def rootfactory(request): + raise NotFound('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(NotFound, router, environ, start_response) + self.failUnless('from root factory' in why[0]) + + def test_root_factory_raises_forbidden(self): + from repoze.bfg.interfaces import IRootFactory + from repoze.bfg.exceptions import Forbidden + from zope.interface import Interface + from zope.interface import directlyProvides + def rootfactory(request): + raise Forbidden('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(Forbidden, router, environ, start_response) + self.failUnless('from root factory' in why[0]) + + def test_root_factory_exception_propagating(self): + from repoze.bfg.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 repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequestFactory + def rfactory(environ): + return request + self.registry.registerUtility(rfactory, IRequestFactory) + from repoze.bfg.request import Request + request = Request.blank('/') + context = DummyContext() + directlyProvides(context, IContext) + self._registerTraverserFactory(context, subpath=['']) + response = DummyResponse() + view = DummyView(response, raise_exception=RuntimeError) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, IContext) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(RuntimeError, router, environ, start_response) + # ``exception`` must be attached to request even if a suitable + # exception view cannot be found + self.assertEqual(request.exception.__class__, RuntimeError) + + def test_call_view_raises_exception_view(self): + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.interfaces import IRequest + 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, + IRequest, RuntimeError) + router = self._makeOne() + start_response = DummyStartResponse() + result = router(environ, start_response) + self.assertEqual(result, ["Hello, world"]) + self.assertEqual(view.request.exception.__class__, RuntimeError) + + def test_call_view_raises_super_exception_sub_exception_view(self): + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IRootFactory + from repoze.bfg.interfaces import IRequest + from repoze.bfg.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 repoze.bfg.interfaces import IRequest + from repoze.bfg.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_response(self): + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + environ = self._makeEnviron() + response = DummyResponse() + view = DummyView(response, raise_exception=RuntimeError) + self._registerView(view, '', IViewClassifier, IRequest, None) + exception_view = DummyView(None) + self._registerView(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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.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 repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IExceptionViewClassifier + req_iface = self._registerRouteRequest('foo') + response = DummyResponse() + exception_response = DummyResponse() + exception_response.app_iter = ["Hello, world"] + view = DummyView(response, raise_exception=RuntimeError) + exception_view = DummyView(exception_response) + environ = self._makeEnviron() + self._registerView(view, '', IViewClassifier, IRequest, None) + self._registerView(exception_view, '', IExceptionViewClassifier, + req_iface, RuntimeError) + router = self._makeOne() + start_response = DummyStartResponse() + self.assertRaises(RuntimeError, router, environ, start_response) + +class DummyContext: + pass + +class DummyView: + def __init__(self, response, raise_exception=None): + self.response = response + self.raise_exception = raise_exception + + def __call__(self, context, request): + self.context = context + self.request = request + if not self.raise_exception is None: + raise self.raise_exception + return self.response + +class DummyRootFactory: + def __init__(self, root): + self.root = root + + def __call__(self, environ): + return self.root + +class DummyStartResponse: + status = () + headers = () + def __call__(self, status, headers): + self.status = status + self.headers = headers + +class DummyResponse: + headerlist = () + app_iter = () + def __init__(self, status='200 OK'): + self.status = status + +class DummyThreadLocalManager: + def __init__(self): + self.pushed = [] + self.popped = [] + + def push(self, val): + self.pushed.append(val) + + def pop(self): + self.popped.append(True) + +class DummyAuthenticationPolicy: + pass + +class DummyLogger: + def __init__(self): + self.messages = [] + def info(self, msg): + self.messages.append(msg) + warn = info + debug = info + +def exc_raised(exc, func, *arg, **kw): + try: + func(*arg, **kw) + except exc, e: + return e + else: + raise AssertionError('%s not raised' % exc) # pragma: no cover + + diff --git a/pyramid/tests/test_scripting.py b/pyramid/tests/test_scripting.py new file mode 100644 index 000000000..2663c4a0f --- /dev/null +++ b/pyramid/tests/test_scripting.py @@ -0,0 +1,81 @@ +import unittest + +class TestGetRoot(unittest.TestCase): + def _callFUT(self, app, request=None): + from repoze.bfg.paster import get_root + return get_root(app, request) + + def test_it_norequest(self): + app = DummyApp() + root, closer = self._callFUT(app) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['registry'], dummy_registry) + self.assertEqual(pushed['request'].registry, app.registry) + self.assertEqual(len(app.threadlocal_manager.popped), 0) + closer() + self.assertEqual(len(app.threadlocal_manager.popped), 1) + + def test_it_withrequest(self): + app = DummyApp() + request = DummyRequest({}) + root, closer = self._callFUT(app, request) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['registry'], dummy_registry) + self.assertEqual(pushed['request'], request) + self.assertEqual(len(app.threadlocal_manager.popped), 0) + closer() + self.assertEqual(len(app.threadlocal_manager.popped), 1) + + def test_it_requestfactory_overridden(self): + app = DummyApp() + request = Dummy() + class DummyFactory(object): + @classmethod + def blank(cls, path): + return request + registry = DummyRegistry(DummyFactory) + app.registry = registry + root, closer = self._callFUT(app) + self.assertEqual(len(app.threadlocal_manager.pushed), 1) + pushed = app.threadlocal_manager.pushed[0] + self.assertEqual(pushed['request'], request) + +class Dummy: + pass + +dummy_root = Dummy() + +class DummyRegistry(object): + def __init__(self, result=None): + self.result = result + + def queryUtility(self, iface, default=None): + return self.result or default + +dummy_registry = DummyRegistry() + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + self.threadlocal_manager = DummyThreadLocalManager() + + def root_factory(self, environ): + return dummy_root + +class DummyThreadLocalManager: + def __init__(self): + self.pushed = [] + self.popped = [] + + def push(self, item): + self.pushed.append(item) + + def pop(self): + self.popped.append(True) + +class DummyRequest: + def __init__(self, environ): + self.environ = environ + diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py new file mode 100644 index 000000000..13a0e2d9b --- /dev/null +++ b/pyramid/tests/test_security.py @@ -0,0 +1,395 @@ +import unittest + +from repoze.bfg.testing import cleanUp + + +class TestAllPermissionsList(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from repoze.bfg.security import AllPermissionsList + return AllPermissionsList + + def _makeOne(self): + return self._getTargetClass()() + + def test_it(self): + thing = self._makeOne() + self.failUnless(thing.__eq__(thing)) + self.assertEqual(thing.__iter__(), ()) + self.failUnless('anything' in thing) + + def test_singleton(self): + from repoze.bfg.security import ALL_PERMISSIONS + self.assertEqual(ALL_PERMISSIONS.__class__, self._getTargetClass()) + +class TestAllowed(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.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.failUnless(allowed) + self.assertEqual(str(allowed), 'hello') + self.failUnless('" in repr(allowed)) + +class TestDenied(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.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.failIf(denied) + self.assertEqual(str(denied), 'hello') + self.failUnless('" in repr(denied)) + +class TestACLAllowed(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.security import ACLAllowed + return ACLAllowed + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + msg = ("ACLAllowed permission 'permission' via ACE 'ace' in ACL 'acl' " + "on context 'ctx' for principals 'principals'") + allowed = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') + self.failUnless(msg in allowed.msg) + self.assertEqual(allowed, True) + self.failUnless(allowed) + self.assertEqual(str(allowed), msg) + self.failUnless('" % msg in repr(allowed)) + +class TestACLDenied(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.security import ACLDenied + return ACLDenied + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_it(self): + msg = ("ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' " + "on context 'ctx' for principals 'principals'") + denied = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') + self.failUnless(msg in denied.msg) + self.assertEqual(denied, False) + self.failIf(denied) + self.assertEqual(str(denied), msg) + self.failUnless('" % msg in repr(denied)) + +class TestViewExecutionPermitted(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.security import view_execution_permitted + return view_execution_permitted(*arg, **kw) + + def _registerSecuredView(self, view_name, allow=True): + from repoze.bfg.threadlocal import get_current_registry + from zope.interface import Interface + from repoze.bfg.interfaces import ISecuredView + from repoze.bfg.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 repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import ISettings + settings = dict(debug_authorization=True) + reg = get_current_registry() + reg.registerUtility(settings, ISettings) + context = DummyContext() + request = DummyRequest({}) + result = self._callFUT(context, request, '') + msg = result.msg + self.failUnless("Allowed: view name '' in context" in msg) + self.failUnless('(no permission defined)' in msg) + self.assertEqual(result, True) + + def test_with_permission(self): + from zope.interface import Interface + from zope.interface import directlyProvides + from repoze.bfg.interfaces import IRequest + class IContext(Interface): + pass + context = DummyContext() + directlyProvides(context, IContext) + self._registerSecuredView('', True) + request = DummyRequest({}) + directlyProvides(request, IRequest) + result = self._callFUT(context, request, '') + self.failUnless(result is True) + +class TestHasPermission(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg): + from repoze.bfg.security import has_permission + return has_permission(*arg) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT('view', None, request) + self.assertEqual(result, True) + self.assertEqual(result.msg, 'No authentication policy in use.') + + def test_authentication_policy_no_authorization_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, None) + self.assertRaises(ValueError, self._callFUT, 'view', None, request) + + def test_authn_and_authz_policies_registered(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, None) + _registerAuthorizationPolicy(request.registry, 'yo') + self.assertEqual(self._callFUT('view', None, request), 'yo') + + def test_no_registry_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + request = DummyRequest({}) + registry = get_current_registry() + _registerAuthenticationPolicy(registry, None) + _registerAuthorizationPolicy(registry, 'yo') + self.assertEqual(self._callFUT('view', None, request), 'yo') + +class TestAuthenticatedUserId(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.security import authenticated_userid + return authenticated_userid(request) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request) + self.assertEqual(result, None) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + request = DummyRequest({}) + registry = get_current_registry() + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + +class TestEffectivePrincipals(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, request): + from repoze.bfg.security import effective_principals + return effective_principals(request) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request) + self.assertEqual(result, []) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + request = DummyRequest({}) + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + +class TestPrincipalsAllowedByPermission(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg): + from repoze.bfg.security import principals_allowed_by_permission + return principals_allowed_by_permission(*arg) + + def test_no_authorization_policy(self): + from repoze.bfg.security import Everyone + context = DummyContext() + result = self._callFUT(context, 'view') + self.assertEqual(result, [Everyone]) + + def test_with_authorization_policy(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + _registerAuthorizationPolicy(registry, 'yo') + context = DummyContext() + result = self._callFUT(context, 'view') + self.assertEqual(result, 'yo') + +class TestRemember(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg): + from repoze.bfg.security import remember + return remember(*arg) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request, 'me') + self.assertEqual(result, []) + + def test_with_authentication_policy(self): + request = _makeRequest() + registry = request.registry + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request, 'me') + self.assertEqual(result, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + request = DummyRequest({}) + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request, 'me') + self.assertEqual(result, 'yo') + +class TestForget(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg): + from repoze.bfg.security import forget + return forget(*arg) + + def test_no_authentication_policy(self): + request = _makeRequest() + result = self._callFUT(request) + self.assertEqual(result, []) + + def test_with_authentication_policy(self): + request = _makeRequest() + _registerAuthenticationPolicy(request.registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + + def test_with_authentication_policy_no_reg_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + registry = get_current_registry() + request = DummyRequest({}) + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request) + self.assertEqual(result, 'yo') + +class DummyContext: + def __init__(self, *arg, **kw): + self.__dict__.update(kw) + +class DummyRequest: + def __init__(self, environ): + self.environ = environ + +class DummyAuthenticationPolicy: + def __init__(self, result): + self.result = result + + def effective_principals(self, request): + return self.result + + def authenticated_userid(self, request): + return self.result + + def remember(self, request, principal, **kw): + return self.result + + def forget(self, request): + return self.result + +class DummyAuthorizationPolicy: + def __init__(self, result): + self.result = result + + def permits(self, context, principals, permission): + return self.result + + def principals_allowed_by_permission(self, context, permission): + return self.result + +def _registerAuthenticationPolicy(reg, result): + from repoze.bfg.interfaces import IAuthenticationPolicy + policy = DummyAuthenticationPolicy(result) + reg.registerUtility(policy, IAuthenticationPolicy) + return policy + +def _registerAuthorizationPolicy(reg, result): + from repoze.bfg.interfaces import IAuthorizationPolicy + policy = DummyAuthorizationPolicy(result) + reg.registerUtility(policy, IAuthorizationPolicy) + return policy + +def _makeRequest(): + from repoze.bfg.registry import Registry + request = DummyRequest({}) + request.registry = Registry() + return request + + diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py new file mode 100644 index 000000000..be73f9dce --- /dev/null +++ b/pyramid/tests/test_settings.py @@ -0,0 +1,203 @@ +import unittest + +class TestSettings(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.settings import Settings + return Settings + + def _makeOne(self, d=None, environ=None): + if environ is None: + environ = {} + klass = self._getTargetClass() + return klass(d, _environ_=environ) + + def test_getattr(self): + settings = self._makeOne({'reload_templates':False}) + self.assertEqual(settings.reload_templates, False) + + def test_getattr_raises_attribute_error(self): + settings = self._makeOne() + self.assertRaises(AttributeError, settings.__getattr__, 'mykey') + + def test_noargs(self): + settings = self._makeOne() + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['debug_notfound'], False) + self.assertEqual(settings['reload_templates'], False) + self.assertEqual(settings['reload_resources'], False) + self.assertEqual(settings['configure_zcml'], '') + + def test_reload_templates(self): + settings = self._makeOne({}) + self.assertEqual(settings['reload_templates'], False) + result = self._makeOne({'reload_templates':'false'}) + self.assertEqual(result['reload_templates'], False) + result = self._makeOne({'reload_templates':'t'}) + self.assertEqual(result['reload_templates'], True) + result = self._makeOne({'reload_templates':'1'}) + self.assertEqual(result['reload_templates'], True) + result = self._makeOne({}, {'BFG_RELOAD_TEMPLATES':'1'}) + self.assertEqual(result['reload_templates'], True) + result = self._makeOne({'reload_templates':'false'}, + {'BFG_RELOAD_TEMPLATES':'1'}) + self.assertEqual(result['reload_templates'], True) + + def test_reload_resources(self): + result = self._makeOne({}) + self.assertEqual(result['reload_resources'], False) + result = self._makeOne({'reload_resources':'false'}) + self.assertEqual(result['reload_resources'], False) + result = self._makeOne({'reload_resources':'t'}) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({'reload_resources':'1'}) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({}, {'BFG_RELOAD_RESOURCES':'1'}) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({'reload_resources':'false'}, + {'BFG_RELOAD_RESOURCES':'1'}) + self.assertEqual(result['reload_resources'], True) + + def test_reload_all(self): + result = self._makeOne({}) + self.assertEqual(result['reload_templates'], False) + self.assertEqual(result['reload_resources'], False) + result = self._makeOne({'reload_all':'false'}) + self.assertEqual(result['reload_templates'], False) + self.assertEqual(result['reload_resources'], False) + result = self._makeOne({'reload_all':'t'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({'reload_all':'1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({}, {'BFG_RELOAD_ALL':'1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + result = self._makeOne({'reload_all':'false'}, + {'BFG_RELOAD_ALL':'1'}) + self.assertEqual(result['reload_templates'], True) + self.assertEqual(result['reload_resources'], True) + + def test_debug_authorization(self): + result = self._makeOne({}) + self.assertEqual(result['debug_authorization'], False) + result = self._makeOne({'debug_authorization':'false'}) + self.assertEqual(result['debug_authorization'], False) + result = self._makeOne({'debug_authorization':'t'}) + self.assertEqual(result['debug_authorization'], True) + result = self._makeOne({'debug_authorization':'1'}) + self.assertEqual(result['debug_authorization'], True) + result = self._makeOne({}, {'BFG_DEBUG_AUTHORIZATION':'1'}) + self.assertEqual(result['debug_authorization'], True) + result = self._makeOne({'debug_authorization':'false'}, + {'BFG_DEBUG_AUTHORIZATION':'1'}) + self.assertEqual(result['debug_authorization'], True) + + def test_debug_notfound(self): + result = self._makeOne({}) + self.assertEqual(result['debug_notfound'], False) + result = self._makeOne({'debug_notfound':'false'}) + self.assertEqual(result['debug_notfound'], False) + result = self._makeOne({'debug_notfound':'t'}) + self.assertEqual(result['debug_notfound'], True) + result = self._makeOne({'debug_notfound':'1'}) + self.assertEqual(result['debug_notfound'], True) + result = self._makeOne({}, {'BFG_DEBUG_NOTFOUND':'1'}) + self.assertEqual(result['debug_notfound'], True) + result = self._makeOne({'debug_notfound':'false'}, + {'BFG_DEBUG_NOTFOUND':'1'}) + self.assertEqual(result['debug_notfound'], True) + + def test_debug_templates(self): + result = self._makeOne({}) + self.assertEqual(result['debug_templates'], False) + result = self._makeOne({'debug_templates':'false'}) + self.assertEqual(result['debug_templates'], False) + result = self._makeOne({'debug_templates':'t'}) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({'debug_templates':'1'}) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({}, {'BFG_DEBUG_TEMPLATES':'1'}) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({'debug_templates':'false'}, + {'BFG_DEBUG_TEMPLATES':'1'}) + self.assertEqual(result['debug_templates'], True) + + def test_debug_all(self): + result = self._makeOne({}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['debug_templates'], False) + result = self._makeOne({'debug_all':'false'}) + self.assertEqual(result['debug_notfound'], False) + self.assertEqual(result['debug_authorization'], False) + self.assertEqual(result['debug_templates'], False) + result = self._makeOne({'debug_all':'t'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({'debug_all':'1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({}, {'BFG_DEBUG_ALL':'1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + result = self._makeOne({'debug_all':'false'}, + {'BFG_DEBUG_ALL':'1'}) + self.assertEqual(result['debug_notfound'], True) + self.assertEqual(result['debug_authorization'], True) + self.assertEqual(result['debug_templates'], True) + + def test_configure_zcml(self): + result = self._makeOne({}) + self.assertEqual(result['configure_zcml'], '') + result = self._makeOne({'configure_zcml':'abc'}) + self.assertEqual(result['configure_zcml'], 'abc') + result = self._makeOne({}, {'BFG_CONFIGURE_ZCML':'abc'}) + self.assertEqual(result['configure_zcml'], 'abc') + result = self._makeOne({'configure_zcml':'def'}, + {'BFG_CONFIGURE_ZCML':'abc'}) + self.assertEqual(result['configure_zcml'], 'abc') + + def test_default_locale_name(self): + result = self._makeOne({}) + self.assertEqual(result['default_locale_name'], 'en') + result = self._makeOne({'default_locale_name':'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + result = self._makeOne({}, {'BFG_DEFAULT_LOCALE_NAME':'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + result = self._makeOne({'default_locale_name':'def'}, + {'BFG_DEFAULT_LOCALE_NAME':'abc'}) + self.assertEqual(result['default_locale_name'], 'abc') + + def test_originals_kept(self): + result = self._makeOne({'a':'i am so a'}) + self.assertEqual(result['a'], 'i am so a') + + +class TestGetSettings(unittest.TestCase): + def setUp(self): + from repoze.bfg.configuration import Configurator + from repoze.bfg.registry import Registry + registry = Registry('testing') + self.config = Configurator(registry=registry) + self.config.begin() + + def tearDown(self): + self.config.end() + + def _callFUT(self): + from repoze.bfg.settings import get_settings + return get_settings() + + def test_it_nosettings(self): + self.assertEqual(self._callFUT(), None) + + def test_it_withsettings(self): + from repoze.bfg.interfaces import ISettings + settings = {'a':1} + self.config.registry.registerUtility(settings, ISettings) + self.assertEqual(self._callFUT(), settings) + diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py new file mode 100644 index 000000000..449860883 --- /dev/null +++ b/pyramid/tests/test_static.py @@ -0,0 +1,343 @@ +import unittest +from repoze.bfg.testing import cleanUp + +class TestPackageURLParser(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.static import PackageURLParser + return PackageURLParser + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + + def _makeEnviron(self, **kw): + environ = { + 'wsgi.url_scheme':'http', + 'wsgi.version':(1,0), + 'SERVER_NAME':'example.com', + 'SERVER_PORT':'6543', + 'PATH_INFO':'/', + 'SCRIPT_NAME':'', + 'REQUEST_METHOD':'GET', + } + environ.update(kw) + return environ + + def test_ctor_allargs(self): + inst = self._makeOne('package', 'resource/name', root_resource='root', + cache_max_age=100) + self.assertEqual(inst.package_name, 'package') + self.assertEqual(inst.resource_name, 'resource/name') + self.assertEqual(inst.root_resource, 'root') + self.assertEqual(inst.cache_max_age, 100) + + def test_ctor_defaultargs(self): + inst = self._makeOne('package', 'resource/name') + self.assertEqual(inst.package_name, 'package') + self.assertEqual(inst.resource_name, 'resource/name') + self.assertEqual(inst.root_resource, 'resource/name') + self.assertEqual(inst.cache_max_age, None) + + def test_call_adds_slash_path_info_empty(self): + environ = self._makeEnviron(PATH_INFO='') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('301 Moved Permanently' in body) + self.failUnless('http://example.com:6543/' in body) + + def test_path_info_slash_means_index_html(self): + environ = self._makeEnviron() + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('static' in body) + + def test_resource_out_of_bounds(self): + environ = self._makeEnviron() + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + inst.root_resource = 'abcdef' + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('404 Not Found' in body) + self.failUnless('http://example.com:6543/' in body) + + def test_resource_doesnt_exist(self): + environ = self._makeEnviron(PATH_INFO='/notthere') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('404 Not Found' in body) + self.failUnless('http://example.com:6543/' in body) + + def test_resource_isdir(self): + environ = self._makeEnviron(PATH_INFO='/subdir/') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('subdir' in body) + + def test_resource_is_file(self): + environ = self._makeEnviron(PATH_INFO='/index.html') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('static' in body) + + def test_resource_is_file_with_cache_max_age(self): + environ = self._makeEnviron(PATH_INFO='/index.html') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static', + cache_max_age=600) + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('static' in body) + self.assertEqual(len(sr.headerlist), 8) + header_names = [ x[0] for x in sr.headerlist ] + header_names.sort() + self.assertEqual(header_names, + ['Accept-Ranges', 'Cache-Control', + 'Content-Length', 'Content-Range', + 'Content-Type', 'ETag', 'Expires', 'Last-Modified']) + + def test_resource_is_file_with_no_cache_max_age(self): + environ = self._makeEnviron(PATH_INFO='/index.html') + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + body = response[0] + self.failUnless('static' in body) + self.assertEqual(len(sr.headerlist), 6) + header_names = [ x[0] for x in sr.headerlist ] + header_names.sort() + self.assertEqual(header_names, + ['Accept-Ranges', 'Content-Length', 'Content-Range', + 'Content-Type', 'ETag', 'Last-Modified']) + + def test_if_none_match(self): + class DummyEq(object): + def __eq__(self, other): + return True + dummy_eq = DummyEq() + environ = self._makeEnviron(HTTP_IF_NONE_MATCH=dummy_eq) + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + sr = DummyStartResponse() + response = inst(environ, sr) + self.assertEqual(len(sr.headerlist), 1) + self.assertEqual(sr.status, '304 Not Modified') + self.assertEqual(sr.headerlist[0][0], 'ETag') + self.assertEqual(response[0], '') + + def test_repr(self): + inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') + self.failUnless( + repr(inst).startswith( + ''%(self.__name__, id(self)) + +class DummyRequest: + application_url = 'http://example.com:5432' # app_url never ends with slash + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + +class DummyContextURL: + def __init__(self, context, request): + pass + + def virtual_root(self): + return '123' + +def _makeRequest(environ=None): + from repoze.bfg.registry import Registry + request = DummyRequest() + request.registry = Registry() + return request diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py new file mode 100644 index 000000000..1b6f7814c --- /dev/null +++ b/pyramid/tests/test_url.py @@ -0,0 +1,304 @@ +import unittest + +from repoze.bfg.testing import cleanUp + +class ModelURLTests(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, model, request, *elements, **kw): + from repoze.bfg.url import model_url + return model_url(model, request, *elements, **kw) + + def _registerContextURL(self, reg): + from repoze.bfg.interfaces import IContextURL + from zope.interface import Interface + class DummyContextURL(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'http://example.com/context/' + reg.registerAdapter(DummyContextURL, (Interface, Interface), + IContextURL) + + def test_root_default(self): + request = _makeRequest() + self._registerContextURL(request.registry) + root = DummyContext() + result = self._callFUT(root, request) + self.assertEqual(result, 'http://example.com/context/') + + def test_extra_args(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, 'this/theotherthing', 'that') + self.assertEqual( + result, + 'http://example.com/context/this%2Ftheotherthing/that') + + def test_unicode_in_element_names(self): + request = _makeRequest() + self._registerContextURL(request.registry) + uc = unicode('La Pe\xc3\xb1a', 'utf-8') + context = DummyContext() + result = self._callFUT(context, request, uc) + self.assertEqual(result, + 'http://example.com/context/La%20Pe%C3%B1a') + + def test_element_names_url_quoted(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, 'a b c') + self.assertEqual(result, 'http://example.com/context/a%20b%20c') + + def test_with_query_dict(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + uc = unicode('La Pe\xc3\xb1a', 'utf-8') + result = self._callFUT(context, request, 'a', query={'a':uc}) + self.assertEqual(result, + 'http://example.com/context/a?a=La+Pe%C3%B1a') + + def test_with_query_seq(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + uc = unicode('La Pe\xc3\xb1a', 'utf-8') + result = self._callFUT(context, request, 'a', query=[('a', 'hi there'), + ('b', uc)]) + self.assertEqual(result, + 'http://example.com/context/a?a=hi+there&b=La+Pe%C3%B1a') + + def test_anchor_is_after_root_when_no_elements(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, anchor='a') + self.assertEqual(result, + 'http://example.com/context/#a') + + def test_anchor_is_after_elements_when_no_qs(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, 'a', anchor='b') + self.assertEqual(result, + 'http://example.com/context/a#b') + + def test_anchor_is_after_qs_when_qs_is_present(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, 'a', + query={'b':'c'}, anchor='d') + self.assertEqual(result, + 'http://example.com/context/a?b=c#d') + + def test_anchor_is_encoded_utf8_if_unicode(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + uc = unicode('La Pe\xc3\xb1a', 'utf-8') + result = self._callFUT(context, request, anchor=uc) + self.assertEqual(result, + 'http://example.com/context/#La Pe\xc3\xb1a') + + def test_anchor_is_not_urlencoded(self): + request = _makeRequest() + self._registerContextURL(request.registry) + context = DummyContext() + result = self._callFUT(context, request, anchor=' /#') + self.assertEqual(result, + 'http://example.com/context/# /#') + + def test_no_IContextURL_registered(self): + # falls back to TraversalContextURL + root = DummyContext() + root.__name__ = '' + root.__parent__ = None + request = _makeRequest() + request.environ = {} + result = self._callFUT(root, request) + self.assertEqual(result, 'http://example.com:5432/') + + def test_no_registry_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + request = DummyRequest() + self._registerContextURL(reg) + root = DummyContext() + result = self._callFUT(root, request) + self.assertEqual(result, 'http://example.com/context/') + +class TestRouteUrl(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.url import route_url + return route_url(*arg, **kw) + + def test_with_elements(self): + from repoze.bfg.interfaces import IRoutesMapper + request = _makeRequest() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = self._callFUT('flub', request, 'extra1', 'extra2', + a=1, b=2, c=3, _query={'a':1}, + _anchor=u"foo") + self.assertEqual(result, + 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') + + def test_no_elements(self): + from repoze.bfg.interfaces import IRoutesMapper + request = _makeRequest() + mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = self._callFUT('flub', request, a=1, b=2, c=3, _query={'a':1}, + _anchor=u"foo") + self.assertEqual(result, + 'http://example.com:5432/1/2/3?a=1#foo') + + def test_it_generation_error(self): + from repoze.bfg.interfaces import IRoutesMapper + request = _makeRequest() + mapper = DummyRoutesMapper(raise_exc=KeyError) + request.registry.registerUtility(mapper, IRoutesMapper) + mapper.raise_exc = KeyError + self.assertRaises(KeyError, self._callFUT, 'flub', request, a=1) + + def test_generate_doesnt_receive_query_or_anchor(self): + from repoze.bfg.interfaces import IRoutesMapper + route = DummyRoute(result='') + mapper = DummyRoutesMapper(route=route) + from zope.component import getSiteManager + sm = getSiteManager() + sm.registerUtility(mapper, IRoutesMapper) + request = DummyRequest() + result = self._callFUT('flub', request, _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_with_app_url(self): + from repoze.bfg.interfaces import IRoutesMapper + request = _makeRequest() + mapper = DummyRoutesMapper(route=DummyRoute(result='/1/2/3')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = self._callFUT('flub', request, _app_url='http://example2.com') + self.assertEqual(result, 'http://example2.com/1/2/3') + + def test_with_pregenerator(self): + from repoze.bfg.interfaces import IRoutesMapper + request = _makeRequest() + 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 = self._callFUT('flub', request) + self.assertEqual(result, 'http://example2.com/1/2/3/a') + self.assertEqual(route.kw, {}) # shouldnt have anchor/query + +class TestStaticUrl(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.url import static_url + return static_url(*arg, **kw) + + def test_staticurlinfo_notfound(self): + request = _makeRequest() + self.assertRaises(ValueError, self._callFUT, 'static/foo.css', request) + + def test_abspath(self): + request = _makeRequest() + self.assertRaises(ValueError, self._callFUT, '/static/foo.css', request) + + def test_found_rel(self): + from repoze.bfg.interfaces import IStaticURLInfo + request = _makeRequest() + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = self._callFUT('static/foo.css', request) + self.assertEqual(result, 'abc') + self.assertEqual(info.args, + ('repoze.bfg.tests:static/foo.css', request, {}) ) + + def test_found_abs(self): + from repoze.bfg.interfaces import IStaticURLInfo + request = _makeRequest() + info = DummyStaticURLInfo('abc') + request.registry.registerUtility(info, IStaticURLInfo) + result = self._callFUT('repoze.bfg.tests:static/foo.css', request) + self.assertEqual(result, 'abc') + self.assertEqual(info.args, + ('repoze.bfg.tests:static/foo.css', request, {}) ) + + def test_found_abs_no_registry_on_request(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IStaticURLInfo + request = DummyRequest() + registry = get_current_registry() + info = DummyStaticURLInfo('abc') + registry.registerUtility(info, IStaticURLInfo) + result = self._callFUT('repoze.bfg.tests:static/foo.css', request) + self.assertEqual(result, 'abc') + self.assertEqual(info.args, + ('repoze.bfg.tests:static/foo.css', request, {}) ) + +class DummyContext(object): + def __init__(self, next=None): + self.next = next + +class DummyRequest: + application_url = 'http://example.com:5432' # app_url never ends with slash + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + +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 + +def _makeRequest(environ=None): + from repoze.bfg.registry import Registry + request = DummyRequest(environ) + request.registry = Registry() + return request + +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/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py new file mode 100644 index 000000000..bf43750ea --- /dev/null +++ b/pyramid/tests/test_urldispatch.py @@ -0,0 +1,318 @@ +import unittest +from repoze.bfg import testing + +class TestRoute(unittest.TestCase): + def _getTargetClass(self): + from repoze.bfg.urldispatch import Route + return Route + + def _makeOne(self, *arg): + return self._getTargetClass()(*arg) + + def test_provides_IRoute(self): + from repoze.bfg.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.failUnless(route.generate.__class__ is types.FunctionType) + self.failUnless(route.match.__class__ is types.FunctionType) + + def test_ctor_defaults(self): + import types + route = self._makeOne('name', ':path') + self.assertEqual(route.pattern, ':path') + self.assertEqual(route.path, ':path') + self.assertEqual(route.name, 'name') + self.assertEqual(route.factory, None) + self.failUnless(route.generate.__class__ is types.FunctionType) + self.failUnless(route.match.__class__ is types.FunctionType) + + def test_match(self): + route = self._makeOne('name', ':path') + self.assertEqual(route.match('/whatever'), {'path':'whatever'}) + + def test_generate(self): + route = self._makeOne('name', ':path') + self.assertEqual(route.generate({'path':'abc'}), '/abc') + +class RoutesMapperTests(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _getRequest(self, **kw): + from repoze.bfg.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 repoze.bfg.urldispatch import RoutesMapper + return RoutesMapper + + def _makeOne(self): + klass = self._getTargetClass() + return klass() + + def test_provides_IRoutesMapper(self): + from repoze.bfg.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___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':u'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__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 repoze.bfg.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 repoze.bfg.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':u'/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_url_decode_error(self): + from repoze.bfg.exceptions import URLDecodeError + matcher, generator = self._callFUT('/:foo') + self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00') + +class TestCompileRouteMatchFunctional(unittest.TestCase): + def matches(self, pattern, path, expected): + from repoze.bfg.urldispatch import _compile_route + matcher = _compile_route(pattern)[0] + result = matcher(path) + self.assertEqual(result, expected) + + def generates(self, pattern, dict, result): + from repoze.bfg.urldispatch import _compile_route + self.assertEqual(_compile_route(pattern)[1](dict), result) + + def test_matcher_functional(self): + self.matches('/', '', None) + self.matches('', '', None) + self.matches('/', '/foo', None) + self.matches('/foo/', '/foo', None) + self.matches('/:x', '', None) + self.matches('/:x', '/', None) + self.matches('/abc/:def', '/abc/', None) + self.matches('/abc/:def:baz', '/abc/bleep', None) # bad pattern + self.matches('', '/', {}) + self.matches('/', '/', {}) + 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/%20abc', {'traverse':('zzz', ' abc')}) + self.matches(':x', '/La%20Pe%C3%B1a', {'x':u'La Pe\xf1a'}) + self.matches('*traverse', '/La%20Pe%C3%B1a/x', + {'traverse':(u'La Pe\xf1a', 'x')}) + self.matches('/foo/:id.html', '/foo/bar.html', {'id':'bar'}) + + def test_generator_functional(self): + self.generates('', {}, '/') + self.generates('/', {}, '/') + 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':unicode('/La Pe\xc3\xb1a', 'utf-8')}, + '/%2FLa%20Pe%C3%B1a') + self.generates('/:x*y', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8'), + 'y':'/rest/of/path'}, + '/%2FLa%20Pe%C3%B1a/rest/of/path') + self.generates('*traverse', {'traverse':('a', u'La Pe\xf1a')}, + '/a/La%20Pe%C3%B1a') + self.generates('/foo/:id.html', {'id':'bar'}, '/foo/bar.html') + +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/pyramid/tests/test_view.py b/pyramid/tests/test_view.py new file mode 100644 index 000000000..e5adc8de1 --- /dev/null +++ b/pyramid/tests/test_view.py @@ -0,0 +1,499 @@ +import unittest +import sys + +from repoze.bfg.testing import cleanUp + +class BaseTest(object): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _registerView(self, reg, app, name): + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IViewClassifier + for_ = (IViewClassifier, IRequest, IContext) + from repoze.bfg.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', + } + environ.update(extras) + return environ + + def _makeRequest(self, **environ): + from repoze.bfg.interfaces import IRequest + from zope.interface import directlyProvides + from webob import Request + from repoze.bfg.registry import Registry + environ = self._makeEnviron(**environ) + request = Request(environ) + request.registry = Registry() + directlyProvides(request, IRequest) + return request + + def _makeContext(self): + from zope.interface import directlyProvides + context = DummyContext() + directlyProvides(context, IContext) + return context + + +class RenderViewToResponseTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from repoze.bfg.view import render_view_to_response + return render_view_to_response(*arg, **kw) + + def test_call_no_view_registered(self): + request = self._makeRequest() + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_no_registry_on_request(self): + request = self._makeRequest() + del request.registry + context = self._makeContext() + result = self._callFUT(context, request, name='notregistered') + self.assertEqual(result, None) + + def test_call_view_registered_secure(self): + request = self._makeRequest() + context = self._makeContext() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + response = self._callFUT(context, request, name='registered', + secure=True) + self.assertEqual(response.status, '200 OK') + + def test_call_view_registered_insecure_no_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + self._registerView(request.registry, view, 'registered') + response = self._callFUT(context, request, name='registered', + secure=False) + self.assertEqual(response.status, '200 OK') + + def test_call_view_registered_insecure_with_call_permissive(self): + context = self._makeContext() + request = self._makeRequest() + response = DummyResponse() + view = make_view(response) + def anotherview(context, request): + return DummyResponse('anotherview') + view.__call_permissive__ = anotherview + self._registerView(request.registry, view, 'registered') + response = self._callFUT(context, request, name='registered', + secure=False) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.app_iter, ['anotherview']) + +class RenderViewToIterableTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from repoze.bfg.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('anotherview') + view.__call_permissive__ = anotherview + self._registerView(request.registry, view, 'registered') + iterable = self._callFUT(context, request, name='registered', + secure=False) + self.assertEqual(iterable, ['anotherview']) + +class RenderViewTests(BaseTest, unittest.TestCase): + def _callFUT(self, *arg, **kw): + from repoze.bfg.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, '') + + 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, '') + + 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') + s = self._callFUT(context, request, name='registered', secure=False) + self.assertEqual(s, 'anotherview') + +class TestIsResponse(unittest.TestCase): + def _callFUT(self, *arg, **kw): + from repoze.bfg.view import is_response + return is_response(*arg, **kw) + + def test_is(self): + response = DummyResponse() + self.assertEqual(self._callFUT(response), True) + + def test_isnt(self): + response = None + self.assertEqual(self._callFUT(response), False) + + def test_partial_inst(self): + response = DummyResponse() + response.app_iter = None + self.assertEqual(self._callFUT(response), False) + + def test_status_not_string(self): + response = DummyResponse() + response.status = None + self.assertEqual(self._callFUT(response), False) + +class TestBFGViewDecorator(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _getTargetClass(self): + from repoze.bfg.view import bfg_view + return bfg_view + + def _makeOne(self, *arg, **kw): + return self._getTargetClass()(*arg, **kw) + + def test_create_defaults(self): + decorator = self._makeOne() + self.assertEqual(decorator.name, '') + self.assertEqual(decorator.request_type, None) + self.assertEqual(decorator.context, None) + self.assertEqual(decorator.permission, None) + + def test_create_nondefaults(self): + decorator = self._makeOne(name=None, request_type=None, for_=None, + permission='foo') + self.assertEqual(decorator.name, None) + self.assertEqual(decorator.request_type, None) + self.assertEqual(decorator.context, None) + self.assertEqual(decorator.permission, 'foo') + + def test_call_function(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + def foo(): pass + wrapped = decorator(foo) + self.failUnless(wrapped is foo) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['permission'], None) + self.assertEqual(settings[0]['context'], None) + self.assertEqual(settings[0]['request_type'], None) + + 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.failUnless(wrapped is foo) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['permission'], None) + self.assertEqual(settings[0]['context'], None) + self.assertEqual(settings[0]['request_type'], None) + + def test_stacking(self): + decorator1 = self._makeOne(name='1') + venusian1 = DummyVenusian() + decorator1.venusian = venusian1 + venusian2 = DummyVenusian() + decorator2 = self._makeOne(name='2') + decorator2.venusian = venusian2 + def foo(): pass + wrapped1 = decorator1(foo) + wrapped2 = decorator2(wrapped1) + self.failUnless(wrapped1 is foo) + self.failUnless(wrapped2 is foo) + settings1 = call_venusian(venusian1) + self.assertEqual(len(settings1), 1) + self.assertEqual(settings1[0]['name'], '1') + settings2 = call_venusian(venusian2) + self.assertEqual(len(settings2), 1) + self.assertEqual(settings2[0]['name'], '2') + + def test_call_as_method(self): + decorator = self._makeOne() + venusian = DummyVenusian() + decorator.venusian = venusian + decorator.venusian.info.scope = 'class' + def foo(self): pass + def bar(self): pass + class foo(object): + foomethod = decorator(foo) + barmethod = decorator(bar) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 2) + self.assertEqual(settings[0]['attr'], 'foo') + self.assertEqual(settings[1]['attr'], 'bar') + + def test_with_custom_predicates(self): + decorator = self._makeOne(custom_predicates=(1,)) + venusian = DummyVenusian() + decorator.venusian = venusian + def foo(context, request): pass + decorated = decorator(foo) + self.failUnless(decorated is foo) + settings = call_venusian(venusian) + self.assertEqual(settings[0]['custom_predicates'], (1,)) + + def test_call_with_renderer_nodot(self): + decorator = self._makeOne(renderer='json') + venusian = DummyVenusian() + decorator.venusian = venusian + def foo(): pass + wrapped = decorator(foo) + self.failUnless(wrapped is foo) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['renderer'], 'json') + + def test_call_with_renderer_relpath(self): + decorator = self._makeOne(renderer='fixtures/minimal.pt') + venusian = DummyVenusian() + decorator.venusian = venusian + def foo(): pass + wrapped = decorator(foo) + self.failUnless(wrapped is foo) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['renderer'], + 'repoze.bfg.tests:fixtures/minimal.pt') + + def test_call_with_renderer_pkgpath(self): + decorator = self._makeOne( + renderer='repoze.bfg.tests:fixtures/minimal.pt') + venusian = DummyVenusian() + decorator.venusian = venusian + def foo(): pass + wrapped = decorator(foo) + self.failUnless(wrapped is foo) + settings = call_venusian(venusian) + self.assertEqual(len(settings), 1) + self.assertEqual(settings[0]['renderer'], + 'repoze.bfg.tests:fixtures/minimal.pt') + +class Test_append_slash_notfound_view(BaseTest, unittest.TestCase): + def _callFUT(self, context, request): + from repoze.bfg.view import append_slash_notfound_view + return append_slash_notfound_view(context, request) + + def _registerMapper(self, reg, match=True): + from repoze.bfg.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_matches(self): + request = self._makeRequest(PATH_INFO='/abc') + context = ExceptionResponse() + self._registerMapper(request.registry, True) + response = self._callFUT(context, request) + self.assertEqual(response.status, '302 Found') + self.assertEqual(response.location, '/abc/') + +class TestAppendSlashNotFoundViewFactory(BaseTest, unittest.TestCase): + def _makeOne(self, notfound_view): + from repoze.bfg.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 repoze.bfg.view import default_exceptionresponse_view + return default_exceptionresponse_view(context, request) + + def test_is_exception(self): + context = Exception() + result = self._callFUT(context, None) + self.failUnless(result is context) + + def test_is_not_exception_no_request_exception(self): + context = object() + request = DummyRequest() + result = self._callFUT(context, request) + self.failUnless(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 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 + +class DummyResponse: + status = '200 OK' + headerlist = () + def __init__(self, body=None): + if body is None: + self.app_iter = () + else: + self.app_iter = [body] + +from zope.interface import Interface +class IContext(Interface): + pass + +class DummyVenusianInfo(object): + scope = 'notaclass' + module = sys.modules['repoze.bfg.tests'] + +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): + self.attachments.append((wrapped, callback, category)) + return self.info + +class DummyConfig(object): + def __init__(self): + self.settings = [] + + def add_view(self, **kw): + self.settings.append(kw) + +class DummyVenusianContext(object): + def __init__(self): + self.config = DummyConfig() + +def call_venusian(venusian): + context = DummyVenusianContext() + for wrapped, callback, category in venusian.attachments: + callback(context, None, None) + return context.config.settings + diff --git a/pyramid/tests/test_wsgi.py b/pyramid/tests/test_wsgi.py new file mode 100644 index 000000000..5d3ec1faa --- /dev/null +++ b/pyramid/tests/test_wsgi.py @@ -0,0 +1,112 @@ +import unittest + +class WSGIAppTests(unittest.TestCase): + def _callFUT(self, app): + from repoze.bfg.wsgi import wsgiapp + return wsgiapp(app) + + def test_decorator(self): + context = DummyContext() + request = DummyRequest() + decorator = self._callFUT(dummyapp) + response = decorator(context, request) + self.assertEqual(response, dummyapp) + +class WSGIApp2Tests(unittest.TestCase): + def _callFUT(self, app): + from repoze.bfg.wsgi import wsgiapp2 + return wsgiapp2(app) + + def test_decorator_with_subpath_and_view_name(self): + context = DummyContext() + request = DummyRequest() + request.traversed = ['a', 'b'] + request.virtual_root_path = ['a'] + request.subpath = ['subpath'] + request.view_name = 'view_name' + request.environ = {'SCRIPT_NAME':'/foo'} + 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.traversed = ['a', 'b'] + request.virtual_root_path = ['a'] + request.subpath = ['subpath'] + request.view_name = '' + request.environ = {'SCRIPT_NAME':'/foo'} + 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.traversed = ['a', 'b'] + request.virtual_root_path = ['a'] + request.subpath = [] + request.view_name = 'view_name' + request.environ = {'SCRIPT_NAME':'/foo'} + 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.traversed = [] + request.virtual_root_path = [] + request.subpath = [] + request.view_name = 'view_name' + request.environ = {'SCRIPT_NAME':'/foo'} + 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.traversed = [] + request.virtual_root_path = [] + request.subpath = [] + request.view_name = '' + request.environ = {'SCRIPT_NAME':'/foo'} + 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.traversed = [] + request.virtual_root_path = [] + request.subpath = [] + request.view_name = '' + request.environ = {'SCRIPT_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'], '') + +def dummyapp(environ, start_response): + """ """ + +class DummyContext: + pass + +class DummyRequest: + def get_response(self, application): + return application diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py new file mode 100644 index 000000000..131122d7b --- /dev/null +++ b/pyramid/tests/test_zcml.py @@ -0,0 +1,1283 @@ +import logging + +logging.basicConfig() + +import unittest + +from repoze.bfg import testing + +from zope.interface import Interface +from zope.interface import implements + +class TestViewDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import view + return view(*arg, **kw) + + def test_request_type_ashttpmethod(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequest + context = DummyContext() + view = lambda *arg: None + self._callFUT(context, 'repoze.view', IDummy, view=view, + request_type='GET') + actions = context.actions + self.assertEqual(len(actions), 1) + action = actions[0] + discrim = ('view', IDummy, '', None, IView, None, None, 'GET', None, + None, False, None, None, None) + self.assertEqual(action['discriminator'], discrim) + register = action['callable'] + register() + reg = get_current_registry() + wrapper = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + request = DummyRequest() + request.method = 'GET' + self.assertEqual(wrapper.__predicated__(None, request), True) + request.method = 'POST' + self.assertEqual(wrapper.__predicated__(None, request), False) + + def test_request_type_asinterfacestring(self): + from zope.interface import directlyProvides + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequest + context = DummyContext(IDummy) + view = lambda *arg: 'OK' + self._callFUT(context, 'repoze.view', IDummy, view=view, + request_type='whatever') + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', IDummy, IView, None, None, None, None, + None, False, None, None, None) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + reg = get_current_registry() + regview = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + self.assertNotEqual(view, regview) + request = DummyRequest() + directlyProvides(request, IDummy) + result = regview(None, request) + self.assertEqual(result, 'OK') + self.failIf(hasattr(view, '__call_permissive__')) + + def test_request_type_asnoninterfacestring(self): + from repoze.bfg.exceptions import ConfigurationError + context = DummyContext('notaninterface') + view = lambda *arg: 'OK' + self.assertRaises(ConfigurationError, + self._callFUT, + context, 'repoze.view', IDummy, view=view, + request_type='whatever') + + def test_with_dotted_renderer(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRendererFactory + from repoze.bfg.interfaces import IRequest + context = DummyContext() + reg = get_current_registry() + def factory(path): + def foo(*arg): + return 'OK' + return foo + reg.registerUtility(factory, IRendererFactory, name='.pt') + view = lambda *arg: None + self._callFUT(context, 'repoze.view', IDummy, view=view, + renderer='foo/template.pt') + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', None, IView, None, None, None, None, + None, False, None, None, None) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + regview = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + self.assertEqual(regview(None, None).body, 'OK') + + def test_with_custom_predicates(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequest + context = DummyContext() + reg = get_current_registry() + view = lambda *arg: 'OK' + def pred1(context, request): + return True + def pred2(context, request): + return True + preds = (pred1, pred2) + self._callFUT(context, 'repoze.view', IDummy, view=view, + custom_predicates=preds) + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', None, IView, None, None, None, None, + None, False, None, None, None) + discrim = discrim + tuple(sorted(preds)) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + regview = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + self.assertEqual(regview(None, None), 'OK') + + def test_context_trumps_for(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequest + context = DummyContext() + reg = get_current_registry() + view = lambda *arg: 'OK' + class Foo: + pass + self._callFUT(context, 'repoze.view', for_=Foo, view=view, + context=IDummy) + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', None, IView, None, None, None, None, + None, False, None, None, None) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + regview = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + self.assertEqual(regview(None, None), 'OK') + + def test_with_for(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRequest + context = DummyContext() + reg = get_current_registry() + view = lambda *arg: 'OK' + class Foo: + pass + self._callFUT(context, 'repoze.view', for_=IDummy, view=view) + actions = context.actions + self.assertEqual(len(actions), 1) + discrim = ('view', IDummy, '', None, IView, None, None, None, None, + None, False, None, None, None) + self.assertEqual(actions[0]['discriminator'], discrim) + register = actions[0]['callable'] + register() + regview = reg.adapters.lookup( + (IViewClassifier, IRequest, IDummy), IView, name='') + self.assertEqual(regview(None, None), 'OK') + +class TestNotFoundDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, view, **kw): + from repoze.bfg.zcml import notfound + return notfound(context, view, **kw) + + def test_it(self): + from zope.interface import implementedBy + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.exceptions import NotFound + + context = DummyContext() + def view(request): + return 'OK' + self._callFUT(context, view) + actions = context.actions + self.assertEqual(len(actions), 1) + + discrim = ('view', NotFound, '', None, IView, None, None, None, None, + None, False, None, None, None) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], discrim) + register = regadapt['callable'] + register() + reg = get_current_registry() + derived_view = reg.adapters.lookup( + (IViewClassifier, IRequest, implementedBy(NotFound)), + IView, default=None) + + self.assertNotEqual(derived_view, None) + self.assertEqual(derived_view(None, None), 'OK') + self.assertEqual(derived_view.__name__, 'bwcompat_view') + + def test_it_with_dotted_renderer(self): + from zope.interface import implementedBy + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.exceptions import NotFound + from repoze.bfg.configuration import Configurator + context = DummyContext() + reg = get_current_registry() + config = Configurator(reg) + def dummy_renderer_factory(*arg, **kw): + return lambda *arg, **kw: 'OK' + config.add_renderer('.pt', dummy_renderer_factory) + def view(request): + return {} + self._callFUT(context, view, renderer='fake.pt') + actions = context.actions + regadapt = actions[0] + register = regadapt['callable'] + register() + derived_view = reg.adapters.lookup( + (IViewClassifier, IRequest, implementedBy(NotFound)), + IView, default=None) + self.assertNotEqual(derived_view, None) + self.assertEqual(derived_view(None, None).body, 'OK') + self.assertEqual(derived_view.__name__, 'bwcompat_view') + +class TestForbiddenDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, view, **kw): + from repoze.bfg.zcml import forbidden + return forbidden(context, view, **kw) + + def test_it(self): + from zope.interface import implementedBy + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.exceptions import Forbidden + context = DummyContext() + def view(request): + return 'OK' + self._callFUT(context, view) + actions = context.actions + + self.assertEqual(len(actions), 1) + + discrim = ('view', Forbidden, '', None, IView, None, None, None, None, + None, False, None, None, None) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], discrim) + register = regadapt['callable'] + register() + reg = get_current_registry() + derived_view = reg.adapters.lookup( + (IViewClassifier, IRequest, implementedBy(Forbidden)), + IView, default=None) + + self.assertNotEqual(derived_view, None) + self.assertEqual(derived_view(None, None), 'OK') + self.assertEqual(derived_view.__name__, 'bwcompat_view') + + def test_it_with_dotted_renderer(self): + from zope.interface import implementedBy + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRequest + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.exceptions import Forbidden + from repoze.bfg.configuration import Configurator + context = DummyContext() + reg = get_current_registry() + config = Configurator(reg) + def dummy_renderer_factory(*arg, **kw): + return lambda *arg, **kw: 'OK' + config.add_renderer('.pt', dummy_renderer_factory) + def view(request): + return {} + self._callFUT(context, view, renderer='fake.pt') + actions = context.actions + regadapt = actions[0] + register = regadapt['callable'] + register() + derived_view = reg.adapters.lookup( + (IViewClassifier, IRequest, implementedBy(Forbidden)), + IView, default=None) + self.assertNotEqual(derived_view, None) + self.assertEqual(derived_view(None, None).body, 'OK') + self.assertEqual(derived_view.__name__, 'bwcompat_view') + +class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, **kw): + from repoze.bfg.zcml import repozewho1authenticationpolicy + return repozewho1authenticationpolicy(context, **kw) + + def test_it_defaults(self): + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + from repoze.bfg.interfaces import IAuthenticationPolicy + context = DummyContext() + self._callFUT(context) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.callback, None) + self.assertEqual(policy.identifier_name, 'auth_tkt') + + def test_it(self): + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + from repoze.bfg.interfaces import IAuthenticationPolicy + context = DummyContext() + def callback(identity, request): + """ """ + self._callFUT(context, identifier_name='something', callback=callback) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.callback, callback) + self.assertEqual(policy.identifier_name, 'something') + +class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, **kw): + from repoze.bfg.zcml import remoteuserauthenticationpolicy + return remoteuserauthenticationpolicy(context, **kw) + + def test_defaults(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + context = DummyContext() + def callback(identity, request): + """ """ + self._callFUT(context) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.environ_key, 'REMOTE_USER') + self.assertEqual(policy.callback, None) + + def test_it(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.threadlocal import get_current_registry + context = DummyContext() + def callback(identity, request): + """ """ + self._callFUT(context, environ_key='BLAH', callback=callback) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + reg = get_current_registry() + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.environ_key, 'BLAH') + self.assertEqual(policy.callback, callback) + +class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, secret, **kw): + from repoze.bfg.zcml import authtktauthenticationpolicy + return authtktauthenticationpolicy(context, secret, **kw) + + def test_it_defaults(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + context = DummyContext() + self._callFUT(context, 'sosecret') + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.cookie.secret, 'sosecret') + self.assertEqual(policy.callback, None) + + def test_it_noconfigerror(self): + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.threadlocal import get_current_registry + reg = get_current_registry() + context = DummyContext() + def callback(identity, request): + """ """ + self._callFUT(context, 'sosecret', callback=callback, + cookie_name='repoze.bfg.auth_tkt', + secure=True, include_ip=True, timeout=100, + reissue_time=60, http_only=True, path="/sub/") + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy.cookie.path, '/sub/') + self.assertEqual(policy.cookie.http_only, True) + self.assertEqual(policy.cookie.secret, 'sosecret') + self.assertEqual(policy.callback, callback) + + def test_it_configerror(self): + from repoze.bfg.exceptions import ConfigurationError + context = DummyContext() + def callback(identity, request): + """ """ + self.assertRaises(ConfigurationError, + self._callFUT, + context, 'sosecret', callback=callback, + cookie_name='repoze.bfg.auth_tkt', + secure=True, include_ip=True, timeout=100, + reissue_time=500, http_only=True, + path="/cgi-bin/bfg.cgi/") + +class TestACLAuthorizationPolicyDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, **kw): + from repoze.bfg.zcml import aclauthorizationpolicy + return aclauthorizationpolicy(context, **kw) + + def test_it(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.authorization import ACLAuthorizationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + reg = get_current_registry() + context = DummyContext() + def callback(identity, request): + """ """ + self._callFUT(context) + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IAuthorizationPolicy) + self.assertEqual(regadapt['callable'], None) + self.assertEqual(regadapt['args'], ()) + policy = reg.getUtility(IAuthorizationPolicy) + self.assertEqual(policy.__class__, ACLAuthorizationPolicy) + +class TestRouteDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import route + return route(*arg, **kw) + + def _assertRoute(self, name, pattern, num_predicates=0): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRoutesMapper + reg = get_current_registry() + mapper = reg.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.pattern, pattern) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def test_with_view(self): + from repoze.bfg.threadlocal import get_current_registry + from zope.interface import Interface + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRouteRequest + context = DummyContext() + view = lambda *arg: 'OK' + self._callFUT(context, 'name', 'pattern', view=view) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + view_action = actions[1] + reg = get_current_registry() + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, 'name', None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup( + (IViewClassifier, request_type, Interface), IView, name='') + self.failUnless(wrapped) + + def test_with_view_and_view_context(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRouteRequest + context = DummyContext() + view = lambda *arg: 'OK' + self._callFUT(context, 'name', 'pattern', view=view, + view_context=IDummy) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + view_action = actions[1] + reg = get_current_registry() + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', None, IView, 'name', None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup( + (IViewClassifier, request_type, IDummy), IView, name='') + self.failUnless(wrapped) + + def test_with_view_context_trumps_view_for(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRouteRequest + context = DummyContext() + view = lambda *arg: 'OK' + class Foo: + pass + self._callFUT(context, 'name', 'pattern', view=view, + view_context=IDummy, view_for=Foo) + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + view_action = actions[1] + reg = get_current_registry() + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', None, IView, 'name', None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup( + (IViewClassifier, request_type, IDummy), IView, name='') + self.failUnless(wrapped) + + def test_with_dotted_renderer(self): + + from repoze.bfg.threadlocal import get_current_registry + from zope.interface import Interface + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRouteRequest + from repoze.bfg.interfaces import IRendererFactory + reg = get_current_registry() + def renderer(path): + return lambda *arg: 'OK' + reg.registerUtility(renderer, IRendererFactory, name='.pt') + + context = DummyContext() + view = lambda *arg: 'OK' + self._callFUT(context, 'name', 'pattern', view=view, + renderer='fixtureapp/templates/foo.pt') + actions = context.actions + self.assertEqual(len(actions), 2) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + view_action = actions[1] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, 'name', None) + self.assertEqual(view_discriminator, discrim) + wrapped = reg.adapters.lookup( + (IViewClassifier, request_type, Interface), IView, name='') + self.failUnless(wrapped) + request = DummyRequest() + result = wrapped(None, request) + self.assertEqual(result.body, 'OK') + + def test_with_custom_predicates(self): + def pred1(context, request): pass + def pred2(context, request): pass + preds = tuple(sorted([pred1, pred2])) + + context = DummyContext() + self._callFUT(context, 'name', 'pattern', + custom_predicates=(pred1, pred2)) + actions = context.actions + self.assertEqual(len(actions), 1) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual( + route_discriminator, + ('route', 'name', False, None, None, None, None,None) + preds) + self._assertRoute('name', 'pattern', 2) + + def test_with_path_argument_no_pattern(self): + context = DummyContext() + self._callFUT(context, 'name', path='pattern') + actions = context.actions + self.assertEqual(len(actions), 1) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + def test_with_path_argument_and_pattern(self): + context = DummyContext() + self._callFUT(context, 'name', pattern='pattern', path='path') + actions = context.actions + self.assertEqual(len(actions), 1) + + route_action = actions[0] + route_action['callable']() + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', 'pattern') + + + def test_with_neither_path_nor_pattern(self): + from repoze.bfg.exceptions import ConfigurationError + context = DummyContext() + self.assertRaises(ConfigurationError, self._callFUT, context, 'name') + +class TestStaticDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import static + return static(*arg, **kw) + + def test_it_with_slash(self): + from repoze.bfg.static import PackageURLParser + from repoze.bfg.threadlocal import get_current_registry + from zope.interface import implementedBy + from repoze.bfg.static import StaticURLInfo + from repoze.bfg.interfaces import IView + from repoze.bfg.interfaces import IViewClassifier + from repoze.bfg.interfaces import IRouteRequest + from repoze.bfg.interfaces import IRoutesMapper + context = DummyContext() + self._callFUT(context, 'name', 'fixtures/static') + actions = context.actions + self.assertEqual(len(actions), 2) + + reg = get_current_registry() + + route_action = actions[0] + discriminator = route_action['discriminator'] + self.assertEqual(discriminator, ('static', 'name')) + route_action['callable'](*route_action['args'], **route_action['kw']) + mapper = reg.getUtility(IRoutesMapper) + routes = mapper.get_routes() + self.assertEqual(len(routes), 1) + self.assertEqual(routes[0].pattern, 'name/*subpath') + self.assertEqual(routes[0].name, 'name/') + + view_action = actions[1] + discriminator = view_action['discriminator'] + self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) + self.assertEqual(discriminator[4], IView) + iface = implementedBy(StaticURLInfo) + request_type = reg.getUtility(IRouteRequest, 'name/') + view = reg.adapters.lookup( + (IViewClassifier, request_type, iface), IView, name='') + request = DummyRequest() + self.assertEqual(view(None, request).__class__, PackageURLParser) + +class TestResourceDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import resource + return resource(*arg, **kw) + + def test_it(self): + from repoze.bfg.configuration import Configurator + context = DummyContext() + self._callFUT(context, 'a', 'b') + actions = context.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['callable'].im_func, + Configurator.override_resource.im_func) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], ('a', 'b', None)) + + +class TestRendererDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import renderer + return renderer(*arg, **kw) + + def test_it(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IRendererFactory + context = DummyContext() + renderer = lambda *arg, **kw: None + self._callFUT(context, renderer, 'r') + actions = context.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['discriminator'], (IRendererFactory, 'r')) + reg = get_current_registry() + self.failUnless(reg.getUtility(IRendererFactory, 'r'), renderer) + +class TestZCMLConfigure(unittest.TestCase): + i = 0 + def _callFUT(self, path, package): + from repoze.bfg.zcml import zcml_configure + return zcml_configure(path, package) + + def setUp(self): + testing.setUp() + self.tempdir = None + import sys + import os + import tempfile + from repoze.bfg.path import package_path + from repoze.bfg.tests import fixtureapp as package + import shutil + tempdir = tempfile.mkdtemp() + modname = 'myfixture%s' % self.i + self.i += 1 + self.packagepath = os.path.join(tempdir, modname) + fixturedir = package_path(package) + shutil.copytree(fixturedir, self.packagepath) + sys.path.insert(0, tempdir) + self.module = __import__(modname) + self.tempdir = tempdir + + def tearDown(self): + testing.tearDown() + import sys + import shutil + if self.module is not None: + del sys.modules[self.module.__name__] + if self.tempdir is not None: + sys.path.pop(0) + shutil.rmtree(self.tempdir) + + def test_zcml_configure(self): + actions = self._callFUT('configure.zcml', self.module) + self.failUnless(actions) + self.failUnless(isinstance(actions, list)) + + def test_zcml_configure_nonexistent_configure_dot_zcml(self): + import os + os.remove(os.path.join(self.packagepath, 'configure.zcml')) + self.assertRaises(IOError, self._callFUT, 'configure.zcml', + self.module) + +class TestZCMLScanDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, package): + from repoze.bfg.zcml import scan + return scan(context, package) + + def test_it(self): + from repoze.bfg.configuration import Configurator + dummy_module = DummyModule() + context = DummyContext() + self._callFUT(context, dummy_module) + actions = context.actions + self.assertEqual(len(actions), 1) + action = actions[0] + self.assertEqual(action['callable'].im_func, Configurator.scan.im_func) + self.assertEqual(action['discriminator'], None) + self.assertEqual(action['args'], (dummy_module, None, None)) + +class TestAdapterDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import adapter + return adapter(*arg, **kw) + + def test_for_is_None_no_adaptedBy(self): + context = DummyContext() + factory = DummyFactory() + self.assertRaises(TypeError, self._callFUT, context, [factory], + provides=None, for_=None) + + def test_for_is_None_adaptedBy_still_None(self): + context = DummyContext() + factory = DummyFactory() + factory.__component_adapts__ = None + self.assertRaises(TypeError, self._callFUT, context, [factory], + provides=None, for_=None) + + def test_for_is_None_adaptedBy_set(self): + from repoze.bfg.registry import Registry + context = DummyContext() + factory = DummyFactory() + factory.__component_adapts__ = (IDummy,) + self._callFUT(context, [factory], provides=IFactory, for_=None) + self.assertEqual(len(context.actions), 1) + regadapt = context.actions[0] + self.assertEqual(regadapt['discriminator'], + ('adapter', (IDummy,), IFactory, '')) + self.assertEqual(regadapt['callable'].im_func, + Registry.registerAdapter.im_func) + self.assertEqual(regadapt['args'], + (factory, (IDummy,), IFactory, '', None)) + + def test_provides_missing(self): + context = DummyContext() + factory = DummyFactory() + self.assertRaises(TypeError, self._callFUT, context, [factory], + provides=None, for_=(IDummy,)) + + def test_provides_obtained_via_implementedBy(self): + from repoze.bfg.registry import Registry + context = DummyContext() + self._callFUT(context, [DummyFactory], for_=(IDummy,)) + regadapt = context.actions[0] + self.assertEqual(regadapt['discriminator'], + ('adapter', (IDummy,), IFactory, '')) + self.assertEqual(regadapt['callable'].im_func, + Registry.registerAdapter.im_func) + self.assertEqual(regadapt['args'], + (DummyFactory, (IDummy,), IFactory, '', None)) + + def test_multiple_factories_multiple_for(self): + context = DummyContext() + factory = DummyFactory() + self.assertRaises(ValueError, self._callFUT, context, + [factory, factory], + provides=IFactory, + for_=(IDummy, IDummy)) + + def test_no_factories_multiple_for(self): + context = DummyContext() + self.assertRaises(ValueError, self._callFUT, context, + factory=[], + provides=IFactory, + for_=(IDummy, IDummy)) + + def test_rolled_up_factories(self): + from repoze.bfg.registry import Registry + context = DummyContext() + factory = DummyFactory() + self._callFUT(context, + [factory, factory], + provides=IFactory, + for_=(IDummy,)) + regadapt = context.actions[0] + self.assertEqual(regadapt['discriminator'], + ('adapter', (IDummy,), IFactory, '')) + self.assertEqual(regadapt['callable'].im_func, + Registry.registerAdapter.im_func) + self.assertEqual(regadapt['args'][0].__module__, 'repoze.bfg.zcml') + +class TestSubscriberDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import subscriber + return subscriber(*arg, **kw) + + def test_no_factory_no_handler(self): + context = DummyContext() + self.assertRaises(TypeError, + self._callFUT, context, for_=None, factory=None, + handler=None, + provides=None) + + def test_handler_with_provides(self): + context = DummyContext() + self.assertRaises(TypeError, + self._callFUT, context, for_=None, factory=None, + handler=1, provides=1) + + def test_handler_and_factory(self): + context = DummyContext() + self.assertRaises(TypeError, + self._callFUT, context, for_=None, factory=1, + handler=1, provides=None) + + def test_no_provides_with_factory(self): + context = DummyContext() + self.assertRaises(TypeError, + self._callFUT, context, for_=None, factory=1, + handler=None, provides=None) + + def test_adapted_by_as_for_is_None(self): + context = DummyContext() + factory = DummyFactory() + factory.__component_adapts__ = None + self.assertRaises(TypeError, self._callFUT, context, for_=None, + factory=factory, handler=None, provides=IFactory) + + def test_register_with_factory(self): + from repoze.bfg.registry import Registry + context = DummyContext() + factory = DummyFactory() + self._callFUT(context, for_=(IDummy,), + factory=factory, handler=None, provides=IFactory) + self.assertEqual(len(context.actions), 1) + subadapt = context.actions[0] + self.assertEqual(subadapt['discriminator'], None) + self.assertEqual(subadapt['callable'].im_func, + Registry.registerSubscriptionAdapter.im_func) + self.assertEqual(subadapt['args'], + (factory, (IDummy,), IFactory, None, None) ) + + def test_register_with_handler(self): + from repoze.bfg.configuration import Configurator + context = DummyContext() + factory = DummyFactory() + self._callFUT(context, for_=(IDummy,), + factory=None, handler=factory) + self.assertEqual(len(context.actions), 1) + subadapt = context.actions[0] + self.assertEqual(subadapt['discriminator'], None) + self.assertEqual(subadapt['callable'].im_func, + Configurator.add_subscriber.im_func) + self.assertEqual(subadapt['args'], (factory, (IDummy,), None) ) + +class TestUtilityDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import utility + return utility(*arg, **kw) + + def test_factory_and_component(self): + context = DummyContext() + self.assertRaises(TypeError, self._callFUT, + context, factory=1, component=1) + + def test_missing_provides(self): + context = DummyContext() + self.assertRaises(TypeError, self._callFUT, context, provides=None) + + def test_provides_from_factory_implements(self): + from repoze.bfg.registry import Registry + context = DummyContext() + self._callFUT(context, factory=DummyFactory) + self.assertEqual(len(context.actions), 1) + utility = context.actions[0] + self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) + self.assertEqual(utility['callable'].im_func, + Registry.registerUtility.im_func) + self.assertEqual(utility['args'], (None, IFactory, '', None)) + self.assertEqual(utility['kw'], {'factory':DummyFactory}) + + def test_provides_from_component_provides(self): + from repoze.bfg.registry import Registry + context = DummyContext() + component = DummyFactory() + self._callFUT(context, component=component) + self.assertEqual(len(context.actions), 1) + utility = context.actions[0] + self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) + self.assertEqual(utility['callable'].im_func, + Registry.registerUtility.im_func) + self.assertEqual(utility['args'], (component, IFactory, '', None)) + self.assertEqual(utility['kw'], {}) + +class TestTranslationDirDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import translationdir + return translationdir(*arg, **kw) + + def test_it(self): + from repoze.bfg.configuration import Configurator + context = DummyContext() + tdir = 'repoze.bfg.tests.localeapp:locale' + self._callFUT(context, tdir) + actions = context.actions + self.assertEqual(len(actions), 1) + action = context.actions[0] + self.assertEqual(action['discriminator'], ('tdir', tdir)) + self.assertEqual(action['callable'].im_func, + Configurator.add_translation_dirs.im_func) + self.assertEqual(action['args'], (tdir,)) + action['callable'](*action['args']) # doesn't blow up + +class TestLocaleNegotiatorDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from repoze.bfg.zcml import localenegotiator + return localenegotiator(*arg, **kw) + + def test_it(self): + from repoze.bfg.configuration import Configurator + context = DummyContext() + dummy_negotiator = object() + self._callFUT(context, dummy_negotiator) + actions = context.actions + self.assertEqual(len(actions), 1) + action = context.actions[0] + self.assertEqual(action['discriminator'], 'lnegotiator') + self.assertEqual(action['callable'].im_func, + Configurator.set_locale_negotiator.im_func) + self.assertEqual(action['args'], (dummy_negotiator,)) + action['callable'](*action['args']) # doesn't blow up + +class TestDefaultPermissionDirective(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, context, name): + from repoze.bfg.zcml import default_permission + return default_permission(context, name) + + def test_it(self): + from repoze.bfg.threadlocal import get_current_registry + from repoze.bfg.interfaces import IDefaultPermission + reg = get_current_registry() + context = DummyContext() + self._callFUT(context, 'view') + actions = context.actions + self.assertEqual(len(actions), 1) + regadapt = actions[0] + self.assertEqual(regadapt['discriminator'], IDefaultPermission) + perm = reg.getUtility(IDefaultPermission) + self.assertEqual(perm, 'view') + +class TestLoadZCML(unittest.TestCase): + def setUp(self): + testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_it(self): + from zope.configuration import xmlconfig + import repoze.bfg.includes + xmlconfig.file('configure.zcml', package=repoze.bfg.includes) + +class TestRolledUpFactory(unittest.TestCase): + def _callFUT(self, *factories): + from repoze.bfg.zcml import _rolledUpFactory + return _rolledUpFactory(factories) + + def test_it(self): + def foo(ob): + return ob + factory = self._callFUT(foo, foo) + result = factory(True) + self.assertEqual(result, True) + +class Test_path_spec(unittest.TestCase): + def _callFUT(self, context, path): + from repoze.bfg.zcml import path_spec + return path_spec(context, path) + + def test_no_package_attr(self): + context = DummyContext() + path = '/thepath' + result = self._callFUT(context, path) + self.assertEqual(result, path) + + def test_package_attr_None(self): + context = DummyContext() + context.package = None + path = '/thepath' + result = self._callFUT(context, path) + self.assertEqual(result, path) + + def test_package_path_doesnt_start_with_abspath(self): + context = DummyContext() + context.package = DummyPackage('repoze.bfg.tests') + path = '/thepath' + result = self._callFUT(context, path) + self.assertEqual(result, path) + + def test_package_path_starts_with_abspath(self): + import pkg_resources + import os + context = DummyContext() + package = DummyPackage('repoze.bfg.tests') + package_path = pkg_resources.resource_filename('repoze.bfg.tests', '') + template_path = os.path.join(package_path, 'templates/foo.pt') + context.package = package + result = self._callFUT(context, template_path) + self.assertEqual(result, 'repoze.bfg.tests:templates/foo.pt') + + def test_package_name_is___main__(self): + context = DummyContext() + package = DummyPackage('__main__') + context.package = package + result = self._callFUT(context, '/foo.pt') + self.assertEqual(result, '/foo.pt') + + def test_path_is_already_resource_spec(self): + context = DummyContext() + result = self._callFUT(context, 'repoze.bfg.tests:foo.pt') + self.assertEqual(result, 'repoze.bfg.tests:foo.pt') + +class IDummy(Interface): + pass + +class IFactory(Interface): + pass + +class DummyFactory(object): + implements(IFactory) + def __call__(self): + """ """ + +class DummyModule: + __path__ = "foo" + __name__ = "dummy" + __file__ = '' + +class DummyContext: + def __init__(self, resolved=DummyModule): + self.actions = [] + self.info = None + self.resolved = resolved + self.package = None + + def action(self, discriminator, callable=None, args=(), kw={}, order=0): + self.actions.append( + {'discriminator':discriminator, + 'callable':callable, + 'args':args, + 'kw':kw} + ) + + def path(self, path): + return path + + def resolve(self, dottedname): + return self.resolved + +class Dummy: + pass + +class DummyRoute: + pass + +class DummyRequest: + subpath = () + def __init__(self, environ=None): + if environ is None: + environ = {} + self.environ = environ + self.path_info = environ.get('PATH_INFO', None) + + def get_response(self, app): + return app + + def copy(self): + return self + +class DummyPackage(object): + def __init__(self, name): + self.__name__ = name + self.__file__ = '/__init__.py' + diff --git a/pyramid/tests/viewdecoratorapp/__init__.py b/pyramid/tests/viewdecoratorapp/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/viewdecoratorapp/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/viewdecoratorapp/configure.zcml b/pyramid/tests/viewdecoratorapp/configure.zcml new file mode 100644 index 000000000..6867046df --- /dev/null +++ b/pyramid/tests/viewdecoratorapp/configure.zcml @@ -0,0 +1,6 @@ + + + + + + diff --git a/pyramid/tests/viewdecoratorapp/views/__init__.py b/pyramid/tests/viewdecoratorapp/views/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/viewdecoratorapp/views/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/viewdecoratorapp/views/templates/foo.pt b/pyramid/tests/viewdecoratorapp/views/templates/foo.pt new file mode 100644 index 000000000..6a2f701b6 --- /dev/null +++ b/pyramid/tests/viewdecoratorapp/views/templates/foo.pt @@ -0,0 +1,3 @@ + +${result} + diff --git a/pyramid/tests/viewdecoratorapp/views/views.py b/pyramid/tests/viewdecoratorapp/views/views.py new file mode 100644 index 000000000..29f8c7fd4 --- /dev/null +++ b/pyramid/tests/viewdecoratorapp/views/views.py @@ -0,0 +1,17 @@ +import os +from repoze.bfg.view import bfg_view + +@bfg_view(renderer='templates/foo.pt', name='first') +def first(request): + return {'result':'OK1'} + +@bfg_view(renderer='repoze.bfg.tests.viewdecoratorapp.views:templates/foo.pt', + name='second') +def second(request): + return {'result':'OK2'} + +here = os.path.normpath(os.path.dirname(os.path.abspath(__file__))) +foo = os.path.join(here, 'templates', 'foo.pt') +@bfg_view(renderer=foo, name='third') +def third(request): + return {'result':'OK3'} diff --git a/pyramid/threadlocal.py b/pyramid/threadlocal.py new file mode 100644 index 000000000..631cca62a --- /dev/null +++ b/pyramid/threadlocal.py @@ -0,0 +1,61 @@ +import threading + +from repoze.bfg.registry import global_registry + +class ThreadLocalManager(threading.local): + def __init__(self, default=None): + # http://code.google.com/p/google-app-engine-django/issues/detail?id=119 + # we *must* use a keword argument for ``default`` here instead + # of a positional argument to work around a bug in the + # implementation of _threading_local.local in Python, which is + # used by GAE instead of _thread.local + self.stack = [] + self.default = default + + def push(self, info): + self.stack.append(info) + + set = push # b/c + + def pop(self): + if self.stack: + return self.stack.pop() + + def get(self): + try: + return self.stack[-1] + except IndexError: + return self.default() + + def clear(self): + self.stack[:] = [] + +def defaults(): + return {'request':None, 'registry':global_registry} + +manager = ThreadLocalManager(default=defaults) + +def get_current_request(): + """Return the currently active request or ``None`` if no request + is currently active. + + This function should be used *extremely sparingly*, usually only + in unit testing code. it's almost always usually a mistake to use + ``get_current_request`` outside a testing context because its + usage makes it possible to write code that can be neither easily + tested nor scripted. + """ + return manager.get()['request'] + +def get_current_registry(context=None): # context required by getSiteManager API + """Return the currently active :term:`application registry` or the + global application registry if no request is currently active. + + This function should be used *extremely sparingly*, usually only + in unit testing code. it's almost always usually a mistake to use + ``get_current_registry`` outside a testing context because its + usage makes it possible to write code that can be neither easily + tested nor scripted. + """ + return manager.get()['registry'] + diff --git a/pyramid/traversal.py b/pyramid/traversal.py new file mode 100644 index 000000000..84365e2ff --- /dev/null +++ b/pyramid/traversal.py @@ -0,0 +1,674 @@ +import urllib + +from zope.interface import implements +from zope.interface.interfaces import IInterface + +from repoze.lru import lru_cache + +from repoze.bfg.interfaces import IContextURL +from repoze.bfg.interfaces import IRequestFactory +from repoze.bfg.interfaces import ITraverser +from repoze.bfg.interfaces import VH_ROOT_KEY + +from repoze.bfg.encode import url_quote +from repoze.bfg.exceptions import URLDecodeError +from repoze.bfg.location import lineage +from repoze.bfg.request import Request +from repoze.bfg.threadlocal import get_current_registry + +def find_root(model): + """ Find the root node in the graph to which ``model`` + belongs. Note that ``model`` should be :term:`location`-aware. + Note that the root node is available in the request object by + accessing the ``request.root`` attribute. + """ + for location in lineage(model): + if location.__parent__ is None: + model = location + break + return model + +def find_model(model, path): + """ Given a model object and a string or tuple representing a path + (such as the return value of + :func:`repoze.bfg.traversal.model_path` or + :func:`repoze.bfg.traversal.model_path_tuple`), return a context + in this application's model graph at the specified path. The + model passed in *must* be :term:`location`-aware. If the path + cannot be resolved (if the respective node in the graph does not + exist), a :exc:`KeyError` will be raised. + + This function is the logical inverse of + :func:`repoze.bfg.traversal.model_path` and + :func:`repoze.bfg.traversal.model_path_tuple`; it can resolve any + path string or tuple generated by either of those functions. + + Rules for passing a *string* as the ``path`` argument: if the + first character in the path string is the with the ``/`` + character, the path will considered absolute and the graph + traversal will start at the root object. If the first character + of the path string is *not* the ``/`` character, the path is + considered relative and graph traversal will begin at the model + object supplied to the function as the ``model`` argument. If an + empty string is passed as ``path``, the ``model`` passed in will + be returned. Model path strings must be escaped in the following + manner: each Unicode path segment must be encoded as UTF-8 and as + each path segment must escaped via Python's :mod:`urllib.quote`. + For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or + ``to%20the/La%20Pe%C3%B1a`` (relative). The + :func:`repoze.bfg.traversal.model_path` function generates strings + which follow these rules (albeit only absolute ones). + + Rules for passing a *tuple* as the ``path`` argument: if the first + element in the path tuple is the empty string (for example ``('', + 'a', 'b', 'c')``, the path is considered absolute and the graph + traversal will start at the graph root object. If the first + element in the path tuple is not the empty string (for example + ``('a', 'b', 'c')``), the path is considered relative and graph + traversal will begin at the model object supplied to the function + as the ``model`` argument. If an empty sequence is passed as + ``path``, the ``model`` passed in itself will be returned. No + URL-quoting or UTF-8-encoding of individual path segments within + the tuple is required (each segment may be any string or unicode + object representing a model name). Model path tuples generated by + :func:`repoze.bfg.traversal.model_path_tuple` can always be + resolved by ``find_model``. + """ + D = traverse(model, path) + view_name = D['view_name'] + context = D['context'] + if view_name: + raise KeyError('%r has no subelement %s' % (context, view_name)) + return context + +def find_interface(model, class_or_interface): + """ + Return the first object found in the parent chain of ``model`` + which, a) if ``class_or_interface`` is a Python class object, is + an instance of the class or any subclass of that class or b) if + ``class_or_interface`` is a :term:`interface`, provides the + specified interface. Return ``None`` if no object providing + ``interface_or_class`` can be found in the parent chain. The + ``model`` passed in *must* be :term:`location`-aware. + """ + if IInterface.providedBy(class_or_interface): + test = class_or_interface.providedBy + else: + test = lambda arg: isinstance(arg, class_or_interface) + for location in lineage(model): + if test(location): + return location + +def model_path(model, *elements): + """ Return a string object representing the absolute physical path + of the model object based on its position in the model graph, e.g + ``/foo/bar``. Any positional arguments passed in as ``elements`` + will be appended as path segments to the end of the model path. + For instance, if the model's path is ``/foo/bar`` and ``elements`` + equals ``('a', 'b')``, the returned string will be + ``/foo/bar/a/b``. The first character in the string will always + be the ``/`` character (a leading ``/`` character in a path string + represents that the path is absolute). + + Model path strings returned will be escaped in the following + manner: each unicode path segment will be encoded as UTF-8 and + each path segment will be escaped via Python's :mod:`urllib.quote`. + For example, ``/path/to%20the/La%20Pe%C3%B1a``. + + This function is a logical inverse of + :mod:`repoze.bfg.traversal.find_model`: it can be used to generate + path references that can later be resolved via that function. + + The ``model`` passed in *must* be :term:`location`-aware. + + .. note:: Each segment in the path string returned will use the + ``__name__`` attribute of the model it represents within + the graph. Each of these segments *should* be a unicode + or string object (as per the contract of + :term:`location`-awareness). However, no conversion or + safety checking of model names is performed. For + instance, if one of the models in your graph has a + ``__name__`` which (by error) is a dictionary, the + :func:`repoze.bfg.traversal.model_path` function will + attempt to append it to a string and it will cause a + :exc:`repoze.bfg.exceptions.URLDecodeError`. + + .. note:: The :term:`root` model *must* have a ``__name__`` + attribute with a value of either ``None`` or the empty + string for paths to be generated properly. If the root + model has a non-null ``__name__`` attribute, its name + will be prepended to the generated path rather than a + single leading '/' character. + """ + # joining strings is a bit expensive so we delegate to a function + # which caches the joined result for us + return _join_path_tuple(model_path_tuple(model, *elements)) + +def traverse(model, path): + """Given a model object as ``model`` and a string or tuple + representing a path as ``path`` (such as the return value of + :func:`repoze.bfg.traversal.model_path` or + :func:`repoze.bfg.traversal.model_path_tuple` or the value of + ``request.environ['PATH_INFO']``), return a dictionary with the + keys ``context``, ``root``, ``view_name``, ``subpath``, + ``traversed``, ``virtual_root``, and ``virtual_root_path``. + + A definition of each value in the returned dictionary: + + - ``context``: The :term:`context` (a :term:`model` object) found + via traversal or url dispatch. If the ``path`` passed in is the + empty string, the value of the ``model`` argument passed to this + function is returned. + + - ``root``: The model object at which :term:`traversal` begins. + If the ``model`` passed in was found via url dispatch or if the + ``path`` passed in was relative (non-absolute), the value of the + ``model`` argument passed to this function is returned. + + - ``view_name``: The :term:`view name` found during + :term:`traversal` or :term:`url dispatch`; if the ``model`` was + found via traversal, this is usually a representation of the + path segment which directly follows the path to the ``context`` + in the ``path``. The ``view_name`` will be a Unicode object or + the empty string. The ``view_name`` will be the empty string if + there is no element which follows the ``context`` path. An + example: if the path passed is ``/foo/bar``, and a context + object is found at ``/foo`` (but not at ``/foo/bar``), the 'view + name' will be ``u'bar'``. If the ``model`` was found via + urldispatch, the view_name will be the name the route found was + registered with. + + - ``subpath``: For a ``model`` found via :term:`traversal`, this + is a sequence of path segments found in the ``path`` that follow + the ``view_name`` (if any). Each of these items is a Unicode + object. If no path segments follow the ``view_name``, the + subpath will be the empty sequence. An example: if the path + passed is ``/foo/bar/baz/buz``, and a context object is found at + ``/foo`` (but not ``/foo/bar``), the 'view name' will be + ``u'bar'`` and the :term:`subpath` will be ``[u'baz', u'buz']``. + For a ``model`` found via url dispatch, the subpath will be a + sequence of values discerned from ``*subpath`` in the route + pattern matched or the empty sequence. + + - ``traversed``: The sequence of path elements traversed from the + root to find the ``context`` object during :term:`traversal`. + Each of these items is a Unicode object. If no path segments + were traversed to find the ``context`` object (e.g. if the + ``path`` provided is the empty string), the ``traversed`` value + will be the empty sequence. If the ``model`` is a model found + via :term:`url dispatch`, traversed will be None. + + - ``virtual_root``: A model object representing the 'virtual' root + of the object graph being traversed during :term:`traversal`. + See :ref:`vhosting_chapter` for a definition of the virtual root + object. If no virtual hosting is in effect, and the ``path`` + passed in was absolute, the ``virtual_root`` will be the + *physical* root object (the object at which :term:`traversal` + begins). If the ``model`` passed in was found via :term:`URL + dispatch` or if the ``path`` passed in was relative, the + ``virtual_root`` will always equal the ``root`` object (the + model passed in). + + - ``virtual_root_path`` -- If :term:`traversal` was used to find + the ``model``, this will be the sequence of path elements + traversed to find the ``virtual_root`` object. Each of these + items is a Unicode object. If no path segments were traversed + to find the ``virtual_root`` object (e.g. if virtual hosting is + not in effect), the ``traversed`` value will be the empty list. + If url dispatch was used to find the ``model``, this will be + ``None``. + + If the path cannot be resolved, a :exc:`KeyError` will be raised. + + Rules for passing a *string* as the ``path`` argument: if the + first character in the path string is the with the ``/`` + character, the path will considered absolute and the graph + traversal will start at the root object. If the first character + of the path string is *not* the ``/`` character, the path is + considered relative and graph traversal will begin at the model + object supplied to the function as the ``model`` argument. If an + empty string is passed as ``path``, the ``model`` passed in will + be returned. Model path strings must be escaped in the following + manner: each Unicode path segment must be encoded as UTF-8 and as + each path segment must escaped via Python's :mod:`urllib.quote`. + For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or + ``to%20the/La%20Pe%C3%B1a`` (relative). The + :func:`repoze.bfg.traversal.model_path` function generates strings + which follow these rules (albeit only absolute ones). + + Rules for passing a *tuple* as the ``path`` argument: if the first + element in the path tuple is the empty string (for example ``('', + 'a', 'b', 'c')``, the path is considered absolute and the graph + traversal will start at the graph root object. If the first + element in the path tuple is not the empty string (for example + ``('a', 'b', 'c')``), the path is considered relative and graph + traversal will begin at the model object supplied to the function + as the ``model`` argument. If an empty sequence is passed as + ``path``, the ``model`` passed in itself will be returned. No + URL-quoting or UTF-8-encoding of individual path segments within + the tuple is required (each segment may be any string or unicode + object representing a model name). + + Explanation of the conversion of ``path`` segment values to + Unicode during traversal: Each segment is URL-unquoted, and + decoded into Unicode. Each segment is assumed to be encoded using + the UTF-8 encoding (or a subset, such as ASCII); a + :exc:`repoze.bfg.exceptions.URLDecodeError` is raised if a segment + cannot be decoded. If a segment name is empty or if it is ``.``, + it is ignored. If a segment name is ``..``, the previous segment + is deleted, and the ``..`` is ignored. As a result of this + process, the return values ``view_name``, each element in the + ``subpath``, each element in ``traversed``, and each element in + the ``virtual_root_path`` will be Unicode as opposed to a string, + and will be URL-decoded. + """ + + if hasattr(path, '__iter__'): + # the traverser factory expects PATH_INFO to be a string, not + # unicode and it expects path segments to be utf-8 and + # urlencoded (it's the same traverser which accepts PATH_INFO + # from user agents; user agents always send strings). + if path: + path = _join_path_tuple(tuple(path)) + else: + path = '' + + if path and path[0] == '/': + model = find_root(model) + + reg = get_current_registry() + request_factory = reg.queryUtility(IRequestFactory, default=Request) + request = request_factory.blank(path) + request.registry = reg + traverser = reg.queryAdapter(model, ITraverser) + if traverser is None: + traverser = ModelGraphTraverser(model) + + return traverser(request) + +def model_path_tuple(model, *elements): + """ + Return a tuple representing the absolute physical path of the + ``model`` object based on its position in an object graph, e.g + ``('', 'foo', 'bar')``. Any positional arguments passed in as + ``elements`` will be appended as elements in the tuple + representing the model path. For instance, if the model's + path is ``('', 'foo', 'bar')`` and elements equals ``('a', 'b')``, + the returned tuple will be ``('', 'foo', 'bar', 'a', b')``. The + first element of this tuple will always be the empty string (a + leading empty string element in a path tuple represents that the + path is absolute). + + This function is a logical inverse of + :func:`repoze.bfg.traversal.find_model`: it can be used to + generate path references that can later be resolved that function. + + The ``model`` passed in *must* be :term:`location`-aware. + + .. note:: Each segment in the path tuple returned will equal the + ``__name__`` attribute of the model it represents within + the graph. Each of these segments *should* be a unicode + or string object (as per the contract of + :term:`location`-awareness). However, no conversion or + safety checking of model names is performed. For + instance, if one of the models in your graph has a + ``__name__`` which (by error) is a dictionary, that + dictionary will be placed in the path tuple; no warning + or error will be given. + + .. note:: The :term:`root` model *must* have a ``__name__`` + attribute with a value of either ``None`` or the empty + string for path tuples to be generated properly. If + the root model has a non-null ``__name__`` attribute, + its name will be the first element in the generated + path tuple rather than the empty string. + """ + return tuple(_model_path_list(model, *elements)) + +def _model_path_list(model, *elements): + """ Implementation detail shared by model_path and model_path_tuple """ + path = [loc.__name__ or '' for loc in lineage(model)] + path.reverse() + path.extend(elements) + return path + +def virtual_root(model, request): + """ + Provided any :term:`model` and a :term:`request` object, return + the model object representing the :term:`virtual root` of the + current :term:`request`. Using a virtual root in a + :term:`traversal` -based :mod:`repoze.bfg` application permits + rooting, for example, the object at the traversal path ``/cms`` at + ``http://example.com/`` instead of rooting it at + ``http://example.com/cms/``. + + If the ``model`` passed in is a context obtained via + :term:`traversal`, and if the ``HTTP_X_VHM_ROOT`` key is in the + WSGI environment, the value of this key will be treated as a + 'virtual root path': the :func:`repoze.bfg.traversal.find_model` + API will be used to find the virtual root object using this path; + if the object is found, it will be returned. If the + ``HTTP_X_VHM_ROOT`` key is is not present in the WSGI environment, + the physical :term:`root` of the graph will be returned instead. + + Virtual roots are not useful at all in applications that use + :term:`URL dispatch`. Contexts obtained via URL dispatch don't + really support being virtually rooted (each URL dispatch context + is both its own physical and virtual root). However if this API + is called with a ``model`` argument which is a context obtained + via URL dispatch, the model passed in will be returned + unconditionally.""" + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + urlgenerator = reg.queryMultiAdapter((model, request), IContextURL) + if urlgenerator is None: + urlgenerator = TraversalContextURL(model, request) + return urlgenerator.virtual_root() + +@lru_cache(1000) +def traversal_path(path): + """ Given a ``PATH_INFO`` string (slash-separated path segments), + return a tuple representing that path which can be used to + traverse a graph. + + The ``PATH_INFO`` is split on slashes, creating a list of + segments. Each segment is URL-unquoted, and subsequently decoded + into Unicode. Each segment is assumed to be encoded using the + UTF-8 encoding (or a subset, such as ASCII); a + :exc:`repoze.bfg.exceptions.URLDecodeError` is raised if a segment + cannot be decoded. If a segment name is empty or if it is ``.``, + it is ignored. If a segment name is ``..``, the previous segment + is deleted, and the ``..`` is ignored. + + If this function is passed a Unicode object instead of a string, + that Unicode object *must* directly encodeable to ASCII. For + example, u'/foo' will work but u'/' (a + Unicode object with characters that cannot be encoded to ascii) + will not. + + .. note: New in version 1.3, this API eagerly attempts to encode a + Unicode ``path`` into ASCII before attempting to split it and + decode its segments. This is for convenience. In version 1.2 + and prior, if the path was Unicode, an inappropriate call to + the ``decode()`` method of a Unicode path segment could cause a + ``UnicodeDecodeError`` to occur even if the Unicode + representation of the path contained no 'high order' + characters. + + Examples: + + ``/`` + + () + + ``/foo/bar/baz`` + + (u'foo', u'bar', u'baz') + + ``foo/bar/baz`` + + (u'foo', u'bar', u'baz') + + ``/foo/bar/baz/`` + + (u'foo', u'bar', u'baz') + + ``/foo//bar//baz/`` + + (u'foo', u'bar', u'baz') + + ``/foo/bar/baz/..`` + + (u'foo', u'bar') + + ``/my%20archives/hello`` + + (u'my archives', u'hello') + + ``/archives/La%20Pe%C3%B1a`` + + (u'archives', u'') + + .. note:: This function does not generate the same type of tuples + that :func:`repoze.bfg.traversal.model_path_tuple` does. + In particular, the leading empty string is not present + in the tuple it returns, unlike tuples returned by + :func:`repoze.bfg.traversal.model_path_tuple`. As a + result, tuples generated by ``traversal_path`` are not + resolveable by the + :func:`repoze.bfg.traversal.find_model` API. + ``traversal_path`` is a function mostly used by the + internals of :mod:`repoze.bfg` and by people writing + their own traversal machinery, as opposed to users + writing applications in :mod:`repoze.bfg`. + """ + if isinstance(path, unicode): + path = path.encode('ascii') + path = path.strip('/') + clean = [] + for segment in path.split('/'): + segment = urllib.unquote(segment) # deal with spaces in path segment + if not segment or segment=='.': + continue + elif segment == '..': + del clean[-1] + else: + try: + segment = segment.decode('utf-8') + except UnicodeDecodeError, e: + raise URLDecodeError( + e.encoding, e.object, e.start, e.end, e.reason + ) + clean.append(segment) + return tuple(clean) + +_segment_cache = {} + +def quote_path_segment(segment): + """ Return a quoted representation of a 'path segment' (such as + the string ``__name__`` attribute of a model) as a string. If the + ``segment`` passed in is a unicode object, it is converted to a + UTF-8 string, then it is URL-quoted using Python's + ``urllib.quote``. If the ``segment`` passed in is a string, it is + URL-quoted using Python's :mod:`urllib.quote`. If the segment + passed in is not a string or unicode object, an error will be + raised. The return value of ``quote_path_segment`` is always a + string, never Unicode. + + .. note:: The return value for each segment passed to this + function is cached in a module-scope dictionary for + speed: the cached version is returned when possible + rather than recomputing the quoted version. No cache + emptying is ever done for the lifetime of an + application, however. If you pass arbitrary + user-supplied strings to this function (as opposed to + some bounded set of values from a 'working set' known to + your application), it may become a memory leak. + """ + # The bit of this code that deals with ``_segment_cache`` is an + # optimization: we cache all the computation of URL path segments + # in this module-scope dictionary with the original string (or + # unicode value) as the key, so we can look it up later without + # needing to reencode or re-url-quote it + try: + return _segment_cache[segment] + except KeyError: + if segment.__class__ is unicode: # isinstance slighly slower (~15%) + result = url_quote(segment.encode('utf-8')) + else: + result = url_quote(segment) + # we don't need a lock to mutate _segment_cache, as the below + # will generate exactly one Python bytecode (STORE_SUBSCR) + _segment_cache[segment] = result + return result + +class ModelGraphTraverser(object): + """ A model graph traverser that should be used (for speed) when + every object in the graph supplies a ``__name__`` and + ``__parent__`` attribute (ie. every object in the graph is + :term:`location` aware) .""" + + implements(ITraverser) + + VIEW_SELECTOR = '@@' + + def __init__(self, root): + self.root = root + + def __call__(self, request): + try: + environ = request.environ + except AttributeError: + # In BFG 1.0 and before, this API expected an environ + # rather than a request; some bit of code may still be + # passing us an environ. If so, deal. + environ = request + + if 'bfg.routes.matchdict' in environ: + matchdict = environ['bfg.routes.matchdict'] + + path = matchdict.get('traverse', '/') + if hasattr(path, '__iter__'): + # this is a *traverse stararg (not a :traverse) + path = '/'.join([quote_path_segment(x) for x in path]) or '/' + + subpath = matchdict.get('subpath', ()) + if not hasattr(subpath, '__iter__'): + # this is not a *subpath stararg (just a :subpath) + subpath = traversal_path(subpath) + + else: + # this request did not match a route + subpath = () + try: + path = environ['PATH_INFO'] or '/' + except KeyError: + path = '/' + + if VH_ROOT_KEY in environ: + vroot_path = environ[VH_ROOT_KEY] + vroot_tuple = traversal_path(vroot_path) + vpath = vroot_path + path + vroot_idx = len(vroot_tuple) -1 + else: + vroot_tuple = () + vpath = path + vroot_idx = -1 + + root = self.root + ob = vroot = root + + if vpath == '/' or (not vpath): + # prevent a call to traversal_path if we know it's going + # to return the empty tuple + vpath_tuple = () + else: + # we do dead reckoning here via tuple slicing instead of + # pushing and popping temporary lists for speed purposes + # and this hurts readability; apologies + i = 0 + view_selector = self.VIEW_SELECTOR + vpath_tuple = traversal_path(vpath) + for segment in vpath_tuple: + if segment[:2] == view_selector: + return {'context':ob, + 'view_name':segment[2:], + 'subpath':vpath_tuple[i+1:], + 'traversed':vpath_tuple[:vroot_idx+i+1], + 'virtual_root':vroot, + 'virtual_root_path':vroot_tuple, + 'root':root} + try: + getitem = ob.__getitem__ + except AttributeError: + return {'context':ob, + 'view_name':segment, + 'subpath':vpath_tuple[i+1:], + 'traversed':vpath_tuple[:vroot_idx+i+1], + 'virtual_root':vroot, + 'virtual_root_path':vroot_tuple, + 'root':root} + + try: + next = getitem(segment) + except KeyError: + return {'context':ob, + 'view_name':segment, + 'subpath':vpath_tuple[i+1:], + 'traversed':vpath_tuple[:vroot_idx+i+1], + 'virtual_root':vroot, + 'virtual_root_path':vroot_tuple, + 'root':root} + if i == vroot_idx: + vroot = next + ob = next + i += 1 + + return {'context':ob, 'view_name':u'', 'subpath':subpath, + 'traversed':vpath_tuple, 'virtual_root':vroot, + 'virtual_root_path':vroot_tuple, 'root':root} + +class TraversalContextURL(object): + """ The IContextURL adapter used to generate URLs for a context + object obtained via graph traversal""" + implements(IContextURL) + + vroot_varname = VH_ROOT_KEY + + def __init__(self, context, request): + self.context = context + self.request = request + + def virtual_root(self): + environ = self.request.environ + vroot_varname = self.vroot_varname + if vroot_varname in environ: + return find_model(self.context, environ[vroot_varname]) + # shortcut instead of using find_root; we probably already + # have it on the request + try: + return self.request.root + except AttributeError: + return find_root(self.context) + + def __call__(self): + """ Generate a URL based on the :term:`lineage` of a + :term:`model` object obtained via :term:`traversal`. If any + model in the context lineage has a Unicode name, it will be + converted to a UTF-8 string before being attached to the URL. + If a ``HTTP_X_VHM_ROOT`` key is present in the WSGI + environment, its value will be treated as a 'virtual root + path': the path of the URL generated by this will be + left-stripped of this virtual root path value. + """ + path = model_path(self.context) + if path != '/': + path = path + '/' + request = self.request + environ = request.environ + vroot_varname = self.vroot_varname + + # if the path starts with the virtual root path, trim it out + if vroot_varname in environ: + vroot_path = environ[vroot_varname] + if path.startswith(vroot_path): + path = path[len(vroot_path):] + + app_url = request.application_url # never ends in a slash + return app_url + path + +@lru_cache(1000) +def _join_path_tuple(tuple): + return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/' + +class DefaultRootFactory: + __parent__ = None + __name__ = None + def __init__(self, request): + matchdict = getattr(request, 'matchdict', {}) + # provide backwards compatibility for applications which + # used routes (at least apps without any custom "context + # factory") in BFG 0.9.X and before + self.__dict__.update(matchdict) diff --git a/pyramid/url.py b/pyramid/url.py new file mode 100644 index 000000000..d1532f7ef --- /dev/null +++ b/pyramid/url.py @@ -0,0 +1,327 @@ +""" Utility functions for dealing with URLs in repoze.bfg """ + +import os + +from repoze.lru import lru_cache + +from repoze.bfg.interfaces import IContextURL +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import IStaticURLInfo + +from repoze.bfg.encode import urlencode +from repoze.bfg.path import caller_package +from repoze.bfg.threadlocal import get_current_registry +from repoze.bfg.traversal import TraversalContextURL +from repoze.bfg.traversal import quote_path_segment + +def route_url(route_name, request, *elements, **kw): + """Generates a fully qualified URL for a named :mod:`repoze.bfg` + :term:`route configuration`. + + Use the route's ``name`` as the first positional argument. Use a + request object as the second positional argument. Additional + positional arguments are appended to the URL as path segments + after it is generated. + + Use keyword arguments to supply values which match any dynamic + path elements in the route definition. Raises a :exc:`KeyError` + exception if the URL cannot be generated for any reason (not + enough arguments, for example). + + For example, if you've defined a route named "foobar" with the path + ``:foo/:bar/*traverse``:: + + route_url('foobar', request, foo='1') => + route_url('foobar', request, foo='1', bar='2') => + route_url('foobar', request, foo='1', bar='2', + 'traverse=('a','b')) => http://e.com/1/2/a/b + route_url('foobar', request, foo='1', bar='2', + 'traverse=('/a/b')) => http://e.com/1/2/a/b + + Values replacing ``:segment`` arguments can be passed as strings + or Unicode objects. They will be encoded to UTF-8 and URL-quoted + before being placed into the generated URL. + + Values replacing ``*remainder`` arguments can be passed as strings + *or* tuples of Unicode/string values. If a tuple is passed as a + ``*remainder`` replacement value, its values are URL-quoted and + encoded to UTF-8. The resulting strings are joined with slashes + and rendered into the URL. If a string is passed as a + ``*remainder`` replacement value, it is tacked on to the URL + untouched. + + If a keyword argument ``_query`` is present, it will used to + compose a query string that will be tacked on to the end of the + URL. The value of ``_query`` must be a sequence of two-tuples + *or* a data structure with an ``.items()`` method that returns a + sequence of two-tuples (presumably a dictionary). This data + structure will be turned into a query string per the documentation + of :func:`repoze.bfg.encode.urlencode` function. After the query + data is turned into a query string, a leading ``?`` is prepended, + and the resulting string is appended to the generated URL. + + .. note:: Python data structures that are passed as ``_query`` + which are sequences or dictionaries are turned into a + string under the same rules as when run through + :func:`urllib.urlencode` with the ``doseq`` argument + equal to ``True``. This means that sequences can be + passed as values, and a k=v pair will be placed into the + query string for each value. + + If a keyword argument ``_anchor`` is present, its string + representation will be used as a named anchor in the generated URL + (e.g. if ``_anchor`` is passed as ``foo`` and the model URL is + ``http://example.com/model/url``, the resulting generated URL will + be ``http://example.com/model/url#foo``). + + .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 + encoded. If ``_anchor`` is passed as a Unicode object, it + will be converted to UTF-8 before being appended to the + URL. The anchor value is not quoted in any way before + being appended to the generated URL. + + If both ``_anchor`` and ``_query`` are specified, the anchor + element will always follow the query element, + e.g. ``http://example.com?foo=1#bar``. + + If a keyword ``_app_url`` is present, it will be used as the + protocol/hostname/port/leading path prefix of the generated URL. + For example, using an ``_app_url`` of + ``http://example.com:8080/foo`` would cause the URL + ``http://example.com:8080/foo/fleeb/flub`` to be returned from + this function if the expansion of the route pattern associated + with the ``route_name`` expanded to ``/fleeb/flub``. If + ``_app_url`` is not specified, the result of + ``request.application_url`` will be used as the prefix (the + default). + + .. note:: Special treatment of ``_app_url`` was added in + :mod:`repoze.bfg` 1.3. + + This function raises a :exc:`KeyError` if the URL cannot be + generated due to missing replacement names. Extra replacement + names are ignored. + + If the route object which matches the ``route_name`` argument has + a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments + arguments passed to this function might be augmented or changed. + """ + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + mapper = reg.getUtility(IRoutesMapper) + route = mapper.get_route(route_name) + + if route is None: + raise KeyError('No such route named %s' % route_name) + + if route.pregenerator is not None: + elements, kw = route.pregenerator(request, elements, kw) + + anchor = '' + qs = '' + app_url = None + + if '_query' in kw: + qs = '?' + urlencode(kw.pop('_query'), doseq=True) + + if '_anchor' in kw: + anchor = kw.pop('_anchor') + if isinstance(anchor, unicode): + anchor = anchor.encode('utf-8') + anchor = '#' + anchor + + if '_app_url' in kw: + app_url = kw.pop('_app_url') + + path = route.generate(kw) # raises KeyError if generate fails + + if elements: + suffix = _join_elements(elements) + if not path.endswith('/'): + suffix = '/' + suffix + else: + suffix = '' + + if app_url is None: + # we only defer lookup of application_url until here because + # it's somewhat expensive; we won't need to do it if we've + # been passed _app_url + app_url = request.application_url + + return app_url + path + suffix + qs + anchor + +def model_url(model, request, *elements, **kw): + """ + Generate a string representing the absolute URL of the ``model`` + object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or + ``SERVER_NAME`` in the ``request``, plus any ``SCRIPT_NAME``. The + overall result of this function is always a UTF-8 encoded string + (never Unicode). + + Examples:: + + model_url(context, request) => + + http://example.com/ + + model_url(context, request, 'a.html') => + + http://example.com/a.html + + model_url(context, request, 'a.html', query={'q':'1'}) => + + http://example.com/a.html?q=1 + + model_url(context, request, 'a.html', anchor='abc') => + + http://example.com/a.html#abc + + Any positional arguments passed in as ``elements`` must be strings + or Unicode objects. These will be joined by slashes and appended + to the generated model URL. Each of the elements passed in is + URL-quoted before being appended; if any element is Unicode, it + will converted to a UTF-8 bytestring before being URL-quoted. + + .. warning:: if no ``elements`` arguments are specified, the model + URL will end with a trailing slash. If any + ``elements`` are used, the generated URL will *not* + end in trailing a slash. + + If a keyword argument ``query`` is present, it will used to + compose a query string that will be tacked on to the end of the + URL. The value of ``query`` must be a sequence of two-tuples *or* + a data structure with an ``.items()`` method that returns a + sequence of two-tuples (presumably a dictionary). This data + structure will be turned into a query string per the documentation + of ``repoze.url.urlencode`` function. After the query data is + turned into a query string, a leading ``?`` is prepended, and the + resulting string is appended to the generated URL. + + .. note:: Python data structures that are passed as ``query`` + which are sequences or dictionaries are turned into a + string under the same rules as when run through + :func:`urllib.urlencode` with the ``doseq`` argument + equal to ``True``. This means that sequences can be + passed as values, and a k=v pair will be placed into the + query string for each value. + + If a keyword argument ``anchor`` is present, its string + representation will be used as a named anchor in the generated URL + (e.g. if ``anchor`` is passed as ``foo`` and the model URL is + ``http://example.com/model/url``, the resulting generated URL will + be ``http://example.com/model/url#foo``). + + .. note:: If ``anchor`` is passed as a string, it should be UTF-8 + encoded. If ``anchor`` is passed as a Unicode object, it + will be converted to UTF-8 before being appended to the + URL. The anchor value is not quoted in any way before + being appended to the generated URL. + + If both ``anchor`` and ``query`` are specified, the anchor element + will always follow the query element, + e.g. ``http://example.com?foo=1#bar``. + + .. note:: If the ``model`` used is the result of a + :term:`traversal`, it must be :term:`location`-aware. + The ``model`` can also be the context of a :term:`URL + dispatch`; contexts found this way do not need to be + location-aware. + + .. note:: If a 'virtual root path' is present in the request + environment (the value of the WSGI environ key + ``HTTP_X_VHM_ROOT``), and the ``model`` was obtained via + :term:`traversal`, the URL path will not include the + virtual root prefix (it will be stripped off the + left hand side of the generated URL). + """ + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + + context_url = reg.queryMultiAdapter((model, request), IContextURL) + if context_url is None: + context_url = TraversalContextURL(model, request) + model_url = context_url() + + qs = '' + anchor = '' + + if 'query' in kw: + qs = '?' + urlencode(kw['query'], doseq=True) + + if 'anchor' in kw: + anchor = kw['anchor'] + if isinstance(anchor, unicode): + anchor = anchor.encode('utf-8') + anchor = '#' + anchor + + if elements: + suffix = _join_elements(elements) + else: + suffix = '' + + return model_url + suffix + qs + anchor + +def static_url(path, request, **kw): + """ + Generates a fully qualified URL for a static :term:`resource`. + The resource must live within a location defined via the + :meth:`repoze.bfg.configuration.Configurator.add_static_view` + :term:`configuration declaration` or the ```` ZCML + directive (see :ref:`static_resources_section`). + + Example:: + + static_url('mypackage:static/foo.css', request) => + + http://example.com/static/foo.css + + + The ``path`` argument points at a file or directory on disk which + a URL should be generated for. The ``path`` may be either a + relative path (e.g. ``static/foo.css``) or a :term:`resource + specification` (e.g. ``mypackage:static/foo.css``). A ``path`` + may not be an absolute filesystem path (a :exc:`ValueError` will + be raised if this function is supplied with an absolute path). + + The ``request`` argument should be a :term:`request` object. + + The purpose of the ``**kw`` argument is the same as the purpose of + the :func:`repoze.bfg.url.route_url` ``**kw`` argument. See the + documentation for that function to understand the arguments which + you can provide to it. However, typically, you don't need to pass + anything as ``*kw`` when generating a static resource URL. + + This function raises a :exc:`ValueError` if a static view + definition cannot be found which matches the path specification. + + .. note:: This feature is new in :mod:`repoze.bfg` 1.1. + """ + if os.path.isabs(path): + raise ValueError('Absolute paths cannot be used to generate static ' + 'urls (use a package-relative path or a resource ' + 'specification).') + if not ':' in path: + # if it's not a package:relative/name and it's not an + # /absolute/path it's a relative/path; this means its relative + # to the package in which the caller's module is defined. + package = caller_package() + path = '%s:%s' % (package.__name__, path) + + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() # b/c + + info = reg.queryUtility(IStaticURLInfo) + if info is None: + raise ValueError('No static URL definition matching %s' % path) + + return info.generate(path, request, **kw) + +@lru_cache(1000) +def _join_elements(elements): + return '/'.join([quote_path_segment(s) for s in elements]) diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py new file mode 100644 index 000000000..8aca2c421 --- /dev/null +++ b/pyramid/urldispatch.py @@ -0,0 +1,148 @@ +import re +from urllib import unquote +from zope.interface import implements + +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import IRoute + +from repoze.bfg.compat import all +from repoze.bfg.encode import url_quote +from repoze.bfg.exceptions import URLDecodeError +from repoze.bfg.traversal import traversal_path +from repoze.bfg.traversal import quote_path_segment + + +_marker = object() + +class Route(object): + implements(IRoute) + def __init__(self, name, pattern, factory=None, predicates=(), + pregenerator=None): + self.pattern = pattern + self.path = pattern # indefinite b/w compat, not in interface + self.match, self.generate = _compile_route(pattern) + self.name = name + self.factory = factory + self.predicates = predicates + self.pregenerator = pregenerator + +class RoutesMapper(object): + implements(IRoutesMapper) + def __init__(self): + self.routelist = [] + self.routes = {} + + def has_routes(self): + return bool(self.routelist) + + def get_routes(self): + return self.routelist + + def get_route(self, name): + return self.routes.get(name) + + def connect(self, name, pattern, factory=None, predicates=(), + pregenerator=None): + if name in self.routes: + oldroute = self.routes[name] + self.routelist.remove(oldroute) + route = Route(name, pattern, factory, predicates, pregenerator) + self.routelist.append(route) + self.routes[name] = route + return route + + def generate(self, name, kw): + return self.routes[name].generate(kw) + + def __call__(self, request): + environ = request.environ + try: + # empty if mounted under a path in mod_wsgi, for example + path = environ['PATH_INFO'] or '/' + except KeyError: + path = '/' + + for route in self.routelist: + match = route.match(path) + if match is not None: + preds = route.predicates + info = {'match':match, 'route':route} + if preds and not all((p(info, request) for p in preds)): + continue + return info + + return {'route':None, 'match':None} + +# stolen from bobo and modified +route_re = re.compile(r'(/:[a-zA-Z]\w*)') +def _compile_route(route): + if not route.startswith('/'): + route = '/' + route + star = None + if '*' in route: + route, star = route.rsplit('*', 1) + pat = route_re.split(route) + pat.reverse() + rpat = [] + gen = [] + prefix = pat.pop() + if prefix: + rpat.append(re.escape(prefix)) + gen.append(prefix) + while pat: + name = pat.pop() + name = name[2:] + gen.append('/%%(%s)s' % name) + name = '/(?P<%s>[^/]+)' % name + rpat.append(name) + s = pat.pop() + if s: + rpat.append(re.escape(s)) + gen.append(s) + + if star: + rpat.append('(?P<%s>.*?)' % star) + gen.append('%%(%s)s' % star) + + pattern = ''.join(rpat) + '$' + + match = re.compile(pattern).match + def matcher(path): + m = match(path) + if m is None: + return m + d = {} + for k,v in m.groupdict().iteritems(): + if k is not None: + if k == star: + d[k] = traversal_path(v) + else: + encoded = unquote(v) + try: + d[k] = encoded.decode('utf-8') + except UnicodeDecodeError, e: + raise URLDecodeError( + e.encoding, e.object, e.start, e.end, e.reason + ) + + + return d + + + gen = ''.join(gen) + def generator(dict): + newdict = {} + for k, v in dict.items(): + if isinstance(v, unicode): + v = v.encode('utf-8') + if k == star and hasattr(v, '__iter__'): + v = '/'.join([quote_path_segment(x) for x in v]) + elif k != star: + try: + v = url_quote(v) + except TypeError: + pass + newdict[k] = v + return gen % newdict + + return matcher, generator diff --git a/pyramid/view.py b/pyramid/view.py new file mode 100644 index 000000000..c5fd60623 --- /dev/null +++ b/pyramid/view.py @@ -0,0 +1,551 @@ +import mimetypes +import os + +# See http://bugs.python.org/issue5853 which is a recursion bug +# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix +# has been applied on the Python 2 trunk). This workaround should +# really be in Paste if anywhere, but it's easiest to just do it +# here and get it over with to avoid needing to deal with any +# fallout. + +if hasattr(mimetypes, 'init'): + mimetypes.init() + +from webob.exc import HTTPFound + +import venusian + +from zope.deprecation import deprecated +from zope.interface import providedBy + +from repoze.bfg.interfaces import IRoutesMapper +from repoze.bfg.interfaces import IView +from repoze.bfg.interfaces import IViewClassifier + +from repoze.bfg.path import package_path +from repoze.bfg.resource import resource_spec_from_abspath +from repoze.bfg.static import static_view as static # B/C +from repoze.bfg.threadlocal import get_current_registry + +# b/c imports +from repoze.bfg.security import view_execution_permitted + +view_execution_permitted # prevent PyFlakes from complaining + +deprecated('view_execution_permitted', + "('from repoze.bfg.view import view_execution_permitted' was " + "deprecated as of repoze.bfg 1.0; instead use 'from " + "repoze.bfg.security import view_execution_permitted')", + ) + +deprecated('NotFound', + "('from repoze.bfg.view import NotFound' was " + "deprecated as of repoze.bfg 1.1; instead use 'from " + "repoze.bfg.exceptions import NotFound')", + ) + +static = static # dont yet deprecate this (ever?) + +_marker = object() + +def render_view_to_response(context, request, name='', secure=True): + """ Call the :term:`view callable` configured with a :term:`view + configuration` that matches the :term:`view name` ``name`` + registered against the specified ``context`` and ``request`` and + return a :term:`response` object. This function will return + ``None`` if a corresponding :term:`view callable` cannot be found + (when no :term:`view configuration` matches the combination of + ``name`` / ``context`` / and ``request``). + + If `secure`` is ``True``, and the :term:`view callable` found is + protected by a permission, the permission will be checked before + calling the view function. If the permission check disallows view + execution (based on the current :term:`authorization policy`), a + :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised. + The exception's ``args`` attribute explains why the view access + was disallowed. + + If ``secure`` is ``False``, no permission checking is done.""" + provides = [IViewClassifier] + map(providedBy, (request, context)) + try: + reg = request.registry + except AttributeError: + reg = get_current_registry() + view = reg.adapters.lookup(provides, IView, name=name) + if view is None: + return None + + if not secure: + # the view will have a __call_permissive__ attribute if it's + # secured; otherwise it won't. + view = getattr(view, '__call_permissive__', view) + + # if this view is secured, it will raise a Forbidden + # appropriately if the executing user does not have the proper + # permission + return view(context, request) + +def render_view_to_iterable(context, request, name='', secure=True): + """ Call the :term:`view callable` configured with a :term:`view + configuration` that matches the :term:`view name` ``name`` + registered against the specified ``context`` and ``request`` and + return an iterable object which represents the body of a response. + This function will return ``None`` if a corresponding :term:`view + callable` cannot be found (when no :term:`view configuration` + matches the combination of ``name`` / ``context`` / and + ``request``). Additionally, this function will raise a + :exc:`ValueError` if a view function is found and called but the + view function's result does not have an ``app_iter`` attribute. + + You can usually get the string representation of the return value + of this function by calling ``''.join(iterable)``, or just use + :func:`repoze.bfg.view.render_view` instead. + + If ``secure`` is ``True``, and the view is protected by a + permission, the permission will be checked before the view + function is invoked. If the permission check disallows view + execution (based on the current :term:`authentication policy`), a + :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised; + its ``args`` attribute explains why the view access was + disallowed. + + If ``secure`` is ``False``, no permission checking is + done.""" + response = render_view_to_response(context, request, name, secure) + if response is None: + return None + return response.app_iter + +def render_view(context, request, name='', secure=True): + """ Call the :term:`view callable` configured with a :term:`view + configuration` that matches the :term:`view name` ``name`` + registered against the specified ``context`` and ``request`` + and unwind the view response's ``app_iter`` (see + :ref:`the_response`) into a single string. This function will + return ``None`` if a corresponding :term:`view callable` cannot be + found (when no :term:`view configuration` matches the combination + of ``name`` / ``context`` / and ``request``). Additionally, this + function will raise a :exc:`ValueError` if a view function is + found and called but the view function's result does not have an + ``app_iter`` attribute. This function will return ``None`` if a + corresponding view cannot be found. + + If ``secure`` is ``True``, and the view is protected by a + permission, the permission will be checked before the view is + invoked. If the permission check disallows view execution (based + on the current :term:`authorization policy`), a + :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised; + its ``args`` attribute explains why the view access was + disallowed. + + If ``secure`` is ``False``, no permission checking is done.""" + iterable = render_view_to_iterable(context, request, name, secure) + if iterable is None: + return None + return ''.join(iterable) + +def is_response(ob): + """ Return ``True`` if ``ob`` implements the interface implied by + :ref:`the_response`. ``False`` if not. + + .. note:: this isn't a true interface check (in Zope terms), it's a + duck-typing check, as response objects are not obligated to + actually implement a Zope interface.""" + # response objects aren't obligated to implement a Zope interface, + # so we do it the hard way + if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and + hasattr(ob, 'status') ): + if ( hasattr(ob.app_iter, '__iter__') and + hasattr(ob.headerlist, '__iter__') and + isinstance(ob.status, basestring) ) : + return True + return False + +class bfg_view(object): + """ A function, class or method :term:`decorator` which allows a + developer to create view registrations nearer to a :term:`view + callable` definition than use of :term:`ZCML` or :term:`imperative + configuration` to do the same. + + For example, this code in a module ``views.py``:: + + from models import MyModel + + @bfg_view(name='my_view', context=MyModel, permission='read', + route_name='site1') + def my_view(context, request): + return 'OK' + + Might replace the following call to the + :meth:`repoze.bfg.configuration.Configurator.add_view` method:: + + import views + import models + config.add_view(views.my_view, context=models.MyModel, name='my_view', + permission='read', 'route_name='site1') + + Or might replace the following ZCML ``view`` declaration:: + + + + The following arguments are supported as arguments to + ``bfg_view``: ``context``, ``permission``, ``name``, + ``request_type``, ``route_name``, ``request_method``, + ``request_param``, ``containment``, ``xhr``, ``accept``, + ``header`` and ``path_info``. + + ``context`` should be a Python object or :term:`dotted Python + name` representing the context type that must be found for this + view to be called. If ``context`` is not supplied, the interface + ``zope.interface.Interface`` (matching any context) is used. An + alias for ``context`` is ``for_``. + + If ``permission`` is not supplied, no permission is registered for + this view (it's accessible by any caller). + + If ``name`` is not supplied, the empty string is used (implying + the default view name). + + If ``attr`` is not supplied, ``None`` is used (implying the + function itself if the view is a function, or the ``__call__`` + callable attribute if the view is a class). + + If ``renderer`` is not supplied, ``None`` is used (meaning that no + renderer is associated with this view). + + If ``wrapper`` is not supplied, ``None`` is used (meaning that no + view wrapper is associated with this view). + + If ``request_type`` is not supplied, the interface + :class:`repoze.bfg.interfaces.IRequest` is used, implying the + standard request interface type. + + If ``route_name`` is not supplied, the view configuration is + considered to be made against a URL that doesn't match any defined + :term:`route`. The use of a ``route_name`` is an advanced + feature, useful only if you're also using :term:`url dispatch`. + + If ``request_method`` is not supplied, this view will match a + request with any HTTP ``REQUEST_METHOD`` + (GET/POST/PUT/HEAD/DELETE). If this parameter *is* supplied, it + must be a string naming an HTTP ``REQUEST_METHOD``, indicating + that this view will only match when the current request has a + ``REQUEST_METHOD`` that matches this value. + + If ``request_param`` is not supplied, this view will be called + when a request with any (or no) request GET or POST parameters is + encountered. If the value is present, it must be a string. If + the value supplied to the parameter has no ``=`` sign in it, it + implies that the key must exist in the ``request.params`` + dictionary for this view to 'match' the current request. If the value + supplied to the parameter has a ``=`` sign in it, e.g. + ``request_params="foo=123"``, then the key (``foo``) must both exist + in the ``request.params`` dictionary, and the value must match the + right hand side of the expression (``123``) for the view to "match" the + current request. + + ``containment`` should be a Python object or :term:`dotted Python + name` representing a class or interface type which must be found + as one of the context's location parents for this view to be + called. If ``containment`` is not supplied, this view will be + called when the context of the request has any (or no) + :term:`lineage`. If ``containment`` *is* supplied, it must be a + class or :term:`interface`, denoting that the view'matches' the + current request only if any graph :term:`lineage` node possesses + this class or interface. + + If ``xhr`` is specified, it must be a boolean value. If the value + is ``True``, the view will only be invoked if the request's + ``X-Requested-With`` header has the value ``XMLHttpRequest``. + + If ``accept`` is specified, it must be a mimetype value. If + ``accept`` is specified, the view will only be invoked if the + ``Accept`` HTTP header matches the value requested. See the + description of ``accept`` in :ref:`view_directive` for information + about the allowable composition and matching behavior of this + value. + + If ``header`` is specified, it must be a header name or a + ``headername:headervalue`` pair. If ``header`` is specified, and + possesses a value the view will only be invoked if an HTTP header + matches the value requested. If ``header`` is specified without a + value (a bare header name only), the view will only be invoked if + the HTTP header exists with any value in the request. See the + description of ``header`` in :ref:`view_directive` for information + about the allowable composition and matching behavior of this + value. + + If ``path_info`` is specified, it must be a regular + expression. The view will only be invoked if the ``PATH_INFO`` + WSGI environment variable matches the expression. + + If ``custom_predicates`` is specified, it must be a sequence of + :term:`predicate` callables (a predicate callable accepts two + arguments: ``context`` and ``request`` and returns ``True`` or + ``False``). The view will only be invoked if all custom + predicates return ``True``. + + Any individual or all parameters can be omitted. The simplest + ``bfg_view`` declaration is:: + + @bfg_view() + def my_view(...): + ... + + Such a registration implies that the view name will be + ``my_view``, registered for any :term:`context` object, using no + permission, registered against all non-URL-dispatch-based + requests, with any ``REQUEST_METHOD``, any set of request.params + values, without respect to any object in the :term:`lineage`. + + The ``bfg_view`` decorator can also be used as a class decorator + in Python 2.6 and better (Python 2.5 and below do not support + class decorators):: + + from webob import Response + from repoze.bfg.view import bfg_view + + @bfg_view() + class MyView(object): + def __init__(self, context, request): + self.context = context + self.request = request + def __call__(self): + return Response('hello from %s!' % self.context) + + In Python 2.5 and below, the ``bfg_view`` decorator can still be + used against a class, although not in decorator form:: + + from webob import Response + from repoze.bfg.view import bfg_view + + class MyView(object): + def __init__(self, context, request): + self.context = context + self.request = request + def __call__(self): + return Response('hello from %s!' % self.context) + + MyView = bfg_view()(MyView) + + .. note:: When a view is a class, the calling semantics are + different than when it is a function or another + non-class callable. See :ref:`class_as_view` for more + information. + + .. warning:: Using a class as a view is a new feature in 0.8.1+. + + The bfg_view decorator can also be used against a class method:: + + from webob import Response + from repoze.bfg.view import bfg_view + + class MyView(object): + def __init__(self, context, request): + self.context = context + self.request = request + + @bfg_view(name='hello') + def amethod(self): + return Response('hello from %s!' % self.context) + + When the ``bfg_view`` decorator is used against a class method, a + view is registered for the *class* (as described above), so the + class constructor must accept either ``request`` or ``context, + request``. The method which is decorated must return a response + (or rely on a :term:`renderer` to generate one). Using the + decorator against a particular method of a class is equivalent to + using the ``attr`` parameter in a decorator attached to the class + itself. For example, the above registration implied by the + decorator being used against the ``amethod`` method could be + spelled equivalently as:: + + from webob import Response + from repoze.bfg.view import bfg_view + + @bfg_view(attr='amethod', name='hello') + class MyView(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def amethod(self): + return Response('hello from %s!' % self.context) + + .. warning:: The ability to use the ``bfg_view`` decorator as a + method decorator is new in :mod:`repoze.bfg` version + 1.1. + + To make use of any ``bfg_view`` declaration, you must perform a + :term:`scan`. To do so, either insert the following boilerplate + into your application registry's ZCML:: + + + + See :ref:`scan_directive` for more information about the ZCML + ``scan`` directive. + + Or, if you don't use ZCML, use the + :meth:`repoze.bfg.configuration.Configurator.scan` method:: + + config.scan() + """ + venusian = venusian # for testing injection + def __init__(self, name='', request_type=None, for_=None, permission=None, + route_name=None, request_method=None, request_param=None, + containment=None, attr=None, renderer=None, wrapper=None, + xhr=False, accept=None, header=None, path_info=None, + custom_predicates=(), context=None): + self.name = name + self.request_type = request_type + self.context = context or for_ + self.permission = permission + self.route_name = route_name + self.request_method = request_method + self.request_param = request_param + self.containment = containment + self.attr = attr + self.renderer = renderer + self.wrapper = wrapper + self.xhr = xhr + self.accept = accept + self.header = header + self.path_info = path_info + self.custom_predicates = custom_predicates + + def __call__(self, wrapped): + settings = self.__dict__.copy() + + def callback(context, name, ob): + context.config.add_view(view=ob, **settings) + + info = self.venusian.attach(wrapped, callback, category='bfg') + + if info.scope == 'class': + # if the decorator was attached to a method in a class, or + # otherwise executed at class scope, we need to set an + # 'attr' into the settings if one isn't already in there + if settings['attr'] is None: + settings['attr'] = wrapped.__name__ + + # try to convert the renderer provided into a fully qualified + # resource specification + abspath = settings.get('renderer') + if abspath is not None and '.' in abspath: + isabs = os.path.isabs(abspath) + if not (':' in abspath and not isabs): + # not already a resource spec + if not isabs: + pp = package_path(info.module) + abspath = os.path.join(pp, abspath) + resource = resource_spec_from_abspath(abspath, info.module) + settings['renderer'] = resource + + return wrapped + +def default_exceptionresponse_view(context, request): + if not isinstance(context, Exception): + # backwards compat for an exception response view registered via + # config.set_notfound_view or config.set_forbidden_view + # instead of as a proper exception view + context = request.exception or context + return context + +class AppendSlashNotFoundViewFactory(object): + """ There can only be one :term:`Not Found view` in any + :mod:`repoze.bfg` application. Even if you use + :func:`repoze.bfg.view.append_slash_notfound_view` as the Not + Found view, :mod:`repoze.bfg` still must generate a ``404 Not + Found`` response when it cannot redirect to a slash-appended URL; + this not found response will be visible to site users. + + If you don't care what this 404 response looks like, and you only + need redirections to slash-appended route URLs, you may use the + :func:`repoze.bfg.view.append_slash_notfound_view` object as the + Not Found view. However, if you wish to use a *custom* notfound + view callable when a URL cannot be redirected to a slash-appended + URL, you may wish to use an instance of this class as the Not + Found view, supplying a :term:`view callable` to be used as the + custom notfound view as the first argument to its constructor. + For instance: + + .. code-block:: python + + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import AppendSlashNotFoundViewFactory + + def notfound_view(context, request): + return HTTPNotFound('It aint there, stop trying!') + + custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) + config.add_view(custom_append_slash, context=NotFound) + + The ``notfound_view`` supplied must adhere to the two-argument + view callable calling convention of ``(context, request)`` + (``context`` will be the exception object). + + .. note:: This class is new as of :mod:`repoze.bfg` version 1.3. + + """ + def __init__(self, notfound_view=None): + if notfound_view is None: + notfound_view = default_exceptionresponse_view + self.notfound_view = notfound_view + + def __call__(self, context, request): + if not isinstance(context, Exception): + # backwards compat for an append_notslash_view registered via + # config.set_notfound_view instead of as a proper exception view + context = request.exception + path = request.environ.get('PATH_INFO', '/') + registry = request.registry + mapper = registry.queryUtility(IRoutesMapper) + if mapper is not None and not path.endswith('/'): + slashpath = path + '/' + for route in mapper.get_routes(): + if route.match(slashpath) is not None: + return HTTPFound(location=slashpath) + return self.notfound_view(context, request) + +append_slash_notfound_view = AppendSlashNotFoundViewFactory() +append_slash_notfound_view.__doc__ = """\ +For behavior like Django's ``APPEND_SLASH=True``, use this view as the +:term:`Not Found view` in your application. + +When this view is the Not Found view (indicating that no view was +found), and any routes have been defined in the configuration of your +application, if the value of the ``PATH_INFO`` WSGI environment +variable does not already end in a slash, and if the value of +``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP +redirect to the slash-appended PATH_INFO. Note that this will *lose* +``POST`` data information (turning it into a GET), so you shouldn't +rely on this to redirect POST requests. + +If you use :term:`ZCML`, add the following to your application's +``configure.zcml`` to use this view as the Not Found view:: + + + +Or use the +:meth:`repoze.bfg.configuration.Configurator.add_view` +method if you don't use ZCML:: + + from repoze.bfg.exceptions import NotFound + from repoze.bfg.view import append_slash_notfound_view + config.add_view(append_slash_notfound_view, context=NotFound) + +See also :ref:`changing_the_notfound_view`. + +.. note:: This function is new as of :mod:`repoze.bfg` version 1.1. +""" + + + diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py new file mode 100644 index 000000000..c576253b3 --- /dev/null +++ b/pyramid/wsgi.py @@ -0,0 +1,97 @@ +from repoze.bfg.compat import wraps +from repoze.bfg.traversal import quote_path_segment + +def wsgiapp(wrapped): + """ Decorator to turn a WSGI application into a :mod:`repoze.bfg` + :term:`view callable`. This decorator differs from the + :func:`repoze.bfg.wsgi.wsgiapp2` decorator inasmuch as fixups of + ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment *are + not* performed before the application is invoked. + + E.g., the following in a ``views.py`` module:: + + @wsgiapp + def hello_world(environ, start_response): + body = 'Hello world' + start_response('200 OK', [ ('Content-Type', 'text/plain'), + ('Content-Length', len(body)) ] ) + return [body] + + Allows the following ZCML view declaration to be made:: + + + + Or the following call to + :meth:`repoze.bfg.configuration.Configurator.add_view`:: + + from views import hello_world + config.add_view(hello_world, name='hello_world.txt') + + The ``wsgiapp`` decorator will convert the result of the WSGI + application to a :term:`Response` and return it to + :mod:`repoze.bfg` as if the WSGI app were a :mod:`repoze.bfg` + view. + + """ + def decorator(context, request): + return request.get_response(wrapped) + return wraps(wrapped)(decorator) # grokkability + +def wsgiapp2(wrapped): + """ Decorator to turn a WSGI application into a :mod:`repoze.bfg` + view callable. This decorator differs from the + :func:`repoze.bfg.wsgi.wsgiapp` decorator inasmuch as fixups of + ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment + *are* performed before the application is invoked. + + E.g. the following in a ``views.py`` module:: + + @wsgiapp2 + def hello_world(environ, start_response): + body = 'Hello world' + start_response('200 OK', [ ('Content-Type', 'text/plain'), + ('Content-Length', len(body)) ] ) + return [body] + + Allows the following ZCML view declaration to be made:: + + + + Or the following call to + :meth:`repoze.bfg.configuration.Configurator.add_view`:: + + from views import hello_world + config.add_view(hello_world, name='hello_world.txt') + + The ``wsgiapp2`` decorator will convert the result of the WSGI + application to a Response and return it to :mod:`repoze.bfg` as if + the WSGI app were a :mod:`repoze.bfg` view. The ``SCRIPT_NAME`` + and ``PATH_INFO`` values present in the WSGI environment are fixed + up before the application is invoked. """ + + def decorator(context, request): + traversed = request.traversed + vroot_path = request.virtual_root_path or () + view_name = request.view_name + subpath = request.subpath or () + script_tuple = traversed[len(vroot_path):] + script_list = [ quote_path_segment(name) for name in script_tuple ] + if view_name: + script_list.append(quote_path_segment(view_name)) + script_name = '/' + '/'.join(script_list) + path_list = [ quote_path_segment(name) for name in subpath ] + path_info = '/' + '/'.join(path_list) + request.environ['PATH_INFO'] = path_info + script_name = request.environ['SCRIPT_NAME'] + script_name + if script_name.endswith('/'): + script_name = script_name[:-1] + request.environ['SCRIPT_NAME'] = script_name + return request.get_response(wrapped) + return wraps(wrapped)(decorator) # grokkability + diff --git a/pyramid/zcml.py b/pyramid/zcml.py new file mode 100644 index 000000000..2bf394eb8 --- /dev/null +++ b/pyramid/zcml.py @@ -0,0 +1,903 @@ +import os + +from zope.configuration import xmlconfig +from zope.configuration.config import ConfigurationMachine +from zope.configuration.fields import GlobalInterface +from zope.configuration.fields import GlobalObject +from zope.configuration.fields import Tokens + +from zope.interface.interfaces import IInterface +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface import providedBy + +from zope.schema import ASCIILine +from zope.schema import Bool +from zope.schema import Int +from zope.schema import TextLine + +from repoze.bfg.interfaces import IAuthenticationPolicy +from repoze.bfg.interfaces import IAuthorizationPolicy +from repoze.bfg.interfaces import IDefaultPermission +from repoze.bfg.interfaces import IRendererFactory +from repoze.bfg.interfaces import IRouteRequest +from repoze.bfg.interfaces import IView + +from repoze.bfg.authentication import AuthTktAuthenticationPolicy +from repoze.bfg.authentication import RemoteUserAuthenticationPolicy +from repoze.bfg.authentication import RepozeWho1AuthenticationPolicy +from repoze.bfg.authorization import ACLAuthorizationPolicy +from repoze.bfg.configuration import Configurator +from repoze.bfg.exceptions import ConfigurationError +from repoze.bfg.exceptions import NotFound +from repoze.bfg.exceptions import Forbidden +from repoze.bfg.request import route_request_iface +from repoze.bfg.resource import resource_spec_from_abspath +from repoze.bfg.static import StaticURLInfo +from repoze.bfg.threadlocal import get_current_registry + +###################### directives ########################## + +class IViewDirective(Interface): + context = GlobalObject( + title=u"The interface or class this view is for.", + required=False + ) + + for_ = GlobalObject( + title=(u"The interface or class this view is for (alternate spelling " + "of ``context``)."), + required=False + ) + + permission = TextLine( + title=u"Permission", + description=u"The permission needed to use the view.", + required=False + ) + + view = GlobalObject( + title=u"", + description=u"The view function", + required=False, + ) + + name = TextLine( + title=u"The name of the view", + description=u""" + The name shows up in URLs/paths. For example 'foo' or 'foo.html'.""", + required=False, + ) + + attr = TextLine( + title=u'The callable attribute of the view object(default is __call__)', + description=u'', + required=False) + + renderer = TextLine( + title=u'The renderer asssociated with the view', + description=u'', + required=False) + + wrapper = TextLine( + title = u'The *name* of the view that acts as a wrapper for this view.', + description = u'', + required=False) + + request_type = TextLine( + title=u"The request type string or dotted name interface for the view", + description=(u"The view will be called if the interface represented by " + u"'request_type' is implemented by the request. The " + u"default request type is repoze.bfg.interfaces.IRequest"), + required=False + ) + + route_name = TextLine( + title = u'The route that must match for this view to be used', + required = False) + + containment = GlobalObject( + title = u'Dotted name of a containment class or interface', + required=False) + + request_method = TextLine( + title = u'Request method name that must be matched (e.g. GET/POST)', + description = (u'The view will be called if and only if the request ' + 'method (``request.method``) matches this string. This' + 'functionality replaces the older ``request_type`` ' + 'functionality.'), + required=False) + + request_param = TextLine( + title = (u'Request parameter name that must exist in ' + '``request.params`` for this view to match'), + description = (u'The view will be called if and only if the request ' + 'parameter exists which matches this string.'), + required=False) + + xhr = Bool( + title = (u'True if request has an X-Requested-With header with the ' + 'value "XMLHttpRequest"'), + description=(u'Useful for detecting AJAX requests issued from ' + 'jQuery, Protoype and other JavaScript libraries'), + required=False) + + accept = TextLine( + title = (u'Mimetype(s) that must be present in "Accept" HTTP header ' + 'for the view to match a request'), + description=(u'Accepts a mimetype match token in the form ' + '"text/plain", a wildcard mimetype match token in the ' + 'form "text/*" or a match-all wildcard mimetype match ' + 'token in the form "*/*".'), + required = False) + + header = TextLine( + title=u'Header name/value pair in the form "name="', + description=u'Regular expression matching for header values', + required = False) + + path_info = TextLine( + title = (u'Regular expression which must match the ``PATH_INFO`` ' + 'header for the view to match a request'), + description=(u'Accepts a regular expression.'), + required = False) + + custom_predicates = Tokens( + title=u"One or more custom dotted names to custom predicate callables", + description=(u"A list of dotted name references to callables that " + "will be used as predicates for this view configuration"), + required=False, + value_type=GlobalObject() + ) + + +def view( + _context, + permission=None, + for_=None, + view=None, + name="", + request_type=None, + route_name=None, + request_method=None, + request_param=None, + containment=None, + attr=None, + renderer=None, + wrapper=None, + xhr=False, + accept=None, + header=None, + path_info=None, + traverse=None, + custom_predicates=(), + context=None, + cacheable=True, # not used, here for b/w compat < 0.8 + ): + + reg = get_current_registry() + + if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'): + # b/w compat for 1.0 + request_method = request_type + request_type = None + + if request_type is not None: + request_type = _context.resolve(request_type) + if not IInterface.providedBy(request_type): + raise ConfigurationError( + 'request_type must be an interface, not %s' % request_type) + + if renderer and '.' in renderer: + renderer = path_spec(_context, renderer) + + context = context or for_ + + def register(): + config = Configurator(reg, package=_context.package) + config.add_view( + permission=permission, context=context, view=view, name=name, + request_type=request_type, route_name=route_name, + request_method=request_method, request_param=request_param, + containment=containment, attr=attr, renderer=renderer, + wrapper=wrapper, xhr=xhr, accept=accept, header=header, + path_info=path_info, custom_predicates=custom_predicates, + _info=_context.info) + + discriminator = ['view', context, name, request_type, IView, containment, + request_param, request_method, route_name, attr, + xhr, accept, header, path_info] + + discriminator.extend(sorted(custom_predicates)) + discriminator = tuple(discriminator) + + _context.action( + discriminator = discriminator, + callable = register, + ) + +_view = view # for directives that take a view arg + +class IRouteDirective(Interface): + """ The interface for the ``route`` ZCML directive + """ + name = TextLine(title=u'name', required=True) + pattern = TextLine(title=u'pattern', required=False) + # alias for pattern + path = TextLine(title=u'path', required=False) + factory = GlobalObject(title=u'context factory', required=False) + view = GlobalObject(title=u'view', required=False) + + view_context = GlobalObject(title=u'view_context', required=False) + # aliases for view_context + for_ = GlobalObject(title=u'for', required=False) + view_for = GlobalObject(title=u'view_for', required=False) + + view_permission = TextLine(title=u'view_permission', required=False) + # alias for view_permission + permission = TextLine(title=u'permission', required=False) + + view_renderer = TextLine(title=u'view_renderer', required=False) + # alias for view_renderer + renderer = TextLine(title=u'renderer', required=False) + + view_attr = TextLine(title=u'view_attr', required=False) + + request_method = TextLine(title=u'request_method', required=False) + request_param = TextLine(title=u'request_param', required=False) + header = TextLine(title=u'header', required=False) + accept = TextLine(title=u'accept', required=False) + xhr = Bool(title=u'xhr', required=False) + path_info = TextLine(title=u'path_info', required=False) + + traverse = TextLine( + title=u'Traverse pattern"', + description=u'A pattern which will compose a traversal path', + required = False) + + custom_predicates = Tokens( + title=u"One or more custom dotted names to custom predicate callables", + description=(u"A list of dotted name references to callables that " + "will be used as predicates for this view configuration"), + required=False, + value_type=GlobalObject() + ) + use_global_views = Bool(title=u'use_global_views', required=False) + +def route(_context, + name, + pattern=None, + view=None, + view_for=None, + permission=None, + factory=None, + for_=None, + header=None, + xhr=False, + accept=None, + path_info=None, + request_method=None, + request_param=None, + custom_predicates=(), + view_permission=None, + view_attr=None, + renderer=None, + view_renderer=None, + view_context=None, + traverse=None, + use_global_views=False, + path=None): + """ Handle ``route`` ZCML directives + """ + # the strange ordering of the request kw args above is for b/w + # compatibility purposes. + + # these are route predicates; if they do not match, the next route + # in the routelist will be tried + reg = get_current_registry() + + if view_context is None: + view_context = view_for or for_ + + view_permission = view_permission or permission + view_renderer = view_renderer or renderer + if view_renderer and '.' in view_renderer: + view_renderer = path_spec(_context, view_renderer) + + if pattern is None: + pattern = path + + if pattern is None: + raise ConfigurationError('route directive must include a "pattern"') + + def register(): + config = Configurator(reg, package=_context.package) + config.add_route( + name, + pattern, + factory=factory, + header=header, + xhr=xhr, + accept=accept, + path_info=path_info, + request_method=request_method, + request_param=request_param, + custom_predicates=custom_predicates, + view=view, + view_context=view_context, + view_permission=view_permission, + view_renderer=view_renderer, + view_attr=view_attr, + use_global_views=use_global_views, + traverse=traverse, + _info=_context.info + ) + + discriminator = ['route', name, xhr, request_method, path_info, + request_param, header, accept] + discriminator.extend(sorted(custom_predicates)) + discriminator = tuple(discriminator) + + _context.action( + discriminator=discriminator, + callable = register, + ) + + if view: + request_iface = reg.queryUtility(IRouteRequest, name=name) + if request_iface is None: + request_iface = route_request_iface(name) + reg.registerUtility(request_iface, IRouteRequest, name=name) + _context.action( + discriminator = ( + 'view', view_context, '', None, IView, name, view_attr), + ) + +class ISystemViewDirective(Interface): + view = GlobalObject( + title=u"", + description=u"The view function", + required=False, + ) + + attr = TextLine( + title=u'The callable attribute of the view object(default is __call__)', + description=u'', + required=False) + + renderer = TextLine( + title=u'The renderer asssociated with the view', + description=u'', + required=False) + + wrapper = TextLine( + title = u'The *name* of the view that acts as a wrapper for this view.', + description = u'', + required=False) + +def notfound(_context, + view=None, + attr=None, + renderer=None, + wrapper=None): + + if renderer and '.' in renderer: + renderer = path_spec(_context, renderer) + + def register(): + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config.set_notfound_view(view=view, attr=attr, renderer=renderer, + wrapper=wrapper, _info=_context.info) + + discriminator = ('view', NotFound, '', None, IView, None, None, None, + None, attr, False, None, None, None) + + _context.action( + discriminator = discriminator, + callable = register, + ) + +def forbidden(_context, + view=None, + attr=None, + renderer=None, + wrapper=None): + + if renderer and '.' in renderer: + renderer = path_spec(_context, renderer) + + def register(): + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config.set_forbidden_view(view=view, attr=attr, renderer=renderer, + wrapper=wrapper, _info=_context.info) + + discriminator = ('view', Forbidden, '', None, IView, None, None, None, + None, attr, False, None, None, None) + + _context.action( + discriminator = discriminator, + callable = register, + ) + +class IResourceDirective(Interface): + """ + Directive for specifying that one package may override resources from + another package. + """ + to_override = TextLine( + title=u"Override spec", + description=u'The spec of the resource to override.', + required=True) + override_with = TextLine( + title=u"With spec", + description=u"The spec of the resource providing the override.", + required=True) + +def resource(_context, to_override, override_with): + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + + _context.action( + discriminator = None, + callable = config.override_resource, + args = (to_override, override_with, _context.info), + ) + +class IRepozeWho1AuthenticationPolicyDirective(Interface): + identifier_name = TextLine(title=u'identitfier_name', required=False, + default=u'auth_tkt') + callback = GlobalObject(title=u'callback', required=False) + +def repozewho1authenticationpolicy(_context, identifier_name='auth_tkt', + callback=None): + policy = RepozeWho1AuthenticationPolicy(identifier_name=identifier_name, + callback=callback) + # authentication policies must be registered eagerly so they can + # be found by the view registration machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config._set_authentication_policy(policy, _info=_context.info) + _context.action(discriminator=IAuthenticationPolicy) + +class IRemoteUserAuthenticationPolicyDirective(Interface): + environ_key = TextLine(title=u'environ_key', required=False, + default=u'REMOTE_USER') + callback = GlobalObject(title=u'callback', required=False) + +def remoteuserauthenticationpolicy(_context, environ_key='REMOTE_USER', + callback=None): + policy = RemoteUserAuthenticationPolicy(environ_key=environ_key, + callback=callback) + # authentication policies must be registered eagerly so they can + # be found by the view registration machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config._set_authentication_policy(policy, _info=_context.info) + _context.action(discriminator=IAuthenticationPolicy) + +class IAuthTktAuthenticationPolicyDirective(Interface): + secret = TextLine(title=u'secret', required=True) + callback = GlobalObject(title=u'callback', required=False) + cookie_name = ASCIILine(title=u'cookie_name', required=False, + default='repoze.bfg.auth_tkt') + secure = Bool(title=u"secure", required=False, default=False) + include_ip = Bool(title=u"include_ip", required=False, default=False) + timeout = Int(title=u"timeout", required=False, default=None) + reissue_time = Int(title=u"reissue_time", required=False, default=None) + max_age = Int(title=u"max_age", required=False, default=None) + path = ASCIILine(title=u"path", required=False, default='/') + http_only = Bool(title=u"http_only", required=False, default=False) + +def authtktauthenticationpolicy(_context, + secret, + callback=None, + cookie_name='repoze.bfg.auth_tkt', + secure=False, + include_ip=False, + timeout=None, + reissue_time=None, + max_age=None, + http_only=False, + path='/'): + try: + policy = AuthTktAuthenticationPolicy(secret, + callback=callback, + cookie_name=cookie_name, + secure=secure, + include_ip = include_ip, + timeout = timeout, + reissue_time = reissue_time, + max_age=max_age, + http_only=http_only, + path=path) + except ValueError, why: + raise ConfigurationError(str(why)) + # authentication policies must be registered eagerly so they can + # be found by the view registration machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config._set_authentication_policy(policy, _info=_context.info) + _context.action(discriminator=IAuthenticationPolicy) + +class IACLAuthorizationPolicyDirective(Interface): + pass + +def aclauthorizationpolicy(_context): + policy = ACLAuthorizationPolicy() + # authorization policies must be registered eagerly so they can be + # found by the view registration machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config._set_authorization_policy(policy, _info=_context.info) + _context.action(discriminator=IAuthorizationPolicy) + +class IRendererDirective(Interface): + factory = GlobalObject( + title=u'IRendererFactory implementation', + required=True) + + name = TextLine( + title=u'Token (e.g. ``json``) or filename extension (e.g. ".pt")', + required=False) + +def renderer(_context, factory, name=''): + # renderer factories must be registered eagerly so they can be + # found by the view machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config.add_renderer(name, factory, _info=_context.info) + _context.action(discriminator=(IRendererFactory, name)) + +class IStaticDirective(Interface): + name = TextLine( + title=u"The URL prefix of the static view", + description=u""" + The directory will be served up for the route that starts with + this prefix.""", + required=True) + + path = TextLine( + title=u'Path to the directory which contains resources', + description=u'May be package-relative by using a colon to ' + 'seperate package name and path relative to the package directory.', + required=True) + + cache_max_age = Int( + title=u"Cache maximum age in seconds", + required=False, + default=None) + +def static(_context, name, path, cache_max_age=3600): + """ Handle ``static`` ZCML directives + """ + path = path_spec(_context, path) + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + + _context.action( + discriminator=('static', name), + callable=config.add_static_view, + args = (name, path), + kw = {'cache_max_age':cache_max_age, '_info':_context.info}, + ) + + if not '/' in name: + _context.action( + discriminator = ( + 'view', StaticURLInfo, '', None, IView, None, None, None, + name, None, None, None, None, None, + ) + ) + +class IScanDirective(Interface): + package = GlobalObject( + title=u"The package we'd like to scan.", + required=True, + ) + +def scan(_context, package): + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + _context.action( + discriminator=None, + callable=config.scan, + args=(package, None, _context.info) + ) + +class ITranslationDirDirective(Interface): + dir = TextLine( + title=u"Add a translation directory", + description=(u"Add a translation directory"), + required=True, + ) + +def translationdir(_context, dir): + path = path_spec(_context, dir) + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + + _context.action( + discriminator = ('tdir', path), + callable=config.add_translation_dirs, + args = (dir,), + ) + +class ILocaleNegotiatorDirective(Interface): + negotiator = GlobalObject( + title=u"Configure a locale negotiator", + description=(u'Configure a locale negotiator'), + required=True, + ) + +def localenegotiator(_context, negotiator): + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + + _context.action( + discriminator = 'lnegotiator', + callable=config.set_locale_negotiator, + args = (negotiator,) + ) + +class IAdapterDirective(Interface): + """ + Register an adapter + """ + + factory = Tokens( + title=u"Adapter factory/factories", + description=(u"A list of factories (usually just one) that create" + " the adapter instance."), + required=True, + value_type=GlobalObject() + ) + + provides = GlobalInterface( + title=u"Interface the component provides", + description=(u"This attribute specifies the interface the adapter" + " instance must provide."), + required=False, + ) + + for_ = Tokens( + title=u"Specifications to be adapted", + description=u"This should be a list of interfaces or classes", + required=False, + value_type=GlobalObject( + missing_value=object(), + ), + ) + + name = TextLine( + title=u"Name", + description=(u"Adapters can have names.\n\n" + "This attribute allows you to specify the name for" + " this adapter."), + required=False, + ) + +def adapter(_context, factory, provides=None, for_=None, name=''): + if for_ is None: + if len(factory) == 1: + for_ = getattr(factory[0], '__component_adapts__', None) + + if for_ is None: + raise TypeError("No for argument was provided and can't " + "determine what the factory adapts.") + + for_ = tuple(for_) + + if provides is None: + if len(factory) == 1: + p = list(implementedBy(factory[0])) + if len(p) == 1: + provides = p[0] + + if provides is None: + raise TypeError("Missing 'provided' argument") + + # Generate a single factory from multiple factories: + factories = factory + if len(factories) == 1: + factory = factories[0] + elif len(factories) < 1: + raise ValueError("No factory specified") + elif len(factories) > 1 and len(for_) != 1: + raise ValueError("Can't use multiple factories and multiple " + "for") + else: + factory = _rolledUpFactory(factories) + + registry = get_current_registry() + _context.action( + discriminator = ('adapter', for_, provides, name), + callable = registry.registerAdapter, + args = (factory, for_, provides, name, _context.info), + ) + +class ISubscriberDirective(Interface): + """ + Register a subscriber + """ + + factory = GlobalObject( + title=u"Subscriber factory", + description=u"A factory used to create the subscriber instance.", + required=False, + ) + + handler = GlobalObject( + title=u"Handler", + description=u"A callable object that handles events.", + required=False, + ) + + provides = GlobalInterface( + title=u"Interface the component provides", + description=(u"This attribute specifies the interface the adapter" + " instance must provide."), + required=False, + ) + + for_ = Tokens( + title=u"Interfaces or classes that this subscriber depends on", + description=u"This should be a list of interfaces or classes", + required=False, + value_type=GlobalObject( + missing_value = object(), + ), + ) + +def subscriber(_context, for_=None, factory=None, handler=None, provides=None): + if factory is None: + if handler is None: + raise TypeError("No factory or handler provided") + if provides is not None: + raise TypeError("Cannot use handler with provides") + factory = handler + else: + if handler is not None: + raise TypeError("Cannot use handler with factory") + if provides is None: + raise TypeError( + "You must specify a provided interface when registering " + "a factory") + + if for_ is None: + for_ = getattr(factory, '__component_adapts__', None) + if for_ is None: + raise TypeError("No for attribute was provided and can't " + "determine what the factory (or handler) adapts.") + + for_ = tuple(for_) + + registry = get_current_registry() + config = Configurator(registry=registry, package=_context.package) + + if handler is not None: + _context.action( + discriminator = None, + callable = config.add_subscriber, + args = (handler, for_, _context.info), + ) + else: + _context.action( + discriminator = None, + callable = registry.registerSubscriptionAdapter, + args = (factory, for_, provides, None, _context.info), + ) + +class IUtilityDirective(Interface): + """Register a utility.""" + + component = GlobalObject( + title=u"Component to use", + description=(u"Python name of the implementation object. This" + " must identify an object in a module using the" + " full dotted name. If specified, the" + " ``factory`` field must be left blank."), + required=False, + ) + + factory = GlobalObject( + title=u"Factory", + description=(u"Python name of a factory which can create the" + " implementation object. This must identify an" + " object in a module using the full dotted name." + " If specified, the ``component`` field must" + " be left blank."), + required=False, + ) + + provides = GlobalInterface( + title=u"Provided interface", + description=u"Interface provided by the utility.", + required=False, + ) + + name = TextLine( + title=u"Name", + description=(u"Name of the registration. This is used by" + " application code when locating a utility."), + required=False, + ) + +def utility(_context, provides=None, component=None, factory=None, name=''): + if factory and component: + raise TypeError("Can't specify factory and component.") + + if provides is None: + if factory: + provides = list(implementedBy(factory)) + else: + provides = list(providedBy(component)) + if len(provides) == 1: + provides = provides[0] + else: + raise TypeError("Missing 'provides' attribute") + + if factory: + kw = dict(factory=factory) + else: + # older component registries don't accept factory as a kwarg, + # so if we don't need it, we don't pass it + kw = {} + + registry = get_current_registry() + _context.action( + discriminator = ('utility', provides, name), + callable = registry.registerUtility, + args = (component, provides, name, _context.info), + kw = kw, + ) + +class IDefaultPermissionDirective(Interface): + name = TextLine(title=u'name', required=True) + +def default_permission(_context, name): + """ Register a default permission name """ + # the default permission must be registered eagerly so it can + # be found by the view registration machinery + reg = get_current_registry() + config = Configurator(reg, package=_context.package) + config.set_default_permission(name) + _context.action(discriminator=IDefaultPermission) + +def path_spec(context, path): + # we prefer registering resource specifications over absolute + # paths because these can be overridden by the resource directive. + if ':' in path and not os.path.isabs(path): + # it's already a resource specification + return path + abspath = context.path(path) + if hasattr(context, 'package') and context.package: + return resource_spec_from_abspath(abspath, context.package) + return abspath + +def zcml_configure(name, package): + """ Given a ZCML filename as ``name`` and a Python package as + ``package`` which the filename should be relative to, load the + ZCML into the current ZCML registry. + + .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. + """ + context = ConfigurationMachine() + xmlconfig.registerCommonDirectives(context) + context.package = package + xmlconfig.include(context, name, package) + context.execute_actions(clear=False) # the raison d'etre + return context.actions + +file_configure = zcml_configure # backwards compat (>0.8.1) + +def _rolledUpFactory(factories): + def factory(ob): + for f in factories: + ob = f(ob) + return ob + # Store the original factory for documentation + factory.factory = factories[0] + return factory + diff --git a/repoze/bfg/__init__.py b/repoze/bfg/__init__.py deleted file mode 100644 index f33bdbc35..000000000 --- a/repoze/bfg/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__('pkg_resources').declare_namespace(__name__) #pragma NO COVERAGE diff --git a/repoze/bfg/authentication.py b/repoze/bfg/authentication.py deleted file mode 100644 index 54e106af5..000000000 --- a/repoze/bfg/authentication.py +++ /dev/null @@ -1,441 +0,0 @@ -from codecs import utf_8_decode -from codecs import utf_8_encode -import datetime -import time - -from paste.auth import auth_tkt -from paste.request import get_cookies - -from zope.interface import implements - -from repoze.bfg.interfaces import IAuthenticationPolicy - -from repoze.bfg.request import add_global_response_headers -from repoze.bfg.security import Authenticated -from repoze.bfg.security import Everyone - -class CallbackAuthenticationPolicy(object): - """ Abstract class """ - def authenticated_userid(self, request): - userid = self._get_userid(request) - if userid is None: - return None - if self.callback is None: - return userid - if self.callback(userid, request) is not None: # is not None! - return userid - - def effective_principals(self, request): - effective_principals = [Everyone] - userid = self._get_userid(request) - if userid is None: - return effective_principals - if self.callback is None: - groups = [] - else: - groups = self.callback(userid, request) - if groups is None: # is None! - return effective_principals - effective_principals.append(Authenticated) - effective_principals.append(userid) - effective_principals.extend(groups) - - return effective_principals - - -class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): - """ A :mod:`repoze.bfg` :term:`authentication policy` which - obtains data from the :mod:`repoze.who` 1.X WSGI 'API' (the - ``repoze.who.identity`` key in the WSGI environment). - - Constructor Arguments - - ``identifier_name`` - - Default: ``auth_tkt``. The :mod:`repoze.who` plugin name that - performs remember/forget. Optional. - - ``callback`` - - Default: ``None``. A callback passed the :mod:`repoze.who` - identity and the :term:`request`, expected to return ``None`` - if the user represented by the identity doesn't exist or a - sequence of group identifiers (possibly empty) if the user - does exist. If ``callback`` is None, the userid will be - assumed to exist with no groups. - """ - implements(IAuthenticationPolicy) - - def __init__(self, identifier_name='auth_tkt', callback=None): - self.identifier_name = identifier_name - self.callback = callback - - def _get_identity(self, request): - return request.environ.get('repoze.who.identity') - - def _get_identifier(self, request): - plugins = request.environ.get('repoze.who.plugins') - if plugins is None: - return None - identifier = plugins[self.identifier_name] - return identifier - - def authenticated_userid(self, request): - identity = self._get_identity(request) - if identity is None: - return None - if self.callback is None: - return identity['repoze.who.userid'] - if self.callback(identity, request) is not None: # is not None! - return identity['repoze.who.userid'] - - def effective_principals(self, request): - effective_principals = [Everyone] - identity = self._get_identity(request) - if identity is None: - return effective_principals - if self.callback is None: - groups = [] - else: - groups = self.callback(identity, request) - if groups is None: # is None! - return effective_principals - userid = identity['repoze.who.userid'] - effective_principals.append(Authenticated) - effective_principals.append(userid) - effective_principals.extend(groups) - - return effective_principals - - def remember(self, request, principal, **kw): - identifier = self._get_identifier(request) - if identifier is None: - return [] - environ = request.environ - identity = {'repoze.who.userid':principal} - return identifier.remember(environ, identity) - - def forget(self, request): - identifier = self._get_identifier(request) - if identifier is None: - return [] - identity = self._get_identity(request) - return identifier.forget(request.environ, identity) - -class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy): - """ A :mod:`repoze.bfg` :term:`authentication policy` which - obtains data from the ``REMOTE_USER`` WSGI environment variable. - - Constructor Arguments - - ``environ_key`` - - Default: ``REMOTE_USER``. The key in the WSGI environ which - provides the userid. - - ``callback`` - - Default: ``None``. A callback passed the userid and the request, - expected to return None if the userid doesn't exist or a sequence - of group identifiers (possibly empty) if the user does exist. - If ``callback`` is None, the userid will be assumed to exist with no - groups. - """ - implements(IAuthenticationPolicy) - - def __init__(self, environ_key='REMOTE_USER', callback=None): - self.environ_key = environ_key - self.callback = callback - - def _get_userid(self, request): - return request.environ.get(self.environ_key) - - def remember(self, request, principal, **kw): - return [] - - def forget(self, request): - return [] - -class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): - """ A :mod:`repoze.bfg` :term:`authentication policy` which - obtains data from an :class:`paste.auth.auth_tkt` cookie. - - Constructor Arguments - - ``secret`` - - The secret (a string) used for auth_tkt cookie signing. - Required. - - ``callback`` - - Default: ``None``. A callback passed the userid and the - request, expected to return ``None`` if the userid doesn't - exist or a sequence of group identifiers (possibly empty) if - the user does exist. If ``callback`` is ``None``, the userid - will be assumed to exist with no groups. Optional. - - ``cookie_name`` - - Default: ``repoze.bfg.auth_tkt``. The cookie name used - (string). Optional. - - ``secure`` - - Default: ``False``. Only send the cookie back over a secure - conn. Optional. - - ``include_ip`` - - Default: ``False``. Make the requesting IP address part of - the authentication data in the cookie. Optional. - - ``timeout`` - - Default: ``None``. Maximum number of seconds which a newly - issued ticket will be considered valid. After this amount of - time, the ticket will expire (effectively logging the user - out). If this value is ``None``, the ticket never expires. - Optional. - - ``reissue_time`` - - Default: ``None``. If this parameter is set, it represents the - number of seconds that must pass before an authentication token - cookie is reissued. The duration is measured as the number of - seconds since the last auth_tkt cookie was issued and 'now'. - If the ``timeout`` value is ``None``, this parameter has no - effect. If this parameter is provided, and the value of - ``timeout`` is not ``None``, the value of ``reissue_time`` must - be smaller than value of ``timeout``. A good rule of thumb: if - you want auto-reissued cookies: set this to the ``timeout`` - value divided by ten. If this value is ``0``, a new ticket - cookie will be reissued on every request which needs - authentication. Optional. - - ``max_age`` - - Default: ``None``. The max age of the auth_tkt cookie, in - seconds. This differs from ``timeout`` inasmuch as ``timeout`` - represents the lifetime of the ticket contained in the cookie, - while this value represents the lifetime of the cookie itself. - When this value is set, the cookie's ``Max-Age`` and - ``Expires`` settings will be set, allowing the auth_tkt cookie - to last between browser sessions. It is typically nonsensical - to set this to a value that is lower than ``timeout`` or - ``reissue_time``, although it is not explicitly prevented. - Optional. - - ``path`` - - Default: ``/``. The path for which the auth_tkt cookie is valid. - May be desirable if the application only serves part of a domain. - Optional. - - ``http_only`` - - Default: ``False``. Hide cookie from JavaScript by setting the - HttpOnly flag. Not honored by all browsers. - Optional. - """ - implements(IAuthenticationPolicy) - def __init__(self, - secret, - callback=None, - cookie_name='repoze.bfg.auth_tkt', - secure=False, - include_ip=False, - timeout=None, - reissue_time=None, - max_age=None, - path="/", - http_only=False, - ): - self.cookie = AuthTktCookieHelper( - secret, - cookie_name=cookie_name, - secure=secure, - include_ip=include_ip, - timeout=timeout, - reissue_time=reissue_time, - max_age=max_age, - http_only=http_only, - path=path, - ) - self.callback = callback - - def _get_userid(self, request): - result = self.cookie.identify(request) - if result: - return result['userid'] - - def remember(self, request, principal, **kw): - """ Accepts the following kw args: ``max_age``.""" - return self.cookie.remember(request, principal, **kw) - - def forget(self, request): - return self.cookie.forget(request) - -def b64encode(v): - return v.encode('base64').strip().replace('\n', '') - -def b64decode(v): - return v.decode('base64') - -EXPIRE = object() - -class AuthTktCookieHelper(object): - auth_tkt = auth_tkt # for tests - - userid_type_decoders = { - 'int':int, - 'unicode':lambda x: utf_8_decode(x)[0], # bw compat for old cookies - 'b64unicode': lambda x: utf_8_decode(b64decode(x))[0], - 'b64str': lambda x: b64decode(x), - } - - userid_type_encoders = { - int: ('int', str), - long: ('int', str), - unicode: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])), - str: ('b64str', lambda x: b64encode(x)), - } - - def __init__(self, secret, cookie_name='auth_tkt', secure=False, - include_ip=False, timeout=None, reissue_time=None, - max_age=None, http_only=False, path="/"): - self.secret = secret - self.cookie_name = cookie_name - self.include_ip = include_ip - self.secure = secure - self.timeout = timeout - if reissue_time is not None and timeout is not None: - if reissue_time > timeout: - raise ValueError('reissue_time must be lower than timeout') - self.reissue_time = reissue_time - self.max_age = max_age - self.http_only = http_only - self.path = path - - static_flags = [] - if self.secure: - static_flags.append('; Secure') - if self.http_only: - static_flags.append('; HttpOnly') - self.static_flags = "".join(static_flags) - - def _get_cookies(self, environ, value, max_age=None): - if max_age is EXPIRE: - max_age = "; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT" - elif max_age is not None: - later = datetime.datetime.utcnow() + datetime.timedelta( - seconds=int(max_age)) - # Wdy, DD-Mon-YY HH:MM:SS GMT - expires = later.strftime('%a, %d %b %Y %H:%M:%S GMT') - # the Expires header is *required* at least for IE7 (IE7 does - # not respect Max-Age) - max_age = "; Max-Age=%s; Expires=%s" % (max_age, expires) - else: - max_age = '' - - cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) - wild_domain = '.' + cur_domain - - cookies = [ - ('Set-Cookie', '%s="%s"; Path=%s%s%s' % ( - self.cookie_name, value, self.path, max_age, self.static_flags)), - ('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % ( - self.cookie_name, value, self.path, cur_domain, max_age, - self.static_flags)), - ('Set-Cookie', '%s="%s"; Path=%s; Domain=%s%s%s' % ( - self.cookie_name, value, self.path, wild_domain, max_age, - self.static_flags)) - ] - - return cookies - - def identify(self, request): - environ = request.environ - cookies = get_cookies(environ) - cookie = cookies.get(self.cookie_name) - - if cookie is None or not cookie.value: - return None - - if self.include_ip: - remote_addr = environ['REMOTE_ADDR'] - else: - remote_addr = '0.0.0.0' - - try: - timestamp, userid, tokens, user_data = self.auth_tkt.parse_ticket( - self.secret, cookie.value, remote_addr) - except self.auth_tkt.BadTicket: - return None - - now = time.time() - - if self.timeout and ( (timestamp + self.timeout) < now ): - return None - - userid_typename = 'userid_type:' - user_data_info = user_data.split('|') - for datum in filter(None, user_data_info): - if datum.startswith(userid_typename): - userid_type = datum[len(userid_typename):] - decoder = self.userid_type_decoders.get(userid_type) - if decoder: - userid = decoder(userid) - - reissue = self.reissue_time is not None - - if not hasattr(request, '_authtkt_reissued'): - if reissue and ( (now - timestamp) > self.reissue_time): - headers = self.remember(request, userid, max_age=self.max_age) - add_global_response_headers(request, headers) - request._authtkt_reissued = True - - environ['REMOTE_USER_TOKENS'] = tokens - environ['REMOTE_USER_DATA'] = user_data - environ['AUTH_TYPE'] = 'cookie' - - identity = {} - identity['timestamp'] = timestamp - identity['userid'] = userid - identity['tokens'] = tokens - identity['userdata'] = user_data - return identity - - def forget(self, request): - # return a set of expires Set-Cookie headers - environ = request.environ - return self._get_cookies(environ, '', max_age=EXPIRE) - - def remember(self, request, userid, max_age=None): - max_age = max_age or self.max_age - environ = request.environ - - if self.include_ip: - remote_addr = environ['REMOTE_ADDR'] - else: - remote_addr = '0.0.0.0' - - user_data = '' - - encoding_data = self.userid_type_encoders.get(type(userid)) - if encoding_data: - encoding, encoder = encoding_data - userid = encoder(userid) - user_data = 'userid_type:%s' % encoding - - ticket = self.auth_tkt.AuthTicket( - self.secret, - userid, - remote_addr, - user_data=user_data, - cookie_name=self.cookie_name, - secure=self.secure) - - cookie_value = ticket.cookie_value() - return self._get_cookies(environ, cookie_value, max_age) - diff --git a/repoze/bfg/authorization.py b/repoze/bfg/authorization.py deleted file mode 100644 index 8c46b4a33..000000000 --- a/repoze/bfg/authorization.py +++ /dev/null @@ -1,133 +0,0 @@ -from zope.interface import implements - -from repoze.bfg.interfaces import IAuthorizationPolicy - -from repoze.bfg.location import lineage -from repoze.bfg.security import ACLAllowed -from repoze.bfg.security import ACLDenied -from repoze.bfg.security import Allow -from repoze.bfg.security import Deny -from repoze.bfg.security import Everyone - -class ACLAuthorizationPolicy(object): - """ An :term:`authorization policy` which consults an :term:`ACL` - object attached to a :term:`context` to determine authorization - information about a :term:`principal` or multiple principals. - If the context is part of a :term:`lineage`, the context's parents - are consulted for ACL information too. The following is true - about this security policy. - - - When checking whether the 'current' user is permitted (via the - ``permits`` method), the security policy consults the - ``context`` for an ACL first. If no ACL exists on the context, - or one does exist but the ACL does not explicitly allow or deny - access for any of the effective principals, consult the - context's parent ACL, and so on, until the lineage is exhausted - or we determine that the policy permits or denies. - - During this processing, if any :data:`repoze.bfg.security.Deny` - ACE is found matching any principal in ``principals``, stop - processing by returning an - :class:`repoze.bfg.security.ACLDenied` instance (equals - ``False``) immediately. If any - :data:`repoze.bfg.security.Allow` ACE is found matching any - principal, stop processing by returning an - :class:`repoze.bfg.security.ACLAllowed` instance (equals - ``True``) immediately. If we exhaust the context's - :term:`lineage`, and no ACE has explicitly permitted or denied - access, return an instance of - :class:`repoze.bfg.security.ACLDenied` (equals ``False``). - - - When computing principals allowed by a permission via the - :func:`repoze.bfg.security.principals_allowed_by_permission` - method, we compute the set of principals that are explicitly - granted the ``permission`` in the provided ``context``. We do - this by walking 'up' the object graph *from the root* to the - context. During this walking process, if we find an explicit - :data:`repoze.bfg.security.Allow` ACE for a principal that - matches the ``permission``, the principal is included in the - allow list. However, if later in the walking process that - principal is mentioned in any :data:`repoze.bfg.security.Deny` - ACE for the permission, the principal is removed from the allow - list. If a :data:`repoze.bfg.security.Deny` to the principal - :data:`repoze.bfg.security.Everyone` is encountered during the - walking process that matches the ``permission``, the allow list - is cleared for all principals encountered in previous ACLs. The - walking process ends after we've processed the any ACL directly - attached to ``context``; a set of principals is returned. - """ - - implements(IAuthorizationPolicy) - - def permits(self, context, principals, permission): - """ Return an instance of - :class:`repoze.bfg.security.ACLAllowed` instance if the policy - permits access, return an instance of - :class:`repoze.bfg.security.ACLDenied` if not.""" - - acl = '' - - for location in lineage(context): - try: - acl = location.__acl__ - except AttributeError: - continue - - for ace in acl: - ace_action, ace_principal, ace_permissions = ace - if ace_principal in principals: - if not hasattr(ace_permissions, '__iter__'): - ace_permissions = [ace_permissions] - if permission in ace_permissions: - if ace_action == Allow: - return ACLAllowed(ace, acl, permission, - principals, location) - else: - return ACLDenied(ace, acl, permission, - principals, location) - - # default deny (if no ACL in lineage at all, or if none of the - # principals were mentioned in any ACE we found) - return ACLDenied( - '', - acl, - permission, - principals, - context) - - def principals_allowed_by_permission(self, context, permission): - """ Return the set of principals explicitly granted the - permission named ``permission`` according to the ACL directly - attached to the ``context`` as well as inherited ACLs based on - the :term:`lineage`.""" - allowed = set() - - for location in reversed(list(lineage(context))): - # NB: we're walking *up* the object graph from the root - try: - acl = location.__acl__ - except AttributeError: - continue - - allowed_here = set() - denied_here = set() - - for ace_action, ace_principal, ace_permissions in acl: - if not hasattr(ace_permissions, '__iter__'): - ace_permissions = [ace_permissions] - if ace_action == Allow and permission in ace_permissions: - if not ace_principal in denied_here: - allowed_here.add(ace_principal) - if ace_action == Deny and permission in ace_permissions: - denied_here.add(ace_principal) - if ace_principal == Everyone: - # clear the entire allowed set, as we've hit a - # deny of Everyone ala (Deny, Everyone, ALL) - allowed = set() - break - elif ace_principal in allowed: - allowed.remove(ace_principal) - - allowed.update(allowed_here) - - return allowed diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py deleted file mode 100644 index 4a9d2655e..000000000 --- a/repoze/bfg/chameleon_text.py +++ /dev/null @@ -1,139 +0,0 @@ -import sys - -from zope.interface import implements - -try: - from chameleon.core.template import TemplateFile - TemplateFile # prevent pyflakes complaining about a redefinition below -except ImportError: # pragma: no cover - exc_class, exc, tb = sys.exc_info() - # Chameleon doesn't work on non-CPython platforms - class TemplateFile(object): - def __init__(self, *arg, **kw): - raise ImportError, exc, tb - -try: - from chameleon.zpt.language import Parser - Parser # prevent pyflakes complaining about a redefinition below -except ImportError: # pragma: no cover - # Chameleon doesn't work on non-CPython platforms - class Parser(object): - pass - -from repoze.bfg.interfaces import ITemplateRenderer -from repoze.bfg.interfaces import IChameleonTranslate - -from repoze.bfg.decorator import reify -from repoze.bfg import renderers -from repoze.bfg.path import caller_package -from repoze.bfg.settings import get_settings -from repoze.bfg.threadlocal import get_current_registry - -class TextTemplateFile(TemplateFile): - default_parser = Parser() - - def __init__(self, filename, parser=None, format='text', doctype=None, - **kwargs): - if parser is None: - parser = self.default_parser - super(TextTemplateFile, self).__init__(filename, parser, format, - doctype, **kwargs) - -def renderer_factory(path): - return renderers.template_renderer_factory(path, TextTemplateRenderer) - -class TextTemplateRenderer(object): - implements(ITemplateRenderer) - def __init__(self, path): - self.path = path - - @reify # avoid looking up reload_templates before manager pushed - def template(self): - settings = get_settings() - debug = False - auto_reload = False - if settings: - # using .get here is a strategy to be kind to old *tests* rather - # than being kind to any existing production system - auto_reload = settings.get('reload_templates') - debug = settings.get('debug_templates') - reg = get_current_registry() - translate = None - if reg is not None: - translate = reg.queryUtility(IChameleonTranslate) - return TextTemplateFile(self.path, - auto_reload=auto_reload, - debug=debug, - translate=translate) - - def implementation(self): - return self.template - - def __call__(self, value, system): - try: - system.update(value) - except (TypeError, ValueError): - raise ValueError('renderer was passed non-dictionary as value') - result = self.template(**system) - return result - -def get_renderer(path): - """ Return a callable object which can be used to render a - :term:`Chameleon` text template using the template implied by the - ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.get_renderer` instead. - """ - package = caller_package() - factory = renderers.RendererHelper(path, package=package) - return factory.get_renderer() - -def get_template(path): - """ Return the underyling object representing a :term:`Chameleon` - text template using the template implied by the ``path`` argument. - The ``path`` argument may be a package-relative path, an absolute - path, or a :term:`resource specification`. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - the ``implementation()`` method of a template renderer retrieved via - :func:`repoze.bfg.renderers.get_renderer` instead. - """ - package = caller_package() - factory = renderers.RendererHelper(path, package=package) - return factory.get_renderer().implementation() - -def render_template(path, **kw): - """ Render a :term:`Chameleon` text template using the template - implied by the ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. The arguments in ``*kw`` are passed as top-level - names to the template, and so may be used within the template - itself. Returns a string. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.render` instead. - """ - package = caller_package() - request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) - return renderer.render(kw, None, request=request) - -def render_template_to_response(path, **kw): - """ Render a :term:`Chameleon` text template using the template - implied by the ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. The arguments in ``*kw`` are passed as top-level - names to the template, and so may be used within the template - itself. Returns a :term:`Response` object with the body as the - template result. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.render_to_response` instead. - """ - package = caller_package() - request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) - return renderer.render_to_response(kw, None, request=request) diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py deleted file mode 100644 index 3c1c59f1c..000000000 --- a/repoze/bfg/chameleon_zpt.py +++ /dev/null @@ -1,121 +0,0 @@ -import sys - -from zope.interface import implements - -try: - from chameleon.zpt.template import PageTemplateFile - PageTemplateFile # prevent pyflakes complaining about a redefinition below -except ImportError: # pragma: no cover - exc_class, exc, tb = sys.exc_info() - # Chameleon doesn't work on non-CPython platforms - class PageTemplateFile(object): - def __init__(self, *arg, **kw): - raise ImportError, exc, tb - -from repoze.bfg.interfaces import IChameleonTranslate -from repoze.bfg.interfaces import ITemplateRenderer - -from repoze.bfg.decorator import reify -from repoze.bfg.path import caller_package -from repoze.bfg import renderers -from repoze.bfg.settings import get_settings -from repoze.bfg.threadlocal import get_current_registry - -def renderer_factory(path): - return renderers.template_renderer_factory(path, ZPTTemplateRenderer) - -class ZPTTemplateRenderer(object): - implements(ITemplateRenderer) - def __init__(self, path): - self.path = path - - @reify # avoid looking up reload_templates before manager pushed - def template(self): - settings = get_settings() - debug = False - auto_reload = False - if settings: - # using .get here is a strategy to be kind to old *tests* rather - # than being kind to any existing production system - auto_reload = settings.get('reload_templates') - debug = settings.get('debug_templates') - reg = get_current_registry() - translate = None - if reg is not None: - translate = reg.queryUtility(IChameleonTranslate) - return PageTemplateFile(self.path, - auto_reload=auto_reload, - debug=debug, - translate=translate) - - def implementation(self): - return self.template - - def __call__(self, value, system): - try: - system.update(value) - except (TypeError, ValueError): - raise ValueError('renderer was passed non-dictionary as value') - result = self.template(**system) - return result - -def get_renderer(path): - """ Return a callable object which can be used to render a - :term:`Chameleon` ZPT template using the template implied by the - ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.get_renderer` instead. - """ - package = caller_package() - factory = renderers.RendererHelper(path, package=package) - return factory.get_renderer() - -def get_template(path): - """ Return the underyling object representing a :term:`Chameleon` - ZPT template using the template implied by the ``path`` argument. - The ``path`` argument may be a package-relative path, an absolute - path, or a :term:`resource specification`. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - the ``implementation()`` method of a template renderer retrieved via - :func:`repoze.bfg.renderers.get_renderer` instead. - """ - package = caller_package() - factory = renderers.RendererHelper(path, package=package) - return factory.get_renderer().implementation() - -def render_template(path, **kw): - """ Render a :term:`Chameleon` ZPT template using the template - implied by the ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. The arguments in ``*kw`` are passed as top-level - names to the template, and so may be used within the template - itself. Returns a string. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.render` instead. - """ - package = caller_package() - request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) - return renderer.render(kw, None, request=request) - -def render_template_to_response(path, **kw): - """ Render a :term:`Chameleon` ZPT template using the template - implied by the ``path`` argument. The ``path`` argument may be a - package-relative path, an absolute path, or a :term:`resource - specification`. The arguments in ``*kw`` are passed as top-level - names to the template, and so may be used within the template - itself. Returns a :term:`Response` object with the body as the - template result. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use - :func:`repoze.bfg.renderers.render_to_response` instead. - """ - package = caller_package() - request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) - return renderer.render_to_response(kw, None, request=request) diff --git a/repoze/bfg/compat/__init__.py b/repoze/bfg/compat/__init__.py deleted file mode 100644 index 205175132..000000000 --- a/repoze/bfg/compat/__init__.py +++ /dev/null @@ -1,143 +0,0 @@ -# Some code in this file was lifted wholesale from Django -# (see -# http://code.djangoproject.com/browser/django/trunk/LICENSE for -# license text; BSD-like) - -# License for code in this file that was taken from Python 2.5. - -# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -# -------------------------------------------- -# -# 1. This LICENSE AGREEMENT is between the Python Software Foundation -# ("PSF"), and the Individual or Organization ("Licensee") accessing and -# otherwise using this software ("Python") in source or binary form and -# its associated documentation. -# -# 2. Subject to the terms and conditions of this License Agreement, PSF -# hereby grants Licensee a nonexclusive, royalty-free, world-wide -# license to reproduce, analyze, test, perform and/or display publicly, -# prepare derivative works, distribute, and otherwise use Python -# alone or in any derivative version, provided, however, that PSF's -# License Agreement and PSF's notice of copyright, i.e., "Copyright (c) -# 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation; -# All Rights Reserved" are retained in Python alone or in any derivative -# version prepared by Licensee. -# -# 3. In the event Licensee prepares a derivative work that is based on -# or incorporates Python or any part thereof, and wants to make -# the derivative work available to others as provided herein, then -# Licensee hereby agrees to include in any such work a brief summary of -# the changes made to Python. -# -# 4. PSF is making Python available to Licensee on an "AS IS" -# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -# INFRINGE ANY THIRD PARTY RIGHTS. -# -# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. -# -# 6. This License Agreement will automatically terminate upon a material -# breach of its terms and conditions. -# -# 7. Nothing in this License Agreement shall be deemed to create any -# relationship of agency, partnership, or joint venture between PSF and -# Licensee. This License Agreement does not grant permission to use PSF -# trademarks or trade name in a trademark sense to endorse or promote -# products or services of Licensee, or any third party. -# -# 8. By copying, installing or otherwise using Python, Licensee -# agrees to be bound by the terms and conditions of this License -# Agreement. - - -try: # pragma: no cover - from functools import wraps -except ImportError: #pragma no cover - # < 2.5 - def curry(_curried_func, *args, **kwargs): - def _curried(*moreargs, **morekwargs): - return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) - return _curried - - ### Begin from Python 2.5 functools.py ################################### - # Summary of changes made to the Python 2.5 code below: - # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility - # in Django. - # * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except - # block to make it compatible with Python 2.3, which doesn't allow - # assigning to ``__name__``. - - # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software - # Foundation. - # All Rights Reserved. - ########################################################################## - - # update_wrapper() and wraps() are tools to help write - # wrapper functions that can handle naive introspection - - WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') - WRAPPER_UPDATES = ('__dict__',) - def update_wrapper(wrapper, - wrapped, - assigned = WRAPPER_ASSIGNMENTS, - updated = WRAPPER_UPDATES): - """Update a wrapper function to look like the wrapped function - - wrapper is the function to be updated - wrapped is the original function - assigned is a tuple naming the attributes assigned directly - from the wrapped function to the wrapper function (defaults to - functools.WRAPPER_ASSIGNMENTS) - updated is a tuple naming the attributes off the wrapper that - are updated with the corresponding attribute from the wrapped - function (defaults to functools.WRAPPER_UPDATES) - """ - for attr in assigned: - setattr(wrapper, attr, getattr(wrapped, attr)) - - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr)) - # Return the wrapper so this can be used as a decorator via curry() - return wrapper - - def wraps(wrapped, - assigned = WRAPPER_ASSIGNMENTS, - updated = WRAPPER_UPDATES): - """Decorator factory to apply update_wrapper() to a wrapper function - - Returns a decorator that invokes update_wrapper() with the decorated - function as the wrapper argument and the arguments to wraps() as the - remaining arguments. Default arguments are as for update_wrapper(). - This is a convenience function to simplify applying curry() to - update_wrapper(). - """ - return curry(update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - -### End from Python 2.5 functools.py ########################################## - -try: - all = all -except NameError: # pragma: no cover - def all(iterable): - for element in iterable: - if not element: - return False - return True - -try: - import json -except ImportError: # pragma: no cover - import simplejson as json - -try: - from hashlib import md5 -except ImportError: # pragma: no cover - import md5 - md5 = md5.new - diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py deleted file mode 100644 index 88bdcba58..000000000 --- a/repoze/bfg/configuration.py +++ /dev/null @@ -1,2697 +0,0 @@ -import os -import re -import sys -import threading -import inspect -import pkg_resources - -import venusian - -from translationstring import ChameleonTranslate - -from zope.configuration import xmlconfig - -from zope.interface import Interface -from zope.interface import implementedBy -from zope.interface.interfaces import IInterface -from zope.interface import implements - -from repoze.bfg.interfaces import IAuthenticationPolicy -from repoze.bfg.interfaces import IAuthorizationPolicy -from repoze.bfg.interfaces import IChameleonTranslate -from repoze.bfg.interfaces import IDebugLogger -from repoze.bfg.interfaces import IDefaultPermission -from repoze.bfg.interfaces import IDefaultRootFactory -from repoze.bfg.interfaces import IExceptionViewClassifier -from repoze.bfg.interfaces import ILocaleNegotiator -from repoze.bfg.interfaces import IMultiView -from repoze.bfg.interfaces import IPackageOverrides -from repoze.bfg.interfaces import IRendererFactory -from repoze.bfg.interfaces import IRendererGlobalsFactory -from repoze.bfg.interfaces import IRequest -from repoze.bfg.interfaces import IRequestFactory -from repoze.bfg.interfaces import IRootFactory -from repoze.bfg.interfaces import IRouteRequest -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import ISecuredView -from repoze.bfg.interfaces import ISettings -from repoze.bfg.interfaces import IStaticURLInfo -from repoze.bfg.interfaces import ITranslationDirectories -from repoze.bfg.interfaces import ITraverser -from repoze.bfg.interfaces import IView -from repoze.bfg.interfaces import IViewClassifier -from repoze.bfg.interfaces import IExceptionResponse -from repoze.bfg.interfaces import IException - -from repoze.bfg import chameleon_text -from repoze.bfg import chameleon_zpt -from repoze.bfg import renderers -from repoze.bfg.renderers import RendererHelper -from repoze.bfg.authorization import ACLAuthorizationPolicy -from repoze.bfg.compat import all -from repoze.bfg.compat import md5 -from repoze.bfg.events import ApplicationCreated -from repoze.bfg.exceptions import Forbidden -from repoze.bfg.exceptions import NotFound -from repoze.bfg.exceptions import PredicateMismatch -from repoze.bfg.exceptions import ConfigurationError -from repoze.bfg.i18n import get_localizer -from repoze.bfg.log import make_stream_logger -from repoze.bfg.path import caller_package -from repoze.bfg.path import package_path -from repoze.bfg.path import package_of -from repoze.bfg.registry import Registry -from repoze.bfg.request import route_request_iface -from repoze.bfg.resource import PackageOverrides -from repoze.bfg.resource import resolve_resource_spec -from repoze.bfg.settings import Settings -from repoze.bfg.static import StaticURLInfo -from repoze.bfg.threadlocal import get_current_registry -from repoze.bfg.threadlocal import get_current_request -from repoze.bfg.threadlocal import manager -from repoze.bfg.traversal import traversal_path -from repoze.bfg.traversal import DefaultRootFactory -from repoze.bfg.traversal import find_interface -from repoze.bfg.urldispatch import RoutesMapper -from repoze.bfg.view import render_view_to_response -from repoze.bfg.view import default_exceptionresponse_view - -MAX_ORDER = 1 << 30 -DEFAULT_PHASH = md5().hexdigest() - -DEFAULT_RENDERERS = ( - ('.pt', chameleon_zpt.renderer_factory), - ('.txt', chameleon_text.renderer_factory), - ('json', renderers.json_renderer_factory), - ('string', renderers.string_renderer_factory), - ) - -_marker = object() - -class Configurator(object): - """ - A Configurator is used to configure a :mod:`repoze.bfg` - :term:`application registry`. - - The Configurator accepts a number of arguments: ``registry``, - ``package``, ``settings``, ``root_factory``, - ``authentication_policy``, ``authorization_policy``, ``renderers`` - ``debug_logger``, ``locale_negotiator``, ``request_factory``, and - ``renderer_globals_factory``. - - If the ``registry`` argument is passed as a non-``None`` value, it - must be an instance of the :class:`repoze.bfg.registry.Registry` - class representing the registry to configure. If ``registry`` is - ``None``, the configurator will create a - :class:`repoze.bfg.registry.Registry` instance itself; it will - also perform some default configuration that would not otherwise - be done. After construction, the configurator may be used to add - configuration to the registry. The overall state of a registry is - called the 'configuration state'. - - .. warning:: If a ``registry`` is passed to the Configurator - constructor, all other constructor arguments except ``package`` - are ignored. - - If the ``package`` argument is passed, it must be a reference to a - Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a - :term:`dotted Python name` to same. This value is used as a basis - to convert relative paths passed to various configuration methods, - such as methods which accept a ``renderer`` argument, into - absolute paths. If ``None`` is passed (the default), the package - is assumed to be the Python package in which the *caller* of the - ``Configurator`` constructor lives. - - If the ``settings`` argument is passed, it should be a Python - dictionary representing the deployment settings for this - application. These are later retrievable using the - :meth:`repoze.bfg.configuration.Configurator.get_settings` and - :func:`repoze.bfg.settings.get_settings` APIs. - - If the ``root_factory`` argument is passed, it should be an object - representing the default :term:`root factory` for your application - or a :term:`dotted Python name` to same. If it is ``None``, a - default root factory will be used. - - If ``authentication_policy`` is passed, it should be an instance - of an :term:`authentication policy` or a :term:`dotted Python - name` to same. - - If ``authorization_policy`` is passed, it should be an instance of - an :term:`authorization policy` or a :term:`dotted Python name` to - same. - - .. note:: A ``ConfigurationError`` will be raised when an - authorization policy is supplied without also supplying an - authentication policy (authorization requires authentication). - - If ``renderers`` is passed, it should be a list of tuples - representing a set of :term:`renderer` factories which should be - configured into this application (each tuple representing a set of - positional values that should be passed to - :meth:`repoze.bfg.configuration.Configurator.add_renderer`). If - it is not passed, a default set of renderer factories is used. - - If ``debug_logger`` is not passed, a default debug logger that - logs to stderr will be used. If it is passed, it should be an - instance of the :class:`logging.Logger` (PEP 282) standard library - class or a :term:`dotted Python name` to same. The debug logger - is used by :mod:`repoze.bfg` itself to log warnings and - authorization debugging information. - - If ``locale_negotiator`` is passed, it should be a :term:`locale - negotiator` implementation or a :term:`dotted Python name` to - same. See :ref:`custom_locale_negotiator`. - - If ``request_factory`` is passed, it should be a :term:`request - factory` implementation or a :term:`dotted Python name` to same. - See :ref:`custom_request_factory`. By default it is ``None``, - which means use the default request factory. - - If ``renderer_globals_factory`` is passed, it should be a - :term:`renderer globals` factory implementation or a :term:`dotted - Python name` to same. See :ref:`custom_renderer_globals_factory`. - By default, it is ``None``, which means use no renderer globals - factory. - - If ``default_permission`` is passed, it should be a - :term:`permission` string to be used as the default permission for - all view configuration registrations performed against this - Configurator. An example of a permission string:``'view'``. - Adding a default permission makes it unnecessary to protect each - view configuration with an explicit permission, unless your - application policy requires some exception for a particular view. - By default, ``default_permission`` is ``None``, meaning that view - configurations which do not explicitly declare a permission will - always be executable by entirely anonymous users (any - authorization policy in effect is ignored). See also - :ref:`setting_a_default_permission`. - """ - - manager = manager # for testing injection - venusian = venusian # for testing injection - def __init__(self, - registry=None, - package=None, - settings=None, - root_factory=None, - authentication_policy=None, - authorization_policy=None, - renderers=DEFAULT_RENDERERS, - debug_logger=None, - locale_negotiator=None, - request_factory=None, - renderer_globals_factory=None, - default_permission=None): - if package is None: - package = caller_package() - name_resolver = DottedNameResolver(package) - self.name_resolver = name_resolver - self.package_name = name_resolver.package_name - self.package = name_resolver.package - self.registry = registry - if registry is None: - registry = Registry(self.package_name) - self.registry = registry - self.setup_registry( - settings=settings, - root_factory=root_factory, - authentication_policy=authentication_policy, - authorization_policy=authorization_policy, - renderers=renderers, - debug_logger=debug_logger, - locale_negotiator=locale_negotiator, - request_factory=request_factory, - renderer_globals_factory=renderer_globals_factory, - default_permission=default_permission, - ) - - def _set_settings(self, mapping): - settings = Settings(mapping or {}) - self.registry.registerUtility(settings, ISettings) - return settings - - def _set_root_factory(self, factory): - """ Add a :term:`root factory` to the current configuration - state. If the ``factory`` argument is ``None`` a default root - factory will be registered.""" - factory = self.maybe_dotted(factory) - if factory is None: - factory = DefaultRootFactory - self.registry.registerUtility(factory, IRootFactory) - self.registry.registerUtility(factory, IDefaultRootFactory) # b/c - - def _set_authentication_policy(self, policy, _info=u''): - """ Add a :mod:`repoze.bfg` :term:`authentication policy` to - the current configuration.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthenticationPolicy, info=_info) - - def _set_authorization_policy(self, policy, _info=u''): - """ Add a :mod:`repoze.bfg` :term:`authorization policy` to - the current configuration state (also accepts a :term:`dotted - Python name`.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthorizationPolicy, info=_info) - - def _make_spec(self, path_or_spec): - package, filename = resolve_resource_spec(path_or_spec, - self.package_name) - if package is None: - return filename # absolute filename - return '%s:%s' % (package, filename) - - def _split_spec(self, path_or_spec): - return resolve_resource_spec(path_or_spec, self.package_name) - - def _derive_view(self, view, permission=None, predicates=(), - attr=None, renderer_name=None, wrapper_viewname=None, - viewname=None, accept=None, order=MAX_ORDER, - phash=DEFAULT_PHASH): - view = self.maybe_dotted(view) - authn_policy = self.registry.queryUtility(IAuthenticationPolicy) - authz_policy = self.registry.queryUtility(IAuthorizationPolicy) - settings = self.registry.queryUtility(ISettings) - logger = self.registry.queryUtility(IDebugLogger) - mapped_view = _map_view(view, attr, renderer_name, self.registry, - self.package) - owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) - secured_view = _secure_view(owrapped_view, permission, - authn_policy, authz_policy) - debug_view = _authdebug_view(secured_view, permission, - authn_policy, authz_policy, settings, - logger) - predicated_view = _predicate_wrap(debug_view, predicates) - derived_view = _attr_wrap(predicated_view, accept, order, phash) - return derived_view - - def _override(self, package, path, override_package, override_prefix, - _info=u'', PackageOverrides=PackageOverrides): - pkg_name = package.__name__ - override_pkg_name = override_package.__name__ - override = self.registry.queryUtility( - IPackageOverrides, name=pkg_name) - if override is None: - override = PackageOverrides(package) - self.registry.registerUtility(override, IPackageOverrides, - name=pkg_name, info=_info) - override.insert(path, override_pkg_name, override_prefix) - - def _set_security_policies(self, authentication, authorization=None): - if authorization is None: - authorization = ACLAuthorizationPolicy() # default - if authorization and not authentication: - raise ConfigurationError( - 'If the "authorization" is passed a value, ' - 'the "authentication" argument must also be ' - 'passed a value; authorization requires authentication.') - self._set_authentication_policy(authentication) - self._set_authorization_policy(authorization) - - def _fix_registry(self): - """ Fix up a ZCA component registry that is not a - repoze.bfg.registry.Registry by adding analogues of - ``has_listeners`` and ``notify`` through monkey-patching.""" - - if not hasattr(self.registry, 'notify'): - def notify(*events): - [ _ for _ in self.registry.subscribers(events, None) ] - self.registry.notify = notify - - if not hasattr(self.registry, 'has_listeners'): - self.registry.has_listeners = True - - # API - - def with_package(self, package): - """ Return a new Configurator instance with the same registry - as this configurator using the package supplied as the - ``package`` argument to the new configurator. ``package`` may - be an actual Python package object or a Python dotted name - representing a package.""" - return self.__class__(registry=self.registry, package=package) - - def maybe_dotted(self, dotted): - """ Resolve the :term:`dotted Python name` ``dotted`` to a - global Python object. If ``dotted`` is not a string, return - it without attempting to do any name resolution. If - ``dotted`` is a relative dotted name (e.g. ``.foo.bar``, - consider it relative to the ``package`` argument supplied to - this Configurator's constructor.""" - return self.name_resolver.maybe_resolve(dotted) - - def absolute_resource_spec(self, relative_spec): - """ Resolve the potentially relative :term:`resource - specification` string passed as ``relative_spec`` into an - absolute resource specification string and return the string. - Use the ``package`` of this configurator as the package to - which the resource specification will be considered relative - when generating an absolute resource specification. If the - provided ``relative_spec`` argument is already absolute, or if - the ``relative_spec`` is not a string, it is simply returned.""" - if not isinstance(relative_spec, basestring): - return relative_spec - return self._make_spec(relative_spec) - - def setup_registry(self, settings=None, root_factory=None, - authentication_policy=None, authorization_policy=None, - renderers=DEFAULT_RENDERERS, debug_logger=None, - locale_negotiator=None, request_factory=None, - renderer_globals_factory=None, - default_permission=None): - """ When you pass a non-``None`` ``registry`` argument to the - :term:`Configurator` constructor, no initial 'setup' is - performed against the registry. This is because the registry - you pass in may have already been initialized for use under - :mod:`repoze.bfg` via a different configurator. However, in - some circumstances, such as when you want to use the Zope - 'global` registry instead of a registry created as a result of - the Configurator constructor, or when you want to reset the - initial setup of a registry, you *do* want to explicitly - initialize the registry associated with a Configurator for use - under :mod:`repoze.bfg`. Use ``setup_registry`` to do this - initialization. - - ``setup_registry`` configures settings, a root factory, - security policies, renderers, a debug logger, a locale - negotiator, and various other settings using the - configurator's current registry, as per the descriptions in - the Configurator constructor.""" - self._fix_registry() - self._set_settings(settings) - self._set_root_factory(root_factory) - debug_logger = self.maybe_dotted(debug_logger) - if debug_logger is None: - debug_logger = make_stream_logger('repoze.bfg.debug', sys.stderr) - registry = self.registry - registry.registerUtility(debug_logger, IDebugLogger) - registry.registerUtility(debug_logger, IDebugLogger, - 'repoze.bfg.debug') # b /c - if authentication_policy or authorization_policy: - self._set_security_policies(authentication_policy, - authorization_policy) - for name, renderer in renderers: - self.add_renderer(name, renderer) - self.add_view(default_exceptionresponse_view, - context=IExceptionResponse) - if locale_negotiator: - locale_negotiator = self.maybe_dotted(locale_negotiator) - registry.registerUtility(locale_negotiator, ILocaleNegotiator) - if request_factory: - request_factory = self.maybe_dotted(request_factory) - self.set_request_factory(request_factory) - if renderer_globals_factory: - renderer_globals_factory=self.maybe_dotted(renderer_globals_factory) - self.set_renderer_globals_factory(renderer_globals_factory) - if default_permission: - self.set_default_permission(default_permission) - - # getSiteManager is a unit testing dep injection - def hook_zca(self, getSiteManager=None): - """ Call :func:`zope.component.getSiteManager.sethook` with - the argument - :data:`repoze.bfg.threadlocal.get_current_registry`, causing - the :term:`Zope Component Architecture` 'global' APIs such as - :func:`zope.component.getSiteManager`, - :func:`zope.component.getAdapter` and others to use the - :mod:`repoze.bfg` :term:`application registry` rather than the - Zope 'global' registry. If :mod:`zope.component` cannot be - imported, this method will raise an :exc:`ImportError`.""" - if getSiteManager is None: - from zope.component import getSiteManager - getSiteManager.sethook(get_current_registry) - - # getSiteManager is a unit testing dep injection - def unhook_zca(self, getSiteManager=None): - """ Call :func:`zope.component.getSiteManager.reset` to undo - the action of - :meth:`repoze.bfg.configuration.Configurator.hook_zca`. If - :mod:`zope.component` cannot be imported, this method will - raise an :exc:`ImportError`.""" - if getSiteManager is None: # pragma: no cover - from zope.component import getSiteManager - getSiteManager.reset() - - def begin(self, request=None): - """ Indicate that application or test configuration has begun. - This pushes a dictionary containing the :term:`application - registry` implied by ``registry`` attribute of this - configurator and the :term:`request` implied by the - ``request`` argument on to the :term:`thread local` stack - consulted by various :mod:`repoze.bfg.threadlocal` API - functions.""" - self.manager.push({'registry':self.registry, 'request':request}) - - def end(self): - """ Indicate that application or test configuration has ended. - This pops the last value pushed on to the :term:`thread local` - stack (usually by the ``begin`` method) and returns that - value. - """ - return self.manager.pop() - - def derive_view(self, view, attr=None, renderer=None): - """ - - Create a :term:`view callable` using the function, instance, - or class (or :term:`dotted Python name` referring to the same) - provided as ``view`` object. - - This is API is useful to framework extenders who create - pluggable systems which need to register 'proxy' view - callables for functions, instances, or classes which meet the - requirements of being a :mod:`repoze.bfg` view callable. For - example, a ``some_other_framework`` function in another - framework may want to allow a user to supply a view callable, - but he may want to wrap the view callable in his own before - registering the wrapper as a :mod:`repoze.bfg` view callable. - Because a :mod:`repoze.bfg` view callable can be any of a - number of valid objects, the framework extender will not know - how to call the user-supplied object. Running it through - ``derive_view`` normalizes it to a callable which accepts two - arguments: ``context`` and ``request``. - - For example: - - .. code-block:: python - - def some_other_framework(user_supplied_view): - config = Configurator(reg) - proxy_view = config.derive_view(user_supplied_view) - def my_wrapper(context, request): - do_something_that_mutates(request) - return proxy_view(context, request) - config.add_view(my_wrapper) - - The ``view`` object provided should be one of the following: - - - A function or another non-class callable object that accepts - a :term:`request` as a single positional argument and which - returns a :term:`response` object. - - - A function or other non-class callable object that accepts - two positional arguments, ``context, request`` and which - returns a :term:`response` object. - - - A class which accepts a single positional argument in its - constructor named ``request``, and which has a ``__call__`` - method that accepts no arguments that returns a - :term:`response` object. - - - A class which accepts two positional arguments named - ``context, request``, and which has a ``__call__`` method - that accepts no arguments that returns a :term:`response` - object. - - - A :term:`dotted Python name` which refers to any of the - kinds of objects above. - - This API returns a callable which accepts the arguments - ``context, request`` and which returns the result of calling - the provided ``view`` object. - - The ``attr`` keyword argument is most useful when the view - object is a class. It names the method that should be used as - the callable. If ``attr`` is not provided, the attribute - effectively defaults to ``__call__``. See - :ref:`class_as_view` for more information. - - The ``renderer`` keyword argument should be a renderer - name. If supplied, it will cause the returned callable to use - a :term:`renderer` to convert the user-supplied view result to - a :term:`response` object. If a ``renderer`` argument is not - supplied, the user-supplied view must itself return a - :term:`response` object. """ - - return self._derive_view(view, attr=attr, renderer_name=renderer) - - def add_subscriber(self, subscriber, iface=None, info=u''): - """Add an event :term:`subscriber` for the event stream - implied by the supplied ``iface`` interface. The - ``subscriber`` argument represents a callable object (or a - :term:`dotted Python name` which identifies a callable); it - will be called with a single object ``event`` whenever - :mod:`repoze.bfg` emits an :term:`event` associated with the - ``iface``, which may be an :term:`interface` or a class or a - :term:`dotted Python name` to a global object representing an - interface or a class. Using the default ``iface`` value, - ``None`` will cause the subscriber to be registered for all - event types. See :ref:`events_chapter` for more information - about events and subscribers.""" - dotted = self.maybe_dotted - subscriber, iface = dotted(subscriber), dotted(iface) - if iface is None: - iface = (Interface,) - if not isinstance(iface, (tuple, list)): - iface = (iface,) - self.registry.registerHandler(subscriber, iface, info=info) - return subscriber - - def add_settings(self, settings=None, **kw): - """Augment the ``settings`` argument passed in to the - Configurator constructor with one or more 'setting' key/value - pairs. A setting is a single key/value pair in the - dictionary-ish object returned from the API - :func:`repoze.bfg.settings.get_settings` and - :meth:`repoze.bfg.configuration.Configurator.get_settings`. - - You may pass a dictionary:: - - config.add_settings({'external_uri':'http://example.com'}) - - Or a set of key/value pairs:: - - config.add_settings(external_uri='http://example.com') - - This function is useful when you need to test code that calls - the :func:`repoze.bfg.settings.get_settings` API (or the - :meth:`repoze.bfg.configuration.Configurator.get_settings` - API) and which uses return values from that API. - - .. note:: This method is new as of :mod:`repoze.bfg` 1.2. - """ - if settings is None: - settings = {} - utility = self.registry.queryUtility(ISettings) - if utility is None: - utility = self._set_settings(settings) - utility.update(settings) - utility.update(kw) - - def get_settings(self): - """ - Return a 'settings' object for the current application. A - 'settings' object is a dictionary-like object that contains - key/value pairs based on the dictionary passed as the ``settings`` - argument to the :class:`repoze.bfg.configuration.Configurator` - constructor or the :func:`repoze.bfg.router.make_app` API. - - .. note:: For backwards compatibility, dictionary keys can also be - looked up as attributes of the settings object. - - .. note:: the :class:`repoze.bfg.settings.get_settings` function - performs the same duty.""" - return self.registry.queryUtility(ISettings) - - def make_wsgi_app(self): - """ Returns a :mod:`repoze.bfg` WSGI application representing - the current configuration state and sends a - :class:`repoze.bfg.interfaces.IApplicationCreated` - event to all listeners.""" - from repoze.bfg.router import Router # avoid circdep - app = Router(self.registry) - # We push the registry on to the stack here in case any code - # that depends on the registry threadlocal APIs used in - # listeners subscribed to the IApplicationCreated event. - self.manager.push({'registry':self.registry, 'request':None}) - try: - self.registry.notify(ApplicationCreated(app)) - finally: - self.manager.pop() - return app - - def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): - """ Load configuration from a :term:`ZCML` file into the - current configuration state. The ``spec`` argument is an - absolute filename, a relative filename, or a :term:`resource - specification`, defaulting to ``configure.zcml`` (relative to - the package of the configurator's caller).""" - package_name, filename = self._split_spec(spec) - if package_name is None: # absolute filename - package = self.package - else: - __import__(package_name) - package = sys.modules[package_name] - - lock.acquire() - self.manager.push({'registry':self.registry, 'request':None}) - try: - xmlconfig.file(filename, package, execute=True) - finally: - lock.release() - self.manager.pop() - return self.registry - - def add_view(self, view=None, name="", for_=None, permission=_marker, - request_type=None, route_name=None, request_method=None, - request_param=None, containment=None, attr=None, - renderer=None, wrapper=None, xhr=False, accept=None, - header=None, path_info=None, custom_predicates=(), - context=None, _info=u''): - """ Add a :term:`view configuration` to the current - configuration state. Arguments to ``add_view`` are broken - down below into *predicate* arguments and *non-predicate* - arguments. Predicate arguments narrow the circumstances in - which the view callable will be invoked when a request is - presented to :mod:`repoze.bfg`; non-predicate arguments are - informational. - - Non-Predicate Arguments - - view - - A :term:`view callable` or a :term:`dotted Python name` - which refers to a view callable. This argument is required - unless a ``renderer`` argument also exists. If a - ``renderer`` argument is passed, and a ``view`` argument is - not provided, the view callable defaults to a callable that - returns an empty dictionary (see - :ref:`views_which_use_a_renderer`). - - permission - - The name of a :term:`permission` that the user must possess - in order to invoke the :term:`view callable`. See - :ref:`view_security_section` for more information about view - security and permissions. If ``permission`` is omitted, a - *default* permission may be used for this view registration - if one was named as the - :class:`repoze.bfg.configuration.Configurator` constructor's - ``default_permission`` argument, or if - :meth:`repoze.bfg.configuration.Configurator.set_default_permission` - was used prior to this view registration. Pass ``None`` as - the permission to explicitly indicate that the view should - always be executable by entirely anonymous users, regardless - of the default permission, bypassing any - :term:`authorization policy` that may be in effect. - - attr - - The view machinery defaults to using the ``__call__`` method - of the :term:`view callable` (or the function itself, if the - view callable is a function) to obtain a response. The - ``attr`` value allows you to vary the method attribute used - to obtain the response. For example, if your view was a - class, and the class has a method named ``index`` and you - wanted to use this method instead of the class' ``__call__`` - method to return the response, you'd say ``attr="index"`` in the - view configuration for the view. This is - most useful when the view definition is a class. - - renderer - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``) naming a :term:`renderer` - implementation. If the ``renderer`` value does not contain - a dot ``.``, the specified string will be used to look up a - renderer implementation, and that renderer implementation - will be used to construct a response from the view return - value. If the ``renderer`` value contains a dot (``.``), - the specified term will be treated as a path, and the - filename extension of the last element in the path will be - used to look up the renderer implementation, which will be - passed the full path. The renderer implementation will be - used to construct a :term:`response` from the view return - value. - - Note that if the view itself returns a :term:`response` (see - :ref:`the_response`), the specified renderer implementation - is never called. - - When the renderer is a path, although a path is usually just - a simple relative pathname (e.g. ``templates/foo.pt``, - implying that a template named "foo.pt" is in the - "templates" directory relative to the directory of the - current :term:`package` of the Configurator), a path can be - absolute, starting with a slash on UNIX or a drive letter - prefix on Windows. The path can alternately be a - :term:`resource specification` in the form - ``some.dotted.package_name:relative/path``, making it - possible to address template resources which live in a - separate package. - - The ``renderer`` attribute is optional. If it is not - defined, the "null" renderer is assumed (no rendering is - performed and the value is passed back to the upstream - :mod:`repoze.bfg` machinery unmolested). - - wrapper - - The :term:`view name` of a different :term:`view - configuration` which will receive the response body of this - view as the ``request.wrapped_body`` attribute of its own - :term:`request`, and the :term:`response` returned by this - view as the ``request.wrapped_response`` attribute of its - own request. Using a wrapper makes it possible to "chain" - views together to form a composite response. The response - of the outermost wrapper view will be returned to the user. - The wrapper view will be found as any view is found: see - :ref:`view_lookup`. The "best" wrapper view will be found - based on the lookup ordering: "under the hood" this wrapper - view is looked up via - ``repoze.bfg.view.render_view_to_response(context, request, - 'wrapper_viewname')``. The context and request of a wrapper - view is the same context and request of the inner view. If - this attribute is unspecified, no view wrapping is done. - - Predicate Arguments - - name - - The :term:`view name`. Read :ref:`traversal_chapter` to - understand the concept of a view name. - - context - - An object or a :term:`dotted Python name` referring to an - interface or class object that the :term:`context` must be - an instance of, *or* the :term:`interface` that the - :term:`context` must provide in order for this view to be - found and called. This predicate is true when the - :term:`context` is an instance of the represented class or - if the :term:`context` provides the represented interface; - it is otherwise false. This argument may also be provided - to ``add_view`` as ``for_`` (an older, still-supported - spelling). - - route_name - - This value must match the ``name`` of a :term:`route - configuration` declaration (see :ref:`urldispatch_chapter`) - that must match before this view will be called. Note that - the ``route`` configuration referred to by ``route_name`` - usually has a ``*traverse`` token in the value of its - ``path``, representing a part of the path that will be used - by :term:`traversal` against the result of the route's - :term:`root factory`. - - .. warning:: Using this argument services an advanced - feature that isn't often used unless you want to perform - traversal *after* a route has matched. See - :ref:`hybrid_chapter` for more information on using this - advanced feature. - - request_type - - This value should be an :term:`interface` that the - :term:`request` must provide in order for this view to be - found and called. This value exists only for backwards - compatibility purposes. - - request_method - - This value can either be one of the strings ``GET``, - ``POST``, ``PUT``, ``DELETE``, or ``HEAD`` representing an - HTTP ``REQUEST_METHOD``. A view declaration with this - argument ensures that the view will only be called when the - request's ``method`` attribute (aka the ``REQUEST_METHOD`` of - the WSGI environment) string matches the supplied value. - - request_param - - This value can be any string. A view declaration with this - argument ensures that the view will only be called when the - :term:`request` has a key in the ``request.params`` - dictionary (an HTTP ``GET`` or ``POST`` variable) that has a - name which matches the supplied value. If the value - supplied has a ``=`` sign in it, - e.g. ``request_params="foo=123"``, then the key (``foo``) - must both exist in the ``request.params`` dictionary, *and* - the value must match the right hand side of the expression - (``123``) for the view to "match" the current request. - - containment - - This value should be a Python class or :term:`interface` or - a :term:`dotted Python name` to such an object that a parent - object in the :term:`lineage` must provide in order for this - view to be found and called. The nodes in your object graph - must be "location-aware" to use this feature. See - :ref:`location_aware` for more information about - location-awareness. - - xhr - - This value should be either ``True`` or ``False``. If this - value is specified and is ``True``, the :term:`request` - must possess an ``HTTP_X_REQUESTED_WITH`` (aka - ``X-Requested-With``) header that has the value - ``XMLHttpRequest`` for this view to be found and called. - This is useful for detecting AJAX requests issued from - jQuery, Prototype and other Javascript libraries. - - accept - - The value of this argument represents a match query for one - or more mimetypes in the ``Accept`` HTTP request header. If - this value is specified, it must be in one of the following - forms: a mimetype match token in the form ``text/plain``, a - wildcard mimetype match token in the form ``text/*`` or a - match-all wildcard mimetype match token in the form ``*/*``. - If any of the forms matches the ``Accept`` header of the - request, this predicate will be true. - - header - - This value represents an HTTP header name or a header - name/value pair. If the value contains a ``:`` (colon), it - will be considered a name/value pair - (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The - value portion should be a regular expression. If the value - does not contain a colon, the entire value will be - considered to be the header name - (e.g. ``If-Modified-Since``). If the value evaluates to a - header name only without a value, the header specified by - the name must be present in the request for this predicate - to be true. If the value evaluates to a header name/value - pair, the header specified by the name must be present in - the request *and* the regular expression specified as the - value must match the header value. Whether or not the value - represents a header name or a header name/value pair, the - case of the header name is not significant. - - path_info - - This value represents a regular expression pattern that will - be tested against the ``PATH_INFO`` WSGI environment - variable. If the regex matches, this predicate will be - ``True``. - - - custom_predicates - - This value should be a sequence of references to custom - predicate callables. Use custom predicates when no set of - predefined predicates do what you need. Custom predicates - can be combined with predefined predicates as necessary. - Each custom predicate callable should accept two arguments: - ``context`` and ``request`` and should return either - ``True`` or ``False`` after doing arbitrary evaluation of - the context and/or the request. If all callables return - ``True``, the associated view callable will be considered - viable for a given request. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. - """ - view = self.maybe_dotted(view) - context = self.maybe_dotted(context) - for_ = self.maybe_dotted(for_) - containment = self.maybe_dotted(containment) - - if not view: - if renderer: - def view(context, request): - return {} - else: - raise ConfigurationError('"view" was not specified and ' - 'no "renderer" specified') - - if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'): - # b/w compat for 1.0 - request_method = request_type - request_type = None - - if request_type is not None: - request_type = self.maybe_dotted(request_type) - if not IInterface.providedBy(request_type): - raise ConfigurationError( - 'request_type must be an interface, not %s' % request_type) - - request_iface = IRequest - - if route_name is not None: - request_iface = self.registry.queryUtility(IRouteRequest, - name=route_name) - if request_iface is None: - deferred_views = getattr(self.registry, - 'deferred_route_views', None) - if deferred_views is None: - deferred_views = self.registry.deferred_route_views = {} - info = dict( - view=view, name=name, for_=for_, permission=permission, - request_type=request_type, route_name=route_name, - request_method=request_method, request_param=request_param, - containment=containment, attr=attr, - renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept, - header=header, path_info=path_info, - custom_predicates=custom_predicates, context=context, - _info=u'' - ) - view_info = deferred_views.setdefault(route_name, []) - view_info.append(info) - return - - order, predicates, phash = _make_predicates(xhr=xhr, - request_method=request_method, path_info=path_info, - request_param=request_param, header=header, accept=accept, - containment=containment, request_type=request_type, - custom=custom_predicates) - - if permission is _marker: - # intent: will be None if no default permission is registered - permission = self.registry.queryUtility(IDefaultPermission) - - derived_view = self._derive_view(view, permission, predicates, attr, - renderer, wrapper, name, accept, order, - phash) - - if context is None: - context = for_ - - r_context = context - if r_context is None: - r_context = Interface - if not IInterface.providedBy(r_context): - r_context = implementedBy(r_context) - - registered = self.registry.adapters.registered - - # A multiviews is a set of views which are registered for - # exactly the same context type/request type/name triad. Each - # consituent view in a multiview differs only by the - # predicates which it possesses. - - # To find a previously registered view for a context - # type/request type/name triad, we need to use the - # ``registered`` method of the adapter registry rather than - # ``lookup``. ``registered`` ignores interface inheritance - # for the required and provided arguments, returning only a - # view registered previously with the *exact* triad we pass - # in. - - # We need to do this three times, because we use three - # different interfaces as the ``provided`` interface while - # doing registrations, and ``registered`` performs exact - # matches on all the arguments it receives. - - old_view = None - - for view_type in (IView, ISecuredView, IMultiView): - old_view = registered((IViewClassifier, request_iface, r_context), - view_type, name) - if old_view is not None: - break - - isexc = isexception(context) - - def regclosure(): - if hasattr(view, '__call_permissive__'): - view_iface = ISecuredView - else: - view_iface = IView - self.registry.registerAdapter( - derived_view, - (IViewClassifier, request_iface, context), - view_iface, name, info=_info) - if isexc: - self.registry.registerAdapter( - derived_view, - (IExceptionViewClassifier, request_iface, context), - view_iface, name, info=_info) - - is_multiview = IMultiView.providedBy(old_view) - old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) - - if old_view is None: - # - No component was yet registered for any of our I*View - # interfaces exactly; this is the first view for this - # triad. - regclosure() - - elif (not is_multiview) and (old_phash == phash): - # - A single view component was previously registered with - # the same predicate hash as this view; this registration - # is therefore an override. - regclosure() - - else: - # - A view or multiview was already registered for this - # triad, and the new view is not an override. - - # XXX we could try to be more efficient here and register - # a non-secured view for a multiview if none of the - # multiview's consituent views have a permission - # associated with them, but this code is getting pretty - # rough already - if is_multiview: - multiview = old_view - else: - multiview = MultiView(name) - old_accept = getattr(old_view, '__accept__', None) - old_order = getattr(old_view, '__order__', MAX_ORDER) - multiview.add(old_view, old_order, old_accept, old_phash) - multiview.add(derived_view, order, accept, phash) - for view_type in (IView, ISecuredView): - # unregister any existing views - self.registry.adapters.unregister( - (IViewClassifier, request_iface, r_context), - view_type, name=name) - if isexc: - self.registry.adapters.unregister( - (IExceptionViewClassifier, request_iface, r_context), - view_type, name=name) - self.registry.registerAdapter( - multiview, - (IViewClassifier, request_iface, context), - IMultiView, name=name, info=_info) - if isexc: - self.registry.registerAdapter( - multiview, - (IExceptionViewClassifier, request_iface, context), - IMultiView, name=name, info=_info) - - def add_route(self, - name, - pattern=None, - view=None, - view_for=None, - permission=None, - factory=None, - for_=None, - header=None, - xhr=False, - accept=None, - path_info=None, - request_method=None, - request_param=None, - traverse=None, - custom_predicates=(), - view_permission=None, - renderer=None, - view_renderer=None, - view_context=None, - view_attr=None, - use_global_views=False, - path=None, - pregenerator=None, - _info=u''): - """ Add a :term:`route configuration` to the current - configuration state, as well as possibly a :term:`view - configuration` to be used to specify a :term:`view callable` - that will be invoked when this route matches. The arguments - to this method are divided into *predicate*, *non-predicate*, - and *view-related* types. :term:`Route predicate` arguments - narrow the circumstances in which a route will be match a - request; non-predicate arguments are informational. - - Non-Predicate Arguments - - name - - The name of the route, e.g. ``myroute``. This attribute is - required. It must be unique among all defined routes in a given - application. - - factory - - A Python object (often a function or a class) or a - :term:`dotted Python name` which refers to the same object - that will generate a :mod:`repoze.bfg` :term:`context` - object when this route matches. For example, - ``mypackage.models.MyFactoryClass``. If this argument is - not specified, a default root factory will be used. - - traverse - - If you would like to cause the :term:`context` to be - something other than the :term:`root` object when this route - matches, you can spell a traversal pattern as the - ``traverse`` argument. This traversal pattern will be used - as the traversal path: traversal will begin at the root - object implied by this route (either the global root, or the - object returned by the ``factory`` associated with this - route). - - The syntax of the ``traverse`` argument is the same as it is - for ``pattern``. For example, if the ``pattern`` provided to - ``add_route`` is ``articles/:article/edit``, and the - ``traverse`` argument provided to ``add_route`` is - ``/:article``, when a request comes in that causes the route - to match in such a way that the ``article`` match value is - '1' (when the request URI is ``/articles/1/edit``), the - traversal path will be generated as ``/1``. This means that - the root object's ``__getitem__`` will be called with the - name ``1`` during the traversal phase. If the ``1`` object - exists, it will become the :term:`context` of the request. - :ref:`traversal_chapter` has more information about - traversal. - - If the traversal path contains segment marker names which - are not present in the ``pattern`` argument, a runtime error - will occur. The ``traverse`` pattern should not contain - segment markers that do not exist in the ``pattern`` - argument. - - A similar combining of routing and traversal is available - when a route is matched which contains a ``*traverse`` - remainder marker in its pattern (see - :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` - argument to add_route allows you to associate route patterns - with an arbitrary traversal path without using a a - ``*traverse`` remainder marker; instead you can use other - match information. - - Note that the ``traverse`` argument to ``add_route`` is - ignored when attached to a route that has a ``*traverse`` - remainder marker in its pattern. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.3. - - pregenerator - - This option should be a callable object that implements the - :class:`repoze.bfg.interfaces.IRoutePregenerator` - interface. A :term:`pregenerator` is a callable called by - the :mod:`repoze.bfg.url.route_url` function to augment or - replace the arguments it is passed when generating a URL - for the route. This is a feature not often used directly - by applications, it is meant to be hooked by frameworks - that use :mod:`repoze.bfg` as a base. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.3. - - Predicate Arguments - - pattern - - The pattern of the route e.g. ``ideas/:idea``. This - argument is required. See :ref:`route_path_pattern_syntax` - for information about the syntax of route patterns. If the - pattern doesn't match the current URL, route matching - continues. - - .. note:: For backwards compatibility purposes (as of - :mod:`repoze.bfg` 1.3), a ``path`` keyword argument - passed to this function will be used to represent the - pattern value if the ``pattern`` argument is ``None``. - If both ``path`` and ``pattern`` are passed, ``pattern`` - wins. - - xhr - - This value should be either ``True`` or ``False``. If this - value is specified and is ``True``, the :term:`request` must - possess an ``HTTP_X_REQUESTED_WITH`` (aka - ``X-Requested-With``) header for this route to match. This - is useful for detecting AJAX requests issued from jQuery, - Prototype and other Javascript libraries. If this predicate - returns ``False``, route matching continues. - - request_method - - A string representing an HTTP method name, e.g. ``GET``, - ``POST``, ``HEAD``, ``DELETE``, ``PUT``. If this argument - is not specified, this route will match if the request has - *any* request method. If this predicate returns ``False``, - route matching continues. - - path_info - - This value represents a regular expression pattern that will - be tested against the ``PATH_INFO`` WSGI environment - variable. If the regex matches, this predicate will return - ``True``. If this predicate returns ``False``, route - matching continues. - - request_param - - This value can be any string. A view declaration with this - argument ensures that the associated route will only match - when the request has a key in the ``request.params`` - dictionary (an HTTP ``GET`` or ``POST`` variable) that has a - name which matches the supplied value. If the value - supplied as the argument has a ``=`` sign in it, - e.g. ``request_params="foo=123"``, then the key - (``foo``) must both exist in the ``request.params`` dictionary, and - the value must match the right hand side of the expression (``123``) - for the route to "match" the current request. If this predicate - returns ``False``, route matching continues. - - header - - This argument represents an HTTP header name or a header - name/value pair. If the argument contains a ``:`` (colon), - it will be considered a name/value pair - (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If - the value contains a colon, the value portion should be a - regular expression. If the value does not contain a colon, - the entire value will be considered to be the header name - (e.g. ``If-Modified-Since``). If the value evaluates to a - header name only without a value, the header specified by - the name must be present in the request for this predicate - to be true. If the value evaluates to a header name/value - pair, the header specified by the name must be present in - the request *and* the regular expression specified as the - value must match the header value. Whether or not the value - represents a header name or a header name/value pair, the - case of the header name is not significant. If this - predicate returns ``False``, route matching continues. - - accept - - This value represents a match query for one or more - mimetypes in the ``Accept`` HTTP request header. If this - value is specified, it must be in one of the following - forms: a mimetype match token in the form ``text/plain``, a - wildcard mimetype match token in the form ``text/*`` or a - match-all wildcard mimetype match token in the form ``*/*``. - If any of the forms matches the ``Accept`` header of the - request, this predicate will be true. If this predicate - returns ``False``, route matching continues. - - custom_predicates - - This value should be a sequence of references to custom - predicate callables. Use custom predicates when no set of - predefined predicates does what you need. Custom predicates - can be combined with predefined predicates as necessary. - Each custom predicate callable should accept two arguments: - ``info`` and ``request`` and should return either ``True`` - or ``False`` after doing arbitrary evaluation of the info - and/or the request. If all custom and non-custom predicate - callables return ``True`` the associated route will be - considered viable for a given request. If any predicate - callable returns ``False``, route matching continues. Note - that the value ``info`` passed to a custom route predicate - is a dictionary containing matching information; see - :ref:`custom_route_predicates` for more information about - ``info``. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. - - .. note:: The ``info`` argument passed to a custom predicate - in versions prior to :mod:`repoze.bfg` 1.3 was - always ``None``. - - View-Related Arguments - - view - - A Python object or :term:`dotted Python name` to the same - object that will be used as a view callable when this route - matches. e.g. ``mypackage.views.my_view``. - - view_context - - A class or an :term:`interface` or :term:`dotted Python - name` to the same object which the :term:`context` of the - view should match for the view named by the route to be - used. This argument is only useful if the ``view`` - attribute is used. If this attribute is not specified, the - default (``None``) will be used. - - If the ``view`` argument is not provided, this argument has - no effect. - - This attribute can also be spelled as ``for_`` or ``view_for``. - - view_permission - - The permission name required to invoke the view associated - with this route. e.g. ``edit``. (see - :ref:`using_security_with_urldispatch` for more information - about permissions). - - If the ``view`` attribute is not provided, this argument has - no effect. - - This argument can also be spelled as ``permission``. - - view_renderer - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``). If the renderer value is a - single term (does not contain a dot ``.``), the specified - term will be used to look up a renderer implementation, and - that renderer implementation will be used to construct a - response from the view return value. If the renderer term - contains a dot (``.``), the specified term will be treated - as a path, and the filename extension of the last element in - the path will be used to look up the renderer - implementation, which will be passed the full path. The - renderer implementation will be used to construct a response - from the view return value. See - :ref:`views_which_use_a_renderer` for more information. - - If the ``view`` argument is not provided, this argument has - no effect. - - This argument can also be spelled as ``renderer``. - - view_attr - - The view machinery defaults to using the ``__call__`` method - of the view callable (or the function itself, if the view - callable is a function) to obtain a response dictionary. - The ``attr`` value allows you to vary the method attribute - used to obtain the response. For example, if your view was - a class, and the class has a method named ``index`` and you - wanted to use this method instead of the class' ``__call__`` - method to return the response, you'd say ``attr="index"`` in - the view configuration for the view. This is - most useful when the view definition is a class. - - If the ``view`` argument is not provided, this argument has no - effect. - - use_global_views - - When a request matches this route, and view lookup cannot - find a view which has a ``route_name`` predicate argument - that matches the route, try to fall back to using a view - that otherwise matches the context, request, and view name - (but which does not match the route_name predicate). - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.2. - - """ - # these are route predicates; if they do not match, the next route - # in the routelist will be tried - ignored, predicates, ignored = _make_predicates( - xhr=xhr, - request_method=request_method, - path_info=path_info, - request_param=request_param, - header=header, - accept=accept, - traverse=traverse, - custom=custom_predicates - ) - - - request_iface = self.registry.queryUtility(IRouteRequest, name=name) - if request_iface is None: - bases = use_global_views and (IRequest,) or () - request_iface = route_request_iface(name, bases) - self.registry.registerUtility( - request_iface, IRouteRequest, name=name) - deferred_views = getattr(self.registry, 'deferred_route_views', {}) - view_info = deferred_views.pop(name, ()) - for info in view_info: - self.add_view(**info) - - if view: - if view_context is None: - view_context = view_for - if view_context is None: - view_context = for_ - view_permission = view_permission or permission - view_renderer = view_renderer or renderer - self.add_view( - permission=view_permission, - context=view_context, - view=view, - name='', - route_name=name, - renderer=view_renderer, - attr=view_attr, - _info=_info, - ) - - mapper = self.get_routes_mapper() - - factory = self.maybe_dotted(factory) - if pattern is None: - pattern = path - if pattern is None: - raise ConfigurationError('"pattern" argument may not be None') - return mapper.connect(name, pattern, factory, predicates=predicates, - pregenerator=pregenerator) - - def get_routes_mapper(self): - """ Return the :term:`routes mapper` object associated with - this configurator's :term:`registry`.""" - mapper = self.registry.queryUtility(IRoutesMapper) - if mapper is None: - mapper = RoutesMapper() - self.registry.registerUtility(mapper, IRoutesMapper) - return mapper - - def scan(self, package=None, categories=None, _info=u''): - """ Scan a Python package and any of its subpackages for - objects marked with :term:`configuration decoration` such as - :class:`repoze.bfg.view.bfg_view`. Any decorated object found - will influence the current configuration state. - - The ``package`` argument should be a Python :term:`package` or - module object (or a :term:`dotted Python name` which refers to - such a package or module). If ``package`` is ``None``, the - package of the *caller* is used. - - The ``categories`` argument, if provided, should be the - :term:`Venusian` 'scan categories' to use during scanning. - Providing this argument is not often necessary; specifying - scan categories is an extremely advanced usage. - - By default, ``categories`` is ``None`` which will execute - *all* Venusian decorator callbacks including - :mod:`repoze.bfg`-related decorators such as ``bfg_view``. If - this is not desirable because the codebase has other - Venusian-using decorators that aren't meant to be invoked - during a particular scan, use ``('bfg',)`` as a ``categories`` - value to limit the execution of decorator callbacks to only - those registered by :mod:`repoze.bfg` itself. Or pass a - sequence of Venusian scan categories as necessary - (e.g. ``('bfg', 'myframework')``) to limit the decorators - called to the set of categories required. - """ - package = self.maybe_dotted(package) - if package is None: # pragma: no cover - package = caller_package() - - scanner = self.venusian.Scanner(config=self) - scanner.scan(package, categories=categories) - - def add_renderer(self, name, factory, _info=u''): - """ - Add a :mod:`repoze.bfg` :term:`renderer` factory to the - current configuration state. - - The ``name`` argument is the renderer name. - - The ``factory`` argument is Python reference to an - implementation of a :term:`renderer` factory or a - :term:`dotted Python name` to same. - - Note that this function must be called *before* any - ``add_view`` invocation that names the renderer name as an - argument. As a result, it's usually a better idea to pass - globally used renderers into the ``Configurator`` constructor - in the sequence of renderers passed as ``renderer`` than it is - to use this method. - """ - factory = self.maybe_dotted(factory) - self.registry.registerUtility( - factory, IRendererFactory, name=name, info=_info) - - def override_resource(self, to_override, override_with, - _info=u'', _override=None,): - """ Add a :mod:`repoze.bfg` resource override to the current - configuration state. - - ``to_override`` is a :term:`resource specification` to the - resource being overridden. - - ``override_with`` is a :term:`resource specification` to the - resource that is performing the override. - - See :ref:`resources_chapter` for more - information about resource overrides.""" - if to_override == override_with: - raise ConfigurationError('You cannot override a resource with ' - 'itself') - - package = to_override - path = '' - if ':' in to_override: - package, path = to_override.split(':', 1) - - override_package = override_with - override_prefix = '' - if ':' in override_with: - override_package, override_prefix = override_with.split(':', 1) - - if path and path.endswith('/'): - if override_prefix and (not override_prefix.endswith('/')): - raise ConfigurationError( - 'A directory cannot be overridden with a file (put a slash ' - 'at the end of override_with if necessary)') - - if override_prefix and override_prefix.endswith('/'): - if path and (not path.endswith('/')): - raise ConfigurationError( - 'A file cannot be overridden with a directory (put a slash ' - 'at the end of to_override if necessary)') - - __import__(package) - __import__(override_package) - package = sys.modules[package] - override_package = sys.modules[override_package] - - override = _override or self._override # test jig - override(package, path, override_package, override_prefix, - _info=_info) - - def set_forbidden_view(self, view=None, attr=None, renderer=None, - wrapper=None, _info=u''): - """ Add a default forbidden view to the current configuration - state. - - .. warning:: This method has been deprecated in - :mod:`repoze.bfg` 1.3. *Do not use it for new development; - it should only be used to support older code bases which - depend upon it.* See :ref:`changing_the_forbidden_view` to - see how a forbidden view should be registered in new - projects. - - .. note:: For backwards compatibility with :mod:`repoze.bfg` - 1.2, unlike an 'exception view' as described in - :ref:`exception_views`, a ``context, request`` view - callable registered using this API should not expect to - receive the exception as its first (``context``) argument. - Instead it should expect to receive the 'real' context as - found via context-finding or ``None`` if no context could - be found. The exception causing the registered view to be - called is however still available as ``request.exception``. - - The ``view`` argument should be a :term:`view callable` or a - :term:`dotted Python name` which refers to a view callable. - - The ``attr`` argument should be the attribute of the view - callable used to retrieve the response (see the ``add_view`` - method's ``attr`` argument for a description). - - The ``renderer`` argument should be the name of (or path to) a - :term:`renderer` used to generate a response for this view - (see the - :meth:`repoze.bfg.configuration.Configurator.add_view` - method's ``renderer`` argument for information about how a - configurator relates to a renderer). - - The ``wrapper`` argument should be the name of another view - which will wrap this view when rendered (see the ``add_view`` - method's ``wrapper`` argument for a description).""" - view = self._derive_view(view, attr=attr, renderer_name=renderer) - def bwcompat_view(context, request): - context = getattr(request, 'context', None) - return view(context, request) - return self.add_view(bwcompat_view, context=Forbidden, - wrapper=wrapper, _info=_info) - - def set_notfound_view(self, view=None, attr=None, renderer=None, - wrapper=None, _info=u''): - """ Add a default not found view to the current configuration - state. - - .. warning:: This method has been deprecated in - :mod:`repoze.bfg` 1.3. *Do not use it for new development; - it should only be used to support older code bases which - depend upon it.* See :ref:`changing_the_notfound_view` to - see how a not found view should be registered in new - projects. - - ..note:: For backwards compatibility with :mod:`repoze.bfg` - 1.2, unlike an 'exception view' as described in - :ref:`exception_views`, a ``context, request`` view - callable registered using this API should not expect to - receive the exception as its first (``context``) argument. - Instead it should expect to receive the 'real' context as - found via context-finding or ``None`` if no context could - be found. The exception causing the registered view to be - called is however still available as ``request.exception``. - - The ``view`` argument should be a :term:`view callable` or a - :term:`dotted Python name` which refers to a view callable. - - The ``attr`` argument should be the attribute of the view - callable used to retrieve the response (see the ``add_view`` - method's ``attr`` argument for a description). - - The ``renderer`` argument should be the name of (or path to) a - :term:`renderer` used to generate a response for this view - (see the - :meth:`repoze.bfg.configuration.Configurator.add_view` - method's ``renderer`` argument for information about how a - configurator relates to a renderer). - - The ``wrapper`` argument should be the name of another view - which will wrap this view when rendered (see the ``add_view`` - method's ``wrapper`` argument for a description). - """ - view = self._derive_view(view, attr=attr, renderer_name=renderer) - def bwcompat_view(context, request): - context = getattr(request, 'context', None) - return view(context, request) - return self.add_view(bwcompat_view, context=NotFound, - wrapper=wrapper, _info=_info) - - def set_request_factory(self, factory): - """ The object passed as ``factory`` should be an object (or a - :term:`dotted Python name` which refers to an object) which - will be used by the :mod:`repoze.bfg` router to create all - request objects. This factory object must have the same - methods and attributes as the - :class:`repoze.bfg.request.Request` class (particularly - ``__call__``, and ``blank``). - - .. note:: Using the :meth:``request_factory`` argument to the - :class:`repoze.bfg.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - factory = self.maybe_dotted(factory) - self.registry.registerUtility(factory, IRequestFactory) - - def set_renderer_globals_factory(self, factory): - """ The object passed as ``factory`` should be an callable (or - a :term:`dotted Python name` which refers to an callable) that - will be used by the :mod:`repoze.bfg` rendering machinery as a - renderers global factory (see :ref:`adding_renderer_globals`). - - The ``factory`` callable must accept a single argument named - ``system`` (which will be a dictionary) and it must return a - dictionary. When an application uses a renderer, the - factory's return dictionary will be merged into the ``system`` - dictionary, and therefore will be made available to the code - which uses the renderer. - - .. note:: Using the :meth:`renderer_globals_factory` - argument to the - :class:`repoze.bfg.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - factory = self.maybe_dotted(factory) - self.registry.registerUtility(factory, IRendererGlobalsFactory) - - def set_locale_negotiator(self, negotiator): - """ - Set the :term:`locale negotiator` for this application. The - :term:`locale negotiator` is a callable which accepts a - :term:`request` object and which returns a :term:`locale - name`. The ``negotiator`` argument should be the locale - negotiator implementation or a :term:`dotted Python name` - which refers to such an implementation. - - Later calls to this method override earlier calls; there can - be only one locale negotiator active at a time within an - application. See :ref:`activating_translation` for more - information. - - .. note: This API is new as of :mod:`repoze.bfg` version 1.3. - - .. note:: Using the ``locale_negotiator`` argument to the - :class:`repoze.bfg.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - negotiator = self.maybe_dotted(negotiator) - self.registry.registerUtility(negotiator, ILocaleNegotiator) - - def set_default_permission(self, permission): - """ - Set the default permission to be used by all subsequent - :term:`view configuration` registrations. ``permission`` - should be a :term:`permission` string to be used as the - default permission. An example of a permission - string:``'view'``. Adding a default permission makes it - unnecessary to protect each view configuration with an - explicit permission, unless your application policy requires - some exception for a particular view. - - If a default permission is *not* set, views represented by - view configuration registrations which do not explicitly - declare a permission will be executable by entirely anonymous - users (any authorization policy is ignored). - - Later calls to this method override earlier calls; there can - be only one default permission active at a time within an - application. - - See also :ref:`setting_a_default_permission`. - - .. note: This API is new as of :mod:`repoze.bfg` version 1.3. - - .. note:: Using the ``default_permission`` argument to the - :class:`repoze.bfg.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - self.registry.registerUtility(permission, IDefaultPermission) - - def add_translation_dirs(self, *specs): - """ Add one or more :term:`translation directory` paths to the - current configuration state. The ``specs`` argument is a - sequence that may contain absolute directory paths - (e.g. ``/usr/share/locale``) or :term:`resource specification` - names naming a directory path (e.g. ``some.package:locale``) - or a combination of the two. - - Example: - - .. code-block:: python - - add_translations_dirs('/usr/share/locale', 'some.package:locale') - - .. note: This API is new as of :mod:`repoze.bfg` version 1.3. - """ - for spec in specs: - - package_name, filename = self._split_spec(spec) - if package_name is None: # absolute filename - directory = filename - else: - __import__(package_name) - package = sys.modules[package_name] - directory = os.path.join(package_path(package), filename) - - if not os.path.isdir(os.path.realpath(directory)): - raise ConfigurationError('"%s" is not a directory' % directory) - - tdirs = self.registry.queryUtility(ITranslationDirectories) - if tdirs is None: - tdirs = [] - self.registry.registerUtility(tdirs, ITranslationDirectories) - - tdirs.insert(0, directory) - - if specs: - - # We actually only need an IChameleonTranslate function - # utility to be registered zero or one times. We register the - # same function once for each added translation directory, - # which does too much work, but has the same effect. - - def translator(msg): - request = get_current_request() - localizer = get_localizer(request) - return localizer.translate(msg) - - ctranslate = ChameleonTranslate(translator) - self.registry.registerUtility(ctranslate, IChameleonTranslate) - - def add_static_view(self, name, path, **kw): - """ Add a view used to render static resources such as images - and CSS files. - - The ``name`` argument is a string representing :term:`view - name` of the view which is registered. It may alternately be - a *url prefix*. - - The ``path`` argument is the path on disk where the static - files reside. This can be an absolute path, a - package-relative path, or a :term:`resource specification`. - - The ``cache_max_age`` keyword argument is input to set the - ``Expires`` and ``Cache-Control`` headers for static resources - served. Note that this argument has no effect when the - ``name`` is a *url prefix*. - - *Usage* - - The ``add_static_view`` function is typically used in - conjunction with the :func:`repoze.bfg.url.static_url` - function. ``add_static_view`` adds a view which renders a - static resource when some URL is visited; - :func:`repoze.bfg.url.static_url` generates a URL to that - resource. - - The ``name`` argument to ``add_static_view`` is usually a - :term:`view name`. When this is the case, the - :func:`repoze.bfg.url.static_url` API will generate a URL - which points to a BFG view, which will serve up a set of - resources that live in the package itself. For example: - - .. code-block:: python - - add_static_view('images', 'mypackage:images/') - - Code that registers such a view can generate URLs to the view - via :func:`repoze.bfg.url.static_url`: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that represents a simple view name, as it is above, subsequent - calls to :func:`repoze.bfg.url.static_url` with paths that - start with the ``path`` argument passed to ``add_static_view`` - will generate a URL something like ``http:///images/logo.png``, which will cause the ``logo.png`` file - in the ``images`` subdirectory of the ``mypackage`` package to - be served. - - ``add_static_view`` can alternately be used with a ``name`` - argument which is a *URL*, causing static resources to be - served from an external webserver. This happens when the - ``name`` argument is a URL (detected as any string with a - slash in it). In this mode, the ``name`` is used as the URL - prefix when generating a URL using - :func:`repoze.bfg.url.static_url`. For example, if - ``add_static_view`` is called like so: - - .. code-block:: python - - add_static_view('http://example.com/images', 'mypackage:images/') - - Subsequently, the URLs generated by - :func:`repoze.bfg.url.static_url` for that static view will be - prefixed with ``http://example.com/images``: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that is the URL prefix ``http://example.com/images``, - subsequent calls to :func:`repoze.bfg.url.static_url` with - paths that start with the ``path`` argument passed to - ``add_static_view`` will generate a URL something like - ``http://example.com/logo.png``. The external webserver - listening on ``example.com`` must be itself configured to - respond properly to such a request. - - See :ref:`static_resources_section` for more information. - """ - spec = self._make_spec(path) - info = self.registry.queryUtility(IStaticURLInfo) - if info is None: - info = StaticURLInfo(self) - self.registry.registerUtility(info, IStaticURLInfo) - - info.add(name, spec, **kw) - - # testing API - def testing_securitypolicy(self, userid=None, groupids=(), - permissive=True): - """Unit/integration testing helper: Registers a pair of faux - :mod:`repoze.bfg` security policies: a :term:`authentication - policy` and a :term:`authorization policy`. - - The behavior of the registered :term:`authorization policy` - depends on the ``permissive`` argument. If ``permissive`` is - true, a permissive :term:`authorization policy` is registered; - this policy allows all access. If ``permissive`` is false, a - nonpermissive :term:`authorization policy` is registered; this - policy denies all access. - - The behavior of the registered :term:`authentication policy` - depends on the values provided for the ``userid`` and - ``groupids`` argument. The authentication policy will return - the userid identifier implied by the ``userid`` argument and - the group ids implied by the ``groupids`` argument when the - :func:`repoze.bfg.security.authenticated_userid` or - :func:`repoze.bfg.security.effective_principals` APIs are - used. - - This function is most useful when testing code that uses - the APIs named :func:`repoze.bfg.security.has_permission`, - :func:`repoze.bfg.security.authenticated_userid`, - :func:`repoze.bfg.security.effective_principals`, and - :func:`repoze.bfg.security.principals_allowed_by_permission`. - """ - from repoze.bfg.testing import DummySecurityPolicy - policy = DummySecurityPolicy(userid, groupids, permissive) - self.registry.registerUtility(policy, IAuthorizationPolicy) - self.registry.registerUtility(policy, IAuthenticationPolicy) - - def testing_models(self, models): - """Unit/integration testing helper: registers a dictionary of - :term:`model` objects that can be resolved via the - :func:`repoze.bfg.traversal.find_model` API. - - The :func:`repoze.bfg.traversal.find_model` API is called with - a path as one of its arguments. If the dictionary you - register when calling this method contains that path as a - string key (e.g. ``/foo/bar`` or ``foo/bar``), the - corresponding value will be returned to ``find_model`` (and - thus to your code) when - :func:`repoze.bfg.traversal.find_model` is called with an - equivalent path string or tuple. - """ - class DummyTraverserFactory: - def __init__(self, context): - self.context = context - - def __call__(self, request): - path = request['PATH_INFO'] - ob = models[path] - traversed = traversal_path(path) - return {'context':ob, 'view_name':'','subpath':(), - 'traversed':traversed, 'virtual_root':ob, - 'virtual_root_path':(), 'root':ob} - self.registry.registerAdapter(DummyTraverserFactory, (Interface,), - ITraverser) - return models - - def testing_add_subscriber(self, event_iface=None): - """Unit/integration testing helper: Registers a - :term:`subscriber` which listens for events of the type - ``event_iface``. This method returns a list object which is - appended to by the subscriber whenever an event is captured. - - When an event is dispatched that matches the value implied by - the ``event_iface`` argument, that event will be appended to - the list. You can then compare the values in the list to - expected event notifications. This method is useful when - testing code that wants to call - :meth:`repoze.bfg.registry.Registry.notify`, - :func:`zope.component.event.dispatch` or - :func:`zope.component.event.objectEventNotify`. - - The default value of ``event_iface`` (``None``) implies a - subscriber registered for *any* kind of event. - """ - event_iface = self.maybe_dotted(event_iface) - L = [] - def subscriber(*event): - L.extend(event) - self.add_subscriber(subscriber, event_iface) - return L - - def testing_add_renderer(self, path, renderer=None): - """Unit/integration testing helper: register a renderer at - ``path`` (usually a relative filename ala ``templates/foo.pt`` - or a resource specification) and return the renderer object. - If the ``renderer`` argument is None, a 'dummy' renderer will - be used. This function is useful when testing code that calls - the :func:`repoze.bfg.renderers.render` function or - :func:`repoze.bfg.renderers.render_to_response` function or - any other ``render_*`` or ``get_*`` API of the - :mod:`repoze.bfg.renderers` module. - - Note that calling this method for with a ``path`` argument - representing a renderer factory type (e.g. for ``foo.pt`` - usually implies the ``chameleon_zpt`` renderer factory) - clobbers any existing renderer factory registered for that - type. - - .. note:: This method is also available under the alias - ``testing_add_template`` (an older name for it). - - .. note:: This method is new in :mod:`repoze.bfg` 1.3 (the - method named ``testing_add_template`` had the same signature - and purpose in previous releases).. - """ - from repoze.bfg.testing import DummyRendererFactory - helper = RendererHelper(path, registry=self.registry) - factory = helper.factory - if not isinstance(factory, DummyRendererFactory): - factory = DummyRendererFactory(helper.renderer_type, helper.factory) - self.registry.registerUtility(factory, IRendererFactory, - name=helper.renderer_type) - - from repoze.bfg.testing import DummyTemplateRenderer - if renderer is None: - renderer = DummyTemplateRenderer() - factory.add(helper.renderer_name, renderer) - return renderer - - testing_add_template = testing_add_renderer - -def _make_predicates(xhr=None, request_method=None, path_info=None, - request_param=None, header=None, accept=None, - containment=None, request_type=None, - traverse=None, custom=()): - - # PREDICATES - # ---------- - # - # Given an argument list, a predicate list is computed. - # Predicates are added to a predicate list in (presumed) - # computation expense order. All predicates associated with a - # view or route must evaluate true for the view or route to - # "match" during a request. Elsewhere in the code, we evaluate - # predicates using a generator expression. The fastest predicate - # should be evaluated first, then the next fastest, and so on, as - # if one returns false, the remainder of the predicates won't need - # to be evaluated. - # - # While we compute predicates, we also compute a predicate hash - # (aka phash) that can be used by a caller to identify identical - # predicate lists. - # - # ORDERING - # -------- - # - # A "order" is computed for the predicate list. An order is - # a scoring. - # - # Each predicate is associated with a weight value, which is a - # multiple of 2. The weight of a predicate symbolizes the - # relative potential "importance" of the predicate to all other - # predicates. A larger weight indicates greater importance. - # - # All weights for a given predicate list are bitwise ORed together - # to create a "score"; this score is then subtracted from - # MAX_ORDER and divided by an integer representing the number of - # predicates+1 to determine the order. - # - # The order represents the ordering in which a "multiview" ( a - # collection of views that share the same context/request/name - # triad but differ in other ways via predicates) will attempt to - # call its set of views. Views with lower orders will be tried - # first. The intent is to a) ensure that views with more - # predicates are always evaluated before views with fewer - # predicates and b) to ensure a stable call ordering of views that - # share the same number of predicates. Views which do not have - # any predicates get an order of MAX_ORDER, meaning that they will - # be tried very last. - - predicates = [] - weights = [] - h = md5() - - if xhr: - def xhr_predicate(context, request): - return request.is_xhr - weights.append(1 << 1) - predicates.append(xhr_predicate) - h.update('xhr:%r' % bool(xhr)) - - if request_method is not None: - def request_method_predicate(context, request): - return request.method == request_method - weights.append(1 << 2) - predicates.append(request_method_predicate) - h.update('request_method:%r' % request_method) - - if path_info is not None: - try: - path_info_val = re.compile(path_info) - except re.error, why: - raise ConfigurationError(why[0]) - def path_info_predicate(context, request): - return path_info_val.match(request.path_info) is not None - weights.append(1 << 3) - predicates.append(path_info_predicate) - h.update('path_info:%r' % path_info) - - if request_param is not None: - request_param_val = None - if '=' in request_param: - request_param, request_param_val = request_param.split('=', 1) - def request_param_predicate(context, request): - if request_param_val is None: - return request_param in request.params - return request.params.get(request_param) == request_param_val - weights.append(1 << 4) - predicates.append(request_param_predicate) - h.update('request_param:%r=%r' % (request_param, request_param_val)) - - if header is not None: - header_name = header - header_val = None - if ':' in header: - header_name, header_val = header.split(':', 1) - try: - header_val = re.compile(header_val) - except re.error, why: - raise ConfigurationError(why[0]) - def header_predicate(context, request): - if header_val is None: - return header_name in request.headers - val = request.headers.get(header_name) - if val is None: - return False - return header_val.match(val) is not None - weights.append(1 << 5) - predicates.append(header_predicate) - h.update('header:%r=%r' % (header_name, header_val)) - - if accept is not None: - def accept_predicate(context, request): - return accept in request.accept - weights.append(1 << 6) - predicates.append(accept_predicate) - h.update('accept:%r' % accept) - - if containment is not None: - def containment_predicate(context, request): - return find_interface(context, containment) is not None - weights.append(1 << 7) - predicates.append(containment_predicate) - h.update('containment:%r' % hash(containment)) - - if request_type is not None: - def request_type_predicate(context, request): - return request_type.providedBy(request) - weights.append(1 << 8) - predicates.append(request_type_predicate) - h.update('request_type:%r' % hash(request_type)) - - if traverse is not None: - # ``traverse`` can only be used as a *route* "predicate"; it - # adds 'traverse' to the matchdict if it's specified in the - # routing args. This causes the ModelGraphTraverser to use - # the resolved traverse pattern as the traversal path. - from repoze.bfg.urldispatch import _compile_route - _, tgenerate = _compile_route(traverse) - def traverse_predicate(context, request): - if 'traverse' in context: - return True - m = context['match'] - tvalue = tgenerate(m) - m['traverse'] = traversal_path(tvalue) - return True - # This isn't actually a predicate, it's just a infodict - # modifier that injects ``traverse`` into the matchdict. As a - # result, the ``traverse_predicate`` function above always - # returns True, and we don't need to update the hash or attach - # a weight to it - predicates.append(traverse_predicate) - - if custom: - for num, predicate in enumerate(custom): - predicates.append(predicate) - # using hash() here rather than id() is intentional: we - # want to allow custom predicates that are part of - # frameworks to be able to define custom __hash__ - # functions for custom predicates, so that the hash output - # of predicate instances which are "logically the same" - # may compare equal. - h.update('custom%s:%r' % (num, hash(predicate))) - weights.append(1 << 10) - - score = 0 - for bit in weights: - score = score | bit - order = (MAX_ORDER - score) / (len(predicates) + 1) - phash = h.hexdigest() - return order, predicates, phash - -class MultiView(object): - implements(IMultiView) - - def __init__(self, name): - self.name = name - self.media_views = {} - self.views = [] - self.accepts = [] - - def add(self, view, order, accept=None, phash=None): - if phash is not None: - for i, (s, v, h) in enumerate(list(self.views)): - if phash == h: - self.views[i] = (order, view, phash) - return - - if accept is None or '*' in accept: - self.views.append((order, view, phash)) - self.views.sort() - else: - subset = self.media_views.setdefault(accept, []) - subset.append((order, view, phash)) - subset.sort() - accepts = set(self.accepts) - accepts.add(accept) - self.accepts = list(accepts) # dedupe - - def get_views(self, request): - if self.accepts and hasattr(request, 'accept'): - accepts = self.accepts[:] - views = [] - while accepts: - match = request.accept.best_match(accepts) - if match is None: - break - subset = self.media_views[match] - views.extend(subset) - accepts.remove(match) - views.extend(self.views) - return views - return self.views - - def match(self, context, request): - for order, view, phash in self.get_views(request): - if not hasattr(view, '__predicated__'): - return view - if view.__predicated__(context, request): - return view - raise PredicateMismatch(self.name) - - def __permitted__(self, context, request): - view = self.match(context, request) - if hasattr(view, '__permitted__'): - return view.__permitted__(context, request) - return True - - def __call_permissive__(self, context, request): - view = self.match(context, request) - view = getattr(view, '__call_permissive__', view) - return view(context, request) - - def __call__(self, context, request): - for order, view, phash in self.get_views(request): - try: - return view(context, request) - except PredicateMismatch: - continue - raise PredicateMismatch(self.name) - -def decorate_view(wrapped_view, original_view): - if wrapped_view is original_view: - return False - wrapped_view.__module__ = original_view.__module__ - wrapped_view.__doc__ = original_view.__doc__ - try: - wrapped_view.__name__ = original_view.__name__ - except AttributeError: - wrapped_view.__name__ = repr(original_view) - try: - wrapped_view.__permitted__ = original_view.__permitted__ - except AttributeError: - pass - try: - wrapped_view.__call_permissive__ = original_view.__call_permissive__ - except AttributeError: - pass - try: - wrapped_view.__predicated__ = original_view.__predicated__ - except AttributeError: - pass - try: - wrapped_view.__accept__ = original_view.__accept__ - except AttributeError: - pass - try: - wrapped_view.__order__ = original_view.__order__ - except AttributeError: - pass - return True - -def requestonly(class_or_callable, attr=None): - """ Return true of the class or callable accepts only a request argument, - as opposed to something that accepts context, request """ - if attr is None: - attr = '__call__' - if inspect.isfunction(class_or_callable): - fn = class_or_callable - elif inspect.isclass(class_or_callable): - try: - fn = class_or_callable.__init__ - except AttributeError: - return False - else: - try: - fn = getattr(class_or_callable, attr) - except AttributeError: - return False - - try: - argspec = inspect.getargspec(fn) - except TypeError: - return False - - args = argspec[0] - defaults = argspec[3] - - if hasattr(fn, 'im_func'): - # it's an instance method - if not args: - return False - args = args[1:] - if not args: - return False - - if len(args) == 1: - return True - - elif args[0] == 'request': - if len(args) - len(defaults) == 1: - return True - - return False - -def is_response(ob): - if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and - hasattr(ob, 'status') ): - return True - return False - -def _map_view(view, attr=None, renderer_name=None, registry=None, package=None): - wrapped_view = view - - helper = None - if renderer_name: - helper = RendererHelper(renderer_name, - registry=registry, - package=package) - - if inspect.isclass(view): - # If the object we've located is a class, turn it into a - # function that operates like a Zope view (when it's invoked, - # construct an instance using 'context' and 'request' as - # position arguments, then immediately invoke the __call__ - # method of the instance with no arguments; __call__ should - # return an IResponse). - if requestonly(view, attr): - # its __init__ accepts only a single request argument, - # instead of both context and request - def _bfg_class_requestonly_view(context, request): - inst = view(request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = {'view':inst, 'renderer_name':renderer_name, - 'context':context, 'request':request} - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _bfg_class_requestonly_view - else: - # its __init__ accepts both context and request - def _bfg_class_view(context, request): - inst = view(context, request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = {'view':inst, 'renderer_name':renderer_name, - 'context':context, 'request':request} - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _bfg_class_view - - elif requestonly(view, attr): - # its __call__ accepts only a single request argument, - # instead of both context and request - def _bfg_requestonly_view(context, request): - if attr is None: - response = view(request) - else: - response = getattr(view, attr)(request) - - if helper is not None: - if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _bfg_requestonly_view - - elif attr: - def _bfg_attr_view(context, request): - response = getattr(view, attr)(context, request) - if helper is not None: - if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _bfg_attr_view - - elif helper is not None: - def _rendered_view(context, request): - response = view(context, request) - if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _rendered_view - - decorate_view(wrapped_view, view) - return wrapped_view - -def _owrap_view(view, viewname, wrapper_viewname): - if not wrapper_viewname: - return view - def _owrapped_view(context, request): - response = view(context, request) - request.wrapped_response = response - request.wrapped_body = response.body - request.wrapped_view = view - wrapped_response = render_view_to_response(context, request, - wrapper_viewname) - if wrapped_response is None: - raise ValueError( - 'No wrapper view named %r found when executing view ' - 'named %r' % (wrapper_viewname, viewname)) - return wrapped_response - decorate_view(_owrapped_view, view) - return _owrapped_view - -def _predicate_wrap(view, predicates): - if not predicates: - return view - def predicate_wrapper(context, request): - if all((predicate(context, request) for predicate in predicates)): - return view(context, request) - raise PredicateMismatch('predicate mismatch for view %s' % view) - def checker(context, request): - return all((predicate(context, request) for predicate in - predicates)) - predicate_wrapper.__predicated__ = checker - decorate_view(predicate_wrapper, view) - return predicate_wrapper - -def _secure_view(view, permission, authn_policy, authz_policy): - wrapped_view = view - if authn_policy and authz_policy and (permission is not None): - def _secured_view(context, request): - principals = authn_policy.effective_principals(request) - if authz_policy.permits(context, principals, permission): - return view(context, request) - msg = getattr(request, 'authdebug_message', - 'Unauthorized: %s failed permission check' % view) - raise Forbidden(msg) - _secured_view.__call_permissive__ = view - def _permitted(context, request): - principals = authn_policy.effective_principals(request) - return authz_policy.permits(context, principals, permission) - _secured_view.__permitted__ = _permitted - wrapped_view = _secured_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _authdebug_view(view, permission, authn_policy, authz_policy, settings, - logger): - wrapped_view = view - if settings and settings.get('debug_authorization', False): - def _authdebug_view(context, request): - view_name = getattr(request, 'view_name', None) - - if authn_policy and authz_policy: - if permission is None: - msg = 'Allowed (no permission registered)' - else: - principals = authn_policy.effective_principals(request) - msg = str(authz_policy.permits(context, principals, - permission)) - else: - msg = 'Allowed (no authorization policy in use)' - - view_name = getattr(request, 'view_name', None) - url = getattr(request, 'url', None) - msg = ('debug_authorization of url %s (view name %r against ' - 'context %r): %s' % (url, view_name, context, msg)) - logger and logger.debug(msg) - if request is not None: - request.authdebug_message = msg - return view(context, request) - - wrapped_view = _authdebug_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _attr_wrap(view, accept, order, phash): - # this is a little silly but we don't want to decorate the original - # function with attributes that indicate accept, order, and phash, - # so we use a wrapper - if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH): - return view # defaults - def attr_view(context, request): - return view(context, request) - attr_view.__accept__ = accept - attr_view.__order__ = order - attr_view.__phash__ = phash - decorate_view(attr_view, view) - return attr_view - -def isexception(o): - if IInterface.providedBy(o): - if IException.isEqualOrExtendedBy(o): - return True - return ( - isinstance(o, Exception) or - (inspect.isclass(o) and (issubclass(o, Exception))) - ) - -# note that ``options`` is a b/w compat alias for ``settings`` and -# ``Configurator`` is a testing dep inj -def make_app(root_factory, package=None, filename='configure.zcml', - settings=None, options=None, Configurator=Configurator): - """ Return a Router object, representing a fully configured - :mod:`repoze.bfg` WSGI application. - - .. warning:: Use of this function is deprecated as of - :mod:`repoze.bfg` 1.2. You should instead use a - :class:`repoze.bfg.configuration.Configurator` instance to - perform startup configuration as shown in - :ref:`configuration_narr`. - - ``root_factory`` must be a callable that accepts a :term:`request` - object and which returns a traversal root object. The traversal - root returned by the root factory is the *default* traversal root; - it can be overridden on a per-view basis. ``root_factory`` may be - ``None``, in which case a 'default default' traversal root is - used. - - ``package`` is a Python :term:`package` or module representing the - application's package. It is optional, defaulting to ``None``. - ``package`` may be ``None``. If ``package`` is ``None``, the - ``filename`` passed or the value in the ``options`` dictionary - named ``configure_zcml`` must be a) absolute pathname to a - :term:`ZCML` file that represents the application's configuration - *or* b) a :term:`resource specification` to a :term:`ZCML` file in - the form ``dotted.package.name:relative/file/path.zcml``. - - ``filename`` is the filesystem path to a ZCML file (optionally - relative to the package path) that should be parsed to create the - application registry. It defaults to ``configure.zcml``. It can - also be a ;term:`resource specification` in the form - ``dotted_package_name:relative/file/path.zcml``. Note that if any - value for ``configure_zcml`` is passed within the ``settings`` - dictionary, the value passed as ``filename`` will be ignored, - replaced with the ``configure_zcml`` value. - - ``settings``, if used, should be a dictionary containing runtime - settings (e.g. the key/value pairs in an app section of a - PasteDeploy file), with each key representing the option and the - key's value representing the specific option value, - e.g. ``{'reload_templates':True}``. Note that the keyword - parameter ``options`` is a backwards compatibility alias for the - ``settings`` keyword parameter. - """ - settings = settings or options or {} - zcml_file = settings.get('configure_zcml', filename) - config = Configurator(package=package, settings=settings, - root_factory=root_factory) - config.hook_zca() - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() - - -class DottedNameResolver(object): - """ This class resolves dotted name references to 'global' Python - objects (objects which can be imported) to those objects. - - Two dotted name styles are supported during deserialization: - - - ``pkg_resources``-style dotted names where non-module attributes - of a package are separated from the rest of the path using a ':' - e.g. ``package.module:attr``. - - - ``zope.dottedname``-style dotted names where non-module - attributes of a package are separated from the rest of the path - using a '.' e.g. ``package.module.attr``. - - These styles can be used interchangeably. If the serialization - contains a ``:`` (colon), the ``pkg_resources`` resolution - mechanism will be chosen, otherwise the ``zope.dottedname`` - resolution mechanism will be chosen. - - The constructor accepts a single argument named ``package`` which - should be a one of: - - - a Python module or package object - - - A fully qualified (not relative) dotted name to a module or package - - - The value ``None`` - - The ``package`` is used when relative dotted names are supplied to - the resolver's ``resolve`` and ``maybe_resolve`` methods. A - dotted name which has a ``.`` (dot) or ``:`` (colon) as its first - character is treated as relative. - - If the value ``None`` is supplied as the package name, the - resolver will only be able to resolve fully qualified (not - relative) names. Any attempt to resolve a relative name when the - ``package`` is ``None`` will result in an - :exc:`repoze.bfg.configuration.ConfigurationError` exception. - - If a *module* or *module name* (as opposed to a package or package - name) is supplied as ``package``, its containing package is - computed and this package used to derive the package name (all - names are resolved relative to packages, never to modules). For - example, if the ``package`` argument to this type was passed the - string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to - the ``resolve`` method, the resulting import would be for - ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module - object, not a package object. - - If a *package* or *package name* (as opposed to a module or module - name) is supplied as ``package``, this package will be used to - relative compute dotted names. For example, if the ``package`` - argument to this type was passed the string ``xml.dom``, and - ``.minidom`` is supplied to the ``resolve`` method, the resulting - import would be for ``xml.minidom``. - - When a dotted name cannot be resolved, a - :class:`repoze.bfg.exceptions.ConfigurationError` error is raised. - """ - def __init__(self, package): - if package is None: - self.package_name = None - self.package = None - else: - if isinstance(package, basestring): - try: - __import__(package) - except ImportError: - raise ConfigurationError( - 'The dotted name %r cannot be imported' % (package,)) - package = sys.modules[package] - self.package = package_of(package) - self.package_name = self.package.__name__ - - def _pkg_resources_style(self, value): - """ package.module:attr style """ - if value.startswith('.') or value.startswith(':'): - if not self.package_name: - raise ConfigurationError( - 'relative name %r irresolveable without ' - 'package_name' % (value,)) - if value in ['.', ':']: - value = self.package_name - else: - value = self.package_name + value - return pkg_resources.EntryPoint.parse( - 'x=%s' % value).load(False) - - def _zope_dottedname_style(self, value): - """ package.module.attr style """ - module = self.package_name and self.package_name or None - if value == '.': - if self.package_name is None: - raise ConfigurationError( - 'relative name %r irresolveable without package' % (value,)) - name = module.split('.') - else: - name = value.split('.') - if not name[0]: - if module is None: - raise ConfigurationError( - 'relative name %r irresolveable without ' - 'package' % (value,) - ) - module = module.split('.') - name.pop(0) - while not name[0]: - module.pop() - name.pop(0) - name = module + name - - used = name.pop(0) - found = __import__(used) - for n in name: - used += '.' + n - try: - found = getattr(found, n) - except AttributeError: - __import__(used) - found = getattr(found, n) # pragma: no cover - - return found - - def resolve(self, dotted): - if not isinstance(dotted, basestring): - raise ConfigurationError('%r is not a string' % (dotted,)) - return self.maybe_resolve(dotted) - - def maybe_resolve(self, dotted): - if isinstance(dotted, basestring): - try: - if ':' in dotted: - return self._pkg_resources_style(dotted) - else: - return self._zope_dottedname_style(dotted) - except ImportError: - raise ConfigurationError( - 'The dotted name %r cannot be imported' % (dotted,)) - return dotted - diff --git a/repoze/bfg/decorator.py b/repoze/bfg/decorator.py deleted file mode 100644 index 98d7b36b5..000000000 --- a/repoze/bfg/decorator.py +++ /dev/null @@ -1,20 +0,0 @@ -class reify(object): - - """ Put the result of a method which uses this (non-data) - descriptor decorator in the instance dict after the first call, - effectively replacing the decorator with an instance variable.""" - - def __init__(self, wrapped): - self.wrapped = wrapped - try: - self.__doc__ = wrapped.__doc__ - except: # pragma: no cover - pass - - def __get__(self, inst, objtype=None): - if inst is None: - return self - val = self.wrapped(inst) - setattr(inst, self.wrapped.__name__, val) - return val - diff --git a/repoze/bfg/encode.py b/repoze/bfg/encode.py deleted file mode 100644 index 127c405ed..000000000 --- a/repoze/bfg/encode.py +++ /dev/null @@ -1,107 +0,0 @@ -import re - -always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' - 'abcdefghijklmnopqrstuvwxyz' - '0123456789' '_.-') -_safemaps = {} -_must_quote = {} - -def url_quote(s, safe=''): - """quote('abc def') -> 'abc%20def' - - Faster version of Python stdlib urllib.quote which also quotes - the '/' character. - - Each part of a URL, e.g. the path info, the query, etc., has a - different set of reserved characters that must be quoted. - - RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists - the following reserved characters. - - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | - "$" | "," - - Each of these characters is reserved in some component of a URL, - but not necessarily in all of them. - - Unlike the default version of this function in the Python stdlib, - by default, the quote function is intended for quoting individual - path segments instead of an already composed path that might have - '/' characters in it. Thus, it *will* encode any '/' character it - finds in a string. - """ - cachekey = (safe, always_safe) - try: - safe_map = _safemaps[cachekey] - if not _must_quote[cachekey].search(s): - return s - except KeyError: - safe += always_safe - _must_quote[cachekey] = re.compile(r'[^%s]' % safe) - safe_map = {} - for i in range(256): - c = chr(i) - safe_map[c] = (c in safe) and c or ('%%%02X' % i) - _safemaps[cachekey] = safe_map - res = map(safe_map.__getitem__, s) - return ''.join(res) - -def quote_plus(s, safe=''): - """ Version of stdlib quote_plus which uses faster url_quote """ - if ' ' in s: - s = url_quote(s, safe + ' ') - return s.replace(' ', '+') - return url_quote(s, safe) - -def urlencode(query, doseq=True): - """ - An alternate implementation of Python's stdlib `urllib.urlencode - function `_ which - accepts unicode keys and values within the ``query`` - dict/sequence; all Unicode keys and values are first converted to - UTF-8 before being used to compose the query string. - - The value of ``query`` must be a sequence of two-tuples - representing key/value pairs *or* an object (often a dictionary) - with an ``.items()`` method that returns a sequence of two-tuples - representing key/value pairs. - - For minimal calling convention backwards compatibility, this - version of urlencode accepts *but ignores* a second argument - conventionally named ``doseq``. The Python stdlib version behaves - differently when ``doseq`` is False and when a sequence is - presented as one of the values. This version always behaves in - the ``doseq=True`` mode, no matter what the value of the second - argument. - - See the Python stdlib documentation for ``urllib.urlencode`` for - more information. - """ - try: - # presumed to be a dictionary - query = query.items() - except AttributeError: - pass - - result = '' - prefix = '' - - for (k, v) in query: - if k.__class__ is unicode: - k = k.encode('utf-8') - k = quote_plus(str(k)) - if hasattr(v, '__iter__'): - for x in v: - if x.__class__ is unicode: - x = x.encode('utf-8') - x = quote_plus(str(x)) - result += '%s%s=%s' % (prefix, k, x) - prefix = '&' - else: - if v.__class__ is unicode: - v = v.encode('utf-8') - v = quote_plus(str(v)) - result += '%s%s=%s' % (prefix, k, v) - prefix = '&' - - return result diff --git a/repoze/bfg/events.py b/repoze/bfg/events.py deleted file mode 100644 index efcbdf61f..000000000 --- a/repoze/bfg/events.py +++ /dev/null @@ -1,163 +0,0 @@ -import venusian - -from zope.interface import implements - -from repoze.bfg.interfaces import IContextFound -from repoze.bfg.interfaces import INewRequest -from repoze.bfg.interfaces import INewResponse -from repoze.bfg.interfaces import IApplicationCreated - -class subscriber(object): - """ Decorator activated via a :term:`scan` which treats the - function being decorated as an event subscriber for the set of - interfaces passed as ``*ifaces`` to the decorator constructor. - - For example: - - .. code-block:: python - - from repoze.bfg.interfaces import INewRequest - from repoze.bfg.events import subscriber - - @subscriber(INewRequest) - def mysubscriber(event): - event.request.foo = 1 - - More than one event type can be passed as a construtor argument: - - .. code-block:: python - - from repoze.bfg.interfaces import INewRequest - from repoze.bfg.events import subscriber - - @subscriber(INewRequest, INewResponse) - def mysubscriber(event): - print event - - When the ``subscriber`` decorator is used without passing an arguments, - the function it decorates is called for every event sent: - - .. code-block:: python - - from repoze.bfg.interfaces import INewRequest - from repoze.bfg.events import subscriber - - @subscriber() - def mysubscriber(event): - print event - - This method will have no effect until a :term:`scan` is performed - against the package or module which contains it, ala: - - .. code-block:: python - - from repoze.bfg.configuration import Configurator - config = Configurator() - config.scan('somepackage_containing_subscribers') - - """ - venusian = venusian # for unit testing - - def __init__(self, *ifaces): - self.ifaces = ifaces - - def register(self, scanner, name, wrapped): - config = scanner.config - config.add_subscriber(wrapped, self.ifaces) - - def __call__(self, wrapped): - self.venusian.attach(wrapped, self.register, category='bfg') - return wrapped - -class NewRequest(object): - """ An instance of this class is emitted as an :term:`event` - whenever :mod:`repoze.bfg` begins to process a new request. The - even instance has an attribute, ``request``, which is a - :term:`request` object. This event class implements the - :class:`repoze.bfg.interfaces.INewRequest` interface.""" - implements(INewRequest) - def __init__(self, request): - self.request = request - -class NewResponse(object): - """ An instance of this class is emitted as an :term:`event` - whenever any :mod:`repoze.bfg` :term:`view` or :term:`exception - view` returns a :term:`response`. - - The instance has two attributes:``request``, which is the request - which caused the response, and ``response``, which is the response - object returned by a view or renderer. - - If the ``response`` was generated by an :term:`exception view`, - the request will have an attribute named ``exception``, which is - the exception object which caused the exception view to be - executed. If the response was generated by a 'normal' view, the - request will not have this attribute. - - This event will not be generated if a response cannot be created - due to an exception that is not caught by an exception view (no - response is created under this circumstace). - - This class implements the - :class:`repoze.bfg.interfaces.INewResponse` interface. - - .. note:: - - Postprocessing a response is usually better handled in a WSGI - :term:`middleware` component than in subscriber code that is - called by a :class:`repoze.bfg.interfaces.INewResponse` event. - The :class:`repoze.bfg.interfaces.INewResponse` event exists - almost purely for symmetry with the - :class:`repoze.bfg.interfaces.INewRequest` event. - """ - implements(INewResponse) - def __init__(self, request, response): - self.request = request - self.response = response - -class ContextFound(object): - """ An instance of this class is emitted as an :term:`event` after - the :mod:`repoze.bfg` :term:`router` finds a :term:`context` - object (after it performs traversal) but before any view code is - executed. The instance has an attribute, ``request``, which is - the request object generated by :mod:`repoze.bfg`. - - Notably, the request object will have an attribute named - ``context``, which is the context that will be provided to the - view which will eventually be called, as well as other attributes - attached by context-finding code. - - This class implements the - :class:`repoze.bfg.interfaces.IContextFound` interface. - - .. note:: As of :mod:`repoze.bfg` 1.3, for backwards compatibility - purposes, this event may also be imported as - :class:`repoze.bfg.events.AfterTraversal`. - """ - implements(IContextFound) - def __init__(self, request): - self.request = request - -AfterTraversal = ContextFound # b/c as of 1.3 - -class ApplicationCreated(object): - """ An instance of this class is emitted as an :term:`event` when - the :meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` is - called. The instance has an attribute, ``app``, which is an - instance of the :term:`router` that will handle WSGI requests. - This class implements the - :class:`repoze.bfg.interfaces.IApplicationCreated` interface. - - .. note:: For backwards compatibility purposes, this class can - also be imported as - :class:`repoze.bfg.events.WSGIApplicationCreatedEvent`. This - was the name of the event class before :mod:`repoze.bfg` 1.3. - - """ - implements(IApplicationCreated) - def __init__(self, app): - self.app = app - self.object = app - -WSGIApplicationCreatedEvent = ApplicationCreated # b/c (as of 1.3) - diff --git a/repoze/bfg/exceptions.py b/repoze/bfg/exceptions.py deleted file mode 100644 index 9b885d9dc..000000000 --- a/repoze/bfg/exceptions.py +++ /dev/null @@ -1,92 +0,0 @@ -from zope.configuration.exceptions import ConfigurationError as ZCE -from zope.interface import implements - -from repoze.bfg.decorator import reify -from repoze.bfg.interfaces import IExceptionResponse -import cgi - -class ExceptionResponse(Exception): - """ Abstract class to support behaving as a WSGI response object """ - implements(IExceptionResponse) - status = None - - def __init__(self, message=''): - Exception.__init__(self, message) # B / C - self.message = message - - @reify # defer execution until asked explicitly - def app_iter(self): - return [ - """ - - %s - -

%s

- %s - - - """ % (self.status, self.status, cgi.escape(self.message)) - ] - - @reify # defer execution until asked explicitly - def headerlist(self): - return [ - ('Content-Length', str(len(self.app_iter[0]))), - ('Content-Type', 'text/html') - ] - - -class Forbidden(ExceptionResponse): - """ - Raise this exception within :term:`view` code to immediately - return the :term:`forbidden view` to the invoking user. Usually - this is a basic ``401`` page, but the forbidden view can be - customized as necessary. See :ref:`changing_the_forbidden_view`. - - This exception's constructor accepts a single positional argument, - which should be a string. The value of this string will be placed - into the WSGI environment by the router under the - ``repoze.bfg.message`` key, for availability to the - :term:`Forbidden View`. - """ - status = '401 Unauthorized' - -class NotFound(ExceptionResponse): - """ - Raise this exception within :term:`view` code to immediately - return the :term:`Not Found view` to the invoking user. Usually - this is a basic ``404`` page, but the Not Found view can be - customized as necessary. See :ref:`changing_the_notfound_view`. - - This exception's constructor accepts a single positional argument, - which should be a string. The value of this string will be placed - into the WSGI environment by the router under the - ``repoze.bfg.message`` key, for availability to the :term:`Not Found - View`. - """ - status = '404 Not Found' - -class PredicateMismatch(NotFound): - """ - Internal exception (not an API) raised by multiviews when no - view matches. This exception subclasses the ``NotFound`` - exception only one reason: if it reaches the main exception - handler, it should be treated like a ``NotFound`` by any exception - view registrations. - """ - -class URLDecodeError(UnicodeDecodeError): - """ - This exception is raised when :mod:`repoze.bfg` cannot - successfully decode a URL or a URL path segment. This exception - it behaves just like the Python builtin - :exc:`UnicodeDecodeError`. It is a subclass of the builtin - :exc:`UnicodeDecodeError` exception only for identity purposes, - mostly so an exception view can be registered when a URL cannot be - decoded. - """ - -class ConfigurationError(ZCE): - """ Raised when inappropriate input values are supplied to an API - method of a :term:`Configurator`""" - diff --git a/repoze/bfg/i18n.py b/repoze/bfg/i18n.py deleted file mode 100644 index c3ba7efad..000000000 --- a/repoze/bfg/i18n.py +++ /dev/null @@ -1,322 +0,0 @@ -import gettext -import os - -from translationstring import Translator -from translationstring import Pluralizer -from translationstring import TranslationString # API -from translationstring import TranslationStringFactory # API - -TranslationString = TranslationString # PyFlakes -TranslationStringFactory = TranslationStringFactory # PyFlakes - -from repoze.bfg.interfaces import ILocalizer -from repoze.bfg.interfaces import ITranslationDirectories -from repoze.bfg.interfaces import ILocaleNegotiator - -from repoze.bfg.settings import get_settings -from repoze.bfg.threadlocal import get_current_registry - -class Localizer(object): - """ - An object providing translation and pluralizations related to - the current request's locale name. A - :class:`repoze.bfg.i18n.Localizer` object is created using the - :func:`repoze.bfg.i18n.get_localizer` function. - """ - def __init__(self, locale_name, translations): - self.locale_name = locale_name - self.translations = translations - self.pluralizer = None - self.translator = None - - def translate(self, tstring, domain=None, mapping=None): - """ - Translate a :term:`translation string` to the current language - and interpolate any *replacement markers* in the result. The - ``translate`` method accepts three arguments: ``tstring`` - (required), ``domain`` (optional) and ``mapping`` (optional). - When called, it will translate the ``tstring`` translation - string to a ``unicode`` object using the current locale. If - the current locale could not be determined, the result of - interpolation of the default value is returned. The optional - ``domain`` argument can be used to specify or override the - domain of the ``tstring`` (useful when ``tstring`` is a normal - string rather than a translation string). The optional - ``mapping`` argument can specify or override the ``tstring`` - interpolation mapping, useful when the ``tstring`` argument is - a simple string instead of a translation string. - - Example:: - - from repoze.bfg.18n import TranslationString - ts = TranslationString('Add ${item}', domain='mypackage', - mapping={'item':'Item'}) - translated = localizer.translate(ts) - - Example:: - - translated = localizer.translate('Add ${item}', domain='mypackage', - mapping={'item':'Item'}) - - """ - if self.translator is None: - self.translator = Translator(self.translations) - return self.translator(tstring, domain=domain, mapping=mapping) - - def pluralize(self, singular, plural, n, domain=None, mapping=None): - """ - Return a Unicode string translation by using two - :term:`message identifier` objects as a singular/plural pair - and an ``n`` value representing the number that appears in the - message using gettext plural forms support. The ``singular`` - and ``plural`` objects passed may be translation strings or - unicode strings. ``n`` represents the number of elements. - ``domain`` is the translation domain to use to do the - pluralization, and ``mapping`` is the interpolation mapping - that should be used on the result. Note that if the objects - passed are translation strings, their domains and mappings are - ignored. The domain and mapping arguments must be used - instead. If the ``domain`` is not supplied, a default domain - is used (usually ``messages``). - - Example:: - - num = 1 - translated = localizer.pluralize('Add ${num} item', - 'Add ${num} items', - num, - mapping={'num':num}) - - - """ - if self.pluralizer is None: - self.pluralizer = Pluralizer(self.translations) - return self.pluralizer(singular, plural, n, domain=domain, - mapping=mapping) - - -def default_locale_negotiator(request): - """ The default :term:`locale negotiator`. Returns a locale name - or ``None``. - - - First, the negotiator looks for the ``_LOCALE_`` attribute of - the request object (possibly set by a view or a listener for an - :term:`event`). - - - Then it looks for the ``request.params['_LOCALE_']`` value. - - - Then it looks for the ``request.cookies['_LOCALE_']`` value. - - - Finally, the negotiator returns ``None`` if the locale could not - be determined via any of the previous checks (when a locale - negotiator returns ``None``, it signifies that the - :term:`default locale name` should be used.) - """ - name = '_LOCALE_' - locale_name = getattr(request, name, None) - if locale_name is None: - locale_name = request.params.get(name) - if locale_name is None: - locale_name = request.cookies.get(name) - return locale_name - -def negotiate_locale_name(request): - """ Negotiate and return the :term:`locale name` associated with - the current request (never cached).""" - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - negotiator = registry.queryUtility(ILocaleNegotiator, - default=default_locale_negotiator) - locale_name = negotiator(request) - - if locale_name is None: - settings = get_settings() or {} - locale_name = settings.get('default_locale_name', 'en') - - return locale_name - -def get_locale_name(request): - """ Return the :term:`locale name` associated with the current - request (possibly cached).""" - locale_name = getattr(request, 'bfg_locale_name', None) - if locale_name is None: - locale_name = negotiate_locale_name(request) - request.bfg_locale_name = locale_name - return locale_name - -def get_localizer(request): - """ Retrieve a :class:`repoze.bfg.i18n.Localizer` object - corresponding to the current request's locale name. """ - localizer = getattr(request, 'bfg_localizer', None) - - if localizer is None: - # no locale object cached on request - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() - - current_locale_name = get_locale_name(request) - localizer = registry.queryUtility(ILocalizer, name=current_locale_name) - - if localizer is None: - # no localizer utility registered yet - translations = Translations() - translations._catalog = {} - tdirs = registry.queryUtility(ITranslationDirectories, default=[]) - for tdir in tdirs: - locale_dirs = [ (lname, os.path.join(tdir, lname)) for lname in - os.listdir(tdir) ] - for locale_name, locale_dir in locale_dirs: - if locale_name != current_locale_name: - continue - messages_dir = os.path.join(locale_dir, 'LC_MESSAGES') - if not os.path.isdir(os.path.realpath(messages_dir)): - continue - for mofile in os.listdir(messages_dir): - mopath = os.path.realpath(os.path.join(messages_dir, - mofile)) - if mofile.endswith('.mo') and os.path.isfile(mopath): - mofp = open(mopath, 'rb') - domain = mofile[:-3] - dtrans = Translations(mofp, domain) - translations.add(dtrans) - - localizer = Localizer(locale_name=current_locale_name, - translations=translations) - registry.registerUtility(localizer, ILocalizer, - name=current_locale_name) - request.bfg_localizer = localizer - - return localizer - -class Translations(gettext.GNUTranslations, object): - """An extended translation catalog class (ripped off from Babel) """ - - DEFAULT_DOMAIN = 'messages' - - def __init__(self, fileobj=None, domain=DEFAULT_DOMAIN): - """Initialize the translations catalog. - - :param fileobj: the file-like object the translation should be read - from - """ - gettext.GNUTranslations.__init__(self, fp=fileobj) - self.files = filter(None, [getattr(fileobj, 'name', None)]) - self.domain = domain - self._domains = {} - - @classmethod - def load(cls, dirname=None, locales=None, domain=DEFAULT_DOMAIN): - """Load translations from the given directory. - - :param dirname: the directory containing the ``MO`` files - :param locales: the list of locales in order of preference (items in - this list can be either `Locale` objects or locale - strings) - :param domain: the message domain - :return: the loaded catalog, or a ``NullTranslations`` instance if no - matching translations were found - :rtype: `Translations` - """ - if locales is not None: - if not isinstance(locales, (list, tuple)): - locales = [locales] - locales = [str(l) for l in locales] - if not domain: - domain = cls.DEFAULT_DOMAIN - filename = gettext.find(domain, dirname, locales) - if not filename: - return gettext.NullTranslations() - return cls(fileobj=open(filename, 'rb'), domain=domain) - - def __repr__(self): - return '<%s: "%s">' % (type(self).__name__, - self._info.get('project-id-version')) - - def add(self, translations, merge=True): - """Add the given translations to the catalog. - - If the domain of the translations is different than that of the - current catalog, they are added as a catalog that is only accessible - by the various ``d*gettext`` functions. - - :param translations: the `Translations` instance with the messages to - add - :param merge: whether translations for message domains that have - already been added should be merged with the existing - translations - :return: the `Translations` instance (``self``) so that `merge` calls - can be easily chained - :rtype: `Translations` - """ - domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN) - if merge and domain == self.domain: - return self.merge(translations) - - existing = self._domains.get(domain) - if merge and existing is not None: - existing.merge(translations) - else: - translations.add_fallback(self) - self._domains[domain] = translations - - return self - - def merge(self, translations): - """Merge the given translations into the catalog. - - Message translations in the specified catalog override any messages - with the same identifier in the existing catalog. - - :param translations: the `Translations` instance with the messages to - merge - :return: the `Translations` instance (``self``) so that `merge` calls - can be easily chained - :rtype: `Translations` - """ - if isinstance(translations, gettext.GNUTranslations): - self._catalog.update(translations._catalog) - if isinstance(translations, Translations): - self.files.extend(translations.files) - - return self - - def dgettext(self, domain, message): - """Like ``gettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).gettext(message) - - def ldgettext(self, domain, message): - """Like ``lgettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).lgettext(message) - - def dugettext(self, domain, message): - """Like ``ugettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ugettext(message) - - def dngettext(self, domain, singular, plural, num): - """Like ``ngettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ngettext(singular, plural, num) - - def ldngettext(self, domain, singular, plural, num): - """Like ``lngettext()``, but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).lngettext(singular, plural, num) - - def dungettext(self, domain, singular, plural, num): - """Like ``ungettext()`` but look the message up in the specified - domain. - """ - return self._domains.get(domain, self).ungettext(singular, plural, num) - diff --git a/repoze/bfg/includes/__init__.py b/repoze/bfg/includes/__init__.py deleted file mode 100644 index 9faa798e8..000000000 --- a/repoze/bfg/includes/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# includes package: referred to by generated apps diff --git a/repoze/bfg/includes/configure.zcml b/repoze/bfg/includes/configure.zcml deleted file mode 100644 index 6a373fa5d..000000000 --- a/repoze/bfg/includes/configure.zcml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/repoze/bfg/includes/meta.zcml b/repoze/bfg/includes/meta.zcml deleted file mode 100644 index 58ab1c782..000000000 --- a/repoze/bfg/includes/meta.zcml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py deleted file mode 100644 index d67fd2dfb..000000000 --- a/repoze/bfg/interfaces.py +++ /dev/null @@ -1,379 +0,0 @@ -from zope.interface import Attribute -from zope.interface import Interface - -# public API interfaces - -class IContextFound(Interface): - """ An event type that is emitted after :mod:`repoze.bfg` finds a - :term:`context` object but before it calls any view code. See the - documentation attached to :class:`repoze.bfg.events.ContextFound` - for more information. - - .. note:: For backwards compatibility with versions of - :mod:`repoze.bfg` before 1.3, this event interface can also be - imported as :class:`repoze.bfg.interfaces.IAfterTraversal`. - """ - request = Attribute('The request object') - -IAfterTraversal = IContextFound - -class INewRequest(Interface): - """ An event type that is emitted whenever :mod:`repoze.bfg` - begins to process a new request. See the documentation attached - to :class:`repoze.bfg.events.NewRequest` for more information.""" - request = Attribute('The request object') - -class INewResponse(Interface): - """ An event type that is emitted whenever any :mod:`repoze.bfg` - view returns a response. See the - documentation attached to :class:`repoze.bfg.events.NewResponse` - for more information.""" - request = Attribute('The request object') - response = Attribute('The response object') - -class IApplicationCreated(Interface): - """ Event issued when the - :meth:`repoze.bfg.configuration.Configurator.make_wsgi_app` method - is called. See the documentation attached to - :class:`repoze.bfg.events.ApplicationCreated` for more - information. - - .. note:: For backwards compatibility with :mod:`repoze.bfg` - versions before 1.3, this interface can also be imported as - :class:`repoze.bfg.interfaces.IWSGIApplicationCreatedEvent`. - """ - app = Attribute(u"Created application") - -IWSGIApplicationCreatedEvent = IApplicationCreated # b /c - -class IResponse(Interface): # not an API - status = Attribute('WSGI status code of response') - headerlist = Attribute('List of response headers') - app_iter = Attribute('Iterable representing the response body') - -class IException(Interface): # not an API - """ An interface representing a generic exception """ - -class IExceptionResponse(IException, IResponse): - """ An interface representing a WSGI response which is also an - exception object. Register an exception view using this interface - as a ``context`` to apply the registered view for all exception - types raised by :mod:`repoze.bfg` internally - (:class:`repoze.bfg.exceptions.NotFound` and - :class:`repoze.bfg.exceptions.Forbidden`).""" - -# internal interfaces - -class IRequest(Interface): - """ Request type interface attached to all request objects """ - -IRequest.combined = IRequest # for exception view lookups - -class IRouteRequest(Interface): - """ *internal only* interface used as in a utility lookup to find - route-specific interfaces. Not an API.""" - -class IAuthenticationPolicy(Interface): - """ An object representing a BFG authentication policy. """ - def authenticated_userid(request): - """ Return the authenticated userid or ``None`` if no - authenticated userid can be found. """ - - def effective_principals(request): - """ Return a sequence representing the effective principals - including the userid and any groups belonged to by the current - user, including 'system' groups such as Everyone and - Authenticated. """ - - def remember(request, principal, **kw): - """ Return a set of headers suitable for 'remembering' the - principal named ``principal`` when set in a response. An - individual authentication policy and its consumers can decide - on the composition and meaning of **kw. """ - - def forget(request): - """ Return a set of headers suitable for 'forgetting' the - current user on subsequent requests. """ - -class IAuthorizationPolicy(Interface): - """ An object representing a BFG authorization policy. """ - def permits(context, principals, permission): - """ Return True if any of the principals is allowed the - permission in the current context, else return False """ - - def principals_allowed_by_permission(context, permission): - """ Return a set of principal identifiers allowed by the permission """ - -class IStaticURLInfo(Interface): - """ A policy for generating URLs to static resources """ - def add(name, spec, **extra): - """ Add a new static info registration """ - - def generate(path, request, **kw): - """ Generate a URL for the given path """ - -class IResponseFactory(Interface): - """ A utility which generates a response factory """ - def __call__(): - """ Return a response factory (e.g. a callable that returns an - object implementing IResponse, e.g. ``webob.Response``; it - should accept all the arguments that the webob.Response class - accepts)""" - -class IRequestFactory(Interface): - """ A utility which generates a request """ - def __call__(environ): - """ Return an object implementing IRequest, e.g. an instance - of ``repoze.bfg.request.Request``""" - - def blank(path): - """ Return an empty request object (see - ``webob.Request.blank``)""" - -class IViewClassifier(Interface): - """ *Internal only* marker interface for views.""" - -class IExceptionViewClassifier(Interface): - """ *Internal only* marker interface for exception views.""" - -class IView(Interface): - def __call__(context, request): - """ Must return an object that implements IResponse. May - optionally raise ``repoze.bfg.exceptions.Forbidden`` if an - authorization failure is detected during view execution or - ``repoze.bfg.exceptions.NotFound`` if the not found page is - meant to be returned.""" - -class ISecuredView(IView): - """ *Internal only* interface. Not an API. """ - def __call_permissive__(context, request): - """ Guaranteed-permissive version of __call__ """ - - def __permitted__(context, request): - """ Return True if view execution will be permitted using the - context and request, False otherwise""" - -class IMultiView(ISecuredView): - """ *internal only*. A multiview is a secured view that is a - collection of other views. Each of the views is associated with - zero or more predicates. Not an API.""" - def add(view, predicates, order, accept=None, phash=None): - """ Add a view to the multiview. """ - -class IRootFactory(Interface): - def __call__(request): - """ Return a root object based on the request """ - -class IDefaultRootFactory(Interface): - def __call__(request): - """ Return the *default* root object for an application """ - -class ITraverser(Interface): - def __call__(request): - """ Return a dictionary with (at least) the keys ``root``, - ``context``, ``view_name``, ``subpath``, ``traversed``, - ``virtual_root``, and ``virtual_root_path``. These values are - typically the result of an object graph traversal. ``root`` - is the physical root object, ``context`` will be a model - object, ``view_name`` will be the view name used (a Unicode - name), ``subpath`` will be a sequence of Unicode names that - followed the view name but were not traversed, ``traversed`` - will be a sequence of Unicode names that were traversed - (including the virtual root path, if any) ``virtual_root`` - will be a model object representing the virtual root (or the - physical root if traversal was not performed), and - ``virtual_root_path`` will be a sequence representing the - virtual root path (a sequence of Unicode names) or None if - traversal was not performed. - - Extra keys for special purpose functionality can be added as - necessary. - - All values returned in the dictionary will be made available - as attributes of the ``request`` object. - """ - -ITraverserFactory = ITraverser # b / c for 1.0 code - -class IRenderer(Interface): - def __call__(value, system): - """ Call a the renderer implementation with the result of the - view (``value``) passed in and return a result (a string or - unicode object useful as a response body). Values computed by - the system are passed by the system in the ``system`` - parameter, which is a dictionary. Keys in the dictionary - include: ``view`` (the view callable that returned the value), - ``renderer_name`` (the template name or simple name of the - renderer), ``context`` (the context object passed to the - view), and ``request`` (the request object passed to the - view).""" - -class IRendererFactory(Interface): - def __call__(name): - """ Return an object that implements ``IRenderer`` """ - -class IRendererGlobalsFactory(Interface): - def __call__(system_values): - """ Return a dictionary of global renderer values (aka - top-level template names). The ``system_values`` value passed - in will be a dictionary that includes at least a ``request`` - key, indicating the current request, and the value - ``renderer_name``, which will be the name of the renderer in - use.""" - -class ITemplateRenderer(IRenderer): - def implementation(): - """ Return the object that the underlying templating system - uses to render the template; it is typically a callable that - accepts arbitrary keyword arguments and returns a string or - unicode object """ - -class IViewPermission(Interface): - def __call__(context, request): - """ Return True if the permission allows, return False if it denies. """ - -class IRouter(Interface): - """WSGI application which routes requests to 'view' code based on - a view registry.""" - registry = Attribute( - """Component architecture registry local to this application.""") - -class ISettings(Interface): - """ Runtime settings utility for repoze.bfg; represents the - deployment settings for the application. Implements a mapping - interface.""" - -# this interface, even if it becomes unused within BFG, is imported by -# other packages (such as repoze.bfg.traversalwrapper) -class ILocation(Interface): - """Objects that have a structural location""" - __parent__ = Attribute("The parent in the location hierarchy") - __name__ = Attribute("The name within the parent") - -class IDebugLogger(Interface): - """ Interface representing a PEP 282 logger """ - -ILogger = IDebugLogger # b/c - -class IRoutePregenerator(Interface): - def __call__(request, elements, kw): - """ A pregenerator is a function associated by a developer - with a :term:`route`. The pregenerator for a route is called - by :func:`repoze.bfg.url.route_url` in order to adjust the set - of arguments passed to it by the user for special purposes, - such as Pylons 'subdomain' support. It will influence the URL - returned by ``route_url``. - - A pregenerator should return a two-tuple of ``(elements, kw)`` - after examining the originals passed to this function, which - are the arguments ``(request, elements, kw)``. The simplest - pregenerator is:: - - def pregenerator(request, elements, kw): - return elements, kw - - You can employ a pregenerator by passing a ``pregenerator`` - argument to the - :meth:`repoze.bfg.configuration.Configurator.add_route` - function. - - .. note:: This interface is new as of :mod:`repoze.bfg` 1.3. - """ - -class IRoute(Interface): - """ Interface representing the type of object returned from - ``IRoutesMapper.get_route``""" - name = Attribute('The route name') - pattern = Attribute('The route pattern') - factory = Attribute( - 'The :term:`root factory` used by the :mod:`repoze.bfg` router ' - 'when this route matches (or ``None``)') - predicates = Attribute( - 'A sequence of :term:`route predicate` objects used to ' - 'determine if a request matches this route or not or not after ' - 'basic pattern matching has been completed.') - pregenerator = Attribute('This attribute should either be ``None`` or ' - 'a callable object implementing the ' - '``IRoutePregenerator`` interface') - def match(path): - """ - If the ``path`` passed to this function can be matched by the - ``pattern`` of this route, return a dictionary (the - 'matchdict'), which will contain keys representing the dynamic - segment markers in the pattern mapped to values extracted from - the provided ``path``. - - If the ``path`` passed to this function cannot be matched by - the ``pattern`` of this route, return ``None``. - """ - def generate(kw): - """ - Generate a URL based on filling in the dynamic segment markers - in the pattern using the ``kw`` dictionary provided. - """ - -class IRoutesMapper(Interface): - """ Interface representing a Routes ``Mapper`` object """ - def get_routes(): - """ Return a sequence of Route objects registered in the mapper.""" - - def has_routes(): - """ Returns ``True`` if any route has been registered. """ - - def get_route(name): - """ Returns an ``IRoute`` object if a route with the name ``name`` - was registered, otherwise return ``None``.""" - - def connect(name, pattern, factory=None, predicates=()): - """ Add a new route. """ - - def generate(name, kw): - """ Generate a URL using the route named ``name`` with the - keywords implied by kw""" - - def __call__(request): - """ Return a dictionary containing matching information for - the request; the ``route`` key of this dictionary will either - be a Route object or ``None`` if no route matched; the - ``match`` key will be the matchdict or ``None`` if no route - matched.""" - -class IContextURL(Interface): - """ An adapter which deals with URLs related to a context. - """ - def virtual_root(): - """ Return the virtual root related to a request and the - current context""" - - def __call__(): - """ Return a URL that points to the context """ - -class IPackageOverrides(Interface): - """ Utility for pkg_resources overrides """ - -# VH_ROOT_KEY is an interface; its imported from other packages (e.g. -# traversalwrapper) -VH_ROOT_KEY = 'HTTP_X_VHM_ROOT' - -class IChameleonTranslate(Interface): - """ Internal interface representing a chameleon translate function """ - def __call__(msgid, domain=None, mapping=None, context=None, - target_language=None, default=None): - """ Translate a mess of arguments to a Unicode object """ - -class ILocalizer(Interface): - """ Localizer for a specific language """ - -class ILocaleNegotiator(Interface): - def __call__(request): - """ Return a locale name """ - -class ITranslationDirectories(Interface): - """ A list object representing all known translation directories - for an application""" - -class IDefaultPermission(Interface): - """ A string object representing the default permission to be used - for all view configurations which do not explicitly declare their - own.""" - diff --git a/repoze/bfg/location.py b/repoze/bfg/location.py deleted file mode 100644 index 7472129d2..000000000 --- a/repoze/bfg/location.py +++ /dev/null @@ -1,66 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## - -def inside(model1, model2): - """Is ``model1`` 'inside' ``model2``? Return ``True`` if so, else - ``False``. - - ``model1`` is 'inside' ``model2`` if ``model2`` is a - :term:`lineage` ancestor of ``model1``. It is a lineage ancestor - if its parent (or one of its parent's parents, etc.) is an - ancestor. - """ - while model1 is not None: - if model1 is model2: - return True - model1 = model1.__parent__ - - return False - -def lineage(model): - """ - Return a generator representing the :term:`lineage` of the - :term:`model` object implied by the ``model`` argument. The - generator first returns ``model`` unconditionally. Then, if - ``model`` supplies a ``__parent__`` attribute, return the object - represented by ``model.__parent__``. If *that* object has a - ``__parent__`` attribute, return that object's parent, and so on, - until the object being inspected either has no ``__parent__`` - attribute or which has a ``__parent__`` attribute of ``None``. - For example, if the object tree is:: - - thing1 = Thing() - thing2 = Thing() - thing2.__parent__ = thing1 - - Calling ``lineage(thing2)`` will return a generator. When we turn - it into a list, we will get:: - - list(lineage(thing2)) - [ , ] - """ - while model is not None: - yield model - # The common case is that the AttributeError exception below - # is exceptional as long as the developer is a "good citizen" - # who has a root object with a __parent__ of None. Using an - # exception here instead of a getattr with a default is an - # important micro-optimization, because this function is - # called in any non-trivial application over and over again to - # generate URLs and paths. - try: - model = model.__parent__ - except AttributeError: - model = None - diff --git a/repoze/bfg/log.py b/repoze/bfg/log.py deleted file mode 100644 index b8762e6e2..000000000 --- a/repoze/bfg/log.py +++ /dev/null @@ -1,16 +0,0 @@ -import logging - -def make_stream_logger( - name, stream, levelname='DEBUG', fmt='%(asctime)s %(message)s'): - """ Return an object which implements - ``repoze.bfg.interfaces.IDebugLogger`` (ie. a Python PEP 282 logger - instance) with the name ``name`` using the stream (or open - filehandle) ``stream``, logging at ``levelname`` log level or - above with format ``fmt``. """ - handler = logging.StreamHandler(stream) - formatter = logging.Formatter(fmt) - handler.setFormatter(formatter) - logger = logging.Logger(name) - logger.addHandler(handler) - logger.setLevel(getattr(logging, levelname)) - return logger diff --git a/repoze/bfg/paster.py b/repoze/bfg/paster.py deleted file mode 100644 index e8ce46ce2..000000000 --- a/repoze/bfg/paster.py +++ /dev/null @@ -1,111 +0,0 @@ -import os -import sys -from code import interact - -from paste.deploy import loadapp -from paste.script.command import Command -from paste.script.templates import Template -from paste.util.template import paste_script_template_renderer - -from repoze.bfg.scripting import get_root - - -class StarterProjectTemplate(Template): - _template_dir = 'paster_templates/starter' - summary = 'repoze.bfg starter project' - template_renderer = staticmethod(paste_script_template_renderer) - -class ZODBProjectTemplate(Template): - _template_dir = 'paster_templates/zodb' - summary = 'repoze.bfg ZODB starter project' - template_renderer = staticmethod(paste_script_template_renderer) - -class RoutesAlchemyProjectTemplate(Template): - _template_dir = 'paster_templates/routesalchemy' - summary = 'repoze.bfg SQLAlchemy project using Routes (no traversal)' - template_renderer = staticmethod(paste_script_template_renderer) - -class AlchemyProjectTemplate(Template): - _template_dir = 'paster_templates/alchemy' - summary = 'repoze.bfg SQLAlchemy project using traversal' - template_renderer = staticmethod(paste_script_template_renderer) - -def get_app(config_file, name, loadapp=loadapp): - """ Return the WSGI application named ``name`` in the PasteDeploy - config file ``config_file``""" - config_name = 'config:%s' % config_file - here_dir = os.getcwd() - app = loadapp(config_name, name=name, relative_to=here_dir) - return app - -_marker = object() -class BFGShellCommand(Command): - """Open an interactive shell with a :mod:`repoze.bfg` app loaded. - - This command accepts two positional arguments: - - ``config_file`` -- specifies the PasteDeploy config file to use - for the interactive shell. - - ``section_name`` -- specifies the section name in the PasteDeploy - config file that represents the application. - - Example:: - - $ paster bfgshell myapp.ini main - - .. note:: You should use a ``section_name`` that refers to the - actual ``app`` section in the config file that points at - your BFG app without any middleware wrapping, or this - command will almost certainly fail. - - """ - summary = "Open an interactive shell with a repoze.bfg app loaded" - - min_args = 2 - max_args = 2 - group_name = 'bfg' - - parser = Command.standard_parser(simulate=True) - parser.add_option('-d', '--disable-ipython', - action='store_true', - dest='disable_ipython', - help="Don't use IPython even if it is available") - - interact = (interact,) # for testing - loadapp = (loadapp,) # for testing - get_app = staticmethod(get_app) # hook point - get_root = staticmethod(get_root) # hook point - verbose = 3 - - def __init__(self, *arg, **kw): - # needs to be in constructor to support Jython (used to be at class - # scope as ``usage = '\n' + __doc__``. - self.usage = '\n' + self.__doc__ - Command.__init__(self, *arg, **kw) - - def command(self, IPShell=_marker): - if IPShell is _marker: - try: #pragma no cover - from IPython.Shell import IPShell - except ImportError: #pragma no cover - IPShell = None - cprt =('Type "help" for more information. "root" is the BFG app ' - 'root object.') - banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) - config_file, section_name = self.args - self.logging_file_config(config_file) - app = self.get_app(config_file, section_name, loadapp=self.loadapp[0]) - root, closer = self.get_root(app) - if IPShell is not None and not self.options.disable_ipython: - try: - shell = IPShell(argv=[], user_ns={'root':root}) - shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner - shell.mainloop() - finally: - closer() - else: - try: - self.interact[0](banner, local={'root':root}) - finally: - closer() diff --git a/repoze/bfg/paster_templates/alchemy/+package+/__init__.py b/repoze/bfg/paster_templates/alchemy/+package+/__init__.py deleted file mode 100755 index cbdfd3ac6..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# A package - diff --git a/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml deleted file mode 100644 index 521f06ba4..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/configure.zcml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - diff --git a/repoze/bfg/paster_templates/alchemy/+package+/models.py b/repoze/bfg/paster_templates/alchemy/+package+/models.py deleted file mode 100755 index 336613cf9..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/models.py +++ /dev/null @@ -1,88 +0,0 @@ -import transaction - -from sqlalchemy.orm import scoped_session -from sqlalchemy.orm import sessionmaker - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm.exc import NoResultFound - -from sqlalchemy import create_engine -from sqlalchemy import Integer -from sqlalchemy import Unicode -from sqlalchemy import Column - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - -class MyModel(Base): - __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Unicode(255), unique=True) - value = Column(Integer) - - def __init__(self, name, value): - self.name = name - self.value = value - -class MyApp(object): - __name__ = None - __parent__ = None - - def __getitem__(self, key): - session= DBSession() - try: - id = int(key) - except (ValueError, TypeError): - raise KeyError(key) - - query = session.query(MyModel).filter_by(id=id) - - try: - item = query.one() - item.__parent__ = self - item.__name__ = key - return item - except NoResultFound: - raise KeyError(key) - - def get(self, key, default=None): - try: - item = self.__getitem__(key) - except KeyError: - item = default - return item - - def __iter__(self): - session= DBSession() - query = session.query(MyModel) - return iter(query) - -root = MyApp() - -def default_get_root(request): - return root - -def populate(): - session = DBSession() - model = MyModel(name=u'test name',value=55) - session.add(model) - session.flush() - transaction.commit() - -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) - DBSession.configure(bind=engine) - Base.metadata.bind = engine - Base.metadata.create_all(engine) - try: - populate() - except IntegrityError: - pass - -def appmaker(db_string, db_echo=False): - initialize_sql(db_string, db_echo) - return default_get_root diff --git a/repoze/bfg/paster_templates/alchemy/+package+/run.py_tmpl b/repoze/bfg/paster_templates/alchemy/+package+/run.py_tmpl deleted file mode 100644 index 026505f3a..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/run.py_tmpl +++ /dev/null @@ -1,23 +0,0 @@ -from repoze.bfg.configuration import Configurator -from paste.deploy.converters import asbool - -from {{package}}.models import appmaker - -def app(global_config, **settings): - """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. - """ - zcml_file = settings.get('configure_zcml', 'configure.zcml') - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - get_root = appmaker(db_string, asbool(db_echo)) - config = Configurator(settings=settings, root_factory=get_root) - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() - diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/model.pt b/repoze/bfg/paster_templates/alchemy/+package+/templates/model.pt deleted file mode 100644 index e29b92836..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/templates/model.pt +++ /dev/null @@ -1,103 +0,0 @@ - - - - -${project} Application - - - - - - - - - -
- -
- -
-
-

Welcome to ${project}, an - application generated by the repoze.bfg web - application framework.

-

- Id: ${item.id}
- Name: ${item.name}
- Value: ${item.value}

-
-
- - - - -
 
-
-
- - - - - - diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/root.pt b/repoze/bfg/paster_templates/alchemy/+package+/templates/root.pt deleted file mode 100644 index 2cac6fe6e..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/templates/root.pt +++ /dev/null @@ -1,101 +0,0 @@ - - - - -${project} Application - - - - - - - - - -
- -
- -
-
-

Welcome to ${project}, an - application generated by the repoze.bfg web - application framework.

-

- ${item.name}

-
-
- - - - -
 
-
-
- - - - - - diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/default.css b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/default.css deleted file mode 100644 index 41b3debde..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/default.css +++ /dev/null @@ -1,380 +0,0 @@ -/* -Design by Free CSS Templates -http://www.freecsstemplates.org -Released for free under a Creative Commons Attribution 2.5 License -*/ - -body { - margin: 0; - padding: 0; - background: url(images/img01.gif) repeat-x left top; - font-size: 13px; - font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; - text-align: justify; - color: #FFFFFF; -} - -h1, h2, h3 { - margin: 0; - text-transform: lowercase; - font-weight: normal; - color: #FFFFFF; -} - -h1 { - letter-spacing: -1px; - font-size: 32px; -} - -h2 { - font-size: 23px; -} - -p, ul, ol { - margin: 0 0 2em 0; - text-align: justify; - line-height: 26px; -} - -a:link { - color: #8BD80E; -} - -a:hover, a:active { - text-decoration: none; - color: #8BD80E; -} - -a:visited { - color: #8BD80E; -} - -img { - border: none; -} - -img.left { - float: left; - margin-right: 15px; -} - -img.right { - float: right; - margin-left: 15px; -} - -/* Form */ - -form { - margin: 0; - padding: 0; -} - -fieldset { - margin: 0; - padding: 0; - border: none; -} - -legend { - display: none; -} - -input, textarea, select { - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; - font-size: 13px; - color: #333333; -} - -#wrapper { - margin: 0; - padding: 0; - background: #000000; -} - -/* Header */ - -#header { - width: 713px; - margin: 0 auto; - height: 42px; -} - -/* Menu */ - -#menu { - float: left; - width: 713px; - height: 50px; - background: url(images/img02.gif) no-repeat left top; -} - -#menu ul { - margin: 0; - padding: 0px 0 0 10px; - list-style: none; - line-height: normal; -} - -#menu li { - display: block; - float: left; -} - -#menu a { - display: block; - float: left; - background: url(images/img04.gif) no-repeat right 55%; - margin-top: 5px; - margin-right: 3px; - padding: 8px 17px; - text-decoration: none; - font-size: 13px; - color: #000000; -} - -#menu a:hover { - color: #000000; -} - -#menu .current_page_item a { - color: #000000; -} - -/** LOGO */ - -#logo { - width: 713px; - height: 80px; - margin: 0 auto; -} - -#logo h1, #logo h2 { - float: left; - margin: 0; - padding: 30px 0 0 0px; - line-height: normal; -} - -#logo h1 { - font-family: Georgia, "Times New Roman", Times, serif; - font-size:40px; -} - -#logo h1 a { - text-decoration: none; - color: #4C4C4C; -} - -#logo h1 a:hover { text-decoration: underline; } - -#logo h2 { - float: left; - padding: 45px 0 0 18px; - font: 18px Georgia, "Times New Roman", Times, serif; - color: #8BD80E; -} - -#logo p a { - text-decoration: none; - color: #8BD80E; -} - -#logo p a:hover { text-decoration: underline; } - - - -/* Page */ - -#page { - width: 663px; - margin: 0 auto; - background: #4C4C4C url(images/img03.gif) no-repeat left bottom; - padding: 0 25px; -} - -/* Content */ - -#content { - float: left; - width: 410px; - -} - -/* Post */ - -.post { - padding: 15px 0px; - margin-bottom: 20px; -} - -.post .title { - margin-bottom: 20px; - padding-bottom: 5px; -} - -.post h1 { - padding: 0px 0 0 0px; - background: url(images/img08.jpg) no-repeat left top; - font-size: 24px; - color: #FFFFFF; -} - -.post h2 { - padding: 0px 0 0 0px; - font-size: 22px; - color: #FFFFFF; -} - -.post .entry { -} - -.post .meta { - padding: 15px 15px 30px 0px; - font-family: Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.post .meta p { - margin: 0; - padding-top: 15px; - line-height: normal; - color: #FFFFFF; -} - -.post .meta .byline { - float: left; -} - -.post .meta .links { - float: right; -} - -.post .meta .more { - padding: 0 10px 0 18px; -} - -.post .meta .comments { -} - -.post .meta b { - display: none; -} - - -/* Sidebar */ - -#sidebar { - width: 210px; - float: right; - margin: 0; - padding: 0; -} - -#sidebar ul { - margin: 0; - padding: 0; - list-style: none; -} - -#sidebar li { - margin-bottom: 40px; -} - -#sidebar li ul { -} - -#sidebar li li { - margin: 0; -} - -#sidebar h2 { - width: 250px; - padding: 8px 0 0 0px; - margin-bottom: 10px; - background: url(images/img07.jpg) no-repeat left top; - font-size: 20px; - color: #FFFFFF; -} - -/* Search */ - -#search { - -} - -#search h2 { - margin-bottom: 20px; -} - -#s { - width: 140px; - margin-right: 5px; - padding: 3px; - border: 1px solid #BED99C; -} - -#x { - padding: 3px; - border: none; - background: #8BD80E; - text-transform: lowercase; - font-size: 11px; - color: #FFFFFF; -} - -/* Boxes */ - -.box1 { - padding: 20px; -} - -.box2 { - color: #BABABA; -} - -.box2 h2 { - margin-bottom: 15px; - font-size: 16px; - color: #FFFFFF; -} - -.box2 ul { - margin: 0; - padding: 0; - list-style: none; -} - -.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { - color: #EDEDED; -} - -/* Footer */ -#footer-wrap { -} - -#footer { - margin: 0 auto; - padding: 20px 0 10px 0; - background: #000000; -} - -html>body #footer { - height: auto; -} - -#footer p { - font-size: 11px; -} - -#legal { - clear: both; - padding-top: 17px; - text-align: center; - color: #FFFFFF; -} - -#legal a { - font-weight: normal; - color: #FFFFFF; -} diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img01.gif b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img01.gif deleted file mode 100644 index 5f082bd99..000000000 Binary files a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img01.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img02.gif b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img02.gif deleted file mode 100644 index 45a3ae976..000000000 Binary files a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img02.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img03.gif b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img03.gif deleted file mode 100644 index d92ea38f9..000000000 Binary files a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img03.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img04.gif b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img04.gif deleted file mode 100644 index 950c4af9d..000000000 Binary files a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/img04.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/spacer.gif b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/spacer.gif deleted file mode 100644 index 5bfd67a2d..000000000 Binary files a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/images/spacer.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/templatelicense.txt b/repoze/bfg/paster_templates/alchemy/+package+/templates/static/templatelicense.txt deleted file mode 100644 index ccb6b06ab..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/templates/static/templatelicense.txt +++ /dev/null @@ -1,243 +0,0 @@ -Creative Commons - -Creative Commons Legal Code - -*Attribution 2.5* - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE -LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN -ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION -ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE -INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM -ITS USE. - -/License/ - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE -RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS -AND CONDITIONS. - -*1. Definitions* - - 1. *"Collective Work"* means a work, such as a periodical issue, - anthology or encyclopedia, in which the Work in its entirety in - unmodified form, along with a number of other contributions, - constituting separate and independent works in themselves, are - assembled into a collective whole. A work that constitutes a - Collective Work will not be considered a Derivative Work (as - defined below) for the purposes of this License. - 2. *"Derivative Work"* means a work based upon the Work or upon the - Work and other pre-existing works, such as a translation, musical - arrangement, dramatization, fictionalization, motion picture - version, sound recording, art reproduction, abridgment, - condensation, or any other form in which the Work may be recast, - transformed, or adapted, except that a work that constitutes a - Collective Work will not be considered a Derivative Work for the - purpose of this License. For the avoidance of doubt, where the - Work is a musical composition or sound recording, the - synchronization of the Work in timed-relation with a moving image - ("synching") will be considered a Derivative Work for the purpose - of this License. - 3. *"Licensor"* means the individual or entity that offers the Work - under the terms of this License. - 4. *"Original Author"* means the individual or entity who created the - Work. - 5. *"Work"* means the copyrightable work of authorship offered under - the terms of this License. - 6. *"You"* means an individual or entity exercising rights under this - License who has not previously violated the terms of this License - with respect to the Work, or who has received express permission - from the Licensor to exercise rights under this License despite a - previous violation. - -*2. Fair Use Rights.* Nothing in this license is intended to reduce, -limit, or restrict any rights arising from fair use, first sale or other -limitations on the exclusive rights of the copyright owner under -copyright law or other applicable laws. - -*3. License Grant.* Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - 1. to reproduce the Work, to incorporate the Work into one or more - Collective Works, and to reproduce the Work as incorporated in the - Collective Works; - 2. to create and reproduce Derivative Works; - 3. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission the Work including as incorporated in Collective Works; - 4. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission Derivative Works. - 5. - - For the avoidance of doubt, where the work is a musical composition: - - 1. *Performance Royalties Under Blanket Licenses*. Licensor - waives the exclusive right to collect, whether individually - or via a performance rights society (e.g. ASCAP, BMI, - SESAC), royalties for the public performance or public - digital performance (e.g. webcast) of the Work. - 2. *Mechanical Rights and Statutory Royalties*. Licensor waives - the exclusive right to collect, whether individually or via - a music rights agency or designated agent (e.g. Harry Fox - Agency), royalties for any phonorecord You create from the - Work ("cover version") and distribute, subject to the - compulsory license created by 17 USC Section 115 of the US - Copyright Act (or the equivalent in other jurisdictions). - 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of - doubt, where the Work is a sound recording, Licensor waives the - exclusive right to collect, whether individually or via a - performance-rights society (e.g. SoundExchange), royalties for the - public digital performance (e.g. webcast) of the Work, subject to - the compulsory license created by 17 USC Section 114 of the US - Copyright Act (or the equivalent in other jurisdictions). - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights -in other media and formats. All rights not expressly granted by Licensor -are hereby reserved. - -*4. Restrictions.*The license granted in Section 3 above is expressly -made subject to and limited by the following restrictions: - - 1. You may distribute, publicly display, publicly perform, or - publicly digitally perform the Work only under the terms of this - License, and You must include a copy of, or the Uniform Resource - Identifier for, this License with every copy or phonorecord of the - Work You distribute, publicly display, publicly perform, or - publicly digitally perform. You may not offer or impose any terms - on the Work that alter or restrict the terms of this License or - the recipients' exercise of the rights granted hereunder. You may - not sublicense the Work. You must keep intact all notices that - refer to this License and to the disclaimer of warranties. You may - not distribute, publicly display, publicly perform, or publicly - digitally perform the Work with any technological measures that - control access or use of the Work in a manner inconsistent with - the terms of this License Agreement. The above applies to the Work - as incorporated in a Collective Work, but this does not require - the Collective Work apart from the Work itself to be made subject - to the terms of this License. If You create a Collective Work, - upon notice from any Licensor You must, to the extent practicable, - remove from the Collective Work any credit as required by clause - 4(b), as requested. If You create a Derivative Work, upon notice - from any Licensor You must, to the extent practicable, remove from - the Derivative Work any credit as required by clause 4(b), as - requested. - 2. If you distribute, publicly display, publicly perform, or publicly - digitally perform the Work or any Derivative Works or Collective - Works, You must keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) - the name of the Original Author (or pseudonym, if applicable) if - supplied, and/or (ii) if the Original Author and/or Licensor - designate another party or parties (e.g. a sponsor institute, - publishing entity, journal) for attribution in Licensor's - copyright notice, terms of service or by other reasonable means, - the name of such party or parties; the title of the Work if - supplied; to the extent reasonably practicable, the Uniform - Resource Identifier, if any, that Licensor specifies to be - associated with the Work, unless such URI does not refer to the - copyright notice or licensing information for the Work; and in the - case of a Derivative Work, a credit identifying the use of the - Work in the Derivative Work (e.g., "French translation of the Work - by Original Author," or "Screenplay based on original Work by - Original Author"). Such credit may be implemented in any - reasonable manner; provided, however, that in the case of a - Derivative Work or Collective Work, at a minimum such credit will - appear where any other comparable authorship credit appears and in - a manner at least as prominent as such other comparable authorship - credit. - -*5. Representations, Warranties and Disclaimer* - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE -EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY -APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL -THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY -DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF -LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. Termination* - - 1. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Derivative Works or - Collective Works from You under this License, however, will not - have their licenses terminated provided such individuals or - entities remain in full compliance with those licenses. Sections - 1, 2, 5, 6, 7, and 8 will survive any termination of this License. - 2. Subject to the above terms and conditions, the license granted - here is perpetual (for the duration of the applicable copyright in - the Work). Notwithstanding the above, Licensor reserves the right - to release the Work under different license terms or to stop - distributing the Work at any time; provided, however that any such - election will not serve to withdraw this License (or any other - license that has been, or is required to be, granted under the - terms of this License), and this License will continue in full - force and effect unless terminated as stated above. - -*8. Miscellaneous* - - 1. Each time You distribute or publicly digitally perform the Work or - a Collective Work, the Licensor offers to the recipient a license - to the Work on the same terms and conditions as the license - granted to You under this License. - 2. Each time You distribute or publicly digitally perform a - Derivative Work, Licensor offers to the recipient a license to the - original Work on the same terms and conditions as the license - granted to You under this License. - 3. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability - of the remainder of the terms of this License, and without further - action by the parties to this agreement, such provision shall be - reformed to the minimum extent necessary to make such provision - valid and enforceable. - 4. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in - writing and signed by the party to be charged with such waiver or - consent. - 5. This License constitutes the entire agreement between the parties - with respect to the Work licensed here. There are no - understandings, agreements or representations with respect to the - Work not specified here. Licensor shall not be bound by any - additional provisions that may appear in any communication from - You. This License may not be modified without the mutual written - agreement of the Licensor and You. - -Creative Commons is not a party to this License, and makes no warranty -whatsoever in connection with the Work. Creative Commons will not be -liable to You or any party on any legal theory for any damages -whatsoever, including without limitation any general, special, -incidental or consequential damages arising in connection to this -license. Notwithstanding the foregoing two (2) sentences, if Creative -Commons has expressly identified itself as the Licensor hereunder, it -shall have all rights and obligations of Licensor. - -Except for the limited purpose of indicating to the public that the Work -is licensed under the CCPL, neither party will use the trademark -"Creative Commons" or any related trademark or logo of Creative Commons -without the prior written consent of Creative Commons. Any permitted use -will be in compliance with Creative Commons' then-current trademark -usage guidelines, as may be published on its website or otherwise made -available upon request from time to time. - -Creative Commons may be contacted at http://creativecommons.org/ -. - -« Back to Commons Deed <./> diff --git a/repoze/bfg/paster_templates/alchemy/+package+/views.py_tmpl b/repoze/bfg/paster_templates/alchemy/+package+/views.py_tmpl deleted file mode 100644 index 12bce138e..000000000 --- a/repoze/bfg/paster_templates/alchemy/+package+/views.py_tmpl +++ /dev/null @@ -1,5 +0,0 @@ -def view_root(context, request): - return {'items':list(context), 'project':'{{project}}'} - -def view_model(context, request): - return {'item':context, 'project':'{{project}}'} diff --git a/repoze/bfg/paster_templates/alchemy/+project+.ini_tmpl b/repoze/bfg/paster_templates/alchemy/+project+.ini_tmpl deleted file mode 100644 index 8260104d5..000000000 --- a/repoze/bfg/paster_templates/alchemy/+project+.ini_tmpl +++ /dev/null @@ -1,22 +0,0 @@ -[DEFAULT] -debug = true - -[app:sqlalchemy] -use = egg:{{package}}#app -reload_templates = true -debug_authorization = false -debug_notfound = false -debug_templates = true -default_locale_name = en -db_string = sqlite:///%(here)s/{{package}}.db -db_echo = false - -[pipeline:main] -pipeline = - egg:repoze.tm2#tm - sqlalchemy - -[server:main] -use = egg:Paste#http -host = 0.0.0.0 -port = 6543 diff --git a/repoze/bfg/paster_templates/alchemy/CHANGES.txt_tmpl b/repoze/bfg/paster_templates/alchemy/CHANGES.txt_tmpl deleted file mode 100644 index 5b34f7803..000000000 --- a/repoze/bfg/paster_templates/alchemy/CHANGES.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -0.1 ---- - -- Initial version diff --git a/repoze/bfg/paster_templates/alchemy/README.txt_tmpl b/repoze/bfg/paster_templates/alchemy/README.txt_tmpl deleted file mode 100644 index 0ddebfc3e..000000000 --- a/repoze/bfg/paster_templates/alchemy/README.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -{{project}} README - - - diff --git a/repoze/bfg/paster_templates/alchemy/setup.cfg_tmpl b/repoze/bfg/paster_templates/alchemy/setup.cfg_tmpl deleted file mode 100644 index 5bec29823..000000000 --- a/repoze/bfg/paster_templates/alchemy/setup.cfg_tmpl +++ /dev/null @@ -1,27 +0,0 @@ -[nosetests] -match=^test -nocapture=1 -cover-package={{package}} -with-coverage=1 -cover-erase=1 - -[compile_catalog] -directory = {{package}}/locale -domain = {{project}} -statistics = true - -[extract_messages] -add_comments = TRANSLATORS: -output_file = {{package}}/locale/{{project}}.pot -width = 80 - -[init_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale - -[update_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale -previous = true diff --git a/repoze/bfg/paster_templates/alchemy/setup.py_tmpl b/repoze/bfg/paster_templates/alchemy/setup.py_tmpl deleted file mode 100644 index 9446964e5..000000000 --- a/repoze/bfg/paster_templates/alchemy/setup.py_tmpl +++ /dev/null @@ -1,45 +0,0 @@ -import os -import sys - -from setuptools import setup, find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.txt')).read() -CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() - -requires = [ - 'repoze.bfg', - 'repoze.tm2', - 'sqlalchemy', - 'zope.sqlalchemy', - ] - -if sys.version_info[:3] < (2,5,0): - requires.append('pysqlite') - -setup(name='{{project}}', - version='0.0', - description='{{project}}', - long_description=README + '\n\n' + CHANGES, - classifiers=[ - "Programming Language :: Python", - "Framework :: BFG", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], - author='', - author_email='', - url='', - keywords='web wsgi bfg', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires = requires, - tests_require = requires, - test_suite="{{package}}", - entry_points = """\ - [paste.app_factory] - app = {{package}}.run:app - """ - ) - diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py b/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py deleted file mode 100644 index cbdfd3ac6..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# A package - diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml b/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml deleted file mode 100644 index 6d16bd089..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/configure.zcml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/models.py b/repoze/bfg/paster_templates/routesalchemy/+package+/models.py deleted file mode 100644 index a1726ebf4..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/models.py +++ /dev/null @@ -1,44 +0,0 @@ -import transaction - -from sqlalchemy import create_engine -from sqlalchemy import Column -from sqlalchemy import Integer -from sqlalchemy import Unicode - -from sqlalchemy.exc import IntegrityError -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import scoped_session -from sqlalchemy.orm import sessionmaker - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - -class MyModel(Base): - __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Unicode(255), unique=True) - value = Column(Integer) - - def __init__(self, name, value): - self.name = name - self.value = value - -def populate(): - session = DBSession() - model = MyModel(name=u'root',value=55) - session.add(model) - session.flush() - transaction.commit() - -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) - DBSession.configure(bind=engine) - Base.metadata.bind = engine - Base.metadata.create_all(engine) - try: - populate() - except IntegrityError: - pass diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl deleted file mode 100644 index b85f57556..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/run.py_tmpl +++ /dev/null @@ -1,23 +0,0 @@ -from repoze.bfg.configuration import Configurator -from paste.deploy.converters import asbool - -from {{package}}.models import initialize_sql - -def app(global_config, **settings): - """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. - """ - zcml_file = settings.get('configure_zcml', 'configure.zcml') - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) - config = Configurator(settings=settings) - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() - diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt deleted file mode 100644 index 2aedcad9f..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/mytemplate.pt +++ /dev/null @@ -1,99 +0,0 @@ - - - - -${project} Application - - - - - - - - - -
- -
- -
-
-

Welcome to ${project}, an - application generated by the repoze.bfg web - application framework.

-
-
- - - - -
 
-
-
- - - - - - diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css deleted file mode 100644 index 41b3debde..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/default.css +++ /dev/null @@ -1,380 +0,0 @@ -/* -Design by Free CSS Templates -http://www.freecsstemplates.org -Released for free under a Creative Commons Attribution 2.5 License -*/ - -body { - margin: 0; - padding: 0; - background: url(images/img01.gif) repeat-x left top; - font-size: 13px; - font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; - text-align: justify; - color: #FFFFFF; -} - -h1, h2, h3 { - margin: 0; - text-transform: lowercase; - font-weight: normal; - color: #FFFFFF; -} - -h1 { - letter-spacing: -1px; - font-size: 32px; -} - -h2 { - font-size: 23px; -} - -p, ul, ol { - margin: 0 0 2em 0; - text-align: justify; - line-height: 26px; -} - -a:link { - color: #8BD80E; -} - -a:hover, a:active { - text-decoration: none; - color: #8BD80E; -} - -a:visited { - color: #8BD80E; -} - -img { - border: none; -} - -img.left { - float: left; - margin-right: 15px; -} - -img.right { - float: right; - margin-left: 15px; -} - -/* Form */ - -form { - margin: 0; - padding: 0; -} - -fieldset { - margin: 0; - padding: 0; - border: none; -} - -legend { - display: none; -} - -input, textarea, select { - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; - font-size: 13px; - color: #333333; -} - -#wrapper { - margin: 0; - padding: 0; - background: #000000; -} - -/* Header */ - -#header { - width: 713px; - margin: 0 auto; - height: 42px; -} - -/* Menu */ - -#menu { - float: left; - width: 713px; - height: 50px; - background: url(images/img02.gif) no-repeat left top; -} - -#menu ul { - margin: 0; - padding: 0px 0 0 10px; - list-style: none; - line-height: normal; -} - -#menu li { - display: block; - float: left; -} - -#menu a { - display: block; - float: left; - background: url(images/img04.gif) no-repeat right 55%; - margin-top: 5px; - margin-right: 3px; - padding: 8px 17px; - text-decoration: none; - font-size: 13px; - color: #000000; -} - -#menu a:hover { - color: #000000; -} - -#menu .current_page_item a { - color: #000000; -} - -/** LOGO */ - -#logo { - width: 713px; - height: 80px; - margin: 0 auto; -} - -#logo h1, #logo h2 { - float: left; - margin: 0; - padding: 30px 0 0 0px; - line-height: normal; -} - -#logo h1 { - font-family: Georgia, "Times New Roman", Times, serif; - font-size:40px; -} - -#logo h1 a { - text-decoration: none; - color: #4C4C4C; -} - -#logo h1 a:hover { text-decoration: underline; } - -#logo h2 { - float: left; - padding: 45px 0 0 18px; - font: 18px Georgia, "Times New Roman", Times, serif; - color: #8BD80E; -} - -#logo p a { - text-decoration: none; - color: #8BD80E; -} - -#logo p a:hover { text-decoration: underline; } - - - -/* Page */ - -#page { - width: 663px; - margin: 0 auto; - background: #4C4C4C url(images/img03.gif) no-repeat left bottom; - padding: 0 25px; -} - -/* Content */ - -#content { - float: left; - width: 410px; - -} - -/* Post */ - -.post { - padding: 15px 0px; - margin-bottom: 20px; -} - -.post .title { - margin-bottom: 20px; - padding-bottom: 5px; -} - -.post h1 { - padding: 0px 0 0 0px; - background: url(images/img08.jpg) no-repeat left top; - font-size: 24px; - color: #FFFFFF; -} - -.post h2 { - padding: 0px 0 0 0px; - font-size: 22px; - color: #FFFFFF; -} - -.post .entry { -} - -.post .meta { - padding: 15px 15px 30px 0px; - font-family: Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.post .meta p { - margin: 0; - padding-top: 15px; - line-height: normal; - color: #FFFFFF; -} - -.post .meta .byline { - float: left; -} - -.post .meta .links { - float: right; -} - -.post .meta .more { - padding: 0 10px 0 18px; -} - -.post .meta .comments { -} - -.post .meta b { - display: none; -} - - -/* Sidebar */ - -#sidebar { - width: 210px; - float: right; - margin: 0; - padding: 0; -} - -#sidebar ul { - margin: 0; - padding: 0; - list-style: none; -} - -#sidebar li { - margin-bottom: 40px; -} - -#sidebar li ul { -} - -#sidebar li li { - margin: 0; -} - -#sidebar h2 { - width: 250px; - padding: 8px 0 0 0px; - margin-bottom: 10px; - background: url(images/img07.jpg) no-repeat left top; - font-size: 20px; - color: #FFFFFF; -} - -/* Search */ - -#search { - -} - -#search h2 { - margin-bottom: 20px; -} - -#s { - width: 140px; - margin-right: 5px; - padding: 3px; - border: 1px solid #BED99C; -} - -#x { - padding: 3px; - border: none; - background: #8BD80E; - text-transform: lowercase; - font-size: 11px; - color: #FFFFFF; -} - -/* Boxes */ - -.box1 { - padding: 20px; -} - -.box2 { - color: #BABABA; -} - -.box2 h2 { - margin-bottom: 15px; - font-size: 16px; - color: #FFFFFF; -} - -.box2 ul { - margin: 0; - padding: 0; - list-style: none; -} - -.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { - color: #EDEDED; -} - -/* Footer */ -#footer-wrap { -} - -#footer { - margin: 0 auto; - padding: 20px 0 10px 0; - background: #000000; -} - -html>body #footer { - height: auto; -} - -#footer p { - font-size: 11px; -} - -#legal { - clear: both; - padding-top: 17px; - text-align: center; - color: #FFFFFF; -} - -#legal a { - font-weight: normal; - color: #FFFFFF; -} diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif deleted file mode 100644 index 5f082bd99..000000000 Binary files a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img01.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif deleted file mode 100644 index 45a3ae976..000000000 Binary files a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img02.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif deleted file mode 100644 index d92ea38f9..000000000 Binary files a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img03.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif deleted file mode 100644 index 950c4af9d..000000000 Binary files a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/img04.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif deleted file mode 100644 index 5bfd67a2d..000000000 Binary files a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/images/spacer.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt b/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt deleted file mode 100644 index ccb6b06ab..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/templates/static/templatelicense.txt +++ /dev/null @@ -1,243 +0,0 @@ -Creative Commons - -Creative Commons Legal Code - -*Attribution 2.5* - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE -LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN -ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION -ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE -INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM -ITS USE. - -/License/ - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE -RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS -AND CONDITIONS. - -*1. Definitions* - - 1. *"Collective Work"* means a work, such as a periodical issue, - anthology or encyclopedia, in which the Work in its entirety in - unmodified form, along with a number of other contributions, - constituting separate and independent works in themselves, are - assembled into a collective whole. A work that constitutes a - Collective Work will not be considered a Derivative Work (as - defined below) for the purposes of this License. - 2. *"Derivative Work"* means a work based upon the Work or upon the - Work and other pre-existing works, such as a translation, musical - arrangement, dramatization, fictionalization, motion picture - version, sound recording, art reproduction, abridgment, - condensation, or any other form in which the Work may be recast, - transformed, or adapted, except that a work that constitutes a - Collective Work will not be considered a Derivative Work for the - purpose of this License. For the avoidance of doubt, where the - Work is a musical composition or sound recording, the - synchronization of the Work in timed-relation with a moving image - ("synching") will be considered a Derivative Work for the purpose - of this License. - 3. *"Licensor"* means the individual or entity that offers the Work - under the terms of this License. - 4. *"Original Author"* means the individual or entity who created the - Work. - 5. *"Work"* means the copyrightable work of authorship offered under - the terms of this License. - 6. *"You"* means an individual or entity exercising rights under this - License who has not previously violated the terms of this License - with respect to the Work, or who has received express permission - from the Licensor to exercise rights under this License despite a - previous violation. - -*2. Fair Use Rights.* Nothing in this license is intended to reduce, -limit, or restrict any rights arising from fair use, first sale or other -limitations on the exclusive rights of the copyright owner under -copyright law or other applicable laws. - -*3. License Grant.* Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - 1. to reproduce the Work, to incorporate the Work into one or more - Collective Works, and to reproduce the Work as incorporated in the - Collective Works; - 2. to create and reproduce Derivative Works; - 3. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission the Work including as incorporated in Collective Works; - 4. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission Derivative Works. - 5. - - For the avoidance of doubt, where the work is a musical composition: - - 1. *Performance Royalties Under Blanket Licenses*. Licensor - waives the exclusive right to collect, whether individually - or via a performance rights society (e.g. ASCAP, BMI, - SESAC), royalties for the public performance or public - digital performance (e.g. webcast) of the Work. - 2. *Mechanical Rights and Statutory Royalties*. Licensor waives - the exclusive right to collect, whether individually or via - a music rights agency or designated agent (e.g. Harry Fox - Agency), royalties for any phonorecord You create from the - Work ("cover version") and distribute, subject to the - compulsory license created by 17 USC Section 115 of the US - Copyright Act (or the equivalent in other jurisdictions). - 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of - doubt, where the Work is a sound recording, Licensor waives the - exclusive right to collect, whether individually or via a - performance-rights society (e.g. SoundExchange), royalties for the - public digital performance (e.g. webcast) of the Work, subject to - the compulsory license created by 17 USC Section 114 of the US - Copyright Act (or the equivalent in other jurisdictions). - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights -in other media and formats. All rights not expressly granted by Licensor -are hereby reserved. - -*4. Restrictions.*The license granted in Section 3 above is expressly -made subject to and limited by the following restrictions: - - 1. You may distribute, publicly display, publicly perform, or - publicly digitally perform the Work only under the terms of this - License, and You must include a copy of, or the Uniform Resource - Identifier for, this License with every copy or phonorecord of the - Work You distribute, publicly display, publicly perform, or - publicly digitally perform. You may not offer or impose any terms - on the Work that alter or restrict the terms of this License or - the recipients' exercise of the rights granted hereunder. You may - not sublicense the Work. You must keep intact all notices that - refer to this License and to the disclaimer of warranties. You may - not distribute, publicly display, publicly perform, or publicly - digitally perform the Work with any technological measures that - control access or use of the Work in a manner inconsistent with - the terms of this License Agreement. The above applies to the Work - as incorporated in a Collective Work, but this does not require - the Collective Work apart from the Work itself to be made subject - to the terms of this License. If You create a Collective Work, - upon notice from any Licensor You must, to the extent practicable, - remove from the Collective Work any credit as required by clause - 4(b), as requested. If You create a Derivative Work, upon notice - from any Licensor You must, to the extent practicable, remove from - the Derivative Work any credit as required by clause 4(b), as - requested. - 2. If you distribute, publicly display, publicly perform, or publicly - digitally perform the Work or any Derivative Works or Collective - Works, You must keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) - the name of the Original Author (or pseudonym, if applicable) if - supplied, and/or (ii) if the Original Author and/or Licensor - designate another party or parties (e.g. a sponsor institute, - publishing entity, journal) for attribution in Licensor's - copyright notice, terms of service or by other reasonable means, - the name of such party or parties; the title of the Work if - supplied; to the extent reasonably practicable, the Uniform - Resource Identifier, if any, that Licensor specifies to be - associated with the Work, unless such URI does not refer to the - copyright notice or licensing information for the Work; and in the - case of a Derivative Work, a credit identifying the use of the - Work in the Derivative Work (e.g., "French translation of the Work - by Original Author," or "Screenplay based on original Work by - Original Author"). Such credit may be implemented in any - reasonable manner; provided, however, that in the case of a - Derivative Work or Collective Work, at a minimum such credit will - appear where any other comparable authorship credit appears and in - a manner at least as prominent as such other comparable authorship - credit. - -*5. Representations, Warranties and Disclaimer* - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE -EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY -APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL -THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY -DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF -LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. Termination* - - 1. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Derivative Works or - Collective Works from You under this License, however, will not - have their licenses terminated provided such individuals or - entities remain in full compliance with those licenses. Sections - 1, 2, 5, 6, 7, and 8 will survive any termination of this License. - 2. Subject to the above terms and conditions, the license granted - here is perpetual (for the duration of the applicable copyright in - the Work). Notwithstanding the above, Licensor reserves the right - to release the Work under different license terms or to stop - distributing the Work at any time; provided, however that any such - election will not serve to withdraw this License (or any other - license that has been, or is required to be, granted under the - terms of this License), and this License will continue in full - force and effect unless terminated as stated above. - -*8. Miscellaneous* - - 1. Each time You distribute or publicly digitally perform the Work or - a Collective Work, the Licensor offers to the recipient a license - to the Work on the same terms and conditions as the license - granted to You under this License. - 2. Each time You distribute or publicly digitally perform a - Derivative Work, Licensor offers to the recipient a license to the - original Work on the same terms and conditions as the license - granted to You under this License. - 3. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability - of the remainder of the terms of this License, and without further - action by the parties to this agreement, such provision shall be - reformed to the minimum extent necessary to make such provision - valid and enforceable. - 4. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in - writing and signed by the party to be charged with such waiver or - consent. - 5. This License constitutes the entire agreement between the parties - with respect to the Work licensed here. There are no - understandings, agreements or representations with respect to the - Work not specified here. Licensor shall not be bound by any - additional provisions that may appear in any communication from - You. This License may not be modified without the mutual written - agreement of the Licensor and You. - -Creative Commons is not a party to this License, and makes no warranty -whatsoever in connection with the Work. Creative Commons will not be -liable to You or any party on any legal theory for any damages -whatsoever, including without limitation any general, special, -incidental or consequential damages arising in connection to this -license. Notwithstanding the foregoing two (2) sentences, if Creative -Commons has expressly identified itself as the Licensor hereunder, it -shall have all rights and obligations of Licensor. - -Except for the limited purpose of indicating to the public that the Work -is licensed under the CCPL, neither party will use the trademark -"Creative Commons" or any related trademark or logo of Creative Commons -without the prior written consent of Creative Commons. Any permitted use -will be in compliance with Creative Commons' then-current trademark -usage guidelines, as may be published on its website or otherwise made -available upon request from time to time. - -Creative Commons may be contacted at http://creativecommons.org/ -. - -« Back to Commons Deed <./> diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl deleted file mode 100644 index ed7f1280b..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/tests.py_tmpl +++ /dev/null @@ -1,24 +0,0 @@ -import unittest -from repoze.bfg.configuration import Configurator -from repoze.bfg import testing - -def _initTestingDB(): - from {{package}}.models import initialize_sql - session = initialize_sql('sqlite://') - return session - -class TestMyView(unittest.TestCase): - def setUp(self): - self.config = Configurator() - self.config.begin() - _initTestingDB() - - def tearDown(self): - self.config.end() - - def test_it(self): - from {{package}}.views import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['root'].name, 'root') - self.assertEqual(info['project'], '{{package}}') diff --git a/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl deleted file mode 100644 index 86cc02e41..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+package+/views.py_tmpl +++ /dev/null @@ -1,7 +0,0 @@ -from {{package}}.models import DBSession -from {{package}}.models import MyModel - -def my_view(request): - dbsession = DBSession() - root = dbsession.query(MyModel).filter(MyModel.name==u'root').first() - return {'root':root, 'project':'{{project}}'} diff --git a/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl b/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl deleted file mode 100644 index 8260104d5..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/+project+.ini_tmpl +++ /dev/null @@ -1,22 +0,0 @@ -[DEFAULT] -debug = true - -[app:sqlalchemy] -use = egg:{{package}}#app -reload_templates = true -debug_authorization = false -debug_notfound = false -debug_templates = true -default_locale_name = en -db_string = sqlite:///%(here)s/{{package}}.db -db_echo = false - -[pipeline:main] -pipeline = - egg:repoze.tm2#tm - sqlalchemy - -[server:main] -use = egg:Paste#http -host = 0.0.0.0 -port = 6543 diff --git a/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl b/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl deleted file mode 100644 index 35a34f332..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/CHANGES.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version diff --git a/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl b/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl deleted file mode 100644 index 0ddebfc3e..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/README.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -{{project}} README - - - diff --git a/repoze/bfg/paster_templates/routesalchemy/setup.cfg_tmpl b/repoze/bfg/paster_templates/routesalchemy/setup.cfg_tmpl deleted file mode 100644 index 5bec29823..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/setup.cfg_tmpl +++ /dev/null @@ -1,27 +0,0 @@ -[nosetests] -match=^test -nocapture=1 -cover-package={{package}} -with-coverage=1 -cover-erase=1 - -[compile_catalog] -directory = {{package}}/locale -domain = {{project}} -statistics = true - -[extract_messages] -add_comments = TRANSLATORS: -output_file = {{package}}/locale/{{project}}.pot -width = 80 - -[init_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale - -[update_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale -previous = true diff --git a/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl b/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl deleted file mode 100644 index 0ce1b8bad..000000000 --- a/repoze/bfg/paster_templates/routesalchemy/setup.py_tmpl +++ /dev/null @@ -1,45 +0,0 @@ -import os -import sys - -from setuptools import setup, find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.txt')).read() -CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() - -requires = [ - 'repoze.bfg', - 'SQLAlchemy', - 'transaction', - 'repoze.tm2', - 'zope.sqlalchemy', - ] - -if sys.version_info[:3] < (2,5,0): - requires.append('pysqlite') - -setup(name='{{project}}', - version='0.0', - description='{{project}}', - long_description=README + '\n\n' + CHANGES, - classifiers=[ - "Programming Language :: Python", - "Framework :: BFG", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], - author='', - author_email='', - url='', - keywords='web wsgi bfg', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - test_suite='{{package}}', - install_requires = requires, - entry_points = """\ - [paste.app_factory] - app = {{package}}.run:app - """ - ) - diff --git a/repoze/bfg/paster_templates/starter/+package+/__init__.py b/repoze/bfg/paster_templates/starter/+package+/__init__.py deleted file mode 100644 index cbdfd3ac6..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# A package - diff --git a/repoze/bfg/paster_templates/starter/+package+/configure.zcml b/repoze/bfg/paster_templates/starter/+package+/configure.zcml deleted file mode 100644 index e83dd3933..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/configure.zcml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/repoze/bfg/paster_templates/starter/+package+/models.py b/repoze/bfg/paster_templates/starter/+package+/models.py deleted file mode 100644 index 75dec7505..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/models.py +++ /dev/null @@ -1,7 +0,0 @@ -class MyModel(object): - pass - -root = MyModel() - -def get_root(request): - return root diff --git a/repoze/bfg/paster_templates/starter/+package+/run.py_tmpl b/repoze/bfg/paster_templates/starter/+package+/run.py_tmpl deleted file mode 100644 index 74876f3f4..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/run.py_tmpl +++ /dev/null @@ -1,15 +0,0 @@ -from repoze.bfg.configuration import Configurator -from {{package}}.models import get_root - -def app(global_config, **settings): - """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. - """ - zcml_file = settings.get('configure_zcml', 'configure.zcml') - config = Configurator(root_factory=get_root, settings=settings) - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/mytemplate.pt b/repoze/bfg/paster_templates/starter/+package+/templates/mytemplate.pt deleted file mode 100644 index 2aedcad9f..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/templates/mytemplate.pt +++ /dev/null @@ -1,99 +0,0 @@ - - - - -${project} Application - - - - - - - - - -
- -
- -
-
-

Welcome to ${project}, an - application generated by the repoze.bfg web - application framework.

-
-
- - - - -
 
-
-
- - - - - - diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/default.css b/repoze/bfg/paster_templates/starter/+package+/templates/static/default.css deleted file mode 100644 index 41b3debde..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/templates/static/default.css +++ /dev/null @@ -1,380 +0,0 @@ -/* -Design by Free CSS Templates -http://www.freecsstemplates.org -Released for free under a Creative Commons Attribution 2.5 License -*/ - -body { - margin: 0; - padding: 0; - background: url(images/img01.gif) repeat-x left top; - font-size: 13px; - font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; - text-align: justify; - color: #FFFFFF; -} - -h1, h2, h3 { - margin: 0; - text-transform: lowercase; - font-weight: normal; - color: #FFFFFF; -} - -h1 { - letter-spacing: -1px; - font-size: 32px; -} - -h2 { - font-size: 23px; -} - -p, ul, ol { - margin: 0 0 2em 0; - text-align: justify; - line-height: 26px; -} - -a:link { - color: #8BD80E; -} - -a:hover, a:active { - text-decoration: none; - color: #8BD80E; -} - -a:visited { - color: #8BD80E; -} - -img { - border: none; -} - -img.left { - float: left; - margin-right: 15px; -} - -img.right { - float: right; - margin-left: 15px; -} - -/* Form */ - -form { - margin: 0; - padding: 0; -} - -fieldset { - margin: 0; - padding: 0; - border: none; -} - -legend { - display: none; -} - -input, textarea, select { - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; - font-size: 13px; - color: #333333; -} - -#wrapper { - margin: 0; - padding: 0; - background: #000000; -} - -/* Header */ - -#header { - width: 713px; - margin: 0 auto; - height: 42px; -} - -/* Menu */ - -#menu { - float: left; - width: 713px; - height: 50px; - background: url(images/img02.gif) no-repeat left top; -} - -#menu ul { - margin: 0; - padding: 0px 0 0 10px; - list-style: none; - line-height: normal; -} - -#menu li { - display: block; - float: left; -} - -#menu a { - display: block; - float: left; - background: url(images/img04.gif) no-repeat right 55%; - margin-top: 5px; - margin-right: 3px; - padding: 8px 17px; - text-decoration: none; - font-size: 13px; - color: #000000; -} - -#menu a:hover { - color: #000000; -} - -#menu .current_page_item a { - color: #000000; -} - -/** LOGO */ - -#logo { - width: 713px; - height: 80px; - margin: 0 auto; -} - -#logo h1, #logo h2 { - float: left; - margin: 0; - padding: 30px 0 0 0px; - line-height: normal; -} - -#logo h1 { - font-family: Georgia, "Times New Roman", Times, serif; - font-size:40px; -} - -#logo h1 a { - text-decoration: none; - color: #4C4C4C; -} - -#logo h1 a:hover { text-decoration: underline; } - -#logo h2 { - float: left; - padding: 45px 0 0 18px; - font: 18px Georgia, "Times New Roman", Times, serif; - color: #8BD80E; -} - -#logo p a { - text-decoration: none; - color: #8BD80E; -} - -#logo p a:hover { text-decoration: underline; } - - - -/* Page */ - -#page { - width: 663px; - margin: 0 auto; - background: #4C4C4C url(images/img03.gif) no-repeat left bottom; - padding: 0 25px; -} - -/* Content */ - -#content { - float: left; - width: 410px; - -} - -/* Post */ - -.post { - padding: 15px 0px; - margin-bottom: 20px; -} - -.post .title { - margin-bottom: 20px; - padding-bottom: 5px; -} - -.post h1 { - padding: 0px 0 0 0px; - background: url(images/img08.jpg) no-repeat left top; - font-size: 24px; - color: #FFFFFF; -} - -.post h2 { - padding: 0px 0 0 0px; - font-size: 22px; - color: #FFFFFF; -} - -.post .entry { -} - -.post .meta { - padding: 15px 15px 30px 0px; - font-family: Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.post .meta p { - margin: 0; - padding-top: 15px; - line-height: normal; - color: #FFFFFF; -} - -.post .meta .byline { - float: left; -} - -.post .meta .links { - float: right; -} - -.post .meta .more { - padding: 0 10px 0 18px; -} - -.post .meta .comments { -} - -.post .meta b { - display: none; -} - - -/* Sidebar */ - -#sidebar { - width: 210px; - float: right; - margin: 0; - padding: 0; -} - -#sidebar ul { - margin: 0; - padding: 0; - list-style: none; -} - -#sidebar li { - margin-bottom: 40px; -} - -#sidebar li ul { -} - -#sidebar li li { - margin: 0; -} - -#sidebar h2 { - width: 250px; - padding: 8px 0 0 0px; - margin-bottom: 10px; - background: url(images/img07.jpg) no-repeat left top; - font-size: 20px; - color: #FFFFFF; -} - -/* Search */ - -#search { - -} - -#search h2 { - margin-bottom: 20px; -} - -#s { - width: 140px; - margin-right: 5px; - padding: 3px; - border: 1px solid #BED99C; -} - -#x { - padding: 3px; - border: none; - background: #8BD80E; - text-transform: lowercase; - font-size: 11px; - color: #FFFFFF; -} - -/* Boxes */ - -.box1 { - padding: 20px; -} - -.box2 { - color: #BABABA; -} - -.box2 h2 { - margin-bottom: 15px; - font-size: 16px; - color: #FFFFFF; -} - -.box2 ul { - margin: 0; - padding: 0; - list-style: none; -} - -.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { - color: #EDEDED; -} - -/* Footer */ -#footer-wrap { -} - -#footer { - margin: 0 auto; - padding: 20px 0 10px 0; - background: #000000; -} - -html>body #footer { - height: auto; -} - -#footer p { - font-size: 11px; -} - -#legal { - clear: both; - padding-top: 17px; - text-align: center; - color: #FFFFFF; -} - -#legal a { - font-weight: normal; - color: #FFFFFF; -} diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img01.gif b/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img01.gif deleted file mode 100644 index 5f082bd99..000000000 Binary files a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img01.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img02.gif b/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img02.gif deleted file mode 100644 index 45a3ae976..000000000 Binary files a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img02.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img03.gif b/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img03.gif deleted file mode 100644 index d92ea38f9..000000000 Binary files a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img03.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img04.gif b/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img04.gif deleted file mode 100644 index 950c4af9d..000000000 Binary files a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/img04.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/spacer.gif b/repoze/bfg/paster_templates/starter/+package+/templates/static/images/spacer.gif deleted file mode 100644 index 5bfd67a2d..000000000 Binary files a/repoze/bfg/paster_templates/starter/+package+/templates/static/images/spacer.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/starter/+package+/templates/static/templatelicense.txt b/repoze/bfg/paster_templates/starter/+package+/templates/static/templatelicense.txt deleted file mode 100644 index ccb6b06ab..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/templates/static/templatelicense.txt +++ /dev/null @@ -1,243 +0,0 @@ -Creative Commons - -Creative Commons Legal Code - -*Attribution 2.5* - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE -LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN -ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION -ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE -INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM -ITS USE. - -/License/ - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE -RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS -AND CONDITIONS. - -*1. Definitions* - - 1. *"Collective Work"* means a work, such as a periodical issue, - anthology or encyclopedia, in which the Work in its entirety in - unmodified form, along with a number of other contributions, - constituting separate and independent works in themselves, are - assembled into a collective whole. A work that constitutes a - Collective Work will not be considered a Derivative Work (as - defined below) for the purposes of this License. - 2. *"Derivative Work"* means a work based upon the Work or upon the - Work and other pre-existing works, such as a translation, musical - arrangement, dramatization, fictionalization, motion picture - version, sound recording, art reproduction, abridgment, - condensation, or any other form in which the Work may be recast, - transformed, or adapted, except that a work that constitutes a - Collective Work will not be considered a Derivative Work for the - purpose of this License. For the avoidance of doubt, where the - Work is a musical composition or sound recording, the - synchronization of the Work in timed-relation with a moving image - ("synching") will be considered a Derivative Work for the purpose - of this License. - 3. *"Licensor"* means the individual or entity that offers the Work - under the terms of this License. - 4. *"Original Author"* means the individual or entity who created the - Work. - 5. *"Work"* means the copyrightable work of authorship offered under - the terms of this License. - 6. *"You"* means an individual or entity exercising rights under this - License who has not previously violated the terms of this License - with respect to the Work, or who has received express permission - from the Licensor to exercise rights under this License despite a - previous violation. - -*2. Fair Use Rights.* Nothing in this license is intended to reduce, -limit, or restrict any rights arising from fair use, first sale or other -limitations on the exclusive rights of the copyright owner under -copyright law or other applicable laws. - -*3. License Grant.* Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - 1. to reproduce the Work, to incorporate the Work into one or more - Collective Works, and to reproduce the Work as incorporated in the - Collective Works; - 2. to create and reproduce Derivative Works; - 3. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission the Work including as incorporated in Collective Works; - 4. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission Derivative Works. - 5. - - For the avoidance of doubt, where the work is a musical composition: - - 1. *Performance Royalties Under Blanket Licenses*. Licensor - waives the exclusive right to collect, whether individually - or via a performance rights society (e.g. ASCAP, BMI, - SESAC), royalties for the public performance or public - digital performance (e.g. webcast) of the Work. - 2. *Mechanical Rights and Statutory Royalties*. Licensor waives - the exclusive right to collect, whether individually or via - a music rights agency or designated agent (e.g. Harry Fox - Agency), royalties for any phonorecord You create from the - Work ("cover version") and distribute, subject to the - compulsory license created by 17 USC Section 115 of the US - Copyright Act (or the equivalent in other jurisdictions). - 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of - doubt, where the Work is a sound recording, Licensor waives the - exclusive right to collect, whether individually or via a - performance-rights society (e.g. SoundExchange), royalties for the - public digital performance (e.g. webcast) of the Work, subject to - the compulsory license created by 17 USC Section 114 of the US - Copyright Act (or the equivalent in other jurisdictions). - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights -in other media and formats. All rights not expressly granted by Licensor -are hereby reserved. - -*4. Restrictions.*The license granted in Section 3 above is expressly -made subject to and limited by the following restrictions: - - 1. You may distribute, publicly display, publicly perform, or - publicly digitally perform the Work only under the terms of this - License, and You must include a copy of, or the Uniform Resource - Identifier for, this License with every copy or phonorecord of the - Work You distribute, publicly display, publicly perform, or - publicly digitally perform. You may not offer or impose any terms - on the Work that alter or restrict the terms of this License or - the recipients' exercise of the rights granted hereunder. You may - not sublicense the Work. You must keep intact all notices that - refer to this License and to the disclaimer of warranties. You may - not distribute, publicly display, publicly perform, or publicly - digitally perform the Work with any technological measures that - control access or use of the Work in a manner inconsistent with - the terms of this License Agreement. The above applies to the Work - as incorporated in a Collective Work, but this does not require - the Collective Work apart from the Work itself to be made subject - to the terms of this License. If You create a Collective Work, - upon notice from any Licensor You must, to the extent practicable, - remove from the Collective Work any credit as required by clause - 4(b), as requested. If You create a Derivative Work, upon notice - from any Licensor You must, to the extent practicable, remove from - the Derivative Work any credit as required by clause 4(b), as - requested. - 2. If you distribute, publicly display, publicly perform, or publicly - digitally perform the Work or any Derivative Works or Collective - Works, You must keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) - the name of the Original Author (or pseudonym, if applicable) if - supplied, and/or (ii) if the Original Author and/or Licensor - designate another party or parties (e.g. a sponsor institute, - publishing entity, journal) for attribution in Licensor's - copyright notice, terms of service or by other reasonable means, - the name of such party or parties; the title of the Work if - supplied; to the extent reasonably practicable, the Uniform - Resource Identifier, if any, that Licensor specifies to be - associated with the Work, unless such URI does not refer to the - copyright notice or licensing information for the Work; and in the - case of a Derivative Work, a credit identifying the use of the - Work in the Derivative Work (e.g., "French translation of the Work - by Original Author," or "Screenplay based on original Work by - Original Author"). Such credit may be implemented in any - reasonable manner; provided, however, that in the case of a - Derivative Work or Collective Work, at a minimum such credit will - appear where any other comparable authorship credit appears and in - a manner at least as prominent as such other comparable authorship - credit. - -*5. Representations, Warranties and Disclaimer* - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE -EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY -APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL -THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY -DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF -LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. Termination* - - 1. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Derivative Works or - Collective Works from You under this License, however, will not - have their licenses terminated provided such individuals or - entities remain in full compliance with those licenses. Sections - 1, 2, 5, 6, 7, and 8 will survive any termination of this License. - 2. Subject to the above terms and conditions, the license granted - here is perpetual (for the duration of the applicable copyright in - the Work). Notwithstanding the above, Licensor reserves the right - to release the Work under different license terms or to stop - distributing the Work at any time; provided, however that any such - election will not serve to withdraw this License (or any other - license that has been, or is required to be, granted under the - terms of this License), and this License will continue in full - force and effect unless terminated as stated above. - -*8. Miscellaneous* - - 1. Each time You distribute or publicly digitally perform the Work or - a Collective Work, the Licensor offers to the recipient a license - to the Work on the same terms and conditions as the license - granted to You under this License. - 2. Each time You distribute or publicly digitally perform a - Derivative Work, Licensor offers to the recipient a license to the - original Work on the same terms and conditions as the license - granted to You under this License. - 3. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability - of the remainder of the terms of this License, and without further - action by the parties to this agreement, such provision shall be - reformed to the minimum extent necessary to make such provision - valid and enforceable. - 4. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in - writing and signed by the party to be charged with such waiver or - consent. - 5. This License constitutes the entire agreement between the parties - with respect to the Work licensed here. There are no - understandings, agreements or representations with respect to the - Work not specified here. Licensor shall not be bound by any - additional provisions that may appear in any communication from - You. This License may not be modified without the mutual written - agreement of the Licensor and You. - -Creative Commons is not a party to this License, and makes no warranty -whatsoever in connection with the Work. Creative Commons will not be -liable to You or any party on any legal theory for any damages -whatsoever, including without limitation any general, special, -incidental or consequential damages arising in connection to this -license. Notwithstanding the foregoing two (2) sentences, if Creative -Commons has expressly identified itself as the Licensor hereunder, it -shall have all rights and obligations of Licensor. - -Except for the limited purpose of indicating to the public that the Work -is licensed under the CCPL, neither party will use the trademark -"Creative Commons" or any related trademark or logo of Creative Commons -without the prior written consent of Creative Commons. Any permitted use -will be in compliance with Creative Commons' then-current trademark -usage guidelines, as may be published on its website or otherwise made -available upon request from time to time. - -Creative Commons may be contacted at http://creativecommons.org/ -. - -« Back to Commons Deed <./> diff --git a/repoze/bfg/paster_templates/starter/+package+/tests.py_tmpl b/repoze/bfg/paster_templates/starter/+package+/tests.py_tmpl deleted file mode 100644 index 578a58d35..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/tests.py_tmpl +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -from repoze.bfg.configuration import Configurator -from repoze.bfg import testing - -class ViewTests(unittest.TestCase): - def setUp(self): - self.config = Configurator() - self.config.begin() - - def tearDown(self): - self.config.end() - - def test_my_view(self): - from {{package}}.views import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], '{{project}}') - - diff --git a/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl b/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl deleted file mode 100644 index 12ed8832d..000000000 --- a/repoze/bfg/paster_templates/starter/+package+/views.py_tmpl +++ /dev/null @@ -1,2 +0,0 @@ -def my_view(request): - return {'project':'{{project}}'} diff --git a/repoze/bfg/paster_templates/starter/+project+.ini_tmpl b/repoze/bfg/paster_templates/starter/+project+.ini_tmpl deleted file mode 100644 index 9bdeec1ae..000000000 --- a/repoze/bfg/paster_templates/starter/+project+.ini_tmpl +++ /dev/null @@ -1,15 +0,0 @@ -[DEFAULT] -debug = true - -[app:main] -use = egg:{{project}}#app -reload_templates = true -debug_authorization = false -debug_notfound = false -debug_templates = true -default_locale_name = en - -[server:main] -use = egg:Paste#http -host = 0.0.0.0 -port = 6543 diff --git a/repoze/bfg/paster_templates/starter/CHANGES.txt_tmpl b/repoze/bfg/paster_templates/starter/CHANGES.txt_tmpl deleted file mode 100644 index 35a34f332..000000000 --- a/repoze/bfg/paster_templates/starter/CHANGES.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version diff --git a/repoze/bfg/paster_templates/starter/README.txt_tmpl b/repoze/bfg/paster_templates/starter/README.txt_tmpl deleted file mode 100644 index 0ddebfc3e..000000000 --- a/repoze/bfg/paster_templates/starter/README.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -{{project}} README - - - diff --git a/repoze/bfg/paster_templates/starter/setup.cfg_tmpl b/repoze/bfg/paster_templates/starter/setup.cfg_tmpl deleted file mode 100644 index 5bec29823..000000000 --- a/repoze/bfg/paster_templates/starter/setup.cfg_tmpl +++ /dev/null @@ -1,27 +0,0 @@ -[nosetests] -match=^test -nocapture=1 -cover-package={{package}} -with-coverage=1 -cover-erase=1 - -[compile_catalog] -directory = {{package}}/locale -domain = {{project}} -statistics = true - -[extract_messages] -add_comments = TRANSLATORS: -output_file = {{package}}/locale/{{project}}.pot -width = 80 - -[init_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale - -[update_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale -previous = true diff --git a/repoze/bfg/paster_templates/starter/setup.py_tmpl b/repoze/bfg/paster_templates/starter/setup.py_tmpl deleted file mode 100644 index df1206c1e..000000000 --- a/repoze/bfg/paster_templates/starter/setup.py_tmpl +++ /dev/null @@ -1,36 +0,0 @@ -import os - -from setuptools import setup, find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.txt')).read() -CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() - -requires = ['repoze.bfg'] - -setup(name='{{project}}', - version='0.0', - description='{{project}}', - long_description=README + '\n\n' + CHANGES, - classifiers=[ - "Programming Language :: Python", - "Framework :: BFG", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], - author='', - author_email='', - url='', - keywords='web wsgi bfg', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=requires, - tests_require=requires, - test_suite="{{package}}", - entry_points = """\ - [paste.app_factory] - app = {{package}}.run:app - """ - ) - diff --git a/repoze/bfg/paster_templates/zodb/+package+/__init__.py b/repoze/bfg/paster_templates/zodb/+package+/__init__.py deleted file mode 100644 index cbdfd3ac6..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# A package - diff --git a/repoze/bfg/paster_templates/zodb/+package+/configure.zcml b/repoze/bfg/paster_templates/zodb/+package+/configure.zcml deleted file mode 100644 index e83dd3933..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/configure.zcml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/repoze/bfg/paster_templates/zodb/+package+/models.py b/repoze/bfg/paster_templates/zodb/+package+/models.py deleted file mode 100644 index 8dd0f5a49..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/models.py +++ /dev/null @@ -1,12 +0,0 @@ -from persistent.mapping import PersistentMapping - -class MyModel(PersistentMapping): - __parent__ = __name__ = None - -def appmaker(zodb_root): - if not 'app_root' in zodb_root: - app_root = MyModel() - zodb_root['app_root'] = app_root - import transaction - transaction.commit() - return zodb_root['app_root'] diff --git a/repoze/bfg/paster_templates/zodb/+package+/run.py_tmpl b/repoze/bfg/paster_templates/zodb/+package+/run.py_tmpl deleted file mode 100644 index c0a245089..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/run.py_tmpl +++ /dev/null @@ -1,23 +0,0 @@ -from repoze.bfg.configuration import Configurator -from repoze.zodbconn.finder import PersistentApplicationFinder -from {{package}}.models import appmaker - -def app(global_config, **settings): - """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. - """ - zodb_uri = settings.get('zodb_uri') - zcml_file = settings.get('configure_zcml', 'configure.zcml') - if zodb_uri is None: - raise ValueError("No 'zodb_uri' in application configuration.") - - finder = PersistentApplicationFinder(zodb_uri, appmaker) - def get_root(request): - return finder(request.environ) - config = Configurator(root_factory=get_root, settings=settings) - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/mytemplate.pt b/repoze/bfg/paster_templates/zodb/+package+/templates/mytemplate.pt deleted file mode 100644 index 2aedcad9f..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/templates/mytemplate.pt +++ /dev/null @@ -1,99 +0,0 @@ - - - - -${project} Application - - - - - - - - - -
- -
- -
-
-

Welcome to ${project}, an - application generated by the repoze.bfg web - application framework.

-
-
- - - - -
 
-
-
- - - - - - diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/default.css b/repoze/bfg/paster_templates/zodb/+package+/templates/static/default.css deleted file mode 100644 index 41b3debde..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/templates/static/default.css +++ /dev/null @@ -1,380 +0,0 @@ -/* -Design by Free CSS Templates -http://www.freecsstemplates.org -Released for free under a Creative Commons Attribution 2.5 License -*/ - -body { - margin: 0; - padding: 0; - background: url(images/img01.gif) repeat-x left top; - font-size: 13px; - font-family: "Trebuchet MS", Georgia, "Times New Roman", Times, serif; - text-align: justify; - color: #FFFFFF; -} - -h1, h2, h3 { - margin: 0; - text-transform: lowercase; - font-weight: normal; - color: #FFFFFF; -} - -h1 { - letter-spacing: -1px; - font-size: 32px; -} - -h2 { - font-size: 23px; -} - -p, ul, ol { - margin: 0 0 2em 0; - text-align: justify; - line-height: 26px; -} - -a:link { - color: #8BD80E; -} - -a:hover, a:active { - text-decoration: none; - color: #8BD80E; -} - -a:visited { - color: #8BD80E; -} - -img { - border: none; -} - -img.left { - float: left; - margin-right: 15px; -} - -img.right { - float: right; - margin-left: 15px; -} - -/* Form */ - -form { - margin: 0; - padding: 0; -} - -fieldset { - margin: 0; - padding: 0; - border: none; -} - -legend { - display: none; -} - -input, textarea, select { - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; - font-size: 13px; - color: #333333; -} - -#wrapper { - margin: 0; - padding: 0; - background: #000000; -} - -/* Header */ - -#header { - width: 713px; - margin: 0 auto; - height: 42px; -} - -/* Menu */ - -#menu { - float: left; - width: 713px; - height: 50px; - background: url(images/img02.gif) no-repeat left top; -} - -#menu ul { - margin: 0; - padding: 0px 0 0 10px; - list-style: none; - line-height: normal; -} - -#menu li { - display: block; - float: left; -} - -#menu a { - display: block; - float: left; - background: url(images/img04.gif) no-repeat right 55%; - margin-top: 5px; - margin-right: 3px; - padding: 8px 17px; - text-decoration: none; - font-size: 13px; - color: #000000; -} - -#menu a:hover { - color: #000000; -} - -#menu .current_page_item a { - color: #000000; -} - -/** LOGO */ - -#logo { - width: 713px; - height: 80px; - margin: 0 auto; -} - -#logo h1, #logo h2 { - float: left; - margin: 0; - padding: 30px 0 0 0px; - line-height: normal; -} - -#logo h1 { - font-family: Georgia, "Times New Roman", Times, serif; - font-size:40px; -} - -#logo h1 a { - text-decoration: none; - color: #4C4C4C; -} - -#logo h1 a:hover { text-decoration: underline; } - -#logo h2 { - float: left; - padding: 45px 0 0 18px; - font: 18px Georgia, "Times New Roman", Times, serif; - color: #8BD80E; -} - -#logo p a { - text-decoration: none; - color: #8BD80E; -} - -#logo p a:hover { text-decoration: underline; } - - - -/* Page */ - -#page { - width: 663px; - margin: 0 auto; - background: #4C4C4C url(images/img03.gif) no-repeat left bottom; - padding: 0 25px; -} - -/* Content */ - -#content { - float: left; - width: 410px; - -} - -/* Post */ - -.post { - padding: 15px 0px; - margin-bottom: 20px; -} - -.post .title { - margin-bottom: 20px; - padding-bottom: 5px; -} - -.post h1 { - padding: 0px 0 0 0px; - background: url(images/img08.jpg) no-repeat left top; - font-size: 24px; - color: #FFFFFF; -} - -.post h2 { - padding: 0px 0 0 0px; - font-size: 22px; - color: #FFFFFF; -} - -.post .entry { -} - -.post .meta { - padding: 15px 15px 30px 0px; - font-family: Arial, Helvetica, sans-serif; - font-size: 11px; -} - -.post .meta p { - margin: 0; - padding-top: 15px; - line-height: normal; - color: #FFFFFF; -} - -.post .meta .byline { - float: left; -} - -.post .meta .links { - float: right; -} - -.post .meta .more { - padding: 0 10px 0 18px; -} - -.post .meta .comments { -} - -.post .meta b { - display: none; -} - - -/* Sidebar */ - -#sidebar { - width: 210px; - float: right; - margin: 0; - padding: 0; -} - -#sidebar ul { - margin: 0; - padding: 0; - list-style: none; -} - -#sidebar li { - margin-bottom: 40px; -} - -#sidebar li ul { -} - -#sidebar li li { - margin: 0; -} - -#sidebar h2 { - width: 250px; - padding: 8px 0 0 0px; - margin-bottom: 10px; - background: url(images/img07.jpg) no-repeat left top; - font-size: 20px; - color: #FFFFFF; -} - -/* Search */ - -#search { - -} - -#search h2 { - margin-bottom: 20px; -} - -#s { - width: 140px; - margin-right: 5px; - padding: 3px; - border: 1px solid #BED99C; -} - -#x { - padding: 3px; - border: none; - background: #8BD80E; - text-transform: lowercase; - font-size: 11px; - color: #FFFFFF; -} - -/* Boxes */ - -.box1 { - padding: 20px; -} - -.box2 { - color: #BABABA; -} - -.box2 h2 { - margin-bottom: 15px; - font-size: 16px; - color: #FFFFFF; -} - -.box2 ul { - margin: 0; - padding: 0; - list-style: none; -} - -.box2 a:link, .box2 a:hover, .box2 a:active, .box2 a:visited { - color: #EDEDED; -} - -/* Footer */ -#footer-wrap { -} - -#footer { - margin: 0 auto; - padding: 20px 0 10px 0; - background: #000000; -} - -html>body #footer { - height: auto; -} - -#footer p { - font-size: 11px; -} - -#legal { - clear: both; - padding-top: 17px; - text-align: center; - color: #FFFFFF; -} - -#legal a { - font-weight: normal; - color: #FFFFFF; -} diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img01.gif b/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img01.gif deleted file mode 100644 index 5f082bd99..000000000 Binary files a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img01.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img02.gif b/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img02.gif deleted file mode 100644 index 45a3ae976..000000000 Binary files a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img02.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img03.gif b/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img03.gif deleted file mode 100644 index d92ea38f9..000000000 Binary files a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img03.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img04.gif b/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img04.gif deleted file mode 100644 index 950c4af9d..000000000 Binary files a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/img04.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/spacer.gif b/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/spacer.gif deleted file mode 100644 index 5bfd67a2d..000000000 Binary files a/repoze/bfg/paster_templates/zodb/+package+/templates/static/images/spacer.gif and /dev/null differ diff --git a/repoze/bfg/paster_templates/zodb/+package+/templates/static/templatelicense.txt b/repoze/bfg/paster_templates/zodb/+package+/templates/static/templatelicense.txt deleted file mode 100644 index ccb6b06ab..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/templates/static/templatelicense.txt +++ /dev/null @@ -1,243 +0,0 @@ -Creative Commons - -Creative Commons Legal Code - -*Attribution 2.5* - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE -LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN -ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION -ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE -INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM -ITS USE. - -/License/ - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE -RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS -AND CONDITIONS. - -*1. Definitions* - - 1. *"Collective Work"* means a work, such as a periodical issue, - anthology or encyclopedia, in which the Work in its entirety in - unmodified form, along with a number of other contributions, - constituting separate and independent works in themselves, are - assembled into a collective whole. A work that constitutes a - Collective Work will not be considered a Derivative Work (as - defined below) for the purposes of this License. - 2. *"Derivative Work"* means a work based upon the Work or upon the - Work and other pre-existing works, such as a translation, musical - arrangement, dramatization, fictionalization, motion picture - version, sound recording, art reproduction, abridgment, - condensation, or any other form in which the Work may be recast, - transformed, or adapted, except that a work that constitutes a - Collective Work will not be considered a Derivative Work for the - purpose of this License. For the avoidance of doubt, where the - Work is a musical composition or sound recording, the - synchronization of the Work in timed-relation with a moving image - ("synching") will be considered a Derivative Work for the purpose - of this License. - 3. *"Licensor"* means the individual or entity that offers the Work - under the terms of this License. - 4. *"Original Author"* means the individual or entity who created the - Work. - 5. *"Work"* means the copyrightable work of authorship offered under - the terms of this License. - 6. *"You"* means an individual or entity exercising rights under this - License who has not previously violated the terms of this License - with respect to the Work, or who has received express permission - from the Licensor to exercise rights under this License despite a - previous violation. - -*2. Fair Use Rights.* Nothing in this license is intended to reduce, -limit, or restrict any rights arising from fair use, first sale or other -limitations on the exclusive rights of the copyright owner under -copyright law or other applicable laws. - -*3. License Grant.* Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - 1. to reproduce the Work, to incorporate the Work into one or more - Collective Works, and to reproduce the Work as incorporated in the - Collective Works; - 2. to create and reproduce Derivative Works; - 3. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission the Work including as incorporated in Collective Works; - 4. to distribute copies or phonorecords of, display publicly, perform - publicly, and perform publicly by means of a digital audio - transmission Derivative Works. - 5. - - For the avoidance of doubt, where the work is a musical composition: - - 1. *Performance Royalties Under Blanket Licenses*. Licensor - waives the exclusive right to collect, whether individually - or via a performance rights society (e.g. ASCAP, BMI, - SESAC), royalties for the public performance or public - digital performance (e.g. webcast) of the Work. - 2. *Mechanical Rights and Statutory Royalties*. Licensor waives - the exclusive right to collect, whether individually or via - a music rights agency or designated agent (e.g. Harry Fox - Agency), royalties for any phonorecord You create from the - Work ("cover version") and distribute, subject to the - compulsory license created by 17 USC Section 115 of the US - Copyright Act (or the equivalent in other jurisdictions). - 6. *Webcasting Rights and Statutory Royalties*. For the avoidance of - doubt, where the Work is a sound recording, Licensor waives the - exclusive right to collect, whether individually or via a - performance-rights society (e.g. SoundExchange), royalties for the - public digital performance (e.g. webcast) of the Work, subject to - the compulsory license created by 17 USC Section 114 of the US - Copyright Act (or the equivalent in other jurisdictions). - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights -in other media and formats. All rights not expressly granted by Licensor -are hereby reserved. - -*4. Restrictions.*The license granted in Section 3 above is expressly -made subject to and limited by the following restrictions: - - 1. You may distribute, publicly display, publicly perform, or - publicly digitally perform the Work only under the terms of this - License, and You must include a copy of, or the Uniform Resource - Identifier for, this License with every copy or phonorecord of the - Work You distribute, publicly display, publicly perform, or - publicly digitally perform. You may not offer or impose any terms - on the Work that alter or restrict the terms of this License or - the recipients' exercise of the rights granted hereunder. You may - not sublicense the Work. You must keep intact all notices that - refer to this License and to the disclaimer of warranties. You may - not distribute, publicly display, publicly perform, or publicly - digitally perform the Work with any technological measures that - control access or use of the Work in a manner inconsistent with - the terms of this License Agreement. The above applies to the Work - as incorporated in a Collective Work, but this does not require - the Collective Work apart from the Work itself to be made subject - to the terms of this License. If You create a Collective Work, - upon notice from any Licensor You must, to the extent practicable, - remove from the Collective Work any credit as required by clause - 4(b), as requested. If You create a Derivative Work, upon notice - from any Licensor You must, to the extent practicable, remove from - the Derivative Work any credit as required by clause 4(b), as - requested. - 2. If you distribute, publicly display, publicly perform, or publicly - digitally perform the Work or any Derivative Works or Collective - Works, You must keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) - the name of the Original Author (or pseudonym, if applicable) if - supplied, and/or (ii) if the Original Author and/or Licensor - designate another party or parties (e.g. a sponsor institute, - publishing entity, journal) for attribution in Licensor's - copyright notice, terms of service or by other reasonable means, - the name of such party or parties; the title of the Work if - supplied; to the extent reasonably practicable, the Uniform - Resource Identifier, if any, that Licensor specifies to be - associated with the Work, unless such URI does not refer to the - copyright notice or licensing information for the Work; and in the - case of a Derivative Work, a credit identifying the use of the - Work in the Derivative Work (e.g., "French translation of the Work - by Original Author," or "Screenplay based on original Work by - Original Author"). Such credit may be implemented in any - reasonable manner; provided, however, that in the case of a - Derivative Work or Collective Work, at a minimum such credit will - appear where any other comparable authorship credit appears and in - a manner at least as prominent as such other comparable authorship - credit. - -*5. Representations, Warranties and Disclaimer* - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE -EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -*6. Limitation on Liability.* EXCEPT TO THE EXTENT REQUIRED BY -APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL -THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY -DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF -LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. Termination* - - 1. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Derivative Works or - Collective Works from You under this License, however, will not - have their licenses terminated provided such individuals or - entities remain in full compliance with those licenses. Sections - 1, 2, 5, 6, 7, and 8 will survive any termination of this License. - 2. Subject to the above terms and conditions, the license granted - here is perpetual (for the duration of the applicable copyright in - the Work). Notwithstanding the above, Licensor reserves the right - to release the Work under different license terms or to stop - distributing the Work at any time; provided, however that any such - election will not serve to withdraw this License (or any other - license that has been, or is required to be, granted under the - terms of this License), and this License will continue in full - force and effect unless terminated as stated above. - -*8. Miscellaneous* - - 1. Each time You distribute or publicly digitally perform the Work or - a Collective Work, the Licensor offers to the recipient a license - to the Work on the same terms and conditions as the license - granted to You under this License. - 2. Each time You distribute or publicly digitally perform a - Derivative Work, Licensor offers to the recipient a license to the - original Work on the same terms and conditions as the license - granted to You under this License. - 3. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability - of the remainder of the terms of this License, and without further - action by the parties to this agreement, such provision shall be - reformed to the minimum extent necessary to make such provision - valid and enforceable. - 4. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in - writing and signed by the party to be charged with such waiver or - consent. - 5. This License constitutes the entire agreement between the parties - with respect to the Work licensed here. There are no - understandings, agreements or representations with respect to the - Work not specified here. Licensor shall not be bound by any - additional provisions that may appear in any communication from - You. This License may not be modified without the mutual written - agreement of the Licensor and You. - -Creative Commons is not a party to this License, and makes no warranty -whatsoever in connection with the Work. Creative Commons will not be -liable to You or any party on any legal theory for any damages -whatsoever, including without limitation any general, special, -incidental or consequential damages arising in connection to this -license. Notwithstanding the foregoing two (2) sentences, if Creative -Commons has expressly identified itself as the Licensor hereunder, it -shall have all rights and obligations of Licensor. - -Except for the limited purpose of indicating to the public that the Work -is licensed under the CCPL, neither party will use the trademark -"Creative Commons" or any related trademark or logo of Creative Commons -without the prior written consent of Creative Commons. Any permitted use -will be in compliance with Creative Commons' then-current trademark -usage guidelines, as may be published on its website or otherwise made -available upon request from time to time. - -Creative Commons may be contacted at http://creativecommons.org/ -. - -« Back to Commons Deed <./> diff --git a/repoze/bfg/paster_templates/zodb/+package+/tests.py_tmpl b/repoze/bfg/paster_templates/zodb/+package+/tests.py_tmpl deleted file mode 100644 index 30da5c9b3..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/tests.py_tmpl +++ /dev/null @@ -1,19 +0,0 @@ -import unittest - -from repoze.bfg.configuration import Configurator -from repoze.bfg import testing - -class ViewTests(unittest.TestCase): - def setUp(self): - self.config = Configurator() - self.config.begin() - - def tearDown(self): - self.config.end() - - def test_my_view(self): - from {{package}}.views import my_view - request = testing.DummyRequest() - info = my_view(request) - self.assertEqual(info['project'], '{{project}}') - diff --git a/repoze/bfg/paster_templates/zodb/+package+/views.py_tmpl b/repoze/bfg/paster_templates/zodb/+package+/views.py_tmpl deleted file mode 100644 index 12ed8832d..000000000 --- a/repoze/bfg/paster_templates/zodb/+package+/views.py_tmpl +++ /dev/null @@ -1,2 +0,0 @@ -def my_view(request): - return {'project':'{{project}}'} diff --git a/repoze/bfg/paster_templates/zodb/+project+.ini_tmpl b/repoze/bfg/paster_templates/zodb/+project+.ini_tmpl deleted file mode 100644 index 993cec596..000000000 --- a/repoze/bfg/paster_templates/zodb/+project+.ini_tmpl +++ /dev/null @@ -1,22 +0,0 @@ -[DEFAULT] -debug = true - -[app:zodb] -use = egg:{{project}}#app -reload_templates = true -debug_authorization = false -debug_notfound = false -debug_templates = true -default_locale_name = en -zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000 - -[pipeline:main] -pipeline = - egg:repoze.zodbconn#closer - egg:repoze.tm#tm - zodb - -[server:main] -use = egg:Paste#http -host = 0.0.0.0 -port = 6543 diff --git a/repoze/bfg/paster_templates/zodb/CHANGES.txt_tmpl b/repoze/bfg/paster_templates/zodb/CHANGES.txt_tmpl deleted file mode 100644 index 35a34f332..000000000 --- a/repoze/bfg/paster_templates/zodb/CHANGES.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version diff --git a/repoze/bfg/paster_templates/zodb/README.txt_tmpl b/repoze/bfg/paster_templates/zodb/README.txt_tmpl deleted file mode 100644 index 0ddebfc3e..000000000 --- a/repoze/bfg/paster_templates/zodb/README.txt_tmpl +++ /dev/null @@ -1,4 +0,0 @@ -{{project}} README - - - diff --git a/repoze/bfg/paster_templates/zodb/setup.cfg_tmpl b/repoze/bfg/paster_templates/zodb/setup.cfg_tmpl deleted file mode 100644 index 5bec29823..000000000 --- a/repoze/bfg/paster_templates/zodb/setup.cfg_tmpl +++ /dev/null @@ -1,27 +0,0 @@ -[nosetests] -match=^test -nocapture=1 -cover-package={{package}} -with-coverage=1 -cover-erase=1 - -[compile_catalog] -directory = {{package}}/locale -domain = {{project}} -statistics = true - -[extract_messages] -add_comments = TRANSLATORS: -output_file = {{package}}/locale/{{project}}.pot -width = 80 - -[init_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale - -[update_catalog] -domain = {{project}} -input_file = {{package}}/locale/{{project}}.pot -output_dir = {{package}}/locale -previous = true diff --git a/repoze/bfg/paster_templates/zodb/setup.py_tmpl b/repoze/bfg/paster_templates/zodb/setup.py_tmpl deleted file mode 100644 index dba0e4ac7..000000000 --- a/repoze/bfg/paster_templates/zodb/setup.py_tmpl +++ /dev/null @@ -1,41 +0,0 @@ -import os - -from setuptools import setup, find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.txt')).read() -CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() - -requires = [ - 'repoze.bfg', - 'repoze.zodbconn', - 'repoze.tm', - 'ZODB3', - ] - -setup(name='{{project}}', - version='0.0', - description='{{project}}', - long_description=README + '\n\n' + CHANGES, - classifiers=[ - "Programming Language :: Python", - "Framework :: BFG", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], - author='', - author_email='', - url='', - keywords='web wsgi bfg', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires = requires, - tests_require= requires, - test_suite="{{package}}", - entry_points = """\ - [paste.app_factory] - app = {{package}}.run:app - """ - ) - diff --git a/repoze/bfg/path.py b/repoze/bfg/path.py deleted file mode 100644 index b5850968f..000000000 --- a/repoze/bfg/path.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import pkg_resources -import sys -import imp - -ignore_types = [ imp.C_EXTENSION, imp.C_BUILTIN ] -init_names = [ '__init__%s' % x[0] for x in imp.get_suffixes() if - x[0] and x[2] not in ignore_types ] - -def caller_path(path, level=2): - if not os.path.isabs(path): - module = caller_module(level+1) - prefix = package_path(module) - path = os.path.join(prefix, path) - return path - -def caller_module(level=2): - module_globals = sys._getframe(level).f_globals - module_name = module_globals['__name__'] - module = sys.modules[module_name] - return module - -def package_name(pkg_or_module): - """ If this function is passed a module, return the dotted Python - package name of the package in which the module lives. If this - function is passed a package, return the dotted Python package - name of the package itself.""" - if pkg_or_module is None: - return '__main__' - pkg_filename = pkg_or_module.__file__ - pkg_name = pkg_or_module.__name__ - splitted = os.path.split(pkg_filename) - if splitted[-1] in init_names: - # it's a package - return pkg_name - return pkg_name.rsplit('.', 1)[0] - -def package_of(pkg_or_module): - """ Return the package of a module or return the package itself """ - pkg_name = package_name(pkg_or_module) - __import__(pkg_name) - return sys.modules[pkg_name] - -def caller_package(level=2, caller_module=caller_module): - # caller_module in arglist for tests - module = caller_module(level+1) - if '__init__.py' in getattr(module, '__file__', ''): # empty at >>> - # Module is a package - return module - # Go up one level to get package - package_name = module.__name__.rsplit('.', 1)[0] - return sys.modules[package_name] - -def package_path(package): - # computing the abspath is actually kinda expensive so we memoize - # the result - prefix = getattr(package, '__bfg_abspath__', None) - if prefix is None: - prefix = pkg_resources.resource_filename(package.__name__, '') - # pkg_resources doesn't care whether we feed it a package - # name or a module name within the package, the result - # will be the same: a directory name to the package itself - try: - package.__bfg_abspath__ = prefix - except: - # this is only an optimization, ignore any error - pass - return prefix - diff --git a/repoze/bfg/registry.py b/repoze/bfg/registry.py deleted file mode 100644 index e935ac165..000000000 --- a/repoze/bfg/registry.py +++ /dev/null @@ -1,24 +0,0 @@ -from zope.component.registry import Components - -class Registry(Components, dict): - - # for optimization purposes, if no listeners are listening, don't try - # to notify them - has_listeners = False - - def registerSubscriptionAdapter(self, *arg, **kw): - result = Components.registerSubscriptionAdapter(self, *arg, **kw) - self.has_listeners = True - return result - - def registerHandler(self, *arg, **kw): - result = Components.registerHandler(self, *arg, **kw) - self.has_listeners = True - return result - - def notify(self, *events): - if self.has_listeners: - # iterating over subscribers assures they get executed - [ _ for _ in self.subscribers(events, None) ] - -global_registry = Registry('global') diff --git a/repoze/bfg/renderers.py b/repoze/bfg/renderers.py deleted file mode 100644 index 00413e3fe..000000000 --- a/repoze/bfg/renderers.py +++ /dev/null @@ -1,320 +0,0 @@ -import os -import pkg_resources -import threading - -from webob import Response - -from zope.deprecation import deprecated - -from repoze.bfg.interfaces import IRendererGlobalsFactory -from repoze.bfg.interfaces import IRendererFactory -from repoze.bfg.interfaces import IResponseFactory -from repoze.bfg.interfaces import ITemplateRenderer - -from repoze.bfg.compat import json -from repoze.bfg.path import caller_package -from repoze.bfg.settings import get_settings -from repoze.bfg.threadlocal import get_current_registry -from repoze.bfg.resource import resolve_resource_spec -from repoze.bfg.decorator import reify - -# API - -def render(renderer_name, value, request=None, package=None): - """ Using the renderer specified as ``renderer_name`` (a template - or a static renderer) render the value (or set of values) present - in ``value``. Return the result of the renderer's ``__call__`` - method (usually a string or Unicode). - - If the renderer name refers to a file on disk (such as when the - renderer is a template), it's usually best to supply the name as a - :term:`resource specification` - (e.g. ``packagename:path/to/template.pt``). - - You may supply a relative resource spec as ``renderer_name``. If - the ``package`` argument is supplied, a relative renderer path - will be converted to an absolute resource specification by - combining the package supplied as ``package`` with the relative - resource specification supplied as ``renderer_name``. If you do - not supply a ``package`` (or ``package`` is ``None``) the package - name of the *caller* of this function will be used as the package. - - The ``value`` provided will be supplied as the input to the - renderer. Usually, for template renderings, this should be a - dictionary. For other renderers, this will need to be whatever - sort of value the renderer expects. - - The 'system' values supplied to the renderer will include a basic - set of top-level system names, such as ``request``, ``context``, - and ``renderer_name``. If :term:`renderer globals` have been - specified, these will also be used to agument the value. - - Supply a ``request`` parameter in order to provide the renderer - with the most correct 'system' values (``request`` and ``context`` - in particular). - - .. note:: This API is new in :mod:`repoze.bfg` 1.3. - - """ - try: - registry = request.registry - except AttributeError: - registry = None - if package is None: - package = caller_package() - renderer = RendererHelper(renderer_name, package=package, registry=registry) - return renderer.render(value, None, request=request) - -def render_to_response(renderer_name, value, request=None, package=None): - """ Using the renderer specified as ``renderer_name`` (a template - or a static renderer) render the value (or set of values) using - the result of the renderer's ``__call__`` method (usually a string - or Unicode) as the response body. - - If the renderer name refers to a file on disk (such as when the - renderer is a template), it's usually best to supply the name as a - :term:`resource specification`. - - You may supply a relative resource spec as ``renderer_name``. If - the ``package`` argument is supplied, a relative renderer name - will be converted to an absolute resource specification by - combining the package supplied as ``package`` with the relative - resource specification supplied as ``renderer_name``. If you do - not supply a ``package`` (or ``package`` is ``None``) the package - name of the *caller* of this function will be used as the package. - - The ``value`` provided will be supplied as the input to the - renderer. Usually, for template renderings, this should be a - dictionary. For other renderers, this will need to be whatever - sort of value the renderer expects. - - The 'system' values supplied to the renderer will include a basic - set of top-level system names, such as ``request``, ``context``, - and ``renderer_name``. If :term:`renderer globals` have been - specified, these will also be used to agument the value. - - Supply a ``request`` parameter in order to provide the renderer - with the most correct 'system' values (``request`` and ``context`` - in particular). - - .. note:: This API is new in :mod:`repoze.bfg` 1.3. - - """ - try: - registry = request.registry - except AttributeError: - registry = None - if package is None: - package = caller_package() - renderer = RendererHelper(renderer_name, package=package, registry=registry) - return renderer.render_to_response(value, None, request=request) - -def get_renderer(renderer_name, package=None): - """ Return the renderer object for the renderer named as - ``renderer_name``. - - You may supply a relative resource spec as ``renderer_name``. If - the ``package`` argument is supplied, a relative renderer name - will be converted to an absolute resource specification by - combining the package supplied as ``package`` with the relative - resource specification supplied as ``renderer_name``. If you do - not supply a ``package`` (or ``package`` is ``None``) the package - name of the *caller* of this function will be used as the package. - """ - if package is None: - package = caller_package() - renderer = RendererHelper(renderer_name, package=package) - return renderer.get_renderer() - - -# concrete renderer factory implementations (also API) - -def json_renderer_factory(name): - def _render(value, system): - request = system.get('request') - if request is not None: - if not hasattr(request, 'response_content_type'): - request.response_content_type = 'application/json' - return json.dumps(value) - return _render - -def string_renderer_factory(name): - def _render(value, system): - if not isinstance(value, basestring): - value = str(value) - request = system.get('request') - if request is not None: - if not hasattr(request, 'response_content_type'): - request.response_content_type = 'text/plain' - return value - return _render - -# utility functions, not API - -# Lock to prevent simultaneous registry writes; used in -# template_renderer_factory. template_renderer_factory may be called -# at runtime, from more than a single thread. -registry_lock = threading.Lock() - -def template_renderer_factory(spec, impl, lock=registry_lock): - reg = get_current_registry() - if os.path.isabs(spec): - # 'spec' is an absolute filename - if not os.path.exists(spec): - raise ValueError('Missing template file: %s' % spec) - renderer = reg.queryUtility(ITemplateRenderer, name=spec) - if renderer is None: - renderer = impl(spec) - # cache the template - try: - lock.acquire() - reg.registerUtility(renderer, ITemplateRenderer, name=spec) - finally: - lock.release() - else: - # spec is a package:relpath resource spec - renderer = reg.queryUtility(ITemplateRenderer, name=spec) - if renderer is None: - try: - package_name, filename = spec.split(':', 1) - except ValueError: # pragma: no cover - # somehow we were passed a relative pathname; this - # should die - package_name = caller_package(4).__name__ - filename = spec - abspath = pkg_resources.resource_filename(package_name, filename) - if not pkg_resources.resource_exists(package_name, filename): - raise ValueError( - 'Missing template resource: %s (%s)' % (spec, abspath)) - renderer = impl(abspath) - if not _reload_resources(): - # cache the template - try: - lock.acquire() - reg.registerUtility(renderer, ITemplateRenderer, name=spec) - finally: - lock.release() - - return renderer - -def _reload_resources(): - settings = get_settings() - return settings and settings.get('reload_resources') - -def renderer_from_name(path, package=None): - return RendererHelper(path, package=package).get_renderer() - -def rendered_response(renderer, result, view, context, request, renderer_name): - # XXX: deprecated, left here only to not break old code; use - # render_to_response instead - if ( hasattr(result, 'app_iter') and hasattr(result, 'headerlist') - and hasattr(result, 'status') ): - return result - - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} - - helper = RendererHelper(renderer_name) - helper.renderer = renderer - return helper.render_to_response(result, system, request=request) - -deprecated('rendered_response', - "('repoze.bfg.renderers.rendered_response' is not a public API; it is " - "officially deprecated as of repoze.bfg 1.3; " - "use repoze.bfg.renderers.render_to_response instead')", - ) - -class RendererHelper(object): - def __init__(self, renderer_name, registry=None, package=None): - if registry is None: - registry = get_current_registry() - self.registry = registry - self.package = package - if renderer_name is None: - factory = registry.queryUtility(IRendererFactory) - renderer_type = None - else: - if '.' in renderer_name: - renderer_type = os.path.splitext(renderer_name)[1] - renderer_name = self.resolve_spec(renderer_name) - else: - renderer_type = renderer_name - renderer_name = renderer_name - factory = registry.queryUtility(IRendererFactory, - name=renderer_type) - self.renderer_name = renderer_name - self.renderer_type = renderer_type - self.factory = factory - - @reify - def renderer(self): - if self.factory is None: - raise ValueError('No such renderer factory %s' % str(self.renderer_type)) - return self.factory(self.renderer_name) - - def resolve_spec(self, path_or_spec): - if path_or_spec is None: - return path_or_spec - - package, filename = resolve_resource_spec(path_or_spec, - self.package) - if package is None: - return filename # absolute filename - return '%s:%s' % (package, filename) - - def get_renderer(self): - return self.renderer - - def render(self, value, system_values, request=None): - renderer = self.renderer - if system_values is None: - system_values = { - 'view':None, - 'renderer_name':self.renderer_name, - 'context':getattr(request, 'context', None), - 'request':request, - } - - registry = self.registry - globals_factory = registry.queryUtility(IRendererGlobalsFactory) - - if globals_factory is not None: - renderer_globals = globals_factory(system_values) - if renderer_globals: - system_values.update(renderer_globals) - - result = renderer(value, system_values) - return result - - def render_to_response(self, value, system_values, request=None): - result = self.render(value, system_values, request=request) - return self._make_response(result, request) - - def _make_response(self, result, request): - registry = self.registry - response_factory = registry.queryUtility(IResponseFactory, - default=Response) - - response = response_factory(result) - - if request is not None: - attrs = request.__dict__ - content_type = attrs.get('response_content_type', None) - if content_type is not None: - response.content_type = content_type - headerlist = attrs.get('response_headerlist', None) - if headerlist is not None: - for k, v in headerlist: - response.headers.add(k, v) - status = attrs.get('response_status', None) - if status is not None: - response.status = status - charset = attrs.get('response_charset', None) - if charset is not None: - response.charset = charset - cache_for = attrs.get('response_cache_for', None) - if cache_for is not None: - response.cache_expires = cache_for - - return response - diff --git a/repoze/bfg/request.py b/repoze/bfg/request.py deleted file mode 100644 index f32a7a40c..000000000 --- a/repoze/bfg/request.py +++ /dev/null @@ -1,229 +0,0 @@ -from zope.deprecation import deprecated -from zope.interface import implements -from zope.interface.interface import InterfaceClass - -from webob import Request as WebobRequest - -from repoze.bfg.interfaces import IRequest - -class Request(WebobRequest): - """ - A subclass of the :term:`WebOb` Request class. An instance of - this class is created by the :term:`router` and is provided to a - view callable (and to other subsystems) as the ``request`` - argument. - - The documentation below (save for the ``add_response_callback`` - and ''add_finished_callback`` methods, which are defined in this - subclass itself, and the attributes ``context``, ``registry``, - ``root``, ``subpath``, ``traversed``, ``view_name``, - ``virtual_root`` , and ``virtual_root_path``, each of which is - added to the request at by the :term:`router` at request ingress - time) are autogenerated from the WebOb source code used when this - documentation was generated. - - Due to technical constraints, we can't yet display the WebOb - version number from which this documentation is autogenerated, but - it will be the 'prevailing WebOb version' at the time of the - release of this :mod:`repoze.bfg` version. See - http://http://pythonpaste.org/webob/ for further information. - """ - implements(IRequest) - response_callbacks = () - finished_callbacks = () - exception = None - - def add_response_callback(self, callback): - """ - Add a callback to the set of callbacks to be called by the - :term:`router` at a point after a :term:`response` object is - successfully created. :mod:`repoze.bfg` does not have a - global response object: this functionality allows an - application to register an action to be performed against the - response once one is created. - - A 'callback' is a callable which accepts two positional - parameters: ``request`` and ``response``. For example: - - .. code-block:: python - :linenos: - - def cache_callback(request, response): - 'Set the cache_control max_age for the response' - response.cache_control.max_age = 360 - request.add_response_callback(cache_callback) - - Response callbacks are called in the order they're added - (first-to-most-recently-added). No response callback is - called if an exception happens in application code, or if the - response object returned by :term:`view` code is invalid. - - All response callbacks are called *after* the - :class:`repoze.bfg.interfaces.INewResponse` event is sent. - - Errors raised by callbacks are not handled specially. They - will be propagated to the caller of the :mod:`repoze.bfg` - router application. - - .. note: ``add_response_callback`` is new in :mod:`repoze.bfg` - 1.3. - - See also: :ref:`using_response_callbacks`. - """ - - callbacks = self.response_callbacks - if not callbacks: - callbacks = [] - callbacks.append(callback) - self.response_callbacks = callbacks - - def _process_response_callbacks(self, response): - callbacks = self.response_callbacks - while callbacks: - callback = callbacks.pop(0) - callback(self, response) - - def add_finished_callback(self, callback): - """ - Add a callback to the set of callbacks to be called - unconditionally by the :term:`router` at the very end of - request processing. - - ``callback`` is a callable which accepts a single positional - parameter: ``request``. For example: - - .. code-block:: python - :linenos: - - import transaction - - def commit_callback(request): - '''commit or abort the transaction associated with request''' - if request.exception is not None: - transaction.abort() - else: - transaction.commit() - request.add_finished_callback(commit_callback) - - Finished callbacks are called in the order they're added ( - first- to most-recently- added). Finished callbacks (unlike - response callbacks) are *always* called, even if an exception - happens in application code that prevents a response from - being generated. - - The set of finished callbacks associated with a request are - called *very late* in the processing of that request; they are - essentially the last thing called by the :term:`router`. They - are called after response processing has already occurred in a - top-level ``finally:`` block within the router request - processing code. As a result, mutations performed to the - ``request`` provided to a finished callback will have no - meaningful effect, because response processing will have - already occurred, and the request's scope will expire almost - immediately after all finished callbacks have been processed. - - Errors raised by finished callbacks are not handled specially. - They will be propagated to the caller of the :mod:`repoze.bfg` - router application. - - .. note: ``add_finished_callback`` is new in :mod:`repoze.bfg` - 1.3. - - See also: :ref:`using_finished_callbacks`. - """ - - callbacks = self.finished_callbacks - if not callbacks: - callbacks = [] - callbacks.append(callback) - self.finished_callbacks = callbacks - - def _process_finished_callbacks(self): - callbacks = self.finished_callbacks - while callbacks: - callback = callbacks.pop(0) - callback(self) - - # override default WebOb "environ['adhoc_attr']" mutation behavior - __getattr__ = object.__getattribute__ - __setattr__ = object.__setattr__ - __delattr__ = object.__delattr__ - - # b/c dict interface for "root factory" code that expects a bare - # environ. Explicitly omitted dict methods: clear (unnecessary), - # copy (implemented by WebOb), fromkeys (unnecessary) - - def __contains__(self, k): - return self.environ.__contains__(k) - - def __delitem__(self, k): - return self.environ.__delitem__(k) - - def __getitem__(self, k): - return self.environ.__getitem__(k) - - def __iter__(self): - return iter(self.environ) - - def __setitem__(self, k, v): - self.environ[k] = v - - def get(self, k, default=None): - return self.environ.get(k, default) - - def has_key(self, k): - return self.environ.has_key(k) - - def items(self): - return self.environ.items() - - def iteritems(self): - return self.environ.iteritems() - - def iterkeys(self): - return self.environ.iterkeys() - - def itervalues(self): - return self.environ.itervalues() - - def keys(self): - return self.environ.keys() - - def pop(self, k): - return self.environ.pop(k) - - def popitem(self): - return self.environ.popitem() - - def setdefault(self, v, default): - return self.environ.setdefault(v, default) - - def update(self, v, **kw): - return self.environ.update(v, **kw) - - def values(self): - return self.environ.values() - -def route_request_iface(name, bases=()): - iface = InterfaceClass('%s_IRequest' % name, bases=bases) - # for exception view lookups - iface.combined = InterfaceClass('%s_combined_IRequest' % name, - bases=(iface, IRequest)) - return iface - -def add_global_response_headers(request, headerlist): - def add_headers(request, response): - for k, v in headerlist: - response.headerlist.append((k, v)) - request.add_response_callback(add_headers) - -from repoze.bfg.threadlocal import get_current_request as get_request # b/c - -get_request # prevent PyFlakes complaints - -deprecated('get_request', - 'As of repoze.bfg 1.0, any import of get_request from' - '``repoze.bfg.request`` is ' - 'deprecated. Use ``from repoze.bfg.threadlocal import ' - 'get_current_request instead.') - diff --git a/repoze/bfg/resource.py b/repoze/bfg/resource.py deleted file mode 100644 index da49f16ec..000000000 --- a/repoze/bfg/resource.py +++ /dev/null @@ -1,201 +0,0 @@ -import os -import pkg_resources - -from zope.interface import implements - -from repoze.bfg.interfaces import IPackageOverrides - -from repoze.bfg.path import package_path -from repoze.bfg.path import package_name -from repoze.bfg.threadlocal import get_current_registry - -class OverrideProvider(pkg_resources.DefaultProvider): - def __init__(self, module): - pkg_resources.DefaultProvider.__init__(self, module) - self.module_name = module.__name__ - - def _get_overrides(self): - reg = get_current_registry() - overrides = reg.queryUtility(IPackageOverrides, self.module_name) - return overrides - - def get_resource_filename(self, manager, resource_name): - """ Return a true filesystem path for resource_name, - co-ordinating the extraction with manager, if the resource - must be unpacked to the filesystem. - """ - overrides = self._get_overrides() - if overrides is not None: - filename = overrides.get_filename(resource_name) - if filename is not None: - return filename - return pkg_resources.DefaultProvider.get_resource_filename( - self, manager, resource_name) - - def get_resource_stream(self, manager, resource_name): - """ Return a readable file-like object for resource_name.""" - overrides = self._get_overrides() - if overrides is not None: - stream = overrides.get_stream(resource_name) - if stream is not None: - return stream - return pkg_resources.DefaultProvider.get_resource_stream( - self, manager, resource_name) - - def get_resource_string(self, manager, resource_name): - """ Return a string containing the contents of resource_name.""" - overrides = self._get_overrides() - if overrides is not None: - string = overrides.get_string(resource_name) - if string is not None: - return string - return pkg_resources.DefaultProvider.get_resource_string( - self, manager, resource_name) - - def has_resource(self, resource_name): - overrides = self._get_overrides() - if overrides is not None: - result = overrides.has_resource(resource_name) - if result is not None: - return result - return pkg_resources.DefaultProvider.has_resource( - self, resource_name) - - def resource_isdir(self, resource_name): - overrides = self._get_overrides() - if overrides is not None: - result = overrides.isdir(resource_name) - if result is not None: - return result - return pkg_resources.DefaultProvider.resource_isdir( - self, resource_name) - - def resource_listdir(self, resource_name): - overrides = self._get_overrides() - if overrides is not None: - result = overrides.listdir(resource_name) - if result is not None: - return result - return pkg_resources.DefaultProvider.resource_listdir( - self, resource_name) - -class PackageOverrides: - implements(IPackageOverrides) - # pkg_resources arg in kw args below for testing - def __init__(self, package, pkg_resources=pkg_resources): - if hasattr(package, '__loader__') and not isinstance(package.__loader__, - self.__class__): - raise TypeError('Package %s already has a non-%s __loader__ ' - '(probably a module in a zipped egg)' % - (package, self.__class__)) - # We register ourselves as a __loader__ *only* to support the - # setuptools _find_adapter adapter lookup; this class doesn't - # actually support the PEP 302 loader "API". This is - # excusable due to the following statement in the spec: - # ... Loader objects are not - # required to offer any useful functionality (any such functionality, - # such as the zipimport get_data() method mentioned above, is - # optional)... - # A __loader__ attribute is basically metadata, and setuptools - # uses it as such. - package.__loader__ = self - # we call register_loader_type for every instantiation of this - # class; that's OK, it's idempotent to do it more than once. - pkg_resources.register_loader_type(self.__class__, OverrideProvider) - self.overrides = [] - self.overridden_package_name = package.__name__ - - def insert(self, path, package, prefix): - if not path or path.endswith('/'): - override = DirectoryOverride(path, package, prefix) - else: - override = FileOverride(path, package, prefix) - self.overrides.insert(0, override) - return override - - def search_path(self, resource_name): - for override in self.overrides: - o = override(resource_name) - if o is not None: - package, name = o - yield package, name - - def get_filename(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_filename(package, rname) - - def get_stream(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_stream(package, rname) - - def get_string(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_string(package, rname) - - def has_resource(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return True - - def isdir(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_isdir(package, rname) - - def listdir(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_listdir(package, rname) - - -class DirectoryOverride: - def __init__(self, path, package, prefix): - self.path = path - self.package = package - self.prefix = prefix - self.pathlen = len(self.path) - - def __call__(self, resource_name): - if resource_name.startswith(self.path): - name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) - return self.package, name - -class FileOverride: - def __init__(self, path, package, prefix): - self.path = path - self.package = package - self.prefix = prefix - - def __call__(self, resource_name): - if resource_name == self.path: - return self.package, self.prefix - -def resolve_resource_spec(spec, pname='__main__'): - if pname and not isinstance(pname, basestring): - pname = pname.__name__ # as package - if os.path.isabs(spec): - return None, spec - filename = spec - if ':' in spec: - pname, filename = spec.split(':', 1) - elif pname is None: - pname, filename = None, spec - return pname, filename - -def resource_spec_from_abspath(abspath, package): - """ Try to convert an absolute path to a resource in a package to - a resource specification if possible; otherwise return the - absolute path. """ - if getattr(package, '__name__', None) == '__main__': - return abspath - pp = package_path(package) + os.path.sep - if abspath.startswith(pp): - relpath = abspath[len(pp):] - return '%s:%s' % (package_name(package), - relpath.replace(os.path.sep, '/')) - return abspath - - diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py deleted file mode 100644 index 7b3bcb75e..000000000 --- a/repoze/bfg/router.py +++ /dev/null @@ -1,189 +0,0 @@ -from zope.interface import implements -from zope.interface import providedBy - -from repoze.bfg.interfaces import IDebugLogger -from repoze.bfg.interfaces import IExceptionViewClassifier -from repoze.bfg.interfaces import IRequest -from repoze.bfg.interfaces import IRootFactory -from repoze.bfg.interfaces import IRouteRequest -from repoze.bfg.interfaces import IRouter -from repoze.bfg.interfaces import IRequestFactory -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import ISettings -from repoze.bfg.interfaces import ITraverser -from repoze.bfg.interfaces import IView -from repoze.bfg.interfaces import IViewClassifier - -from repoze.bfg.configuration import make_app # b/c import -from repoze.bfg.events import ContextFound -from repoze.bfg.events import NewRequest -from repoze.bfg.events import NewResponse -from repoze.bfg.exceptions import NotFound -from repoze.bfg.request import Request -from repoze.bfg.threadlocal import manager -from repoze.bfg.traversal import DefaultRootFactory -from repoze.bfg.traversal import ModelGraphTraverser - -make_app # prevent pyflakes from complaining - -class Router(object): - implements(IRouter) - - debug_notfound = False - threadlocal_manager = manager - - def __init__(self, registry): - q = registry.queryUtility - self.logger = q(IDebugLogger) - self.root_factory = q(IRootFactory, default=DefaultRootFactory) - self.routes_mapper = q(IRoutesMapper) - self.request_factory = q(IRequestFactory, default=Request) - self.root_policy = self.root_factory # b/w compat - self.registry = registry - settings = q(ISettings) - if settings is not None: - self.debug_notfound = settings['debug_notfound'] - - def __call__(self, environ, start_response): - """ - Accept ``environ`` and ``start_response``; create a - :term:`request` and route the request to a :mod:`repoze.bfg` - view based on introspection of :term:`view configuration` - within the application registry; call ``start_response`` and - return an iterable. - """ - registry = self.registry - adapters = registry.adapters - has_listeners = registry.has_listeners - logger = self.logger - manager = self.threadlocal_manager - request = None - threadlocals = {'registry':registry, 'request':request} - manager.push(threadlocals) - - try: # matches finally: manager.pop() - - try: # matches finally: ... call request finished callbacks ... - - # create the request - request = self.request_factory(environ) - context = None - threadlocals['request'] = request - attrs = request.__dict__ - attrs['registry'] = registry - has_listeners and registry.notify(NewRequest(request)) - request_iface = IRequest - - try: - # find the root object - root_factory = self.root_factory - if self.routes_mapper is not None: - info = self.routes_mapper(request) - match, route = info['match'], info['route'] - if route is not None: - # TODO: kill off bfg.routes.* environ keys - # when traverser requires request arg, and - # cant cope with environ anymore (likely 1.4+) - environ['bfg.routes.route'] = route - environ['bfg.routes.matchdict'] = match - attrs['matchdict'] = match - attrs['matched_route'] = route - request_iface = registry.queryUtility( - IRouteRequest, - name=route.name, - default=IRequest) - root_factory = route.factory or self.root_factory - - root = root_factory(request) - attrs['root'] = root - - # find a context - traverser = adapters.queryAdapter(root, ITraverser) - if traverser is None: - traverser = ModelGraphTraverser(root) - tdict = traverser(request) - context, view_name, subpath, traversed, vroot, vroot_path =( - tdict['context'], tdict['view_name'], tdict['subpath'], - tdict['traversed'], tdict['virtual_root'], - tdict['virtual_root_path']) - attrs.update(tdict) - has_listeners and registry.notify(ContextFound(request)) - - # find a view callable - context_iface = providedBy(context) - view_callable = adapters.lookup( - (IViewClassifier, request_iface, context_iface), - IView, name=view_name, default=None) - - # invoke the view callable - if view_callable is None: - if self.debug_notfound: - msg = ( - 'debug_notfound of url %s; path_info: %r, ' - 'context: %r, view_name: %r, subpath: %r, ' - 'traversed: %r, root: %r, vroot: %r, ' - 'vroot_path: %r' % ( - request.url, request.path_info, context, - view_name, - subpath, traversed, root, vroot, vroot_path) - ) - logger and logger.debug(msg) - else: - msg = request.path_info - # XXX repoze.bfg.message should be deprecated - environ['repoze.bfg.message'] = msg - raise NotFound(msg) - else: - response = view_callable(context, request) - - # handle exceptions raised during root finding and view-exec - except Exception, why: - attrs['exception'] = why - - for_ = (IExceptionViewClassifier, - request_iface.combined, - providedBy(why)) - view_callable = adapters.lookup(for_, IView, default=None) - - if view_callable is None: - raise - - # XXX r.b.message should be deprecated - try: - msg = why[0] - except: - msg = '' - environ['repoze.bfg.message'] = msg - - response = view_callable(why, request) - - # process the response - - has_listeners and registry.notify(NewResponse(request,response)) - - if request.response_callbacks: - request._process_response_callbacks(response) - - try: - headers = response.headerlist - app_iter = response.app_iter - status = response.status - except AttributeError: - raise ValueError( - 'Non-response object returned from view named %s ' - '(and no renderer): %r' % (view_name, response)) - - finally: - if request is not None and request.finished_callbacks: - request._process_finished_callbacks() - - start_response(status, headers) - return app_iter - - finally: - manager.pop() - - - - - diff --git a/repoze/bfg/scripting.py b/repoze/bfg/scripting.py deleted file mode 100644 index ca0bea597..000000000 --- a/repoze/bfg/scripting.py +++ /dev/null @@ -1,25 +0,0 @@ -from repoze.bfg.request import Request -from repoze.bfg.interfaces import IRequestFactory - -def get_root(app, request=None): - """ Return a tuple composed of ``(root, closer)`` when provided a - :term:`router` instance as the ``app`` argument. The ``root`` - returned is the application root object. The ``closer`` returned - is a callable (accepting no arguments) that should be called when - your scripting application is finished using the root. If - ``request`` is not None, it is used as the request passed to the - :mod:`repoze.bfg` application root factory. A request is - constructed and passed to the root factory if ``request`` is None.""" - registry = app.registry - if request is None: - request_factory = registry.queryUtility( - IRequestFactory, default=Request) - request = request_factory.blank('/') - request.registry = registry - threadlocals = {'registry':registry, 'request':request} - app.threadlocal_manager.push(threadlocals) - def closer(request=request): # keep request alive via this function default - app.threadlocal_manager.pop() - root = app.root_factory(request) - return root, closer - diff --git a/repoze/bfg/security.py b/repoze/bfg/security.py deleted file mode 100644 index cd1bae9a5..000000000 --- a/repoze/bfg/security.py +++ /dev/null @@ -1,270 +0,0 @@ -from zope.interface import providedBy - -from zope.deprecation import deprecated - -from repoze.bfg.interfaces import IAuthenticationPolicy -from repoze.bfg.interfaces import IAuthorizationPolicy -from repoze.bfg.interfaces import ISecuredView -from repoze.bfg.interfaces import IViewClassifier - -from repoze.bfg.exceptions import Forbidden as Unauthorized # b/c import -from repoze.bfg.threadlocal import get_current_registry - -Unauthorized # prevent PyFlakes from complaining - -deprecated('Unauthorized', - "('from repoze.bfg.security import Unauthorized' was " - "deprecated as of repoze.bfg 1.1; instead use 'from " - "repoze.bfg.exceptions import Forbidden')", - ) - -Everyone = 'system.Everyone' -Authenticated = 'system.Authenticated' -Allow = 'Allow' -Deny = 'Deny' - -class AllPermissionsList(object): - """ Stand in 'permission list' to represent all permissions """ - def __iter__(self): - return () - def __contains__(self, other): - return True - def __eq__(self, other): - return isinstance(other, self.__class__) - -ALL_PERMISSIONS = AllPermissionsList() -DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS) - -def has_permission(permission, context, request): - """ Provided a permission (a string or unicode object), a context - (a :term:`model` instance) and a request object, return an - instance of :data:`repoze.bfg.security.Allowed` if the permission - is granted in this context to the user implied by the - request. Return an instance of :mod:`repoze.bfg.security.Denied` - if this permission is not granted in this context to this user. - This function delegates to the current authentication and - authorization policies. Return - :data:`repoze.bfg.security.Allowed` unconditionally if no - authentication policy has been configured in this application.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - authn_policy = reg.queryUtility(IAuthenticationPolicy) - if authn_policy is None: - return Allowed('No authentication policy in use.') - - authz_policy = reg.queryUtility(IAuthorizationPolicy) - if authz_policy is None: - raise ValueError('Authentication policy registered without ' - 'authorization policy') # should never happen - principals = authn_policy.effective_principals(request) - return authz_policy.permits(context, principals, permission) - -def authenticated_userid(request): - """ Return the userid of the currently authenticated user or - ``None`` if there is no :term:`authentication policy` in effect or - there is no currently authenticated user.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - policy = reg.queryUtility(IAuthenticationPolicy) - if policy is None: - return None - return policy.authenticated_userid(request) - -def effective_principals(request): - """ Return the list of 'effective' :term:`principal` identifiers - for the ``request``. This will include the userid of the - currently authenticated user if a user is currently - authenticated. If no :term:`authentication policy` is in effect, - this will return an empty sequence.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - policy = reg.queryUtility(IAuthenticationPolicy) - if policy is None: - return [] - return policy.effective_principals(request) - -def principals_allowed_by_permission(context, permission): - """ Provided a ``context`` (a model object), and a ``permission`` - (a string or unicode object), if a :term:`authorization policy` is - in effect, return a sequence of :term:`principal` ids that possess - the permission in the ``context``. If no authorization policy is - in effect, this will return a sequence with the single value - :mod:`repoze.bfg.security.Everyone` (the special principal - identifier representing all principals). - - .. note:: even if an :term:`authorization policy` is in effect, - some (exotic) authorization policies may not implement the - required machinery for this function; those will cause a - :exc:`NotImplementedError` exception to be raised when this - function is invoked. - """ - reg = get_current_registry() - policy = reg.queryUtility(IAuthorizationPolicy) - if policy is None: - return [Everyone] - return policy.principals_allowed_by_permission(context, permission) - -def view_execution_permitted(context, request, name=''): - """ If the view specified by ``context`` and ``name`` is protected - by a :term:`permission`, check the permission associated with the - view using the effective authentication/authorization policies and - the ``request``. Return a boolean result. If no - :term:`authorization policy` is in effect, or if the view is not - protected by a permission, return ``True``.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - provides = [IViewClassifier] + map(providedBy, (request, context)) - view = reg.adapters.lookup(provides, ISecuredView, name=name) - if view is None: - return Allowed( - 'Allowed: view name %r in context %r (no permission defined)' % - (name, context)) - return view.__permitted__(context, request) - -def remember(request, principal, **kw): - """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', - 'foo=abc')]``) suitable for 'remembering' a set of credentials - implied by the data passed as ``principal`` and ``*kw`` using the - current :term:`authentication policy`. Common usage might look - like so within the body of a view function (``response`` is - assumed to be an :term:`WebOb` -style :term:`response` object - computed previously by the view code):: - - from repoze.bfg.security import remember - headers = remember(request, 'chrism', password='123', max_age='86400') - response.headerlist.extend(headers) - return response - - If no :term:`authentication policy` is in use, this function will - always return an empty sequence. If used, the composition and - meaning of ``**kw`` must be agreed upon by the calling code and - the effective authentication policy.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - policy = reg.queryUtility(IAuthenticationPolicy) - if policy is None: - return [] - else: - return policy.remember(request, principal, **kw) - -def forget(request): - """ Return a sequence of header tuples (e.g. ``[('Set-Cookie', - 'foo=abc')]``) suitable for 'forgetting' the set of credentials - possessed by the currently authenticated user. A common usage - might look like so within the body of a view function - (``response`` is assumed to be an :term:`WebOb` -style - :term:`response` object computed previously by the view code):: - - from repoze.bfg.security import forget - headers = forget(request) - response.headerlist.extend(headers) - return response - - If no :term:`authentication policy` is in use, this function will - always return an empty sequence.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - policy = reg.queryUtility(IAuthenticationPolicy) - if policy is None: - return [] - else: - return policy.forget(request) - -class PermitsResult(int): - def __new__(cls, s, *args): - inst = int.__new__(cls, cls.boolval) - inst.s = s - inst.args = args - return inst - - @property - def msg(self): - return self.s % self.args - - def __str__(self): - return self.msg - - def __repr__(self): - return '<%s instance at %s with msg %r>' % (self.__class__.__name__, - id(self), - self.msg) - -class Denied(PermitsResult): - """ An instance of ``Denied`` is returned when a security-related - API or other :mod:`repoze.bfg` code denies an action unrelated to - an ACL check. It evaluates equal to all boolean false types. It - has an attribute named ``msg`` describing the circumstances for - the deny.""" - boolval = 0 - -class Allowed(PermitsResult): - """ An instance of ``Allowed`` is returned when a security-related - API or other :mod:`repoze.bfg` code allows an action unrelated to - an ACL check. It evaluates equal to all boolean true types. It - has an attribute named ``msg`` describing the circumstances for - the allow.""" - boolval = 1 - -class ACLPermitsResult(int): - def __new__(cls, ace, acl, permission, principals, context): - inst = int.__new__(cls, cls.boolval) - inst.permission = permission - inst.ace = ace - inst.acl = acl - inst.principals = principals - inst.context = context - return inst - - @property - def msg(self): - s = ('%s permission %r via ACE %r in ACL %r on context %r for ' - 'principals %r') - return s % (self.__class__.__name__, - self.permission, - self.ace, - self.acl, - self.context, - self.principals) - - def __str__(self): - return self.msg - - def __repr__(self): - return '<%s instance at %s with msg %r>' % (self.__class__.__name__, - id(self), - self.msg) - -class ACLDenied(ACLPermitsResult): - """ An instance of ``ACLDenied`` represents that a security check - made explicitly against ACL was denied. It evaluates equal to all - boolean false types. It also has attributes which indicate which - acl, ace, permission, principals, and context were involved in the - request. Its __str__ method prints a summary of these attributes - for debugging purposes. The same summary is available as the - ``msg`` attribute.""" - boolval = 0 - -class ACLAllowed(ACLPermitsResult): - """ An instance of ``ACLAllowed`` represents that a security check - made explicitly against ACL was allowed. It evaluates equal to - all boolean true types. It also has attributes which indicate - which acl, ace, permission, principals, and context were involved - in the request. Its __str__ method prints a summary of these - attributes for debugging purposes. The same summary is available - as the ``msg`` attribute.""" - boolval = 1 - diff --git a/repoze/bfg/settings.py b/repoze/bfg/settings.py deleted file mode 100644 index 5e1181809..000000000 --- a/repoze/bfg/settings.py +++ /dev/null @@ -1,85 +0,0 @@ -import os - -from zope.interface import implements - -from repoze.bfg.interfaces import ISettings - -from repoze.bfg.threadlocal import get_current_registry - -class Settings(dict): - """ Deployment settings. Update application settings (usually - from PasteDeploy keywords) with framework-specific key/value pairs - (e.g. find ``BFG_DEBUG_AUTHORIZATION`` in os.environ and jam into - keyword args).""" - implements(ISettings) - # _environ_ is dep inj for testing - def __init__(self, d=None, _environ_=os.environ, **kw): - if d is None: - d = {} - dict.__init__(self, d, **kw) - eget = _environ_.get - config_debug_all = self.get('debug_all', '') - eff_debug_all = asbool(eget('BFG_DEBUG_ALL', config_debug_all)) - config_reload_all = self.get('reload_all', '') - eff_reload_all = asbool(eget('BFG_RELOAD_ALL',config_reload_all)) - config_debug_auth = self.get('debug_authorization', '') - eff_debug_auth = asbool(eget('BFG_DEBUG_AUTHORIZATION', - config_debug_auth)) - config_debug_notfound = self.get('debug_notfound', '') - eff_debug_notfound = asbool(eget('BFG_DEBUG_NOTFOUND', - config_debug_notfound)) - config_debug_templates = self.get('debug_templates', '') - eff_debug_templates = asbool(eget('BFG_DEBUG_TEMPLATES', - config_debug_templates)) - config_reload_templates = self.get('reload_templates', '') - eff_reload_templates = asbool(eget('BFG_RELOAD_TEMPLATES', - config_reload_templates)) - config_reload_resources = self.get('reload_resources', '') - eff_reload_resources = asbool(eget('BFG_RELOAD_RESOURCES', - config_reload_resources)) - configure_zcml = self.get('configure_zcml', '') - eff_configure_zcml = eget('BFG_CONFIGURE_ZCML', configure_zcml) - locale_name = self.get('default_locale_name', 'en') - eff_locale_name = eget('BFG_DEFAULT_LOCALE_NAME', locale_name) - - update = { - 'debug_authorization': eff_debug_all or eff_debug_auth, - 'debug_notfound': eff_debug_all or eff_debug_notfound, - 'debug_templates': eff_debug_all or eff_debug_templates, - 'reload_templates': eff_reload_all or eff_reload_templates, - 'reload_resources':eff_reload_all or eff_reload_resources, - 'configure_zcml':eff_configure_zcml, - 'default_locale_name':eff_locale_name, - } - - self.update(update) - - def __getattr__(self, name): - # backwards compatibility - try: - return self[name] - except KeyError: - raise AttributeError(name) - -def get_settings(): - """ - Return a 'settings' object for the current application. A - 'settings' object is a dictionary-like object that contains - key/value pairs based on the dictionary passed as the ``settings`` - argument to the :class:`repoze.bfg.configuration.Configurator` - constructor or the :func:`repoze.bfg.router.make_app` API. - - .. note:: For backwards compatibility, dictionary keys can also be - looked up as attributes of the settings object. - - .. note:: the - :class:`repoze.bfg.configuration.Configurator.get_settings` method - performs the same duty. - """ - reg = get_current_registry() - return reg.queryUtility(ISettings) - -def asbool(s): - s = str(s).strip() - return s.lower() in ('t', 'true', 'y', 'yes', 'on', '1') - diff --git a/repoze/bfg/static.py b/repoze/bfg/static.py deleted file mode 100644 index 71db132b7..000000000 --- a/repoze/bfg/static.py +++ /dev/null @@ -1,213 +0,0 @@ -import os -import pkg_resources -from urlparse import urljoin -from urlparse import urlparse - -from paste import httpexceptions -from paste import request -from paste.httpheaders import ETAG -from paste.urlparser import StaticURLParser - -from zope.interface import implements - -from repoze.bfg.interfaces import IStaticURLInfo -from repoze.bfg.path import caller_package -from repoze.bfg.resource import resolve_resource_spec -from repoze.bfg.url import route_url - -class PackageURLParser(StaticURLParser): - """ This probably won't work with zipimported resources """ - def __init__(self, package_name, resource_name, root_resource=None, - cache_max_age=None): - self.package_name = package_name - self.resource_name = os.path.normpath(resource_name) - if root_resource is None: - root_resource = self.resource_name - self.root_resource = root_resource - self.cache_max_age = cache_max_age - - def __call__(self, environ, start_response): - path_info = environ.get('PATH_INFO', '') - if not path_info: - return self.add_slash(environ, start_response) - if path_info == '/': - # @@: This should obviously be configurable - filename = 'index.html' - else: - filename = request.path_info_pop(environ) - resource = os.path.normcase(os.path.normpath( - self.resource_name + '/' + filename)) - if ( (self.root_resource is not None) and - (not resource.startswith(self.root_resource)) ): - # Out of bounds - return self.not_found(environ, start_response) - if not pkg_resources.resource_exists(self.package_name, resource): - return self.not_found(environ, start_response) - if pkg_resources.resource_isdir(self.package_name, resource): - # @@: Cache? - child_root = (self.root_resource is not None and - self.root_resource or self.resource_name) - return self.__class__( - self.package_name, resource, root_resource=child_root, - cache_max_age=self.cache_max_age)(environ, start_response) - if (environ.get('PATH_INFO') - and environ.get('PATH_INFO') != '/'): # pragma: no cover - return self.error_extra_path(environ, start_response) - full = pkg_resources.resource_filename(self.package_name, resource) - if_none_match = environ.get('HTTP_IF_NONE_MATCH') - if if_none_match: - mytime = os.stat(full).st_mtime - if str(mytime) == if_none_match: - headers = [] - ETAG.update(headers, mytime) - start_response('304 Not Modified', headers) - return [''] # empty body - - fa = self.make_app(full) - if self.cache_max_age: - fa.cache_control(max_age=self.cache_max_age) - return fa(environ, start_response) - - def not_found(self, environ, start_response, debug_message=None): - comment=('SCRIPT_NAME=%r; PATH_INFO=%r; looking in package %s; ' - 'subdir %s ;debug: %s' % (environ.get('SCRIPT_NAME'), - environ.get('PATH_INFO'), - self.package_name, - self.resource_name, - debug_message or '(none)')) - exc = httpexceptions.HTTPNotFound( - 'The resource at %s could not be found' - % request.construct_url(environ), - comment=comment) - return exc.wsgi_application(environ, start_response) - - def __repr__(self): - return '<%s %s:%s at %s>' % (self.__class__.__name__, self.package_name, - self.root_resource, id(self)) - -class StaticURLInfo(object): - implements(IStaticURLInfo) - - route_url = staticmethod(route_url) # for testing only - - def __init__(self, config): - self.config = config - self.registrations = [] - - def generate(self, path, request, **kw): - for (name, spec, is_url) in self.registrations: - if path.startswith(spec): - subpath = path[len(spec):] - if is_url: - return urljoin(name, subpath) - else: - kw['subpath'] = subpath - return self.route_url(name, request, **kw) - - raise ValueError('No static URL definition matching %s' % path) - - def add(self, name, spec, **extra): - # This feature only allows for the serving of a directory and - # the files contained within, not of a single resource; - # appending a slash here if the spec doesn't have one is - # required for proper prefix matching done in ``generate`` - # (``subpath = path[len(spec):]``). - if not spec.endswith('/'): - spec = spec + '/' - - # we also make sure the name ends with a slash, purely as a - # convenience: a name that is a url is required to end in a - # slash, so that ``urljoin(name, subpath))`` will work above - # when the name is a URL, and it doesn't hurt things for it to - # have a name that ends in a slash if it's used as a route - # name instead of a URL. - if not name.endswith('/'): - # make sure it ends with a slash - name = name + '/' - - names = [ t[0] for t in self.registrations ] - - if name in names: - idx = names.index(name) - self.registrations.pop(idx) - - if urlparse(name)[0]: - # it's a URL - self.registrations.append((name, spec, True)) - else: - # it's a view name - _info = extra.pop('_info', None) - cache_max_age = extra.pop('cache_max_age', None) - view = static_view(spec, cache_max_age=cache_max_age) - # register a route using this view - self.config.add_route( - name, - "%s*subpath" % name, # name already ends with slash - view=view, - view_for=self.__class__, - factory=lambda *x: self, - _info=_info - ) - self.registrations.append((name, spec, False)) - -class static_view(object): - """ An instance of this class is a callable which can act as a - :mod:`repoze.bfg` :term:`view callable`; this view will serve - static files from a directory on disk based on the ``root_dir`` - you provide to its constructor. - - The directory may contain subdirectories (recursively); the static - view implementation will descend into these directories as - necessary based on the components of the URL in order to resolve a - path into a response. - - You may pass an absolute or relative filesystem path or a - :term:`resource specification` representing the directory - containing static files as the ``root_dir`` argument to this - class' constructor. - - If the ``root_dir`` path is relative, and the ``package_name`` - argument is ``None``, ``root_dir`` will be considered relative to - the directory in which the Python file which *calls* ``static`` - resides. If the ``package_name`` name argument is provided, and a - relative ``root_dir`` is provided, the ``root_dir`` will be - considered relative to the Python :term:`package` specified by - ``package_name`` (a dotted path to a Python package). - - ``cache_max_age`` influences the ``Expires`` and ``Max-Age`` - response headers returned by the view (default is 3600 seconds or - five minutes). - - .. note:: If the ``root_dir`` is relative to a :term:`package`, or - is a :term:`resource specification` the :mod:`repoze.bfg` - ``resource`` ZCML directive or - :class:`repoze.bfg.configuration.Configurator` method can be - used to override resources within the named ``root_dir`` - package-relative directory. However, if the ``root_dir`` is - absolute, the ``resource`` directive will not be able to - override the resources it contains. """ - - def __init__(self, root_dir, cache_max_age=3600, package_name=None): - # package_name is for bw compat; it is preferred to pass in a - # package-relative path as root_dir - # (e.g. ``anotherpackage:foo/static``). - caller_package_name = caller_package().__name__ - package_name = package_name or caller_package_name - package_name, root_dir = resolve_resource_spec(root_dir, package_name) - if package_name is None: - app = StaticURLParser(root_dir, cache_max_age=cache_max_age) - else: - app = PackageURLParser( - package_name, root_dir, cache_max_age=cache_max_age) - self.app = app - - def __call__(self, context, request): - subpath = '/'.join(request.subpath) - request_copy = request.copy() - # Fix up PATH_INFO to get rid of everything but the "subpath" - # (the actual path to the file relative to the root dir). - request_copy.environ['PATH_INFO'] = '/' + subpath - # Zero out SCRIPT_NAME for good measure. - request_copy.environ['SCRIPT_NAME'] = '' - return request_copy.get_response(self.app) - diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py deleted file mode 100644 index c99ba8706..000000000 --- a/repoze/bfg/testing.py +++ /dev/null @@ -1,818 +0,0 @@ -import copy -import types - -from webob import Response - -from zope.configuration.xmlconfig import _clearContext - -from zope.deprecation import deprecated - -from zope.interface import implements -from zope.interface import Interface -from zope.interface import alsoProvides - -from repoze.bfg.interfaces import IRequest -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import ISecuredView -from repoze.bfg.interfaces import IView -from repoze.bfg.interfaces import IViewClassifier -from repoze.bfg.interfaces import IViewPermission - -from repoze.bfg.configuration import Configurator -from repoze.bfg.exceptions import Forbidden -from repoze.bfg.registry import Registry -from repoze.bfg.security import Allowed -from repoze.bfg.security import Authenticated -from repoze.bfg.security import Denied -from repoze.bfg.security import Everyone -from repoze.bfg.security import has_permission -from repoze.bfg.threadlocal import get_current_registry -from repoze.bfg.threadlocal import manager -from repoze.bfg.urldispatch import RoutesMapper -from repoze.bfg.zcml import zcml_configure # API - -zcml_configure # prevent pyflakes from complaining - -_marker = object() - -def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): - """ Registers a pair of faux :mod:`repoze.bfg` security policies: - a :term:`authentication policy` and a :term:`authorization - policy`. - - The behavior of the registered :term:`authorization policy` - depends on the ``permissive`` argument. If ``permissive`` is - true, a permissive :term:`authorization policy` is registered; - this policy allows all access. If ``permissive`` is false, a - nonpermissive :term:`authorization policy` is registered; this - policy denies all access. - - The behavior of the registered :term:`authentication policy` - depends on the values provided for the ``userid`` and ``groupids`` - argument. The authentication policy will return the userid - identifier implied by the ``userid`` argument and the group ids - implied by the ``groupids`` argument when the - :func:`repoze.bfg.security.authenticated_userid` or - :func:`repoze.bfg.security.effective_principals` APIs are used. - - This function is most useful when testing code that uses the APIs - named :func:`repoze.bfg.security.has_permission`, - :func:`repoze.bfg.security.authenticated_userid`, - :func:`repoze.bfg.security.effective_principals`, and - :func:`repoze.bfg.security.principals_allowed_by_permission`. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.testing_securitypolicy` - method in your unit and integration tests. - """ - registry = get_current_registry() - config = Configurator(registry=registry) - return config.testing_securitypolicy(userid=userid, groupids=groupids, - permissive=permissive) - -def registerModels(models): - """ Registers a dictionary of :term:`model` objects that can be - resolved via the :func:`repoze.bfg.traversal.find_model` API. - - The :func:`repoze.bfg.traversal.find_model` API is called with a - path as one of its arguments. If the dictionary you register when - calling this method contains that path as a string key - (e.g. ``/foo/bar`` or ``foo/bar``), the corresponding value will - be returned to ``find_model`` (and thus to your code) when - :func:`repoze.bfg.traversal.find_model` is called with an - equivalent path string or tuple. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.testing_models` - method in your unit and integration tests. - """ - registry = get_current_registry() - config = Configurator(registry=registry) - return config.testing_models(models) - -def registerEventListener(event_iface=None): - """ Registers an :term:`event` listener (aka :term:`subscriber`) - listening for events of the type ``event_iface``. This method - returns a list object which is appended to by the subscriber - whenever an event is captured. - - When an event is dispatched that matches ``event_iface``, that - event will be appended to the list. You can then compare the - values in the list to expected event notifications. This method - is useful when testing code that wants to call - :meth:`repoze.bfg.registry.Registry.notify`, - :func:`zope.component.event.dispatch` or - :func:`zope.component.event.objectEventNotify`. - - The default value of ``event_iface`` (``None``) implies a - subscriber registered for *any* kind of event. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.testing_add_subscriber` - method in your unit and integration tests. - """ - registry = get_current_registry() - config = Configurator(registry=registry) - return config.testing_add_subscriber(event_iface) - -def registerTemplateRenderer(path, renderer=None): - """ Register a template renderer at ``path`` (usually a relative - filename ala ``templates/foo.pt``) and return the renderer object. - If the ``renderer`` argument is None, a 'dummy' renderer will be - used. This function is useful when testing code that calls the - :func:`repoze.bfg.renderers.render` function or - :func:`repoze.bfg.renderers.render_to_response` function or any - other ``render_*`` or ``get_*`` API of the - :mod:`repoze.bfg.renderers` module. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.testing_add_template`` - method in your unit and integration tests. - - """ - registry = get_current_registry() - config = Configurator(registry=registry) - return config.testing_add_template(path, renderer) - -# registerDummyRenderer is a deprecated alias that should never be removed -# (too much usage in the wild) -registerDummyRenderer = registerTemplateRenderer - -def registerView(name, result='', view=None, for_=(Interface, Interface), - permission=None): - """ Registers a :mod:`repoze.bfg` :term:`view callable` under the - name implied by the ``name`` argument. The view will return a - :term:`WebOb` :term:`Response` object with the value implied by - the ``result`` argument as its ``body`` attribute. To gain more - control, if you pass in a non-``None`` ``view`` argument, this - value will be used as a view callable instead of an automatically - generated view callable (and ``result`` is not used). - - To protect the view using a :term:`permission`, pass in a - non-``None`` value as ``permission``. This permission will be - checked by any active :term:`authorization policy` when view - execution is attempted. - - This function is useful when testing code which calls - :func:`repoze.bfg.view.render_view_to_response`. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.add_view`` - method in your unit and integration tests. - """ - for_ = (IViewClassifier, ) + for_ - if view is None: - def view(context, request): - return Response(result) - if permission is None: - return registerAdapter(view, for_, IView, name) - else: - def _secure(context, request): - if not has_permission(permission, context, request): - raise Forbidden('no permission') - else: - return view(context, request) - _secure.__call_permissive__ = view - def permitted(context, request): - return has_permission(permission, context, request) - _secure.__permitted__ = permitted - return registerAdapter(_secure, for_, ISecuredView, name) - -def registerViewPermission(name, result=True, viewpermission=None, - for_=(Interface, Interface)): - """ Registers a :mod:`repoze.bfg` 'view permission' object under a - name implied by the ``name`` argument. - - .. warning:: This function was deprecated in repoze.bfg 1.1; it - has no real effect in 1.2+. - """ - if result is True: - result = Allowed('message') - else: - result = Denied('message') - if viewpermission is None: - def viewpermission(context, request): - return result - return registerAdapter(viewpermission, for_, IViewPermission, name) - -deprecated('registerViewPermission', - 'registerViewPermission has been deprecated. As of repoze.bfg ' - 'version 1.1, view functions are now responsible for protecting ' - 'their own execution. A call to this function won\'t prevent a ' - 'view from being executed by the repoze.bfg router, nor ' - 'will the ``repoze.bfg.security.view_execution_permitted`` function ' - 'use the permission registered with this function. Instead,' - 'to register a view permission during testing, use the ' - '``repoze.bfg.testing.registerView`` directive with a ' - '``permission`` argument.') - -def registerUtility(impl, iface=Interface, name=''): - """ Register a ZCA utility component. - - The ``impl`` argument specifies the implementation of the utility. - The ``iface`` argument specifies the :term:`interface` which will - be later required to look up the utility - (:class:`zope.interface.Interface`, by default). The ``name`` - argument implies the utility name; it is the empty string by - default. - - See `The ZCA book `_ for - more information about ZCA utilities. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the :meth:`repoze.bfg.Registry.registerUtility` - method. The ``registry`` attribute of a :term:`Configurator` - in your unit and integration tests is an instance of the - :class:`repoze.bfg.Registry` class. - """ - reg = get_current_registry() - reg.registerUtility(impl, iface, name=name) - return impl - -def registerAdapter(impl, for_=Interface, provides=Interface, name=''): - """ Register a ZCA adapter component. - - The ``impl`` argument specifies the implementation of the - component (often a class). The ``for_`` argument implies the - ``for`` interface type used for this registration; it is - :class:`zope.interface.Interface` by default. If ``for`` is not a - tuple or list, it will be converted to a one-tuple before being - passed to underlying :meth:`repoze.bfg.registry.registerAdapter` - API. - - The ``provides`` argument specifies the ZCA 'provides' interface, - :class:`zope.interface.Interface` by default. - - The ``name`` argument is the empty string by default; it implies - the name under which the adapter is registered. - - See `The ZCA book `_ for - more information about ZCA adapters. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the :meth:`repoze.bfg.Registry.registerAdapter` - method. The ``registry`` attribute of a :term:`Configurator` - in your unit and integration tests is an instance of the - :class:`repoze.bfg.Registry` class. - """ - reg = get_current_registry() - if not isinstance(for_, (tuple, list)): - for_ = (for_,) - reg.registerAdapter(impl, for_, provides, name=name) - return impl - -def registerSubscriber(subscriber, iface=Interface): - """ Register a ZCA subscriber component. - - The ``subscriber`` argument specifies the implementation of the - subscriber component (often a function). - - The ``iface`` argument is the interface type for which the - subscriber will be registered (:class:`zope.interface.Interface` - by default). If ``iface`` is not a tuple or list, it will be - converted to a one-tuple before being passed to the underlying ZCA - :meth:`repoze.bfg.registry.registerHandler` method. - - See `The ZCA book `_ for - more information about ZCA subscribers. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.add_subscriber` - method in your unit and integration tests. - """ - registry = get_current_registry() - config = Configurator(registry) - return config.add_subscriber(subscriber, iface=iface) - -def registerRoute(pattern, name, factory=None): - """ Register a new :term:`route` using a pattern - (e.g. ``:pagename``), a name (e.g. ``home``), and an optional root - factory. - - The ``pattern`` argument implies the route pattern. The ``name`` - argument implies the route name. The ``factory`` argument implies - a :term:`root factory` associated with the route. - - This API is useful for testing code that calls - e.g. :func:`repoze.bfg.url.route_url`. - - .. note:: This API was added in :mod:`repoze.bfg` version 1.1. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.add_route` - method in your unit and integration tests. - """ - reg = get_current_registry() - config = Configurator(registry=reg) - return config.add_route(name, pattern, factory=factory) - -def registerRoutesMapper(root_factory=None): - """ Register a routes 'mapper' object. - - .. note:: This API was added in :mod:`repoze.bfg` version 1.1. - - .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.2: - a route mapper is no longer required to be present for - successful system operation. - """ - mapper = RoutesMapper() - reg = get_current_registry() - reg.registerUtility(mapper, IRoutesMapper) - return mapper - -deprecated('registerRoutesMapper', - 'registerRoutesMapper has been deprecated. As of repoze.bfg ' - 'version 1.2, a route mapper is not required to be registered ' - 'for normal system operation; if you actually do want a route to ' - 'be registered, use the ' - '``repoze.bfg.configuration.Configurator.add_route`` ' - 'method; this will cause a route mapper to be registered also.') - -def registerSettings(dictarg=None, **kw): - """Register one or more 'setting' key/value pairs. A setting is - a single key/value pair in the dictionary-ish object returned from - the API :func:`repoze.bfg.settings.get_settings`. - - You may pass a dictionary:: - - registerSettings({'external_uri':'http://example.com'}) - - Or a set of key/value pairs:: - - registerSettings(external_uri='http://example.com') - - Use of this function is required when you need to test code that - calls the :func:`repoze.bfg.settings.get_settings` API and which - uses return values from that API. - - .. note:: This API is new as of :mod:`repoze.bfg` 1.1. - - .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. - Instead use the - :meth:`repoze.bfg.configuration.Configurator.add_settings` - method in your unit and integration tests. - """ - registry = get_current_registry() - config = Configurator(registry=registry) - config.add_settings(dictarg, **kw) - -class DummyRootFactory(object): - __parent__ = None - __name__ = None - def __init__(self, request): - if 'bfg.routes.matchdict' in request: - self.__dict__.update(request['bfg.routes.matchdict']) - -class DummySecurityPolicy(object): - """ A standin for both an IAuthentication and IAuthorization policy """ - def __init__(self, userid=None, groupids=(), permissive=True): - self.userid = userid - self.groupids = groupids - self.permissive = permissive - - def authenticated_userid(self, request): - return self.userid - - def effective_principals(self, request): - effective_principals = [Everyone] - if self.userid: - effective_principals.append(Authenticated) - effective_principals.append(self.userid) - effective_principals.extend(self.groupids) - return effective_principals - - def remember(self, request, principal, **kw): - return [] - - def forget(self, request): - return [] - - def permits(self, context, principals, permission): - return self.permissive - - def principals_allowed_by_permission(self, context, permission): - return self.effective_principals(None) - -class DummyTemplateRenderer(object): - """ - An instance of this class is returned from - :func:`repoze.bfg.testing.registerTemplateRenderer`. It has a - helper function (``assert_``) that makes it possible to make an - assertion which compares data passed to the renderer by the view - function against expected key/value pairs. - """ - - def __init__(self, string_response=''): - self._received = {} - self._string_response = string_response - self._implementation = MockTemplate(string_response) - - # For in-the-wild test code that doesn't create its own renderer, - # but mutates our internals instead. When all you read is the - # source code, *everything* is an API! - def _get_string_response(self): - return self._string_response - def _set_string_response(self, response): - self._string_response = response - self._implementation.response = response - string_response = property(_get_string_response, _set_string_response) - - def implementation(self): - return self._implementation - - def __call__(self, kw, system=None): - if system: - self._received.update(system) - self._received.update(kw) - return self.string_response - - def __getattr__(self, k): - """ Backwards compatibility """ - val = self._received.get(k, _marker) - if val is _marker: - val = self._implementation._received.get(k, _marker) - if val is _marker: - raise AttributeError(k) - return val - - def assert_(self, **kw): - """ Accept an arbitrary set of assertion key/value pairs. For - each assertion key/value pair assert that the renderer - (eg. :func:`repoze.bfg.renderer.render_to_response`) - received the key with a value that equals the asserted - value. If the renderer did not receive the key at all, or the - value received by the renderer doesn't match the assertion - value, raise an :exc:`AssertionError`.""" - for k, v in kw.items(): - myval = self._received.get(k, _marker) - if myval is _marker: - myval = self._implementation._received.get(k, _marker) - if myval is _marker: - raise AssertionError( - 'A value for key "%s" was not passed to the renderer' - % k) - - if myval != v: - raise AssertionError( - '\nasserted value for %s: %r\nactual value: %r' % ( - v, k, myval)) - return True - -class DummyModel: - """ A dummy :mod:`repoze.bfg` :term:`model` object.""" - def __init__(self, __name__=None, __parent__=None, __provides__=None, - **kw): - """ The model's ``__name__`` attribute will be set to the - value of the ``__name__`` argument, and the model's - ``__parent__`` attribute will be set to the value of the - ``__parent__`` argument. If ``__provides__`` is specified, it - should be an interface object or tuple of interface objects - that will be attached to the resulting model via - :func:`zope.interface.alsoProvides`. Any extra keywords passed - in the ``kw`` argumnent will be set as direct attributes of - the model object.""" - self.__name__ = __name__ - self.__parent__ = __parent__ - if __provides__ is not None: - alsoProvides(self, __provides__) - self.kw = kw - self.__dict__.update(**kw) - self.subs = {} - - def __setitem__(self, name, val): - """ When the ``__setitem__`` method is called, the object - passed in as ``val`` will be decorated with a ``__parent__`` - attribute pointing at the dummy model and a ``__name__`` - attribute that is the value of ``name``. The value will then - be returned when dummy model's ``__getitem__`` is called with - the name ``name```.""" - val.__name__ = name - val.__parent__ = self - self.subs[name] = val - - def __getitem__(self, name): - """ Return a named subobject (see ``__setitem__``)""" - ob = self.subs[name] - return ob - - def __delitem__(self, name): - del self.subs[name] - - def get(self, name, default=None): - return self.subs.get(name, default) - - def values(self): - """ Return the values set by __setitem__ """ - return self.subs.values() - - def items(self): - """ Return the items set by __setitem__ """ - return self.subs.items() - - def keys(self): - """ Return the keys set by __setitem__ """ - return self.subs.keys() - - __iter__ = keys - - def __nonzero__(self): - return True - - def __len__(self): - return len(self.subs) - - def __contains__(self, name): - return name in self.subs - - def clone(self, __name__=_marker, __parent__=_marker, **kw): - """ Create a clone of the model object. If ``__name__`` or - ``__parent__`` arguments are passed, use these values to - override the existing ``__name__`` or ``__parent__`` of the - model. If any extra keyword args are passed in via the ``kw`` - argument, use these keywords to add to or override existing - model keywords (attributes).""" - oldkw = self.kw.copy() - oldkw.update(kw) - inst = self.__class__(self.__name__, self.__parent__, **oldkw) - inst.subs = copy.deepcopy(self.subs) - if __name__ is not _marker: - inst.__name__ = __name__ - if __parent__ is not _marker: - inst.__parent__ = __parent__ - return inst - -class DummyRequest(object): - """ A dummy request object (imitates a :term:`request` object). - - The ``params``, ``environ``, ``headers``, ``path``, and - ``cookies`` arguments correspond to their :term`WebOb` - equivalents. - - The ``post`` argument, if passed, populates the request's - ``POST`` attribute, but *not* ``params``, in order to allow testing - that the app accepts data for a given view only from POST requests. - This argument also sets ``self.method`` to "POST". - - Extra keyword arguments are assigned as attributes of the request - itself. - """ - implements(IRequest) - method = 'GET' - application_url = 'http://example.com' - host = 'example.com:80' - content_length = 0 - response_callbacks = () - def __init__(self, params=None, environ=None, headers=None, path='/', - cookies=None, post=None, **kw): - if environ is None: - environ = {} - if params is None: - params = {} - if headers is None: - headers = {} - if cookies is None: - cookies = {} - self.environ = environ - self.headers = headers - self.params = params - self.cookies = cookies - self.matchdict = {} - self.GET = params - if post is not None: - self.method = 'POST' - self.POST = post - else: - self.POST = params - self.host_url = self.application_url - self.path_url = self.application_url - self.url = self.application_url - self.path = path - self.path_info = path - self.script_name = '' - self.path_qs = '' - self.body = '' - self.view_name = '' - self.subpath = () - self.traversed = () - self.virtual_root_path = () - self.context = None - self.root = None - self.virtual_root = None - self.marshalled = params # repoze.monty - self.registry = get_current_registry() - self.__dict__.update(kw) - - def add_response_callback(self, callback): - if not self.response_callbacks: - self.response_callbacks = [] - self.response_callbacks.append(callback) - -def setUp(registry=None, request=None, hook_zca=True): - """ - Set :mod:`repoze.bfg` registry and request thread locals for the - duration of a single unit test. - - .. note:: The ``setUp`` function is new as of :mod:`repoze.bfg` - 1.1. - - Use this function in the ``setUp`` method of a unittest test case - which directly or indirectly uses: - - - any of the ``register*`` functions in :mod:`repoze.bfg.testing` - (such as :func:`repoze.bfg.testing.registerModels`) - - - any method of the :class:`repoze.bfg.configuration.Configurator` - object returned by this function. - - - the :func:`repoze.bfg.threadlocal.get_current_registry` or - :func:`repoze.bfg.threadlocal.get_current_request` functions. - - If you use the ``testing.register*`` APIs, or the - ``get_current_*`` functions (or call :mod:`repoze.bfg` code that - uses these functions) without calling ``setUp``, - :func:`repoze.bfg.threadlocal.get_current_registry` will return a - *global* :term:`application registry`, which may cause unit tests - to not be isolated with respect to registrations they perform. - - If the ``registry`` argument is ``None``, a new empty - :term:`application registry` will be created (an instance of the - :class:`repoze.bfg.registry.Registry` class). If the ``registry`` - argument is not ``None``, the value passed in should be an - instance of the :class:`repoze.bfg.registry.Registry` class or a - suitable testing analogue. - - After ``setUp`` is finished, the registry returned by the - :func:`repoze.bfg.threadlocal.get_current_request` function will - be the passed (or constructed) registry until - :func:`repoze.bfg.testing.tearDown` is called (or - :func:`repoze.bfg.testing.setUp` is called again) . - - .. note:: The ``registry`` argument is new as of :mod:`repoze.bfg` - 1.2. - - If the ``hook_zca`` argument is ``True``, ``setUp`` will attempt - to perform the operation ``zope.component.getSiteManager.sethook( - repoze.bfg.threadlocal.get_current_registry)``, which will cause - the :term:`Zope Component Architecture` global API - (e.g. :func:`zope.component.getSiteManager`, - :func:`zope.component.getAdapter`, and so on) to use the registry - constructed by ``setUp`` as the value it returns from - :func:`zope.component.getSiteManager`. If the - :mod:`zope.component` package cannot be imported, or if - ``hook_zca`` is ``False``, the hook will not be set. - - This function returns an instance of the - :class:`repoze.bfg.configuration.Configurator` class, which can be - used for further configuration to set up an environment suitable - for a unit or integration test. The ``registry`` attribute - attached to the Configurator instance represents the 'current' - :term:`application registry`; the same registry will be returned - by :func:`repoze.bfg.threadlocal.get_current_registry` during the - execution of the test. - - .. note:: The ``hook_zca`` argument is new as of :mod:`repoze.bfg` - 1.2. - - .. note:: The return value (a ``Configurator`` instance) is new as - of :mod:`repoze.bfg` 1.2 (previous versions used to return - ``None``) - - .. warning:: Although this method of setting up a test registry - will never disappear, after :mod:`repoze.bfg` 1.2a6, - using the ``begin`` and ``end`` methods of a - ``Configurator`` are preferred to using - ``repoze.bfg.testing.setUp`` and - ``repoze.bfg.testing.tearDown``. See - :ref:`unittesting_chapter` for more information. - """ - manager.clear() - if registry is None: - registry = Registry('testing') - config = Configurator(registry=registry) - if hasattr(registry, 'registerUtility'): - # Sometimes nose calls us with a non-registry object because - # it thinks this function is module test setup. Likewise, - # someone may be passing us an esoteric "dummy" registry, and - # the below won't succeed if it doesn't have a registerUtility - # method. - from repoze.bfg.configuration import DEFAULT_RENDERERS - for name, renderer in DEFAULT_RENDERERS: - # Cause the default renderers to be registered because - # in-the-wild test code relies on being able to call - # e.g. ``repoze.bfg.chameleon_zpt.render_template`` - # without registering a .pt renderer, expecting the "real" - # template to be rendered. This is a holdover from when - # individual template system renderers weren't indirected - # by the ``repoze.bfg.renderers`` machinery, and - # ``render_template`` and friends went behind the back of - # any existing renderer factory lookup system. - config.add_renderer(name, renderer) - hook_zca and config.hook_zca() - config.begin(request=request) - return config - -def tearDown(unhook_zca=True): - """Undo the effects :func:`repoze.bfg.testing.setUp`. Use this - function in the ``tearDown`` method of a unit test that uses - :func:`repoze.bfg.testing.setUp` in its ``setUp`` method. - - .. note:: This function is new as of :mod:`repoze.bfg` 1.1. - - If the ``unhook_zca`` argument is ``True`` (the default), call - :func:`zope.component.getSiteManager.reset`. This undoes the - action of :func:`repoze.bfg.testing.setUp` called with the - argument ``hook_zca=True``. If :mod:`zope.component` cannot be - imported, ignore the argument. - - .. note:: The ``unhook_zca`` argument is new as of - :mod:`repoze.bfg` 1.2. - - .. warning:: Although this method of tearing a test setup down - will never disappear, after :mod:`repoze.bfg` 1.2a6, - using the ``begin`` and ``end`` methods of a - ``Configurator`` are preferred to using - ``repoze.bfg.testing.setUp`` and - ``repoze.bfg.testing.tearDown``. See - :ref:`unittesting_chapter` for more information. - - """ - if unhook_zca: - try: - from zope.component import getSiteManager - getSiteManager.reset() - except ImportError: # pragma: no cover - pass - info = manager.pop() - manager.clear() - if info is not None: - registry = info['registry'] - if hasattr(registry, '__init__') and hasattr(registry, '__name__'): - try: - registry.__init__(registry.__name__) - except TypeError: - # calling __init__ is largely for the benefit of - # people who want to use the global ZCA registry; - # however maybe somebody's using a registry we don't - # understand, let's not blow up - pass - _clearContext() # XXX why? - -def cleanUp(*arg, **kw): - """ :func:`repoze.bfg.testing.cleanUp` is an alias for - :func:`repoze.bfg.testing.setUp`. Although this function is - effectively deprecated as of :mod:`repoze.bfg` 1.1, due to its - extensive production usage, it will never be removed.""" - return setUp(*arg, **kw) - -class DummyRendererFactory(object): - """ Registered by - ``repoze.bfg.configuration.Configurator.testing_add_renderer`` as - a dummy renderer factory. The indecision about what to use as a - key (a spec vs. a relative name) is caused by test suites in the - wild believing they can register either. The ``factory`` argument - passed to this constructor is usually the *real* template renderer - factory, found when ``testing_add_renderer`` is called.""" - def __init__(self, name, factory): - self.name = name - self.factory = factory # the "real" renderer factory reg'd previously - self.renderers = {} - - def add(self, spec, renderer): - self.renderers[spec] = renderer - if ':' in spec: - package, relative = spec.split(':', 1) - self.renderers[relative] = renderer - - def __call__(self, spec): - renderer = self.renderers.get(spec) - if renderer is None: - if ':' in spec: - package, relative = spec.split(':', 1) - renderer = self.renderers.get(relative) - if renderer is None: - if self.factory: - renderer = self.factory(spec) - else: - raise KeyError('No testing renderer registered for %r' % - spec) - return renderer - - -class MockTemplate(object): - def __init__(self, response): - self._received = {} - self.response = response - def __getattr__(self, attrname): - return self - def __getitem__(self, attrname): - return self - def __call__(self, *arg, **kw): - self._received.update(kw) - return self.response diff --git a/repoze/bfg/tests/__init__.py b/repoze/bfg/tests/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/repoze/bfg/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/repoze/bfg/tests/ccbugapp/__init__.py b/repoze/bfg/tests/ccbugapp/__init__.py deleted file mode 100644 index 546616b2c..000000000 --- a/repoze/bfg/tests/ccbugapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# fixture application diff --git a/repoze/bfg/tests/ccbugapp/configure.zcml b/repoze/bfg/tests/ccbugapp/configure.zcml deleted file mode 100644 index cb9ca6f1d..000000000 --- a/repoze/bfg/tests/ccbugapp/configure.zcml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/repoze/bfg/tests/ccbugapp/views.py b/repoze/bfg/tests/ccbugapp/views.py deleted file mode 100644 index 4a6939ac2..000000000 --- a/repoze/bfg/tests/ccbugapp/views.py +++ /dev/null @@ -1,10 +0,0 @@ -from webob import Response - -def rdf_view(request): - """ """ - return Response('rdf') - -def juri_view(request): - """ """ - return Response('juri') - diff --git a/repoze/bfg/tests/exceptionviewapp/__init__.py b/repoze/bfg/tests/exceptionviewapp/__init__.py deleted file mode 100644 index ef5fe8b12..000000000 --- a/repoze/bfg/tests/exceptionviewapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# a package diff --git a/repoze/bfg/tests/exceptionviewapp/configure.zcml b/repoze/bfg/tests/exceptionviewapp/configure.zcml deleted file mode 100644 index 680e065a6..000000000 --- a/repoze/bfg/tests/exceptionviewapp/configure.zcml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/repoze/bfg/tests/exceptionviewapp/models.py b/repoze/bfg/tests/exceptionviewapp/models.py deleted file mode 100644 index fe407badc..000000000 --- a/repoze/bfg/tests/exceptionviewapp/models.py +++ /dev/null @@ -1,18 +0,0 @@ - -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/repoze/bfg/tests/exceptionviewapp/views.py b/repoze/bfg/tests/exceptionviewapp/views.py deleted file mode 100644 index 1432618cf..000000000 --- a/repoze/bfg/tests/exceptionviewapp/views.py +++ /dev/null @@ -1,17 +0,0 @@ -from webob import Response -from models import AnException - -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() diff --git a/repoze/bfg/tests/fixtureapp/__init__.py b/repoze/bfg/tests/fixtureapp/__init__.py deleted file mode 100644 index 546616b2c..000000000 --- a/repoze/bfg/tests/fixtureapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# fixture application diff --git a/repoze/bfg/tests/fixtureapp/another.zcml b/repoze/bfg/tests/fixtureapp/another.zcml deleted file mode 100644 index f8678bad7..000000000 --- a/repoze/bfg/tests/fixtureapp/another.zcml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/repoze/bfg/tests/fixtureapp/configure.zcml b/repoze/bfg/tests/fixtureapp/configure.zcml deleted file mode 100644 index e3470d47a..000000000 --- a/repoze/bfg/tests/fixtureapp/configure.zcml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/repoze/bfg/tests/fixtureapp/models.py b/repoze/bfg/tests/fixtureapp/models.py deleted file mode 100644 index d80d14bb3..000000000 --- a/repoze/bfg/tests/fixtureapp/models.py +++ /dev/null @@ -1,8 +0,0 @@ -from zope.interface import Interface - -class IFixture(Interface): - pass - -def fixture(): - """ """ - diff --git a/repoze/bfg/tests/fixtureapp/subpackage/__init__.py b/repoze/bfg/tests/fixtureapp/subpackage/__init__.py deleted file mode 100644 index d3173e636..000000000 --- a/repoze/bfg/tests/fixtureapp/subpackage/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#package diff --git a/repoze/bfg/tests/fixtureapp/subpackage/templates/bar.pt b/repoze/bfg/tests/fixtureapp/subpackage/templates/bar.pt deleted file mode 100644 index 90531a4b3..000000000 --- a/repoze/bfg/tests/fixtureapp/subpackage/templates/bar.pt +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/repoze/bfg/tests/fixtureapp/subpackage/yetanother.zcml b/repoze/bfg/tests/fixtureapp/subpackage/yetanother.zcml deleted file mode 100644 index 464163477..000000000 --- a/repoze/bfg/tests/fixtureapp/subpackage/yetanother.zcml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/repoze/bfg/tests/fixtureapp/templates/fixture.pt b/repoze/bfg/tests/fixtureapp/templates/fixture.pt deleted file mode 100644 index 06dd4e2b1..000000000 --- a/repoze/bfg/tests/fixtureapp/templates/fixture.pt +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/repoze/bfg/tests/fixtureapp/views.py b/repoze/bfg/tests/fixtureapp/views.py deleted file mode 100644 index 862046d43..000000000 --- a/repoze/bfg/tests/fixtureapp/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from zope.interface import Interface -from webob import Response -from repoze.bfg.exceptions import Forbidden - -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 Forbidden() - -class IDummy(Interface): - pass diff --git a/repoze/bfg/tests/fixtures/minimal.pt b/repoze/bfg/tests/fixtures/minimal.pt deleted file mode 100644 index 693d155ef..000000000 --- a/repoze/bfg/tests/fixtures/minimal.pt +++ /dev/null @@ -1,3 +0,0 @@ -
-
diff --git a/repoze/bfg/tests/fixtures/minimal.txt b/repoze/bfg/tests/fixtures/minimal.txt deleted file mode 100644 index 18832d351..000000000 --- a/repoze/bfg/tests/fixtures/minimal.txt +++ /dev/null @@ -1 +0,0 @@ -Hello. diff --git a/repoze/bfg/tests/fixtures/nonminimal.txt b/repoze/bfg/tests/fixtures/nonminimal.txt deleted file mode 100644 index 9de95ec92..000000000 --- a/repoze/bfg/tests/fixtures/nonminimal.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, ${name}! diff --git a/repoze/bfg/tests/fixtures/pp.pt b/repoze/bfg/tests/fixtures/pp.pt deleted file mode 100644 index 9df7d22da..000000000 --- a/repoze/bfg/tests/fixtures/pp.pt +++ /dev/null @@ -1,3 +0,0 @@ -

WRAPPED

diff --git a/repoze/bfg/tests/fixtures/static/index.html b/repoze/bfg/tests/fixtures/static/index.html deleted file mode 100644 index 6498787a5..000000000 --- a/repoze/bfg/tests/fixtures/static/index.html +++ /dev/null @@ -1 +0,0 @@ -static diff --git a/repoze/bfg/tests/fixtures/static/subdir/index.html b/repoze/bfg/tests/fixtures/static/subdir/index.html deleted file mode 100644 index bb84fad04..000000000 --- a/repoze/bfg/tests/fixtures/static/subdir/index.html +++ /dev/null @@ -1 +0,0 @@ -subdir diff --git a/repoze/bfg/tests/grokkedapp/__init__.py b/repoze/bfg/tests/grokkedapp/__init__.py deleted file mode 100644 index 5d2843885..000000000 --- a/repoze/bfg/tests/grokkedapp/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view() -def grokked(context, request): - return 'grokked' - -@bfg_view(request_method='POST') -def grokked_post(context, request): - return 'grokked_post' - -@bfg_view(name='stacked2') -@bfg_view(name='stacked1') -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 = bfg_view(name='stacked_class1')(stacked_class) -stacked_class = bfg_view(name='stacked_class2')(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 = bfg_view(name='oldstyle_grokked_class')( - 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 = bfg_view(name='grokked_class')(grokked_class) - -class Foo(object): - def __call__(self, context, request): - return 'grokked_instance' - -grokked_instance = Foo() -grokked_instance = bfg_view(name='grokked_instance')(grokked_instance) - -class Base(object): - @bfg_view(name='basemethod') - def basemethod(self): - """ """ - -class MethodViews(Base): - def __init__(self, context, request): - self.context = context - self.request = request - - @bfg_view(name='method1') - def method1(self): - return 'method1' - - @bfg_view(name='method2') - def method2(self): - return 'method2' - - @bfg_view(name='stacked_method2') - @bfg_view(name='stacked_method1') - def stacked(self): - return 'stacked_method' - -# ungrokkable - -A = 1 -B = {} - -def stuff(): - """ """ - -class Whatever(object): - pass - -class Whatever2: - pass diff --git a/repoze/bfg/tests/grokkedapp/another.py b/repoze/bfg/tests/grokkedapp/another.py deleted file mode 100644 index 7dda1d579..000000000 --- a/repoze/bfg/tests/grokkedapp/another.py +++ /dev/null @@ -1,62 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view(name='another') -def grokked(context, request): - return 'another_grokked' - -@bfg_view(request_method='POST', name='another') -def grokked_post(context, request): - return 'another_grokked_post' - -@bfg_view(name='another_stacked2') -@bfg_view(name='another_stacked1') -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 = bfg_view(name='another_stacked_class1')(stacked_class) -stacked_class = bfg_view(name='another_stacked_class2')(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 = bfg_view(name='another_oldstyle_grokked_class')( - 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 = bfg_view(name='another_grokked_class')(grokked_class) - -class Foo(object): - def __call__(self, context, request): - return 'another_grokked_instance' - -grokked_instance = Foo() -grokked_instance = bfg_view(name='another_grokked_instance')(grokked_instance) - -# ungrokkable - -A = 1 -B = {} - -def stuff(): - """ """ - diff --git a/repoze/bfg/tests/grokkedapp/configure.zcml b/repoze/bfg/tests/grokkedapp/configure.zcml deleted file mode 100644 index 6867046df..000000000 --- a/repoze/bfg/tests/grokkedapp/configure.zcml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/repoze/bfg/tests/grokkedapp/pod/notinit.py b/repoze/bfg/tests/grokkedapp/pod/notinit.py deleted file mode 100644 index ca0538123..000000000 --- a/repoze/bfg/tests/grokkedapp/pod/notinit.py +++ /dev/null @@ -1,5 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view(name='pod_notinit') -def subpackage_notinit(context, request): - return 'pod_notinit' diff --git a/repoze/bfg/tests/grokkedapp/subpackage/__init__.py b/repoze/bfg/tests/grokkedapp/subpackage/__init__.py deleted file mode 100644 index bdbe54e13..000000000 --- a/repoze/bfg/tests/grokkedapp/subpackage/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view(name='subpackage_init') -def subpackage_init(context, request): - return 'subpackage_init' diff --git a/repoze/bfg/tests/grokkedapp/subpackage/notinit.py b/repoze/bfg/tests/grokkedapp/subpackage/notinit.py deleted file mode 100644 index 84de6503d..000000000 --- a/repoze/bfg/tests/grokkedapp/subpackage/notinit.py +++ /dev/null @@ -1,5 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view(name='subpackage_notinit') -def subpackage_notinit(context, request): - return 'subpackage_notinit' diff --git a/repoze/bfg/tests/grokkedapp/subpackage/subsubpackage/__init__.py b/repoze/bfg/tests/grokkedapp/subpackage/subsubpackage/__init__.py deleted file mode 100644 index a83be850b..000000000 --- a/repoze/bfg/tests/grokkedapp/subpackage/subsubpackage/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from repoze.bfg.view import bfg_view - -@bfg_view(name='subsubpackage_init') -def subpackage_init(context, request): - return 'subsubpackage_init' diff --git a/repoze/bfg/tests/hybridapp/__init__.py b/repoze/bfg/tests/hybridapp/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/repoze/bfg/tests/hybridapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/repoze/bfg/tests/hybridapp/configure.zcml b/repoze/bfg/tests/hybridapp/configure.zcml deleted file mode 100644 index a94409e26..000000000 --- a/repoze/bfg/tests/hybridapp/configure.zcml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/repoze/bfg/tests/hybridapp/views.py b/repoze/bfg/tests/hybridapp/views.py deleted file mode 100644 index 135ef8290..000000000 --- a/repoze/bfg/tests/hybridapp/views.py +++ /dev/null @@ -1,39 +0,0 @@ -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/repoze/bfg/tests/localeapp/__init__.py b/repoze/bfg/tests/localeapp/__init__.py deleted file mode 100644 index 1a35cdb4a..000000000 --- a/repoze/bfg/tests/localeapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# a file diff --git a/repoze/bfg/tests/localeapp/locale/GARBAGE b/repoze/bfg/tests/localeapp/locale/GARBAGE deleted file mode 100644 index 032c55584..000000000 --- a/repoze/bfg/tests/localeapp/locale/GARBAGE +++ /dev/null @@ -1 +0,0 @@ -Garbage file. diff --git a/repoze/bfg/tests/localeapp/locale/be/LC_MESSAGES b/repoze/bfg/tests/localeapp/locale/be/LC_MESSAGES deleted file mode 100644 index 909cf6a3b..000000000 --- a/repoze/bfg/tests/localeapp/locale/be/LC_MESSAGES +++ /dev/null @@ -1 +0,0 @@ -busted. diff --git a/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo b/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo deleted file mode 100644 index 2924a5eb5..000000000 Binary files a/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.mo and /dev/null differ diff --git a/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po b/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po deleted file mode 100644 index 17f87bc19..000000000 --- a/repoze/bfg/tests/localeapp/locale/de/LC_MESSAGES/deformsite.po +++ /dev/null @@ -1,31 +0,0 @@ -# German translations for deformsite. -# Copyright (C) 2010 ORGANIZATION -# This file is distributed under the same license as the deformsite project. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: deformsite 0.0\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-04-22 14:17+0400\n" -"PO-Revision-Date: 2010-04-22 14:17-0400\n" -"Last-Translator: FULL NAME \n" -"Language-Team: de \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.5\n" - -#: deformsite/__init__.py:458 -msgid "Approve" -msgstr "Genehmigen" - -#: deformsite/__init__.py:459 -msgid "Show approval" -msgstr "Zeigen Genehmigung" - -#: deformsite/__init__.py:466 -msgid "Submit" -msgstr "Beugen" - diff --git a/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo b/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo deleted file mode 100644 index 2924a5eb5..000000000 Binary files a/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.mo and /dev/null differ diff --git a/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po b/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po deleted file mode 100644 index 17f87bc19..000000000 --- a/repoze/bfg/tests/localeapp/locale/en/LC_MESSAGES/deformsite.po +++ /dev/null @@ -1,31 +0,0 @@ -# German translations for deformsite. -# Copyright (C) 2010 ORGANIZATION -# This file is distributed under the same license as the deformsite project. -# FIRST AUTHOR , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: deformsite 0.0\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-04-22 14:17+0400\n" -"PO-Revision-Date: 2010-04-22 14:17-0400\n" -"Last-Translator: FULL NAME \n" -"Language-Team: de \n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.5\n" - -#: deformsite/__init__.py:458 -msgid "Approve" -msgstr "Genehmigen" - -#: deformsite/__init__.py:459 -msgid "Show approval" -msgstr "Zeigen Genehmigung" - -#: deformsite/__init__.py:466 -msgid "Submit" -msgstr "Beugen" - diff --git a/repoze/bfg/tests/restbugapp/__init__.py b/repoze/bfg/tests/restbugapp/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/repoze/bfg/tests/restbugapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/repoze/bfg/tests/restbugapp/configure.zcml b/repoze/bfg/tests/restbugapp/configure.zcml deleted file mode 100644 index 67954b892..000000000 --- a/repoze/bfg/tests/restbugapp/configure.zcml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - diff --git a/repoze/bfg/tests/restbugapp/views.py b/repoze/bfg/tests/restbugapp/views.py deleted file mode 100644 index b94851099..000000000 --- a/repoze/bfg/tests/restbugapp/views.py +++ /dev/null @@ -1,15 +0,0 @@ -from webob 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/repoze/bfg/tests/routesapp/__init__.py b/repoze/bfg/tests/routesapp/__init__.py deleted file mode 100644 index 546616b2c..000000000 --- a/repoze/bfg/tests/routesapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# fixture application diff --git a/repoze/bfg/tests/routesapp/configure.zcml b/repoze/bfg/tests/routesapp/configure.zcml deleted file mode 100644 index 01062b6d4..000000000 --- a/repoze/bfg/tests/routesapp/configure.zcml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/repoze/bfg/tests/routesapp/models.py b/repoze/bfg/tests/routesapp/models.py deleted file mode 100644 index a57b06308..000000000 --- a/repoze/bfg/tests/routesapp/models.py +++ /dev/null @@ -1,5 +0,0 @@ -from zope.interface import Interface - -class IFixture(Interface): - pass - diff --git a/repoze/bfg/tests/routesapp/templates/fixture.pt b/repoze/bfg/tests/routesapp/templates/fixture.pt deleted file mode 100644 index 06dd4e2b1..000000000 --- a/repoze/bfg/tests/routesapp/templates/fixture.pt +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/repoze/bfg/tests/routesapp/views.py b/repoze/bfg/tests/routesapp/views.py deleted file mode 100644 index f805b88c9..000000000 --- a/repoze/bfg/tests/routesapp/views.py +++ /dev/null @@ -1,8 +0,0 @@ -from zope.interface import Interface - -def fixture_view(context, request): - """ """ - -class IDummy(Interface): - pass - diff --git a/repoze/bfg/tests/test_authentication.py b/repoze/bfg/tests/test_authentication.py deleted file mode 100644 index d020a11a6..000000000 --- a/repoze/bfg/tests/test_authentication.py +++ /dev/null @@ -1,664 +0,0 @@ -import unittest - -class TestRepozeWho1AuthenticationPolicy(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy - verifyClass(IAuthenticationPolicy, self._getTargetClass()) - - def test_instance_implements_IAuthenticationPolicy(self): - from zope.interface.verify import verifyObject - from repoze.bfg.interfaces import IAuthenticationPolicy - verifyObject(IAuthenticationPolicy, self._makeOne()) - - 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_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_effective_principals_None(self): - from repoze.bfg.security import Everyone - request = DummyRequest({}) - policy = self._makeOne() - self.assertEqual(policy.effective_principals(request), [Everyone]) - - def test_effective_principals_userid_only(self): - from repoze.bfg.security import Everyone - from repoze.bfg.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 repoze.bfg.security import Everyone - from repoze.bfg.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 repoze.bfg.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_remember_no_plugins(self): - request = DummyRequest({}) - policy = self._makeOne() - result = policy.remember(request, 'fred') - self.assertEqual(result, []) - - def test_remember(self): - authtkt = DummyWhoPlugin() - request = DummyRequest( - {'repoze.who.plugins':{'auth_tkt':authtkt}}) - policy = self._makeOne() - result = policy.remember(request, 'fred') - self.assertEqual(result[0], request.environ) - self.assertEqual(result[1], {'repoze.who.userid':'fred'}) - - def test_forget_no_plugins(self): - request = DummyRequest({}) - policy = self._makeOne() - result = policy.forget(request) - self.assertEqual(result, []) - - def test_forget(self): - authtkt = DummyWhoPlugin() - request = DummyRequest( - {'repoze.who.plugins':{'auth_tkt':authtkt}, - 'repoze.who.identity':{'repoze.who.userid':'fred'}, - }) - policy = self._makeOne() - result = policy.forget(request) - self.assertEqual(result[0], request.environ) - self.assertEqual(result[1], request.environ['repoze.who.identity']) - -class TestRemoteUserAuthenticationPolicy(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy - verifyClass(IAuthenticationPolicy, self._getTargetClass()) - - def test_instance_implements_IAuthenticationPolicy(self): - from zope.interface.verify import verifyObject - from repoze.bfg.interfaces import IAuthenticationPolicy - verifyObject(IAuthenticationPolicy, self._makeOne()) - - 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 repoze.bfg.security import Everyone - request = DummyRequest({}) - policy = self._makeOne() - self.assertEqual(policy.effective_principals(request), [Everyone]) - - def test_effective_principals(self): - from repoze.bfg.security import Everyone - from repoze.bfg.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 TestAutkTktAuthenticationPolicy(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.authentication import AuthTktAuthenticationPolicy - return AuthTktAuthenticationPolicy - - def _makeOne(self, callback, cookieidentity, **kw): - inst = self._getTargetClass()('secret', callback, **kw) - inst.cookie = DummyCookieHelper(cookieidentity) - return inst - - 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, - ) - self.assertEqual(inst.callback, None) - - def test_class_implements_IAuthenticationPolicy(self): - from zope.interface.verify import verifyClass - from repoze.bfg.interfaces import IAuthenticationPolicy - verifyClass(IAuthenticationPolicy, self._getTargetClass()) - - def test_instance_implements_IAuthenticationPolicy(self): - from zope.interface.verify import verifyObject - from repoze.bfg.interfaces import IAuthenticationPolicy - verifyObject(IAuthenticationPolicy, self._makeOne(None, None)) - - 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 repoze.bfg.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 repoze.bfg.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 repoze.bfg.security import Everyone - from repoze.bfg.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, []) - -class TestAuthTktCookieHelper(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.authentication import AuthTktCookieHelper - return AuthTktCookieHelper - - def _makeOne(self, *arg, **kw): - plugin = self._getTargetClass()(*arg, **kw) - plugin.auth_tkt = DummyAuthTktModule() - return plugin - - def _makeRequest(self, kw=None): - environ = {'wsgi.version': (1,0)} - if kw is not None: - environ.update(kw) - environ['REMOTE_ADDR'] = '1.1.1.1' - environ['SERVER_NAME'] = 'localhost' - return DummyRequest(environ) - - def _cookieValue(self, cookie): - return eval(cookie.value) - - def _parseHeaders(self, headers): - return [ self._parseHeader(header) for header in headers ] - - def _parseHeader(self, header): - cookie = self._parseCookie(header[1]) - return cookie - - def _parseCookie(self, cookie): - from Cookie import SimpleCookie - cookies = SimpleCookie() - cookies.load(cookie) - return cookies.get('auth_tkt') - - def test_identify_nocookie(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.identify(request) - self.assertEqual(result, None) - - def test_identify_good_cookie_include_ip(self): - plugin = self._makeOne('secret', include_ip=True) - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.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(plugin.auth_tkt.value, 'ticket') - self.assertEqual(plugin.auth_tkt.remote_addr, '1.1.1.1') - self.assertEqual(plugin.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): - plugin = self._makeOne('secret', include_ip=False) - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.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(plugin.auth_tkt.value, 'ticket') - self.assertEqual(plugin.auth_tkt.remote_addr, '0.0.0.0') - self.assertEqual(plugin.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): - plugin = self._makeOne('secret', include_ip=False) - plugin.auth_tkt.userid = '1' - plugin.auth_tkt.user_data = 'userid_type:int' - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.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_good_cookie_unknown_useridtype(self): - plugin = self._makeOne('secret', include_ip=False) - plugin.auth_tkt.userid = 'abc' - plugin.auth_tkt.user_data = 'userid_type:unknown' - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.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): - plugin = self._makeOne('secret', include_ip=False) - plugin.auth_tkt.userid = 'encoded'.encode('base64').strip() - plugin.auth_tkt.user_data = 'userid_type:b64str' - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.identify(request) - self.assertEqual(len(result), 4) - self.assertEqual(result['tokens'], ()) - self.assertEqual(result['userid'], '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): - plugin = self._makeOne('secret', include_ip=False) - plugin.auth_tkt.userid = '\xc3\xa9ncoded'.encode('base64').strip() - plugin.auth_tkt.user_data = 'userid_type:b64unicode' - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=ticket'}) - result = plugin.identify(request) - self.assertEqual(len(result), 4) - self.assertEqual(result['tokens'], ()) - self.assertEqual(result['userid'], unicode('\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): - plugin = self._makeOne('secret', include_ip=True) - plugin.auth_tkt.parse_raise = True - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) - result = plugin.identify(request) - self.assertEqual(result, None) - - def test_identify_cookie_timed_out(self): - plugin = self._makeOne('secret', timeout=1) - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) - result = plugin.identify(request) - self.assertEqual(result, None) - - def test_identify_cookie_reissue(self): - import time - plugin = self._makeOne('secret', timeout=5, reissue_time=0) - plugin.auth_tkt.timestamp = time.time() - request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) - result = plugin.identify(request) - self.failUnless(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') - - def test_remember(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.remember(request, 'userid') - self.assertEqual(len(result), 3) - - self.assertEqual(result[0][0], 'Set-Cookie') - self.failUnless(result[0][1].endswith('; Path=/')) - self.failUnless(result[0][1].startswith('auth_tkt=')) - - self.assertEqual(result[1][0], 'Set-Cookie') - self.failUnless(result[1][1].endswith('; Path=/; Domain=localhost')) - self.failUnless(result[1][1].startswith('auth_tkt=')) - - self.assertEqual(result[2][0], 'Set-Cookie') - self.failUnless(result[2][1].endswith('; Path=/; Domain=.localhost')) - self.failUnless(result[2][1].startswith('auth_tkt=')) - - def test_remember_include_ip(self): - plugin = self._makeOne('secret', include_ip=True) - request = self._makeRequest() - result = plugin.remember(request, 'other') - self.assertEqual(len(result), 3) - - self.assertEqual(result[0][0], 'Set-Cookie') - self.failUnless(result[0][1].endswith('; Path=/')) - self.failUnless(result[0][1].startswith('auth_tkt=')) - - self.assertEqual(result[1][0], 'Set-Cookie') - self.failUnless(result[1][1].endswith('; Path=/; Domain=localhost')) - self.failUnless(result[1][1].startswith('auth_tkt=')) - - self.assertEqual(result[2][0], 'Set-Cookie') - self.failUnless(result[2][1].endswith('; Path=/; Domain=.localhost')) - self.failUnless(result[2][1].startswith('auth_tkt=')) - - def test_remember_path(self): - plugin = self._makeOne('secret', include_ip=True, - path="/cgi-bin/bfg.cgi/") - request = self._makeRequest() - result = plugin.remember(request, 'other') - self.assertEqual(len(result), 3) - - self.assertEqual(result[0][0], 'Set-Cookie') - self.failUnless(result[0][1].endswith('; Path=/cgi-bin/bfg.cgi/')) - self.failUnless(result[0][1].startswith('auth_tkt=')) - - self.assertEqual(result[1][0], 'Set-Cookie') - self.failUnless(result[1][1].endswith( - '; Path=/cgi-bin/bfg.cgi/; Domain=localhost')) - self.failUnless(result[1][1].startswith('auth_tkt=')) - - self.assertEqual(result[2][0], 'Set-Cookie') - self.failUnless(result[2][1].endswith( - '; Path=/cgi-bin/bfg.cgi/; Domain=.localhost')) - self.failUnless(result[2][1].startswith('auth_tkt=')) - - def test_remember_http_only(self): - plugin = self._makeOne('secret', include_ip=True, http_only=True) - request = self._makeRequest() - result = plugin.remember(request, 'other') - self.assertEqual(len(result), 3) - - self.assertEqual(result[0][0], 'Set-Cookie') - self.failUnless(result[0][1].endswith('; HttpOnly')) - self.failUnless(result[0][1].startswith('auth_tkt=')) - - self.assertEqual(result[1][0], 'Set-Cookie') - self.failUnless(result[1][1].endswith('; HttpOnly')) - self.failUnless(result[1][1].startswith('auth_tkt=')) - - self.assertEqual(result[2][0], 'Set-Cookie') - self.failUnless(result[2][1].endswith('; HttpOnly')) - self.failUnless(result[2][1].startswith('auth_tkt=')) - - def test_remember_secure(self): - plugin = self._makeOne('secret', include_ip=True, secure=True) - request = self._makeRequest() - result = plugin.remember(request, 'other') - self.assertEqual(len(result), 3) - - self.assertEqual(result[0][0], 'Set-Cookie') - self.failUnless('; Secure' in result[0][1]) - self.failUnless(result[0][1].startswith('auth_tkt=')) - - self.assertEqual(result[1][0], 'Set-Cookie') - self.failUnless('; Secure' in result[1][1]) - self.failUnless(result[1][1].startswith('auth_tkt=')) - - self.assertEqual(result[2][0], 'Set-Cookie') - self.failUnless('; Secure' in result[2][1]) - self.failUnless(result[2][1].startswith('auth_tkt=')) - - def test_remember_string_userid(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.remember(request, 'userid') - values = self._parseHeaders(result) - self.assertEqual(len(result), 3) - val = self._cookieValue(values[0]) - self.assertEqual(val['userid'], 'userid'.encode('base64').strip()) - self.assertEqual(val['user_data'], 'userid_type:b64str') - - def test_remember_int_userid(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.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): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.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): - plugin = self._makeOne('secret') - request = self._makeRequest() - userid = unicode('\xc2\xa9', 'utf-8') - result = plugin.remember(request, userid) - values = self._parseHeaders(result) - self.assertEqual(len(result), 3) - val = self._cookieValue(values[0]) - self.assertEqual(val['userid'], - userid.encode('utf-8').encode('base64').strip()) - self.assertEqual(val['user_data'], 'userid_type:b64unicode') - - def test_remember_max_age(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - result = plugin.remember(request, 'userid', max_age='500') - values = self._parseHeaders(result) - self.assertEqual(len(result), 3) - - self.assertEqual(values[0]['max-age'], '500') - self.failUnless(values[0]['expires']) - - def test_forget(self): - plugin = self._makeOne('secret') - request = self._makeRequest() - headers = plugin.forget(request) - self.assertEqual(len(headers), 3) - name, value = headers[0] - self.assertEqual(name, 'Set-Cookie') - self.assertEqual(value, - 'auth_tkt=""; Path=/; Max-Age=0; Expires=Wed, 31-Dec-97 23:59:59 GMT') - name, value = headers[1] - self.assertEqual(name, 'Set-Cookie') - self.assertEqual(value, - 'auth_tkt=""; Path=/; Domain=localhost; Max-Age=0; ' - 'Expires=Wed, 31-Dec-97 23:59:59 GMT') - name, value = headers[2] - self.assertEqual(name, 'Set-Cookie') - self.assertEqual(value, - 'auth_tkt=""; Path=/; Domain=.localhost; Max-Age=0; ' - 'Expires=Wed, 31-Dec-97 23:59:59 GMT') - - def test_timeout_lower_than_reissue(self): - self.assertRaises(ValueError, self._makeOne, 'userid', timeout=1, - reissue_time=2) - -class DummyContext: - pass - -class DummyRequest: - def __init__(self, environ): - self.environ = environ - self.callbacks = [] - - 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): - self.timestamp = timestamp - self.userid = userid - self.tokens = tokens - self.user_data = user_data - self.parse_raise = parse_raise - def parse_ticket(secret, value, remote_addr): - self.secret = secret - self.value = value - self.remote_addr = remote_addr - if self.parse_raise: - raise self.BadTicket() - return self.timestamp, self.userid, self.tokens, self.user_data - self.parse_ticket = parse_ticket - - class AuthTicket(object): - def __init__(self, secret, userid, remote_addr, **kw): - self.secret = secret - self.userid = userid - self.remote_addr = remote_addr - self.kw = kw - - def cookie_value(self): - result = {'secret':self.secret, 'userid':self.userid, - 'remote_addr':self.remote_addr} - result.update(self.kw) - result = repr(result) - return result - self.AuthTicket = AuthTicket - - class BadTicket(Exception): - pass - -class DummyResponse: - def __init__(self): - self.headerlist = [] - diff --git a/repoze/bfg/tests/test_authorization.py b/repoze/bfg/tests/test_authorization.py deleted file mode 100644 index 6b8c8293a..000000000 --- a/repoze/bfg/tests/test_authorization.py +++ /dev/null @@ -1,189 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp - -class TestACLAuthorizationPolicy(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _getTargetClass(self): - from repoze.bfg.authorization import ACLAuthorizationPolicy - return ACLAuthorizationPolicy - - def _makeOne(self): - return self._getTargetClass()() - - def test_class_implements_IAuthorizationPolicy(self): - from zope.interface.verify import verifyClass - from repoze.bfg.interfaces import IAuthorizationPolicy - verifyClass(IAuthorizationPolicy, self._getTargetClass()) - - def test_instance_implements_IAuthorizationPolicy(self): - from zope.interface.verify import verifyObject - from repoze.bfg.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 repoze.bfg.security import Deny - from repoze.bfg.security import Allow - from repoze.bfg.security import Everyone - from repoze.bfg.security import Authenticated - from repoze.bfg.security import ALL_PERMISSIONS - from repoze.bfg.security import DENY_ALL - root = DummyContext() - community = DummyContext(__name__='community', __parent__=root) - blog = DummyContext(__name__='blog', __parent__=community) - root.__acl__ = [ - (Allow, Authenticated, VIEW), - ] - community.__acl__ = [ - (Allow, 'fred', ALL_PERMISSIONS), - (Allow, 'wilma', VIEW), - DENY_ALL, - ] - blog.__acl__ = [ - (Allow, 'barney', MEMBER_PERMS), - (Allow, 'wilma', VIEW), - ] - - policy = self._makeOne() - - result = policy.permits(blog, [Everyone, Authenticated, 'wilma'], - 'view') - self.assertEqual(result, True) - self.assertEqual(result.context, blog) - self.assertEqual(result.ace, (Allow, 'wilma', VIEW)) - self.assertEqual(result.acl, blog.__acl__) - - result = policy.permits(blog, [Everyone, Authenticated, 'wilma'], - 'delete') - self.assertEqual(result, False) - self.assertEqual(result.context, community) - self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) - self.assertEqual(result.acl, community.__acl__) - - result = policy.permits(blog, [Everyone, Authenticated, 'fred'], 'view') - self.assertEqual(result, True) - self.assertEqual(result.context, community) - self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) - result = policy.permits(blog, [Everyone, Authenticated, 'fred'], - 'doesntevenexistyet') - self.assertEqual(result, True) - self.assertEqual(result.context, community) - self.assertEqual(result.ace, (Allow, 'fred', ALL_PERMISSIONS)) - self.assertEqual(result.acl, community.__acl__) - - result = policy.permits(blog, [Everyone, Authenticated, 'barney'], - 'view') - self.assertEqual(result, True) - self.assertEqual(result.context, blog) - self.assertEqual(result.ace, (Allow, 'barney', MEMBER_PERMS)) - result = policy.permits(blog, [Everyone, Authenticated, 'barney'], - 'administer') - self.assertEqual(result, False) - self.assertEqual(result.context, community) - self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) - self.assertEqual(result.acl, community.__acl__) - - result = policy.permits(root, [Everyone, Authenticated, 'someguy'], - 'view') - self.assertEqual(result, True) - self.assertEqual(result.context, root) - self.assertEqual(result.ace, (Allow, Authenticated, VIEW)) - result = policy.permits(blog, - [Everyone, Authenticated, 'someguy'], 'view') - self.assertEqual(result, False) - self.assertEqual(result.context, community) - self.assertEqual(result.ace, (Deny, Everyone, ALL_PERMISSIONS)) - self.assertEqual(result.acl, community.__acl__) - - result = policy.permits(root, [Everyone], 'view') - self.assertEqual(result, False) - self.assertEqual(result.context, root) - self.assertEqual(result.ace, '') - self.assertEqual(result.acl, root.__acl__) - - context = DummyContext() - result = policy.permits(context, [Everyone], 'view') - self.assertEqual(result, False) - self.assertEqual(result.ace, '') - self.assertEqual( - result.acl, - '') - - def test_principals_allowed_by_permission_direct(self): - from repoze.bfg.security import Allow - from repoze.bfg.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(self): - from repoze.bfg.security import Allow - from repoze.bfg.security import Deny - from repoze.bfg.security import DENY_ALL - from repoze.bfg.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, []) - -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/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py deleted file mode 100644 index d9cefbd67..000000000 --- a/repoze/bfg/tests/test_chameleon_text.py +++ /dev/null @@ -1,198 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp - -class Base: - def setUp(self): - cleanUp() - import os - try: - # avoid spew from chameleon logger? - os.unlink(self._getTemplatePath('minimal.txt.py')) - except: - pass - - def tearDown(self): - cleanUp() - - def _getTemplatePath(self, name): - import os - here = os.path.abspath(os.path.dirname(__file__)) - return os.path.join(here, 'fixtures', name) - - def _registerUtility(self, utility, iface, name=''): - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - reg.registerUtility(utility, iface, name=name) - return reg - - -class TextTemplateRendererTests(Base, unittest.TestCase): - def setUp(self): - from repoze.bfg.configuration import Configurator - from repoze.bfg.registry import Registry - registry = Registry() - self.config = Configurator(registry=registry) - self.config.begin() - - def tearDown(self): - self.config.end() - - def _getTargetClass(self): - from repoze.bfg.chameleon_text import TextTemplateRenderer - return TextTemplateRenderer - - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) - - def test_instance_implements_ITemplate(self): - from zope.interface.verify import verifyObject - from repoze.bfg.interfaces import ITemplateRenderer - path = self._getTemplatePath('minimal.txt') - verifyObject(ITemplateRenderer, self._makeOne(path)) - - def test_class_implements_ITemplate(self): - from zope.interface.verify import verifyClass - from repoze.bfg.interfaces import ITemplateRenderer - verifyClass(ITemplateRenderer, self._getTargetClass()) - - def test_template_reified(self): - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template, instance.__dict__['template']) - - def test_template_with_ichameleon_translate(self): - from repoze.bfg.interfaces import IChameleonTranslate - def ct(): pass - self.config.registry.registerUtility(ct, IChameleonTranslate) - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.translate, ct) - - def test_template_with_debug_templates(self): - self.config.add_settings({'debug_templates':True}) - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.debug, True) - - def test_template_with_reload_templates(self): - self.config.add_settings({'reload_templates':True}) - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.auto_reload, True) - - def test_template_with_emptydict(self): - from repoze.bfg.interfaces import ISettings - self.config.registry.registerUtility({}, ISettings) - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.auto_reload, False) - self.assertEqual(template.debug, False) - - def test_call(self): - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - result = instance({}, {}) - self.failUnless(isinstance(result, str)) - self.assertEqual(result, 'Hello.\n') - - def test_call_with_nondict_value(self): - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - self.assertRaises(ValueError, instance, None, {}) - - def test_call_nonminimal(self): - nonminimal = self._getTemplatePath('nonminimal.txt') - instance = self._makeOne(nonminimal) - result = instance({'name':'Chris'}, {}) - self.failUnless(isinstance(result, str)) - self.assertEqual(result, 'Hello, Chris!\n') - - def test_implementation(self): - minimal = self._getTemplatePath('minimal.txt') - instance = self._makeOne(minimal) - result = instance.implementation()() - self.failUnless(isinstance(result, str)) - self.assertEqual(result, 'Hello.\n') - -class RenderTemplateTests(Base, unittest.TestCase): - def _callFUT(self, name, **kw): - from repoze.bfg.chameleon_text import render_template - return render_template(name, **kw) - - def test_it(self): - minimal = self._getTemplatePath('minimal.txt') - result = self._callFUT(minimal) - self.failUnless(isinstance(result, str)) - self.assertEqual(result, 'Hello.\n') - -class RenderTemplateToResponseTests(Base, unittest.TestCase): - def _callFUT(self, name, **kw): - from repoze.bfg.chameleon_text import render_template_to_response - return render_template_to_response(name, **kw) - - def test_minimal(self): - minimal = self._getTemplatePath('minimal.txt') - result = self._callFUT(minimal) - from webob import Response - self.failUnless(isinstance(result, Response)) - self.assertEqual(result.app_iter, ['Hello.\n']) - self.assertEqual(result.status, '200 OK') - self.assertEqual(len(result.headerlist), 2) - - def test_iresponsefactory_override(self): - from webob import Response - class Response2(Response): - pass - from repoze.bfg.interfaces import IResponseFactory - self._registerUtility(Response2, IResponseFactory) - minimal = self._getTemplatePath('minimal.txt') - result = self._callFUT(minimal) - self.failUnless(isinstance(result, Response2)) - -class GetRendererTests(Base, unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.chameleon_text import get_renderer - return get_renderer(name) - - def test_it(self): - from repoze.bfg.interfaces import IRendererFactory - class Dummy: - template = object() - def implementation(self): pass - renderer = Dummy() - def rf(spec): - return renderer - self._registerUtility(rf, IRendererFactory, name='foo') - result = self._callFUT('foo') - self.failUnless(result is renderer) - -class GetTemplateTests(Base, unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.chameleon_text import get_template - return get_template(name) - - def test_it(self): - from repoze.bfg.interfaces import IRendererFactory - class Dummy: - template = object() - def implementation(self): - return self.template - renderer = Dummy() - def rf(spec): - return renderer - self._registerUtility(rf, IRendererFactory, name='foo') - result = self._callFUT('foo') - self.failUnless(result is renderer.template) - diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py deleted file mode 100644 index a0f01701a..000000000 --- a/repoze/bfg/tests/test_chameleon_zpt.py +++ /dev/null @@ -1,194 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp - -class Base(object): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _getTemplatePath(self, name): - import os - here = os.path.abspath(os.path.dirname(__file__)) - return os.path.join(here, 'fixtures', name) - - def _registerUtility(self, utility, iface, name=''): - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - reg.registerUtility(utility, iface, name=name) - return reg - -class ZPTTemplateRendererTests(Base, unittest.TestCase): - def setUp(self): - from repoze.bfg.configuration import Configurator - from repoze.bfg.registry import Registry - registry = Registry() - self.config = Configurator(registry=registry) - self.config.begin() - - def tearDown(self): - self.config.end() - - def _getTargetClass(self): - from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer - return ZPTTemplateRenderer - - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) - - def test_instance_implements_ITemplate(self): - from zope.interface.verify import verifyObject - from repoze.bfg.interfaces import ITemplateRenderer - path = self._getTemplatePath('minimal.pt') - verifyObject(ITemplateRenderer, self._makeOne(path)) - - def test_class_implements_ITemplate(self): - from zope.interface.verify import verifyClass - from repoze.bfg.interfaces import ITemplateRenderer - verifyClass(ITemplateRenderer, self._getTargetClass()) - - def test_call(self): - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - result = instance({}, {}) - self.failUnless(isinstance(result, unicode)) - self.assertEqual(result, - '
\n
') - - def test_template_reified(self): - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template, instance.__dict__['template']) - - def test_template_with_ichameleon_translate(self): - from repoze.bfg.interfaces import IChameleonTranslate - def ct(): pass - self.config.registry.registerUtility(ct, IChameleonTranslate) - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.translate, ct) - - def test_template_with_debug_templates(self): - self.config.add_settings({'debug_templates':True}) - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.debug, True) - - def test_template_with_reload_templates(self): - self.config.add_settings({'reload_templates':True}) - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.auto_reload, True) - - def test_template_with_emptydict(self): - from repoze.bfg.interfaces import ISettings - self.config.registry.registerUtility({}, ISettings) - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.failIf('template' in instance.__dict__) - template = instance.template - self.assertEqual(template.auto_reload, False) - self.assertEqual(template.debug, False) - - def test_call_with_nondict_value(self): - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - self.assertRaises(ValueError, instance, None, {}) - - def test_implementation(self): - minimal = self._getTemplatePath('minimal.pt') - instance = self._makeOne(minimal) - result = instance.implementation()() - self.failUnless(isinstance(result, unicode)) - self.assertEqual(result, - '
\n
') - - -class RenderTemplateTests(Base, unittest.TestCase): - def _callFUT(self, name, **kw): - from repoze.bfg.chameleon_zpt import render_template - return render_template(name, **kw) - - def test_it(self): - minimal = self._getTemplatePath('minimal.pt') - result = self._callFUT(minimal) - self.failUnless(isinstance(result, unicode)) - self.assertEqual(result, - '
\n
') - -class RenderTemplateToResponseTests(Base, unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, name, **kw): - from repoze.bfg.chameleon_zpt import render_template_to_response - return render_template_to_response(name, **kw) - - def test_it(self): - minimal = self._getTemplatePath('minimal.pt') - result = self._callFUT(minimal) - from webob import Response - self.failUnless(isinstance(result, Response)) - self.assertEqual(result.app_iter, - ['
\n
']) - self.assertEqual(result.status, '200 OK') - self.assertEqual(len(result.headerlist), 2) - - def test_iresponsefactory_override(self): - from webob import Response - class Response2(Response): - pass - from repoze.bfg.interfaces import IResponseFactory - self._registerUtility(Response2, IResponseFactory) - minimal = self._getTemplatePath('minimal.pt') - result = self._callFUT(minimal) - self.failUnless(isinstance(result, Response2)) - -class GetRendererTests(Base, unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.chameleon_zpt import get_renderer - return get_renderer(name) - - def test_it(self): - from repoze.bfg.interfaces import IRendererFactory - class Dummy: - template = object() - def implementation(self): pass - renderer = Dummy() - def rf(spec): - return renderer - self._registerUtility(rf, IRendererFactory, name='foo') - result = self._callFUT('foo') - self.failUnless(result is renderer) - -class GetTemplateTests(Base, unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.chameleon_zpt import get_template - return get_template(name) - - def test_it(self): - from repoze.bfg.interfaces import IRendererFactory - class Dummy: - template = object() - def implementation(self): - return self.template - renderer = Dummy() - def rf(spec): - return renderer - self._registerUtility(rf, IRendererFactory, name='foo') - result = self._callFUT('foo') - self.failUnless(result is renderer.template) diff --git a/repoze/bfg/tests/test_compat.py b/repoze/bfg/tests/test_compat.py deleted file mode 100644 index 66ea61860..000000000 --- a/repoze/bfg/tests/test_compat.py +++ /dev/null @@ -1,9 +0,0 @@ -import unittest - -class TestAliases(unittest.TestCase): - def test_all(self): - from repoze.bfg.compat import all - self.assertEqual(all([True, True]), True) - self.assertEqual(all([False, False]), False) - self.assertEqual(all([False, True]), False) - diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py deleted file mode 100644 index 47649d6ba..000000000 --- a/repoze/bfg/tests/test_configuration.py +++ /dev/null @@ -1,4335 +0,0 @@ -import unittest - -from repoze.bfg import testing - -class ConfiguratorTests(unittest.TestCase): - def _makeOne(self, *arg, **kw): - from repoze.bfg.configuration import Configurator - return Configurator(*arg, **kw) - - def _registerRenderer(self, config, name='.txt'): - from repoze.bfg.interfaces import IRendererFactory - from repoze.bfg.interfaces import ITemplateRenderer - from zope.interface import implements - class Renderer: - implements(ITemplateRenderer) - def __init__(self, path): - self.__class__.path = path - def __call__(self, *arg): - return 'Hello!' - config.registry.registerUtility(Renderer, IRendererFactory, name=name) - return Renderer - - def _getViewCallable(self, config, ctx_iface=None, request_iface=None, - name='', exception_view=False): - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - if exception_view: - classifier = IExceptionViewClassifier - else: - classifier = IViewClassifier - if ctx_iface is None: - ctx_iface = Interface - if request_iface is None: - request_iface = IRequest - return config.registry.adapters.lookup( - (classifier, request_iface, ctx_iface), IView, name=name, - default=None) - - def _getRouteRequestIface(self, config, name): - from repoze.bfg.interfaces import IRouteRequest - iface = config.registry.getUtility(IRouteRequest, name) - return iface - - def _assertNotFound(self, wrapper, *arg): - from repoze.bfg.exceptions import NotFound - self.assertRaises(NotFound, wrapper, *arg) - - 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 _registerLogger(self, config): - from repoze.bfg.interfaces import IDebugLogger - logger = DummyLogger() - config.registry.registerUtility(logger, IDebugLogger) - return logger - - def _makeRequest(self, config): - request = DummyRequest() - request.registry = config.registry - return request - - def _registerSecurityPolicy(self, config, permissive): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.interfaces import IAuthorizationPolicy - policy = DummySecurityPolicy(permissive) - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - - def _registerSettings(self, config, **settings): - from repoze.bfg.interfaces import ISettings - config.registry.registerUtility(settings, ISettings) - - def test_ctor_no_registry(self): - import sys - from repoze.bfg.interfaces import ISettings - from repoze.bfg.configuration import Configurator - from repoze.bfg.interfaces import IRendererFactory - config = Configurator() - this_pkg = sys.modules['repoze.bfg.tests'] - self.failUnless(config.registry.getUtility(ISettings)) - self.assertEqual(config.package, this_pkg) - self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) - self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.txt')) - - def test_begin(self): - from repoze.bfg.configuration 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 repoze.bfg.configuration import Configurator - config = Configurator() - request = object() - manager = DummyThreadLocalManager() - config.manager = manager - config.begin(request=request) - self.assertEqual(manager.pushed, - {'registry':config.registry, 'request':request}) - self.assertEqual(manager.popped, False) - - def test_end(self): - from repoze.bfg.configuration import Configurator - config = Configurator() - manager = DummyThreadLocalManager() - config.manager = manager - config.end() - self.assertEqual(manager.pushed, None) - self.assertEqual(manager.popped, True) - - def test_ctor_with_package_registry(self): - import sys - from repoze.bfg.configuration import Configurator - bfg_pkg = sys.modules['repoze.bfg'] - config = Configurator(package=bfg_pkg) - self.assertEqual(config.package, bfg_pkg) - - def test_ctor_noreg_custom_settings(self): - from repoze.bfg.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 repoze.bfg.interfaces import IDebugLogger - config = self._makeOne() - logger = config.registry.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'repoze.bfg.debug') - - def test_ctor_noreg_debug_logger_non_None(self): - from repoze.bfg.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 repoze.bfg.interfaces import IAuthenticationPolicy - policy = object() - config = self._makeOne(authentication_policy=policy) - result = config.registry.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_ctor_authorization_policy_only(self): - from repoze.bfg.exceptions import ConfigurationError - policy = object() - self.assertRaises(ConfigurationError, - self._makeOne, authorization_policy=policy) - - def test_ctor_no_root_factory(self): - from repoze.bfg.interfaces import IRootFactory - config = self._makeOne() - self.failUnless(config.registry.getUtility(IRootFactory)) - - def test_ctor_alternate_renderers(self): - from repoze.bfg.interfaces import IRendererFactory - renderer = object() - config = self._makeOne(renderers=[('yeah', renderer)]) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_ctor_default_permission(self): - from repoze.bfg.interfaces import IDefaultPermission - config = self._makeOne(default_permission='view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') - - def test_with_package_module(self): - from repoze.bfg.tests import test_configuration - import repoze.bfg.tests - config = self._makeOne() - newconfig = config.with_package(test_configuration) - self.assertEqual(newconfig.package, repoze.bfg.tests) - - def test_with_package_package(self): - import repoze.bfg.tests - config = self._makeOne() - newconfig = config.with_package(repoze.bfg.tests) - self.assertEqual(newconfig.package, repoze.bfg.tests) - - def test_maybe_dotted_string_success(self): - import repoze.bfg.tests - config = self._makeOne() - result = config.maybe_dotted('repoze.bfg.tests') - self.assertEqual(result, repoze.bfg.tests) - - def test_maybe_dotted_string_fail(self): - from repoze.bfg.configuration import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.maybe_dotted, 'cant.be.found') - - def test_maybe_dotted_notstring_success(self): - import repoze.bfg.tests - config = self._makeOne() - result = config.maybe_dotted(repoze.bfg.tests) - self.assertEqual(result, repoze.bfg.tests) - - def test_absolute_resource_spec_already_absolute(self): - import repoze.bfg.tests - config = self._makeOne(package=repoze.bfg.tests) - result = config.absolute_resource_spec('already:absolute') - self.assertEqual(result, 'already:absolute') - - def test_absolute_resource_spec_notastring(self): - import repoze.bfg.tests - config = self._makeOne(package=repoze.bfg.tests) - result = config.absolute_resource_spec(None) - self.assertEqual(result, None) - - def test_absolute_resource_spec_relative(self): - import repoze.bfg.tests - config = self._makeOne(package=repoze.bfg.tests) - result = config.absolute_resource_spec('templates') - self.assertEqual(result, 'repoze.bfg.tests:templates') - - def test_setup_registry_fixed(self): - class DummyRegistry(object): - def subscribers(self, events, name): - self.events = events - return events - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - config.add_view = lambda *arg, **kw: False - config.setup_registry() - self.assertEqual(reg.has_listeners, True) - self.assertEqual(reg.notify(1), None) - self.assertEqual(reg.events, (1,)) - - def test_setup_registry_registers_default_exceptionresponse_view(self): - from repoze.bfg.interfaces import IExceptionResponse - from repoze.bfg.view import default_exceptionresponse_view - class DummyRegistry(object): - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - views = [] - config.add_view = lambda *arg, **kw: views.append((arg, kw)) - config.setup_registry() - self.assertEqual(views[0], ((default_exceptionresponse_view,), - {'context':IExceptionResponse})) - - def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.exceptions import NotFound - from repoze.bfg.registry import Registry - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() # registers IExceptionResponse default view - def myview(context, request): - return 'OK' - config.add_view(myview, context=NotFound) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_setup_registry_custom_settings(self): - from repoze.bfg.registry import Registry - from repoze.bfg.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 repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - logger = reg.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'repoze.bfg.debug') - - def test_setup_registry_debug_logger_non_None(self): - from repoze.bfg.registry import Registry - from repoze.bfg.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_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(debug_logger='repoze.bfg.tests') - result = reg.getUtility(IDebugLogger) - import repoze.bfg.tests - self.assertEqual(result, repoze.bfg.tests) - - def test_setup_registry_authentication_policy(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IAuthenticationPolicy - policy = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy=policy) - result = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_setup_registry_authentication_policy_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IAuthenticationPolicy - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy='repoze.bfg.tests') - result = reg.getUtility(IAuthenticationPolicy) - import repoze.bfg.tests - self.assertEqual(result, repoze.bfg.tests) - - def test_setup_registry_authorization_policy_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IAuthorizationPolicy - reg = Registry() - config = self._makeOne(reg) - dummy = object() - config.setup_registry(authentication_policy=dummy, - authorization_policy='repoze.bfg.tests') - result = reg.getUtility(IAuthorizationPolicy) - import repoze.bfg.tests - self.assertEqual(result, repoze.bfg.tests) - - def test_setup_registry_authorization_policy_only(self): - from repoze.bfg.registry import Registry - from repoze.bfg.exceptions import ConfigurationError - policy = object() - reg = Registry() - config = self._makeOne(reg) - config = self.assertRaises(ConfigurationError, - config.setup_registry, - authorization_policy=policy) - - def test_setup_registry_default_root_factory(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - self.failUnless(reg.getUtility(IRootFactory)) - - def test_setup_registry_dottedname_root_factory(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - import repoze.bfg.tests - config.setup_registry(root_factory='repoze.bfg.tests') - self.assertEqual(reg.getUtility(IRootFactory), repoze.bfg.tests) - - def test_setup_registry_locale_negotiator_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - import repoze.bfg.tests - config.setup_registry(locale_negotiator='repoze.bfg.tests') - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, repoze.bfg.tests) - - def test_setup_registry_locale_negotiator(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - negotiator = object() - config.setup_registry(locale_negotiator=negotiator) - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, negotiator) - - def test_setup_registry_request_factory(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(request_factory=factory) - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_request_factory_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - import repoze.bfg.tests - config.setup_registry(request_factory='repoze.bfg.tests') - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, repoze.bfg.tests) - - def test_setup_registry_renderer_globals_factory(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(renderer_globals_factory=factory) - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_renderer_globals_factory_dottedname(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - import repoze.bfg.tests - config.setup_registry(renderer_globals_factory='repoze.bfg.tests') - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, repoze.bfg.tests) - - def test_setup_registry_alternate_renderers(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IRendererFactory - renderer = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(renderers=[('yeah', renderer)]) - self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_setup_registry_default_permission(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import IDefaultPermission - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(default_permission='view') - self.assertEqual(reg.getUtility(IDefaultPermission), 'view') - - def test_get_settings_nosettings(self): - from repoze.bfg.registry import Registry - reg = Registry() - config = self._makeOne(reg) - self.assertEqual(config.get_settings(), None) - - def test_get_settings_withsettings(self): - from repoze.bfg.interfaces import ISettings - settings = {'a':1} - config = self._makeOne() - config.registry.registerUtility(settings, ISettings) - self.assertEqual(config.get_settings(), settings) - - def test_add_settings_settings_already_registered(self): - from repoze.bfg.registry import Registry - from repoze.bfg.interfaces import ISettings - reg = Registry() - config = self._makeOne(reg) - config._set_settings({'a':1}) - config.add_settings({'b':2}) - settings = reg.getUtility(ISettings) - self.assertEqual(settings['a'], 1) - self.assertEqual(settings['b'], 2) - - def test_add_settings_settings_not_yet_registered(self): - from repoze.bfg.registry import Registry - from repoze.bfg.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_subscriber_defaults(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne() - 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 implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne() - 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 repoze.bfg.tests - from repoze.bfg.interfaces import INewRequest - config = self._makeOne() - config.add_subscriber('repoze.bfg.tests', - 'repoze.bfg.interfaces.INewRequest') - handlers = list(config.registry.registeredHandlers()) - self.assertEqual(len(handlers), 1) - handler = handlers[0] - self.assertEqual(handler.handler, repoze.bfg.tests) - self.assertEqual(handler.required, (INewRequest,)) - - def test_add_object_event_subscriber(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - object = 'foo' - implements(IEvent) - event = Event() - L = [] - def subscriber(object, event): - L.append(event) - config = self._makeOne() - 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_make_wsgi_app(self): - from repoze.bfg.router import Router - from repoze.bfg.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.failUnless(manager.popped) - self.assertEqual(len(subscriber), 1) - self.failUnless(IApplicationCreated.providedBy(subscriber[0])) - - def test_load_zcml_default(self): - import repoze.bfg.tests.fixtureapp - config = self._makeOne(package=repoze.bfg.tests.fixtureapp) - registry = config.load_zcml() - from repoze.bfg.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_routesapp(self): - from repoze.bfg.interfaces import IRoutesMapper - config = self._makeOne() - config.load_zcml('repoze.bfg.tests.routesapp:configure.zcml') - self.failUnless(config.registry.getUtility(IRoutesMapper)) - - def test_load_zcml_fixtureapp(self): - from repoze.bfg.tests.fixtureapp.models import IFixture - config = self._makeOne() - config.load_zcml('repoze.bfg.tests.fixtureapp:configure.zcml') - self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_relative_filename(self): - import repoze.bfg.tests.fixtureapp - config = self._makeOne(package=repoze.bfg.tests.fixtureapp) - registry = config.load_zcml('configure.zcml') - from repoze.bfg.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_absolute_filename(self): - import os - import repoze.bfg.tests.fixtureapp - config = self._makeOne(package=repoze.bfg.tests.fixtureapp) - dn = os.path.dirname(repoze.bfg.tests.fixtureapp.__file__) - c_z = os.path.join(dn, 'configure.zcml') - registry = config.load_zcml(c_z) - from repoze.bfg.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_lock_and_unlock(self): - config = self._makeOne() - dummylock = DummyLock() - config.load_zcml( - 'repoze.bfg.tests.fixtureapp:configure.zcml', - lock=dummylock) - self.assertEqual(dummylock.acquired, True) - self.assertEqual(dummylock.released, True) - - def test_add_view_view_callable_None_no_renderer(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_view) - - def test_add_view_with_request_type_and_route_name(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - view = lambda *arg: 'OK' - self.assertRaises(ConfigurationError, config.add_view, view, '', None, - None, True, True) - - def test_add_view_with_request_type_methodname_string(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_type='GET') - wrapper = self._getViewCallable(config) - request = DummyRequest() - request.method = 'POST' - self._assertNotFound(wrapper, None, request) - request = DummyRequest() - request.method = 'GET' - result = wrapper(None, request) - self.assertEqual(result, 'OK') - - def test_add_view_with_request_type(self): - from zope.interface import directlyProvides - from repoze.bfg.interfaces import IRequest - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, - request_type='repoze.bfg.interfaces.IRequest') - 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() - self._registerRenderer(config, name='dummy') - config.add_view(renderer='dummy') - view = self._getViewCallable(config) - self.failUnless('Hello!' in view(None, None).body) - - def test_add_view_wrapped_view_is_decorated(self): - def view(request): # request-only wrapper - """ """ - config = self._makeOne() - 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__) - - def test_add_view_view_callable_dottedname(self): - config = self._makeOne() - config.add_view(view='repoze.bfg.tests.test_configuration.dummy_view') - wrapper = self._getViewCallable(config) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_function_callable(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_with_function_callable_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance(self): - class AView: - def __call__(self, context, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance_requestonly(self): - class AView: - def __call__(self, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class(self): - class view: - def __init__(self, context, request): - self.context = context - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_context_as_class(self): - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne() - config.add_view(context=Foo, view=view) - foo = implementedBy(Foo) - wrapper = self._getViewCallable(config, foo) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_iface(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(context=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(context='repoze.bfg.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for__as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(for_='repoze.bfg.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for_as_class(self): - # ``for_`` is older spelling for ``context`` - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne() - config.add_view(for_=Foo, view=view) - 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`` - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(for_=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_trumps_for(self): - # ``for_`` is older spelling for ``context`` - view = lambda *arg: 'OK' - config = self._makeOne() - class Foo: - pass - config.add_view(context=IDummy, for_=Foo, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_register_secured_view(self): - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import ISecuredView - from repoze.bfg.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne() - config.add_view(view=view) - 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 zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne() - config.add_view(view=view, context=RuntimeError) - 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 repoze.bfg.compat import md5 - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, xhr=True) - wrapper = self._getViewCallable(config) - self.failIf(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 repoze.bfg.compat import md5 - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne() - 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) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(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 zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(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 zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(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 repoze.bfg.configuration import DEFAULT_PHASH - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(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 repoze.bfg.configuration import DEFAULT_PHASH - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne() - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(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 zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - 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) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_multiview_replaces_existing_securedview(self): - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import ISecuredView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - ISecuredView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_securedview(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import ISecuredView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - 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) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_accept_multiview_replaces_existing_view(self): - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2, accept='text/html') - wrapper = self._getViewCallable(config) - self.failUnless(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_exc_with_accept_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne() - 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) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(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 zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.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() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(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 zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.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() - 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) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(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 zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - view = DummyMultiView() - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_exc_multiview_replaces_multiview(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - view = DummyMultiView() - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_multiview_context_superclass_then_subclass(self): - from zope.interface import Interface - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - class ISuper(Interface): - pass - class ISub(ISuper): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, ISuper), IView, name='') - config.add_view(view=view2, for_=ISub) - wrapper = self._getViewCallable(config, ISuper, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - wrapper = self._getViewCallable(config, ISub, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK2') - - def test_add_view_multiview_exception_superclass_then_subclass(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IMultiView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - class Super(Exception): - pass - class Sub(Super): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne() - 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) - wrapper = self._getViewCallable( - config, implementedBy(Super), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Super), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK') - wrapper = self._getViewCallable( - config, implementedBy(Sub), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Sub), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK2') - - def test_add_view_multiview_call_ordering(self): - 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() - config.add_view(view=view1) - config.add_view(view=view2, request_method='POST') - config.add_view(view=view3,request_param='param') - config.add_view(view=view4, containment=IDummy) - config.add_view(view=view5, request_method='POST',request_param='param') - config.add_view(view=view6, request_method='POST', containment=IDummy) - config.add_view(view=view7, request_param='param', containment=IDummy) - config.add_view(view=view8, request_method='POST',request_param='param', - containment=IDummy) - - wrapper = self._getViewCallable(config) - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view1') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view2') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {'param':'1'} - request.method = 'GET' - self.assertEqual(wrapper(ctx, request), 'view3') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view4') - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view5') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view6') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view7') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view8') - - def test_add_view_with_template_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() - renderer = self._registerRenderer(config) - fixture = 'repoze.bfg.tests:fixtures/minimal.txt' - config.add_view(view=view, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - self.assertEqual(renderer.path, 'repoze.bfg.tests:fixtures/minimal.txt') - - def test_add_view_with_template_renderer_no_callable(self): - config = self._makeOne() - renderer = self._registerRenderer(config) - fixture = 'repoze.bfg.tests:fixtures/minimal.txt' - config.add_view(view=None, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - self.assertEqual(renderer.path, 'repoze.bfg.tests:fixtures/minimal.txt') - - def test_add_view_with_request_type_as_iface(self): - from zope.interface import directlyProvides - def view(context, request): - return 'OK' - config = self._makeOne() - config.add_view(request_type=IDummy, view=view) - 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): - from repoze.bfg.exceptions import ConfigurationError - 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 zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo') - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper = self._getViewCallable(config, None) - self.assertEqual(wrapper, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper = self._getViewCallable(config, request_iface=request_iface) - self.failIfEqual(wrapper, None) - self.assertEqual(wrapper(None, None), 'OK') - - def test_deferred_route_views_retains_custom_predicates(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo', custom_predicates=('123',)) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['custom_predicates'], ('123',)) - - def test_add_view_with_route_name_exception(self): - from zope.interface import implementedBy - from zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo', context=RuntimeError) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - exception_view=True) - self.assertEqual(wrapper_exc_view, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_iface, exception_view=True) - self.failIfEqual(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): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_method='POST') - 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() - 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_param_noval_true(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc') - 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() - 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): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc=123') - 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() - 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): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, xhr=True) - 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() - 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): - from repoze.bfg.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, header='Host:a\\') - - def test_add_view_with_header_noval_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header='Host') - 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() - 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): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header=r'Host:\d') - 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() - 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 repoze.bfg.exceptions import NotFound - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'NoHost':'1'} - self.assertRaises(NotFound, wrapper, None, request) - - def test_add_view_with_accept_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_accept_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/html'] - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_containment_true(self): - from zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, containment=IDummy) - 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() - 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 zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view( - view=view, - containment='repoze.bfg.tests.test_configuration.IDummy') - wrapper = self._getViewCallable(config) - context = DummyContext() - directlyProvides(context, IDummy) - self.assertEqual(wrapper(context, None), 'OK') - - def test_add_view_with_path_info_badregex(self): - from repoze.bfg.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, path_info='\\') - - def test_add_view_with_path_info_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_path_info_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/' - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_custom_predicates_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - def pred1(context, request): - return True - def pred2(context, request): - return True - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_custom_predicates_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - def pred1(context, request): - return True - def pred2(context, request): - return False - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self._assertNotFound(wrapper, None, request) - - def test_add_view_custom_predicate_bests_standard_predicate(self): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne() - def pred1(context, request): - return True - config.add_view(view=view, custom_predicates=(pred1,)) - config.add_view(view=view2, request_method='GET') - 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): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne() - config.add_view(view=view, request_method='GET', xhr=True) - config.add_view(view=view2, request_method='GET') - 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) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'second') - - def test_add_view_with_permission(self): - 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) - config.add_view(view=view1, permission='view') - 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): - 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') - config.add_view(view=view1) - 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): - view1 = lambda *arg: 'OK' - class DummyPolicy(object): pass # wont be called - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy) - config.add_view(view=view1) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def _assertRoute(self, config, name, path, num_predicates=0): - from repoze.bfg.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_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 repoze.bfg.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() - route = config.add_route('name', 'path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_with_factory(self): - config = self._makeOne() - factory = object() - route = config.add_route('name', 'path', factory=factory) - self.assertEqual(route.factory, factory) - - def test_add_route_with_factory_dottedname(self): - config = self._makeOne() - route = config.add_route( - 'name', 'path', - factory='repoze.bfg.tests.test_configuration.dummyfactory') - self.assertEqual(route.factory, dummyfactory) - - def test_add_route_with_xhr(self): - config = self._makeOne() - 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() - 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() - config.add_route('name', 'path', path_info='/foo') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.path_info = '/' - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_request_param(self): - config = self._makeOne() - config.add_route('name', 'path', request_param='abc') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.params = {'abc':'123'} - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.params = {} - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_custom_predicates(self): - config = self._makeOne() - def pred1(context, request): pass - def pred2(context, request): pass - config.add_route('name', 'path', custom_predicates=(pred1, pred2)) - route = self._assertRoute(config, 'name', 'path', 2) - self.assertEqual(route.predicates, [pred1, pred2]) - - def test_add_route_with_header(self): - config = self._makeOne() - 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() - config.add_route('name', 'path', accept='text/xml') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.accept = ['text/html'] - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_view(self): - config = self._makeOne() - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - - def test_add_route_with_view_context(self): - config = self._makeOne() - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_exception(self): - from zope.interface import implementedBy - config = self._makeOne() - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=RuntimeError) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable( - config, ctx_iface=IOther, - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_for(self): - config = self._makeOne() - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_for=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_for_(self): - config = self._makeOne() - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, for_=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_renderer(self): - config = self._makeOne() - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - view_renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_attr(self): - config = self._makeOne() - self._registerRenderer(config) - class View(object): - def __init__(self, context, request): - pass - def alt(self): - return 'OK' - config.add_route('name', 'path', view=View, view_attr='alt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_route_with_view_renderer_alias(self): - config = self._makeOne() - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_permission(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.interfaces import IAuthorizationPolicy - config = self._makeOne() - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.failUnless(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_with_view_permission_alias(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.interfaces import IAuthorizationPolicy - config = self._makeOne() - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.failUnless(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_no_pattern_with_path(self): - config = self._makeOne() - route = config.add_route('name', path='path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_no_path_no_pattern(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_route, 'name') - - def test_add_route_with_pregenerator(self): - config = self._makeOne() - route = config.add_route('name', 'pattern', pregenerator='123') - self.assertEqual(route.pregenerator, '123') - - def test__override_not_yet_registered(self): - from repoze.bfg.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - config = self._makeOne() - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - overrides = config.registry.queryUtility(IPackageOverrides, - name='package') - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test__override_already_registered(self): - from repoze.bfg.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - overrides = DummyOverrides(package) - config = self._makeOne() - config.registry.registerUtility(overrides, IPackageOverrides, - name='package') - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test_add_static_here_no_utility_registered(self): - from repoze.bfg.static import PackageURLParser - from zope.interface import implementedBy - from repoze.bfg.static import StaticURLInfo - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - config = self._makeOne() - config.add_static_view('static', 'fixtures/static') - request_type = self._getRouteRequestIface(config, 'static/') - route = self._assertRoute(config, 'static/', 'static/*subpath') - self.assertEqual(route.factory.__class__, type(lambda x: x)) - iface = implementedBy(StaticURLInfo) - wrapped = config.registry.adapters.lookup( - (IViewClassifier, request_type, iface), IView, name='') - request = self._makeRequest(config) - self.assertEqual(wrapped(None, request).__class__, PackageURLParser) - - def test_add_static_view_package_relative(self): - from repoze.bfg.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - config.add_static_view('static', 'repoze.bfg.tests:fixtures/static') - self.assertEqual(info.added, - [('static', 'repoze.bfg.tests:fixtures/static', {})]) - - def test_add_static_view_package_here_relative(self): - from repoze.bfg.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - config.add_static_view('static', 'fixtures/static') - self.assertEqual(info.added, - [('static', 'repoze.bfg.tests:fixtures/static', {})]) - - def test_add_static_view_absolute(self): - import os - from repoze.bfg.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - here = os.path.dirname(__file__) - static_path = os.path.join(here, 'fixtures', 'static') - config.add_static_view('static', static_path) - self.assertEqual(info.added, - [('static', static_path, {})]) - - def test_set_notfound_view(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.exceptions import NotFound - config = self._makeOne() - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, (None, request)) - - def test_set_notfound_view_request_has_context(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.exceptions import NotFound - config = self._makeOne() - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test_set_forbidden_view(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.exceptions import Forbidden - config = self._makeOne() - view = lambda *arg: 'OK' - config.set_forbidden_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_set_forbidden_view_request_has_context(self): - from zope.interface import implementedBy - from repoze.bfg.interfaces import IRequest - from repoze.bfg.exceptions import Forbidden - config = self._makeOne() - view = lambda *arg: arg - config.set_forbidden_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test__set_authentication_policy(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - config = self._makeOne() - policy = object() - config._set_authentication_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthenticationPolicy), policy) - - def test__set_authorization_policy(self): - from repoze.bfg.interfaces import IAuthorizationPolicy - config = self._makeOne() - policy = object() - config._set_authorization_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthorizationPolicy), policy) - - def test_set_locale_negotiator(self): - from repoze.bfg.interfaces import ILocaleNegotiator - config = self._makeOne() - def negotiator(request): pass - config.set_locale_negotiator(negotiator) - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - negotiator) - - def test_set_locale_negotiator_dottedname(self): - from repoze.bfg.interfaces import ILocaleNegotiator - config = self._makeOne() - config.set_locale_negotiator( - 'repoze.bfg.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - dummyfactory) - - def test_set_request_factory(self): - from repoze.bfg.interfaces import IRequestFactory - config = self._makeOne() - factory = object() - config.set_request_factory(factory) - self.assertEqual(config.registry.getUtility(IRequestFactory), factory) - - def test_set_request_factory_dottedname(self): - from repoze.bfg.interfaces import IRequestFactory - config = self._makeOne() - config.set_request_factory( - 'repoze.bfg.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRequestFactory), - dummyfactory) - - def test_set_renderer_globals_factory(self): - from repoze.bfg.interfaces import IRendererGlobalsFactory - config = self._makeOne() - factory = object() - config.set_renderer_globals_factory(factory) - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - factory) - - def test_set_renderer_globals_factory_dottedname(self): - from repoze.bfg.interfaces import IRendererGlobalsFactory - config = self._makeOne() - config.set_renderer_globals_factory( - 'repoze.bfg.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - dummyfactory) - - def test_set_default_permission(self): - from repoze.bfg.interfaces import IDefaultPermission - config = self._makeOne() - config.set_default_permission('view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), - 'view') - - def test_add_translation_dirs_missing_dir(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_translation_dirs, - '/wont/exist/on/my/system') - - def test_add_translation_dirs_resource_spec(self): - import os - from repoze.bfg.interfaces import ITranslationDirectories - config = self._makeOne() - config.add_translation_dirs('repoze.bfg.tests.localeapp:locale') - here = os.path.dirname(__file__) - locale = os.path.join(here, 'localeapp', 'locale') - self.assertEqual(config.registry.getUtility(ITranslationDirectories), - [locale]) - - def test_add_translation_dirs_registers_chameleon_translate(self): - from repoze.bfg.interfaces import IChameleonTranslate - from repoze.bfg.threadlocal import manager - request = DummyRequest() - config = self._makeOne() - manager.push({'request':request, 'registry':config.registry}) - try: - config.add_translation_dirs('repoze.bfg.tests.localeapp:locale') - translate = config.registry.getUtility(IChameleonTranslate) - self.assertEqual(translate('Approve'), u'Approve') - finally: - manager.pop() - - def test_add_translation_dirs_abspath(self): - import os - from repoze.bfg.interfaces import ITranslationDirectories - config = self._makeOne() - here = os.path.dirname(__file__) - locale = os.path.join(here, 'localeapp', 'locale') - config.add_translation_dirs(locale) - self.assertEqual(config.registry.getUtility(ITranslationDirectories), - [locale]) - - def test_derive_view_function(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_dottedname(self): - config = self._makeOne() - result = config.derive_view( - 'repoze.bfg.tests.test_configuration.dummy_view') - self.failIf(result is dummy_view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_with_renderer(self): - def view(request): - return 'OK' - config = self._makeOne() - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer('moo', moo) - result = config.derive_view(view, renderer='moo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_class_without_attr(self): - class View(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_class_with_attr(self): - class View(object): - def __init__(self, request): - pass - def another(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View, attr='another') - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(view(None, None), 'OK') - - def test__derive_view_as_function_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_with_debug_authorization_no_authpol(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - result = 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.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_with_debug_authorization_no_permission(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerSecurityPolicy(config, True) - logger = self._registerLogger(config) - result = config._derive_view(view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_debug_auth_permission_authpol_permitted(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, debug_authorization=True, - reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, True) - result = 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__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - 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__derive_view_debug_auth_permission_authpol_denied(self): - from repoze.bfg.exceptions import Forbidden - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = 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__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertRaises(Forbidden, 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__derive_view_debug_auth_permission_authpol_denied2(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = 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(config) - request.view_name = 'view_name' - request.url = 'url' - permitted = result.__permitted__(None, None) - self.assertEqual(permitted, False) - - def test__derive_view_with_predicates_all(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result(None, None) - self.assertEqual(next, 'OK') - self.assertEqual(predicates, [True, True]) - - def test__derive_view_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 - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result.__predicated__(None, None) - self.assertEqual(next, True) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_notall(self): - from repoze.bfg.exceptions import NotFound - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return False - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - self.assertRaises(NotFound, result, None, None) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_wrapper_viewname(self): - from webob import Response - from repoze.bfg.interfaces import IView - from repoze.bfg.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, inner_view) - return Response('outer ' + request.wrapped_body) - config = self._makeOne() - config.registry.registerAdapter( - outer_view, (IViewClassifier, None, None), IView, 'owrap') - result = config._derive_view(inner_view, viewname='inner', - wrapper_viewname='owrap') - self.failIf(result is inner_view) - self.assertEqual(inner_view.__module__, result.__module__) - self.assertEqual(inner_view.__doc__, result.__doc__) - request = self._makeRequest(config) - request.registry = config.registry - response = result(None, request) - self.assertEqual(response.body, 'outer OK') - - def test__derive_view_with_wrapper_viewname_notfound(self): - from webob import Response - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - config = self._makeOne() - request = self._makeRequest(config) - request.registry = config.registry - wrapped = config._derive_view( - inner_view, viewname='inner', wrapper_viewname='owrap') - self.assertRaises(ValueError, wrapped, None, request) - - def test_override_resource_samename(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') - - def test_override_resource_directory_with_file(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo/', 'a:foo.pt') - - def test_override_resource_file_with_directory(self): - from repoze.bfg.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo.pt', 'a:foo/') - - def test_override_resource_success(self): - config = self._makeOne() - override = DummyUnderOverride() - config.override_resource( - 'repoze.bfg.tests.fixtureapp:templates/foo.pt', - 'repoze.bfg.tests.fixtureapp.subpackage:templates/bar.pt', - _override=override) - from repoze.bfg.tests import fixtureapp - from repoze.bfg.tests.fixtureapp import subpackage - self.assertEqual(override.package, fixtureapp) - self.assertEqual(override.path, 'templates/foo.pt') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/bar.pt') - - def test_add_renderer(self): - from repoze.bfg.interfaces import IRendererFactory - config = self._makeOne() - renderer = object() - config.add_renderer('name', renderer) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - renderer) - - def test_add_renderer_dottedname_factory(self): - from repoze.bfg.interfaces import IRendererFactory - config = self._makeOne() - import repoze.bfg.tests - config.add_renderer('name', 'repoze.bfg.tests') - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - repoze.bfg.tests) - - def test_scan_integration(self): - from zope.interface import alsoProvides - from repoze.bfg.interfaces import IRequest - from repoze.bfg.view import render_view_to_response - import repoze.bfg.tests.grokkedapp as package - config = self._makeOne() - 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') - - 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_dottedname_package(self): - from zope.interface import alsoProvides - from repoze.bfg.interfaces import IRequest - from repoze.bfg.view import render_view_to_response - config = self._makeOne() - config.scan('repoze.bfg.tests.grokkedapp') - - 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_testing_securitypolicy(self): - from repoze.bfg.testing import DummySecurityPolicy - config = self._makeOne() - config.testing_securitypolicy('user', ('group1', 'group2'), - permissive=False) - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.interfaces import IAuthorizationPolicy - ut = config.registry.getUtility(IAuthenticationPolicy) - self.failUnless(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_models(self): - from repoze.bfg.traversal import find_model - from repoze.bfg.interfaces import ITraverser - ob1 = object() - ob2 = object() - models = {'/ob1':ob1, '/ob2':ob2} - config = self._makeOne() - config.testing_models(models) - adapter = config.registry.getAdapter(None, ITraverser) - result = adapter({'PATH_INFO':'/ob1'}) - self.assertEqual(result['context'], ob1) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob1',)) - self.assertEqual(result['virtual_root'], ob1) - self.assertEqual(result['virtual_root_path'], ()) - result = adapter({'PATH_INFO':'/ob2'}) - self.assertEqual(result['context'], ob2) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob2',)) - self.assertEqual(result['virtual_root'], ob2) - self.assertEqual(result['virtual_root_path'], ()) - self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) - try: - config.begin() - self.assertEqual(find_model(None, '/ob1'), ob1) - finally: - config.end() - - def test_testing_add_subscriber_single(self): - config = self._makeOne() - 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() - L = config.testing_add_subscriber( - 'repoze.bfg.tests.test_configuration.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): - config = self._makeOne() - 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() - 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_hook_zca(self): - from repoze.bfg.threadlocal import get_current_registry - gsm = DummyGetSiteManager() - config = self._makeOne() - config.hook_zca(getSiteManager=gsm) - self.assertEqual(gsm.hook, get_current_registry) - - def test_unhook_zca(self): - gsm = DummyGetSiteManager() - config = self._makeOne() - config.unhook_zca(getSiteManager=gsm) - self.assertEqual(gsm.unhooked, True) - - def test_testing_add_renderer(self): - config = self._makeOne() - renderer = config.testing_add_renderer('templates/foo.pt') - from repoze.bfg.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, DummyTemplateRenderer)) - from repoze.bfg.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_explicitrenderer(self): - config = self._makeOne() - 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 repoze.bfg.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() - renderer = config.testing_add_template('templates/foo.pt') - from repoze.bfg.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, DummyTemplateRenderer)) - from repoze.bfg.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) - -class Test__map_view(unittest.TestCase): - def setUp(self): - from repoze.bfg.registry import Registry - self.registry = Registry() - testing.setUp(registry=self.registry) - - def tearDown(self): - del self.registry - testing.tearDown() - - def _registerRenderer(self, typ='.txt'): - from repoze.bfg.interfaces import IRendererFactory - from repoze.bfg.interfaces import ITemplateRenderer - from zope.interface import implements - class Renderer: - implements(ITemplateRenderer) - spec = 'abc' + typ - def __init__(self, path): - self.__class__.path = path - def __call__(self, *arg): - return 'Hello!' - self.registry.registerUtility(Renderer, IRendererFactory, name=typ) - return Renderer - - def _makeRequest(self): - request = DummyRequest() - request.registry = self.registry - return request - - def _callFUT(self, *arg, **kw): - from repoze.bfg.configuration import _map_view - return _map_view(*arg, **kw) - - def test__map_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_with_attr(self): - def view(context, request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_with_attr_and_renderer(self): - renderer = self._registerRenderer() - view = lambda *arg: 'OK' - result = self._callFUT(view, attr='__name__', - renderer_name=renderer.spec) - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_requestonly(self): - def view(request): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_requestonly_with_attr(self): - def view(request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_with_attr(self): - class view(object): - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( - self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_with_attr(self): - class view(object): - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): - class view: - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_with_attr(self): - class view: - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_and_attr(self): - class View: - def index(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, context, request): - return {'a':'1'} - view = View() - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr(self): - class View: - def index(self, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, request): - return {'a':'1'} - view = View() - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_rendereronly(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_with_registry(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec, - registry=self.registry) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_with_package(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec, - package='repoze.bfg') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - self.assertEqual(renderer.path, 'repoze.bfg:abc.txt') - -class Test_decorate_view(unittest.TestCase): - def _callFUT(self, wrapped, original): - from repoze.bfg.configuration import decorate_view - return decorate_view(wrapped, original) - - def test_it_same(self): - def view(context, request): - """ """ - result = self._callFUT(view, view) - self.assertEqual(result, False) - - def test_it_different(self): - class DummyView1: - """ 1 """ - __name__ = '1' - __module__ = '1' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - class DummyView2: - """ 2 """ - __name__ = '2' - __module__ = '2' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - view1 = DummyView1() - view2 = DummyView2() - result = self._callFUT(view1, view2) - self.assertEqual(result, True) - self.failUnless(view1.__doc__ is view2.__doc__) - self.failUnless(view1.__module__ is view2.__module__) - self.failUnless(view1.__name__ is view2.__name__) - self.failUnless(view1.__call_permissive__.im_func is - view2.__call_permissive__.im_func) - self.failUnless(view1.__permitted__.im_func is - view2.__permitted__.im_func) - self.failUnless(view1.__predicated__.im_func is - view2.__predicated__.im_func) - -class Test__make_predicates(unittest.TestCase): - def _callFUT(self, **kw): - from repoze.bfg.configuration import _make_predicates - return _make_predicates(**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.failUnless(order1 < order2) - - def test_ordering_number_of_predicates(self): - order1, _, _ = 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=('a',) - ) - order2, _, _ = 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=('a',) - ) - order3, _, _ = 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', - ) - order4, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - containment='containment', - ) - order5, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - ) - order6, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - ) - order7, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - ) - order8, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order9, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order10, _, _ = self._callFUT( - xhr='xhr', - ) - order11, _, _ = self._callFUT( - ) - self.assertEqual(order1, order2) - self.failUnless(order3 > order2) - self.failUnless(order4 > order3) - self.failUnless(order5 > order4) - self.failUnless(order6 > order5) - self.failUnless(order7 > order6) - self.failUnless(order8 > order7) - self.failUnless(order9 > order8) - self.failUnless(order10 > order9) - self.failUnless(order11 > order10) - - def test_ordering_importance_of_predicates(self): - 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( - custom=('a',), - ) - self.failUnless(order1 > order2) - self.failUnless(order2 > order3) - self.failUnless(order3 > order4) - self.failUnless(order4 > order5) - self.failUnless(order5 > order6) - self.failUnless(order6 > order7) - self.failUnless(order7 > order8) - self.failUnless(order8 > order9) - - def test_ordering_importance_and_number(self): - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - custom=('a',), - ) - self.failUnless(order1 < order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(order1 > order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(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=('a',), - ) - self.failUnless(order1 > order2) - - def test_different_custom_predicates_with_same_hash(self): - class PredicateWithHash(object): - def __hash__(self): - return 1 - a = PredicateWithHash() - b = PredicateWithHash() - _, _, a_phash = self._callFUT(custom=(a,)) - _, _, b_phash = self._callFUT(custom=(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')}}) - -class TestMultiView(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.configuration 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 repoze.bfg.interfaces import ISecuredView - verifyClass(ISecuredView, self._getTargetClass()) - - def test_instance_implements_ISecuredView(self): - from zope.interface.verify import verifyObject - from repoze.bfg.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, 'text/html') - self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)]) - mv.add('view4', 99, 'text/html') - self.assertEqual(mv.media_views['text/html'], - [(99, 'view4', None), (100, 'view3', None)]) - mv.add('view5', 100, '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)]) - mv.add('view6', 98, 'text/*') - self.assertEqual(mv.views, [(98, 'view6', None), - (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_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 repoze.bfg.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv.match, context, request) - - def test_match_predicate_fails(self): - from repoze.bfg.exceptions import NotFound - mv = self._makeOne() - def view(context, request): - """ """ - view.__predicated__ = lambda *arg: False - mv.views = [(100, view, None)] - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv, context, request) - - def test___call__intermediate_not_found(self): - from repoze.bfg.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 repoze.bfg.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - def view1(context, request): - raise NotFound - def view2(context, request): - """ """ - mv.views = [(100, view1, None), (99, view2, None)] - self.assertRaises(NotFound, 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 repoze.bfg.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, 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 TestRequestOnly(unittest.TestCase): - def _callFUT(self, arg): - from repoze.bfg.configuration import requestonly - return requestonly(arg) - - def test_newstyle_class_no_init(self): - class foo(object): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_toomanyargs(self): - class foo(object): - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_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)) - - 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), 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_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)) - - 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), True) - - def test_instance_nocall(self): - class Foo: pass - foo = Foo() - self.assertFalse(self._callFUT(foo)) - -class TestMakeApp(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.configuration import make_app - return make_app(*arg, **kw) - - def test_it(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - self.assertEqual(app.zca_hooked, True) - - def test_it_options_means_settings(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, options=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - - def test_it_with_package(self): - package = object() - rootfactory = object() - app = self._callFUT(rootfactory, package=package, - Configurator=DummyConfigurator) - self.assertEqual(app.package, package) - - def test_it_with_custom_configure_zcml(self): - rootfactory = object() - settings = {'configure_zcml':'2.zcml'} - app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.zcml_file, '2.zcml') - -class TestDottedNameResolver(unittest.TestCase): - def _makeOne(self, package=None): - from repoze.bfg.configuration import DottedNameResolver - return DottedNameResolver(package) - - def config_exc(self, func, *arg, **kw): - from repoze.bfg.exceptions import ConfigurationError - try: - func(*arg, **kw) - except ConfigurationError, e: - return e - else: - raise AssertionError('Invalid not raised') # pragma: no cover - - def test_zope_dottedname_style_resolve_absolute(self): - typ = self._makeOne() - result = typ._zope_dottedname_style( - 'repoze.bfg.tests.test_configuration.TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test_zope_dottedname_style_irrresolveable_absolute(self): - typ = self._makeOne() - self.assertRaises(ImportError, typ._zope_dottedname_style, - 'repoze.bfg.test_configuration.nonexisting_name') - - def test__zope_dottedname_style_resolve_relative(self): - import repoze.bfg.tests - typ = self._makeOne(package=repoze.bfg.tests) - result = typ._zope_dottedname_style( - '.test_configuration.TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test__zope_dottedname_style_resolve_relative_leading_dots(self): - import repoze.bfg.tests.test_configuration - typ = self._makeOne(package=repoze.bfg.tests) - result = typ._zope_dottedname_style( - '..tests.test_configuration.TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test__zope_dottedname_style_resolve_relative_is_dot(self): - import repoze.bfg.tests - typ = self._makeOne(package=repoze.bfg.tests) - result = typ._zope_dottedname_style('.') - self.assertEqual(result, repoze.bfg.tests) - - def test__zope_dottedname_style_irresolveable_relative_is_dot(self): - typ = self._makeOne() - e = self.config_exc(typ._zope_dottedname_style, '.') - 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') - self.assertEqual( - e.args[0], - "relative name '.whatever' irresolveable without package") - - def test_zope_dottedname_style_irrresolveable_relative(self): - import repoze.bfg.tests - typ = self._makeOne(package=repoze.bfg.tests) - self.assertRaises(ImportError, typ._zope_dottedname_style, - '.notexisting') - - def test__zope_dottedname_style_resolveable_relative(self): - import repoze.bfg - typ = self._makeOne(package=repoze.bfg) - result = typ._zope_dottedname_style('.tests') - from repoze.bfg import tests - self.assertEqual(result, tests) - - def test__zope_dottedname_style_irresolveable_absolute(self): - typ = self._makeOne() - self.assertRaises( - ImportError, - typ._zope_dottedname_style, 'repoze.bfg.fudge.bar') - - def test__zope_dottedname_style_resolveable_absolute(self): - typ = self._makeOne() - result = typ._zope_dottedname_style( - 'repoze.bfg.tests.test_configuration.TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test__pkg_resources_style_resolve_absolute(self): - typ = self._makeOne() - result = typ._pkg_resources_style( - 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test__pkg_resources_style_irrresolveable_absolute(self): - typ = self._makeOne() - self.assertRaises(ImportError, typ._pkg_resources_style, - 'repoze.bfg.tests:nonexisting') - - def test__pkg_resources_style_resolve_relative(self): - import repoze.bfg.tests - typ = self._makeOne(package=repoze.bfg.tests) - result = typ._pkg_resources_style( - '.test_configuration:TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test__pkg_resources_style_resolve_relative_is_dot(self): - import repoze.bfg.tests - typ = self._makeOne(package=repoze.bfg.tests) - result = typ._pkg_resources_style('.') - self.assertEqual(result, repoze.bfg.tests) - - def test__pkg_resources_style_resolve_relative_nocurrentpackage(self): - typ = self._makeOne() - from repoze.bfg.exceptions import ConfigurationError - self.assertRaises(ConfigurationError, typ._pkg_resources_style, - '.whatever') - - def test__pkg_resources_style_irrresolveable_relative(self): - import repoze.bfg - typ = self._makeOne(package=repoze.bfg) - self.assertRaises(ImportError, typ._pkg_resources_style, - ':notexisting') - - 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( - 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test_resolve_using_zope_dottedname_style(self): - typ = self._makeOne() - result = typ.resolve( - 'repoze.bfg.tests.test_configuration:TestDottedNameResolver') - self.assertEqual(result, self.__class__) - - def test_resolve_missing_raises(self): - typ = self._makeOne() - e = self.config_exc(typ.resolve, 'cant.be.found') - self.assertEqual(e.args[0], - "The dotted name 'cant.be.found' cannot be imported") - - def test_ctor_string_module_resolveable(self): - import repoze.bfg.tests - typ = self._makeOne('repoze.bfg.tests.test_configuration') - self.assertEqual(typ.package, repoze.bfg.tests) - self.assertEqual(typ.package_name, 'repoze.bfg.tests') - - def test_ctor_string_package_resolveable(self): - import repoze.bfg.tests - typ = self._makeOne('repoze.bfg.tests') - self.assertEqual(typ.package, repoze.bfg.tests) - self.assertEqual(typ.package_name, 'repoze.bfg.tests') - - def test_ctor_string_irresolveable(self): - from repoze.bfg.configuration import ConfigurationError - self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found') - - def test_ctor_module(self): - import repoze.bfg.tests - import repoze.bfg.tests.test_configuration - typ = self._makeOne(repoze.bfg.tests.test_configuration) - self.assertEqual(typ.package, repoze.bfg.tests) - self.assertEqual(typ.package_name, 'repoze.bfg.tests') - - def test_ctor_package(self): - import repoze.bfg.tests - typ = self._makeOne(repoze.bfg.tests) - self.assertEqual(typ.package, repoze.bfg.tests) - self.assertEqual(typ.package_name, 'repoze.bfg.tests') - - def test_ctor_None(self): - typ = self._makeOne(None) - self.assertEqual(typ.package, None) - self.assertEqual(typ.package_name, None) - -class Test_isexception(unittest.TestCase): - def _callFUT(self, ob): - from repoze.bfg.configuration 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 repoze.bfg.interfaces import IException - self.assertEqual(self._callFUT(IException), True) - - def test_is_IException_subinterface(self): - from repoze.bfg.interfaces import IException - class ISubException(IException): - pass - self.assertEqual(self._callFUT(ISubException), True) - -class DummyRequest: - subpath = () - def __init__(self): - self.environ = {'PATH_INFO':'/static'} - self.params = {} - self.cookies = {} - def copy(self): - return self - def get_response(self, app): - return app - -class DummyContext: - pass - -class DummyLock: - def acquire(self): - self.acquired = True - - def release(self): - self.released = True - -class DummyPackage: - def __init__(self, name): - self.__name__ = name - -class DummyOverrides: - def __init__(self, package): - self.package = package - self.inserted = [] - - def insert(self, path, package, prefix): - self.inserted.append((path, package, prefix)) - -class DummyUnderOverride: - def __call__(self, package, path, override_package, override_prefix, - _info=u''): - self.package = package - self.path = path - self.override_package = override_package - self.override_prefix = override_prefix - -from zope.interface import Interface -class IDummy(Interface): - pass - -class IOther(Interface): - pass - -class DummyResponse: - status = '200 OK' - headerlist = () - app_iter = () - body = '' - -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 DummyConfigurator(object): - def __init__(self, registry=None, package=None, root_factory=None, - settings=None): - self.root_factory = root_factory - self.package = package - self.settings = settings - - def begin(self, request=None): - self.begun = True - self.request = request - - def end(self): - self.ended = True - - def load_zcml(self, filename): - self.zcml_file = filename - - def make_wsgi_app(self): - return self - - def hook_zca(self): - self.zca_hooked = True - -class DummyAccept(object): - def __init__(self, *matches): - self.matches = list(matches) - - def best_match(self, offered): - if self.matches: - for match in self.matches: - if match in offered: - self.matches.remove(match) - return match - def __contains__(self, val): - return val in self.matches - -from zope.interface import implements -from repoze.bfg.interfaces import IMultiView -class DummyMultiView: - implements(IMultiView) - def __init__(self): - self.views = [] - self.name = 'name' - def add(self, view, order, accept=None, phash=None): - self.views.append((view, accept, phash)) - def __call__(self, context, request): - return 'OK1' - def __permitted__(self, context, request): - """ """ - -class DummyGetSiteManager(object): - def sethook(self, hook): - self.hook = hook - def reset(self): - self.unhooked = True - -class DummyThreadLocalManager(object): - pushed = None - popped = False - def push(self, d): - self.pushed = d - def pop(self): - self.popped = True - -class IFactory(Interface): - pass - -class DummyFactory(object): - implements(IFactory) - def __call__(self): - """ """ - -class DummyEvent: - implements(IDummy) - -class DummyStaticURLInfo: - def __init__(self): - self.added = [] - - def add(self, name, spec, **kw): - self.added.append((name, spec, kw)) - -def dummy_view(request): - return 'OK' - -def dummyfactory(request): - """ """ diff --git a/repoze/bfg/tests/test_decorator.py b/repoze/bfg/tests/test_decorator.py deleted file mode 100644 index d41c62c65..000000000 --- a/repoze/bfg/tests/test_decorator.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest - -class TestReify(unittest.TestCase): - def _makeOne(self, wrapped): - from repoze.bfg.decorator import reify - return reify(wrapped) - - def test___get__withinst(self): - def wrapped(inst): - return 'a' - decorator = self._makeOne(wrapped) - inst = Dummy() - result = decorator.__get__(inst) - self.assertEqual(result, 'a') - self.assertEqual(inst.__dict__['wrapped'], 'a') - - def test___get__noinst(self): - decorator = self._makeOne(None) - result = decorator.__get__(None) - self.assertEqual(result, decorator) - - def test___doc__copied(self): - def wrapped(inst): - """My doc""" - decorator = self._makeOne(wrapped) - self.assertEqual(decorator.__doc__, "My doc") - -class Dummy(object): - pass diff --git a/repoze/bfg/tests/test_docs.py b/repoze/bfg/tests/test_docs.py deleted file mode 100644 index a00171842..000000000 --- a/repoze/bfg/tests/test_docs.py +++ /dev/null @@ -1,35 +0,0 @@ -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('repoze.bfg').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/repoze/bfg/tests/test_encode.py b/repoze/bfg/tests/test_encode.py deleted file mode 100644 index 364247fb3..000000000 --- a/repoze/bfg/tests/test_encode.py +++ /dev/null @@ -1,61 +0,0 @@ -import unittest - -class UrlEncodeTests(unittest.TestCase): - def _callFUT(self, query, doseq=False): - from repoze.bfg.encode import urlencode - return urlencode(query, doseq) - - def test_ascii_only(self): - result = self._callFUT([('a',1), ('b',2)]) - self.assertEqual(result, 'a=1&b=2') - - def test_unicode_key(self): - la = unicode('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 = unicode('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 = [unicode('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_dict(self): - result = self._callFUT({'a':1}) - self.assertEqual(result, 'a=1') - -class URLQuoteTests(unittest.TestCase): - def _callFUT(self, val, safe=''): - from repoze.bfg.encode import url_quote - return url_quote(val, safe) - - def test_it_default(self): - la = 'La/Pe\xc3\xb1a' - result = self._callFUT(la) - self.assertEqual(result, 'La%2FPe%C3%B1a') - - def test_it_with_safe(self): - la = 'La/Pe\xc3\xb1a' - result = self._callFUT(la, '/') - self.assertEqual(result, 'La/Pe%C3%B1a') - -class TestQuotePlus(unittest.TestCase): - def _callFUT(self, val, safe=''): - from repoze.bfg.encode import quote_plus - return quote_plus(val, safe) - - def test_it_default(self): - la = 'La Pe\xc3\xb1a' - result = self._callFUT(la) - self.assertEqual(result, 'La+Pe%C3%B1a') - - def test_it_with_safe(self): - la = 'La /Pe\xc3\xb1a' - result = self._callFUT(la, '/') - self.assertEqual(result, 'La+/Pe%C3%B1a') - - diff --git a/repoze/bfg/tests/test_events.py b/repoze/bfg/tests/test_events.py deleted file mode 100644 index d097ca0f4..000000000 --- a/repoze/bfg/tests/test_events.py +++ /dev/null @@ -1,181 +0,0 @@ -import unittest - -class NewRequestEventTests(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.events import NewRequest - return NewRequest - - def _makeOne(self, request): - return self._getTargetClass()(request) - - def test_class_implements(self): - from repoze.bfg.interfaces import INewRequest - from zope.interface.verify import verifyClass - klass = self._getTargetClass() - verifyClass(INewRequest, klass) - - def test_instance_implements(self): - from repoze.bfg.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 repoze.bfg.events import NewResponse - return NewResponse - - def _makeOne(self, request, response): - return self._getTargetClass()(request, response) - - def test_class_implements(self): - from repoze.bfg.interfaces import INewResponse - from zope.interface.verify import verifyClass - klass = self._getTargetClass() - verifyClass(INewResponse, klass) - - def test_instance_implements(self): - from repoze.bfg.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 test_alias_object_implements(self): - from repoze.bfg.events import WSGIApplicationCreatedEvent - event = WSGIApplicationCreatedEvent(object()) - from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent - from repoze.bfg.interfaces import IApplicationCreated - from zope.interface.verify import verifyObject - verifyObject(IWSGIApplicationCreatedEvent, event) - verifyObject(IApplicationCreated, event) - - def test_alias_class_implements(self): - from repoze.bfg.events import WSGIApplicationCreatedEvent - from repoze.bfg.interfaces import IWSGIApplicationCreatedEvent - from repoze.bfg.interfaces import IApplicationCreated - from zope.interface.verify import verifyClass - verifyClass(IWSGIApplicationCreatedEvent, WSGIApplicationCreatedEvent) - verifyClass(IApplicationCreated, WSGIApplicationCreatedEvent) - - def test_object_implements(self): - from repoze.bfg.events import ApplicationCreated - event = ApplicationCreated(object()) - from repoze.bfg.interfaces import IApplicationCreated - from zope.interface.verify import verifyObject - verifyObject(IApplicationCreated, event) - - def test_class_implements(self): - from repoze.bfg.events import ApplicationCreated - from repoze.bfg.interfaces import IApplicationCreated - from zope.interface.verify import verifyClass - verifyClass(IApplicationCreated, ApplicationCreated) - -class ContextFoundEventTests(unittest.TestCase): - def test_alias_class_implements(self): - from zope.interface.verify import verifyClass - from repoze.bfg.events import AfterTraversal - from repoze.bfg.interfaces import IAfterTraversal - from repoze.bfg.interfaces import IContextFound - verifyClass(IAfterTraversal, AfterTraversal) - verifyClass(IContextFound, AfterTraversal) - - def test_alias_instance_implements(self): - from zope.interface.verify import verifyObject - from repoze.bfg.events import AfterTraversal - from repoze.bfg.interfaces import IAfterTraversal - from repoze.bfg.interfaces import IContextFound - request = DummyRequest() - inst = AfterTraversal(request) - verifyObject(IAfterTraversal, inst) - verifyObject(IContextFound, inst) - - def test_class_implements(self): - from zope.interface.verify import verifyClass - from repoze.bfg.events import ContextFound - from repoze.bfg.interfaces import IContextFound - verifyClass(IContextFound, ContextFound) - - def test_instance_implements(self): - from zope.interface.verify import verifyObject - from repoze.bfg.events import ContextFound - from repoze.bfg.interfaces import IContextFound - request = DummyRequest() - inst = ContextFound(request) - verifyObject(IContextFound, inst) - -class TestSubscriber(unittest.TestCase): - def setUp(self): - registry = DummyRegistry() - from repoze.bfg.configuration import Configurator - self.config = Configurator(registry) - self.config.begin() - - def tearDown(self): - self.config.end() - - def _makeOne(self, *ifaces): - from repoze.bfg.events import subscriber - return subscriber(*ifaces) - - def test_register(self): - from zope.interface import Interface - class IFoo(Interface): pass - class IBar(Interface): pass - dec = self._makeOne(IFoo, IBar) - def foo(): pass - config = DummyConfigurator() - scanner = Dummy() - scanner.config = config - dec.register(scanner, None, foo) - self.assertEqual(config.subscribed, [(foo, (IFoo, IBar))]) - - def test___call__(self): - dec = self._makeOne() - dummy_venusian = DummyVenusian() - dec.venusian = dummy_venusian - def foo(): pass - dec(foo) - self.assertEqual(dummy_venusian.attached, [(foo, dec.register, 'bfg')]) - -class DummyConfigurator(object): - def __init__(self): - self.subscribed = [] - - def add_subscriber(self, wrapped, ifaces): - self.subscribed.append((wrapped, ifaces)) - -class DummyRegistry(object): - pass - -class DummyVenusian(object): - def __init__(self): - self.attached = [] - - def attach(self, wrapped, fn, category=None): - self.attached.append((wrapped, fn, category)) - -class Dummy: - pass - -class DummyRequest: - pass - -class DummyResponse: - pass - diff --git a/repoze/bfg/tests/test_exceptions.py b/repoze/bfg/tests/test_exceptions.py deleted file mode 100644 index 4091eb941..000000000 --- a/repoze/bfg/tests/test_exceptions.py +++ /dev/null @@ -1,45 +0,0 @@ -import unittest - -class TestExceptionResponse(unittest.TestCase): - def _makeOne(self, message): - from repoze.bfg.exceptions import ExceptionResponse - return ExceptionResponse(message) - - def test_app_iter(self): - exc = self._makeOne('') - self.failUnless('' in exc.app_iter[0]) - - def test_headerlist(self): - exc = self._makeOne('') - headerlist = exc.headerlist - headerlist.sort() - app_iter = exc.app_iter - clen = str(len(app_iter[0])) - self.assertEqual(headerlist[0], ('Content-Length', clen)) - self.assertEqual(headerlist[1], ('Content-Type', 'text/html')) - - def test_withmessage(self): - exc = self._makeOne('abc&123') - self.failUnless('abc&123' in exc.app_iter[0]) - -class TestNotFound(unittest.TestCase): - def _makeOne(self, message): - from repoze.bfg.exceptions import NotFound - return NotFound(message) - - def test_it(self): - from repoze.bfg.exceptions import ExceptionResponse - e = self._makeOne('notfound') - self.failUnless(isinstance(e, ExceptionResponse)) - self.assertEqual(e.status, '404 Not Found') - -class TestForbidden(unittest.TestCase): - def _makeOne(self, message): - from repoze.bfg.exceptions import Forbidden - return Forbidden(message) - - def test_it(self): - from repoze.bfg.exceptions import ExceptionResponse - e = self._makeOne('unauthorized') - self.failUnless(isinstance(e, ExceptionResponse)) - self.assertEqual(e.status, '401 Unauthorized') diff --git a/repoze/bfg/tests/test_i18n.py b/repoze/bfg/tests/test_i18n.py deleted file mode 100644 index 95017807f..000000000 --- a/repoze/bfg/tests/test_i18n.py +++ /dev/null @@ -1,394 +0,0 @@ -# -*- coding: utf-8 -*- -# - -import unittest -from repoze.bfg.testing import cleanUp - -class TestTranslationString(unittest.TestCase): - def _makeOne(self, *arg, **kw): - from repoze.bfg.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 repoze.bfg.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 repoze.bfg.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.failUnless(localizer.translator) - - def test_pluralize(self): - translations = DummyTranslations() - localizer = self._makeOne(None, translations) - self.assertEqual(localizer.pluralize('singular', 'plural', 1, - domain='1', mapping={}), - 'singular') - self.failUnless(localizer.pluralizer) - -class Test_negotiate_locale_name(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.i18n import negotiate_locale_name - return negotiate_locale_name(request) - - def _registerImpl(self, impl): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - from repoze.bfg.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 repoze.bfg.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 repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - settings = {'default_locale_name':'settings'} - from repoze.bfg.interfaces import ISettings - registry.registerUtility(settings, ISettings) - request = DummyRequest() - request.registry = registry - result = self._callFUT(request) - self.assertEqual(result, 'settings') - - def test_use_default_locale_negotiator(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - request = DummyRequest() - request.registry = registry - request._LOCALE_ = 'locale' - result = self._callFUT(request) - self.assertEqual(result, 'locale') - - def test_default_default(self): - request = DummyRequest() - result = self._callFUT(request) - self.assertEqual(result, 'en') - -class Test_get_locale_name(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.i18n import get_locale_name - return get_locale_name(request) - - def _registerImpl(self, impl): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - from repoze.bfg.interfaces import ILocaleNegotiator - registry.registerUtility(impl, ILocaleNegotiator) - - def test_name_on_request(self): - request = DummyRequest() - request.bfg_locale_name = 'ie' - result = self._callFUT(request) - self.assertEqual(result, 'ie') - - def test_name_not_on_request(self): - self._registerImpl(dummy_negotiator) - request = DummyRequest() - result = self._callFUT(request) - self.assertEqual(result, 'bogus') - self.assertEqual(request.bfg_locale_name, 'bogus') - -class Test_get_localizer(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.i18n import get_localizer - return get_localizer(request) - - def test_no_registry_on_request(self): - request = DummyRequest() - request.bfg_localizer = '123' - result = self._callFUT(request) - self.assertEqual(result, '123') - - def test_with_registry_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - request = DummyRequest() - request.bfg_localizer = '123' - request.registry = registry - result = self._callFUT(request) - self.assertEqual(result, '123') - - def test_locale_on_request(self): - request = DummyRequest() - request.bfg_localizer = 'abc' - result = self._callFUT(request) - self.assertEqual(result, 'abc') - - def test_locale_from_registry(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ILocalizer - registry = get_current_registry() - locale = 'abc' - registry.registerUtility(locale, ILocalizer, name='en') - request = DummyRequest() - request.bfg_locale_name = 'en' - result = self._callFUT(request) - self.assertEqual(result, 'abc') - - def test_locale_from_mo(self): - import os - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ITranslationDirectories - from repoze.bfg.i18n import Localizer - registry = get_current_registry() - here = os.path.dirname(__file__) - localedir = os.path.join(here, 'localeapp', 'locale') - localedirs = [localedir] - registry.registerUtility(localedirs, ITranslationDirectories) - request = DummyRequest() - request.bfg_locale_name = 'de' - result = self._callFUT(request) - self.assertEqual(result.__class__, Localizer) - self.assertEqual(result.translate('Approve', 'deformsite'), - 'Genehmigen') - self.assertEqual(result.translate('Approve'), 'Approve') - self.failUnless(hasattr(result, 'pluralize')) - - def test_locale_from_mo_bad_mo(self): - import os - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ITranslationDirectories - from repoze.bfg.i18n import Localizer - registry = get_current_registry() - here = os.path.dirname(__file__) - localedir = os.path.join(here, 'localeapp', 'locale') - localedirs = [localedir] - registry.registerUtility(localedirs, ITranslationDirectories) - request = DummyRequest() - request.bfg_locale_name = 'be' - result = self._callFUT(request) - self.assertEqual(result.__class__, Localizer) - self.assertEqual(result.translate('Approve', 'deformsite'), - 'Approve') - -class Test_default_locale_negotiator(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.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 repoze.bfg.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_domain_None(self): - import gettext - import os - here = os.path.dirname(__file__) - localedir = os.path.join(here, 'localeapp', 'locale') - 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): - import os - here = os.path.dirname(__file__) - localedir = os.path.join(here, 'localeapp', 'locale') - 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): - import os - here = os.path.dirname(__file__) - localedir = os.path.join(here, 'localeapp', 'locale') - locales = 'de' - klass = self._getTargetClass() - result = klass.load(localedir, locales, domain='deformsite') - self.assertEqual(result.__class__, klass) - - def test___repr__(self): - inst = self._makeOne() - result = repr(inst) - self.assertEqual(result, '') - - def test_merge_not_gnutranslations(self): - inst = self._makeOne() - self.assertEqual(inst.merge(None), inst) - - def test_merge_gnutranslations(self): - inst = self._makeOne() - inst2 = self._makeOne() - inst2._catalog['a'] = 'b' - inst.merge(inst2) - self.assertEqual(inst._catalog['a'], 'b') - - def test_add_different_domain_merge_true_notexisting(self): - inst = self._makeOne() - inst2 = self._makeOne() - inst2.domain = 'domain2' - inst.add(inst2) - self.assertEqual(inst._domains['domain2'], inst2) - - def test_add_different_domain_merge_true_existing(self): - inst = self._makeOne() - inst2 = self._makeOne() - inst3 = self._makeOne() - inst2.domain = 'domain2' - inst2._catalog['a'] = 'b' - inst3.domain = 'domain2' - inst._domains['domain2'] = inst3 - inst.add(inst2) - self.assertEqual(inst._domains['domain2'], inst3) - self.assertEqual(inst3._catalog['a'], 'b') - - def test_add_same_domain_merge_true(self): - inst = self._makeOne() - inst2 = self._makeOne() - inst2._catalog['a'] = 'b' - inst.add(inst2) - self.assertEqual(inst._catalog['a'], 'b') - - def test_dgettext(self): - t = self._makeOne() - self.assertEqual(t.dgettext('messages', 'foo'), 'Voh') - self.assertEqual(t.dgettext('messages1', 'foo'), 'VohD') - - def test_ldgettext(self): - t = self._makeOne() - self.assertEqual(t.ldgettext('messages', 'foo'), 'Voh') - self.assertEqual(t.ldgettext('messages1', 'foo'), '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), 'Voh1') - self.assertEqual(t.ldngettext('messages1', 'foo1', 'foos1', 1), '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') - - -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 - - def ungettext(self, singular, plural, n): - return singular diff --git a/repoze/bfg/tests/test_integration.py b/repoze/bfg/tests/test_integration.py deleted file mode 100644 index b6eb860ee..000000000 --- a/repoze/bfg/tests/test_integration.py +++ /dev/null @@ -1,236 +0,0 @@ -import os -import unittest - -from repoze.bfg.wsgi import wsgiapp -from repoze.bfg.view import bfg_view -from repoze.bfg.view import static - -from zope.interface import Interface - -from repoze.bfg import testing - -class INothing(Interface): - pass - -@bfg_view(for_=INothing) -@wsgiapp -def wsgiapptest(environ, start_response): - """ """ - return '123' - -class WGSIAppPlusBFGViewTests(unittest.TestCase): - def test_it(self): - from venusian import ATTACH_ATTR - import types - self.failUnless(getattr(wsgiapptest, ATTACH_ATTR)) - self.failUnless(type(wsgiapptest) is types.FunctionType) - context = DummyContext() - request = DummyRequest() - result = wsgiapptest(context, request) - self.assertEqual(result, '123') - - def test_scanned(self): - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.configuration import Configurator - from repoze.bfg.tests import test_integration - config = Configurator() - config.scan(test_integration) - reg = config.registry - view = reg.adapters.lookup( - (IViewClassifier, IRequest, INothing), IView, name='') - self.assertEqual(view, wsgiapptest) - -here = os.path.dirname(__file__) -staticapp = static(os.path.join(here, 'fixtures')) - -class TestStaticApp(unittest.TestCase): - def test_it(self): - from webob import Request - context = DummyContext() - from StringIO import StringIO - request = Request({'PATH_INFO':'', - 'SCRIPT_NAME':'', - 'SERVER_NAME':'localhost', - 'SERVER_PORT':'80', - 'REQUEST_METHOD':'GET', - 'wsgi.version':(1,0), - 'wsgi.url_scheme':'http', - 'wsgi.input':StringIO()}) - request.subpath = ['minimal.pt'] - result = staticapp(context, request) - self.assertEqual(result.status, '200 OK') - self.assertEqual( - result.body, - open(os.path.join(here, 'fixtures/minimal.pt'), 'r').read()) - -class TwillBase(unittest.TestCase): - root_factory = None - def setUp(self): - import sys - import twill - from repoze.bfg.configuration import Configurator - config = Configurator(root_factory=self.root_factory) - config.load_zcml(self.config) - twill.add_wsgi_intercept('localhost', 6543, config.make_wsgi_app) - if sys.platform is 'win32': # pragma: no cover - out = open('nul:', 'wb') - else: - out = open('/dev/null', 'wb') - twill.set_output(out) - testing.setUp(registry=config.registry) - - def tearDown(self): - import twill - import twill.commands - twill.commands.reset_browser() - twill.remove_wsgi_intercept('localhost', 6543) - twill.set_output(None) - testing.tearDown() - -class TestFixtureApp(TwillBase): - config = 'repoze.bfg.tests.fixtureapp:configure.zcml' - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/another.html') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'fixture') - browser.go('http://localhost:6543') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'fixture') - browser.go('http://localhost:6543/dummyskin.html') - self.assertEqual(browser.get_code(), 404) - browser.go('http://localhost:6543/error.html') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'supressed') - browser.go('http://localhost:6543/protected.html') - self.assertEqual(browser.get_code(), 401) - -class TestCCBug(TwillBase): - # "unordered" as reported in IRC by author of - # http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/ - config = 'repoze.bfg.tests.ccbugapp:configure.zcml' - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/licenses/1/v1/rdf') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'rdf') - browser.go('http://localhost:6543/licenses/1/v1/juri') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'juri') - -class TestHybridApp(TwillBase): - # 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. - config = 'repoze.bfg.tests.hybridapp:configure.zcml' - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'global') - browser.go('http://localhost:6543/abc') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'route') - browser.go('http://localhost:6543/def') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'route2') - browser.go('http://localhost:6543/ghi') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'global') - browser.go('http://localhost:6543/jkl') - self.assertEqual(browser.get_code(), 404) - browser.go('http://localhost:6543/mno/global2') - self.assertEqual(browser.get_code(), 404) - browser.go('http://localhost:6543/pqr/global2') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'global2') - browser.go('http://localhost:6543/error') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'supressed') - browser.go('http://localhost:6543/error2') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'supressed2') - browser.go('http://localhost:6543/error_sub') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'supressed2') - -class TestRestBugApp(TwillBase): - # test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515) - config = 'repoze.bfg.tests.restbugapp:configure.zcml' - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/pet') - self.assertEqual(browser.get_code(), 200) - self.assertEqual(browser.get_html(), 'gotten') - -class TestViewDecoratorApp(TwillBase): - config = 'repoze.bfg.tests.viewdecoratorapp:configure.zcml' - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/first') - self.assertEqual(browser.get_code(), 200) - self.failUnless('OK' in browser.get_html()) - - browser.go('http://localhost:6543/second') - self.assertEqual(browser.get_code(), 200) - self.failUnless('OK2' in browser.get_html()) - - browser.go('http://localhost:6543/third') - self.assertEqual(browser.get_code(), 200) - self.failUnless('OK3' in browser.get_html()) - -from repoze.bfg.tests.exceptionviewapp.models import AnException, NotAnException -excroot = {'anexception':AnException(), - 'notanexception':NotAnException()} - -class TestExceptionViewsApp(TwillBase): - config = 'repoze.bfg.tests.exceptionviewapp:configure.zcml' - root_factory = lambda *arg: excroot - def test_it(self): - import twill.commands - browser = twill.commands.get_browser() - browser.go('http://localhost:6543/') - self.assertEqual(browser.get_code(), 200) - self.failUnless('maybe' in browser.get_html()) - - browser.go('http://localhost:6543/notanexception') - self.assertEqual(browser.get_code(), 200) - self.failUnless('no' in browser.get_html()) - - browser.go('http://localhost:6543/anexception') - self.assertEqual(browser.get_code(), 200) - self.failUnless('yes' in browser.get_html()) - - browser.go('http://localhost:6543/route_raise_exception') - self.assertEqual(browser.get_code(), 200) - self.failUnless('yes' in browser.get_html()) - - browser.go('http://localhost:6543/route_raise_exception2') - self.assertEqual(browser.get_code(), 200) - self.failUnless('yes' in browser.get_html()) - - browser.go('http://localhost:6543/route_raise_exception3') - self.assertEqual(browser.get_code(), 200) - self.failUnless('whoa' in browser.get_html()) - - browser.go('http://localhost:6543/route_raise_exception4') - self.assertEqual(browser.get_code(), 200) - self.failUnless('whoa' in browser.get_html()) - -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) - diff --git a/repoze/bfg/tests/test_location.py b/repoze/bfg/tests/test_location.py deleted file mode 100644 index 9b8360f8b..000000000 --- a/repoze/bfg/tests/test_location.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest - -class TestInside(unittest.TestCase): - def _callFUT(self, one, two): - from repoze.bfg.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 repoze.bfg.location import lineage - return lineage(context) - - def test_lineage(self): - o1 = Location() - o2 = Location(); o2.__parent__ = o1 - o3 = Location(); o3.__parent__ = o2 - o4 = Location(); o4.__parent__ = o3 - result = list(self._callFUT(o3)) - self.assertEqual(result, [o3, o2, o1]) - result = list(self._callFUT(o1)) - self.assertEqual(result, [o1]) - -from repoze.bfg.interfaces import ILocation -from zope.interface import implements -class Location(object): - implements(ILocation) - __name__ = __parent__ = None diff --git a/repoze/bfg/tests/test_log.py b/repoze/bfg/tests/test_log.py deleted file mode 100644 index 4cc8d12a0..000000000 --- a/repoze/bfg/tests/test_log.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest - -class TestFunctions(unittest.TestCase): - def test_make_stream_logger(self): - from repoze.bfg.log import make_stream_logger - import logging - import sys - logger = make_stream_logger('foo', sys.stderr, levelname='DEBUG', - fmt='%(message)s') - self.assertEqual(logger.name, 'foo') - self.assertEqual(logger.handlers[0].stream, sys.stderr) - self.assertEqual(logger.handlers[0].formatter._fmt, '%(message)s') - self.assertEqual(logger.level, logging.DEBUG) - - - diff --git a/repoze/bfg/tests/test_paster.py b/repoze/bfg/tests/test_paster.py deleted file mode 100644 index 43837b5f0..000000000 --- a/repoze/bfg/tests/test_paster.py +++ /dev/null @@ -1,186 +0,0 @@ -import unittest - -class TestBFGShellCommand(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.paster import BFGShellCommand - return BFGShellCommand - - def _makeOne(self): - return self._getTargetClass()('bfgshell') - - def test_command_ipython_disabled(self): - command = self._makeOne() - interact = DummyInteractor() - app = DummyApp() - loadapp = DummyLoadApp(app) - command.interact = (interact,) - command.loadapp = (loadapp,) - command.args = ('/foo/bar/myapp.ini', 'myapp') - class Options(object): pass - command.options = Options() - command.options.disable_ipython =True - command.command(IPShell=None) - self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') - self.assertEqual(loadapp.section_name, 'myapp') - self.failUnless(loadapp.relative_to) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['registry'], dummy_registry) - self.assertEqual(pushed['request'].registry, dummy_registry) - self.assertEqual(interact.local, {'root':dummy_root}) - self.failUnless(interact.banner) - self.assertEqual(len(app.threadlocal_manager.popped), 1) - - def test_command_ipython_enabled(self): - command = self._makeOne() - app = DummyApp() - loadapp = DummyLoadApp(app) - command.loadapp = (loadapp,) - dummy_shell_factory = DummyIPShellFactory() - command.args = ('/foo/bar/myapp.ini', 'myapp') - class Options(object): pass - command.options = Options() - command.options.disable_ipython = False - command.command(IPShell=dummy_shell_factory) - self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') - self.assertEqual(loadapp.section_name, 'myapp') - self.failUnless(loadapp.relative_to) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['registry'], dummy_registry) - self.assertEqual(pushed['request'].registry, dummy_registry) - self.assertEqual(dummy_shell_factory.shell.local_ns,{'root':dummy_root}) - self.assertEqual(dummy_shell_factory.shell.global_ns, {}) - self.failUnless('\n\n' in dummy_shell_factory.shell.IP.BANNER) - self.assertEqual(len(app.threadlocal_manager.popped), 1) - - def test_command_get_app_hookable(self): - from paste.deploy import loadapp - command = self._makeOne() - app = DummyApp() - apped = [] - def get_app(*arg, **kw): - apped.append((arg, kw)) - return app - command.get_app = get_app - interact = DummyInteractor() - app = DummyApp() - command.interact = (interact,) - command.args = ('/foo/bar/myapp.ini', 'myapp') - class Options(object): pass - command.options = Options() - command.options.disable_ipython =True - command.command(IPShell=None) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['registry'], dummy_registry) - self.assertEqual(pushed['request'].registry, dummy_registry) - self.assertEqual(interact.local, {'root':dummy_root}) - self.failUnless(interact.banner) - self.assertEqual(len(app.threadlocal_manager.popped), 1) - self.assertEqual(apped, [(('/foo/bar/myapp.ini', 'myapp'), - {'loadapp': loadapp})]) - - def test_command_get_root_hookable(self): - command = self._makeOne() - interact = DummyInteractor() - app = DummyApp() - loadapp = DummyLoadApp(app) - command.interact = (interact,) - command.loadapp = (loadapp,) - root = Dummy() - apps = [] - def get_root(app): - apps.append(app) - return root, lambda *arg: None - command.get_root =get_root - command.args = ('/foo/bar/myapp.ini', 'myapp') - class Options(object): pass - command.options = Options() - command.options.disable_ipython =True - command.command(IPShell=None) - self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') - self.assertEqual(loadapp.section_name, 'myapp') - self.failUnless(loadapp.relative_to) - self.assertEqual(len(app.threadlocal_manager.pushed), 0) - self.assertEqual(interact.local, {'root':root}) - self.failUnless(interact.banner) - self.assertEqual(apps, [app]) - -class TestGetApp(unittest.TestCase): - def _callFUT(self, config_file, section_name, loadapp): - from repoze.bfg.paster import get_app - return get_app(config_file, section_name, loadapp) - - def test_it(self): - import os - app = DummyApp() - loadapp = DummyLoadApp(app) - result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp) - self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') - self.assertEqual(loadapp.section_name, 'myapp') - self.assertEqual(loadapp.relative_to, os.getcwd()) - self.assertEqual(result, app) - -class Dummy: - pass - -class DummyIPShellFactory(object): - def __call__(self, argv, user_ns=None): - shell = DummyIPShell() - shell(user_ns, {}) - self.shell = shell - return shell - -class DummyIPShell(object): - IP = Dummy() - IP.BANNER = 'foo' - def __call__(self, local_ns, global_ns): - self.local_ns = local_ns - self.global_ns = global_ns - - def mainloop(self): - pass - -dummy_root = Dummy() - -class DummyRegistry(object): - def queryUtility(self, iface, default=None): - return default - -dummy_registry = DummyRegistry() - -class DummyInteractor: - def __call__(self, banner, local): - self.banner = banner - self.local = local - -class DummyLoadApp: - def __init__(self, app): - self.app = app - - def __call__(self, config_name, name=None, relative_to=None): - self.config_name = config_name - self.section_name = name - self.relative_to = relative_to - return self.app - -class DummyApp: - def __init__(self): - self.registry = dummy_registry - self.threadlocal_manager = DummyThreadLocalManager() - - def root_factory(self, environ): - return dummy_root - -class DummyThreadLocalManager: - def __init__(self): - self.pushed = [] - self.popped = [] - - def push(self, item): - self.pushed.append(item) - - def pop(self): - self.popped.append(True) - diff --git a/repoze/bfg/tests/test_path.py b/repoze/bfg/tests/test_path.py deleted file mode 100644 index 8ee0474f9..000000000 --- a/repoze/bfg/tests/test_path.py +++ /dev/null @@ -1,172 +0,0 @@ -import unittest - -class TestCallerPath(unittest.TestCase): - def tearDown(self): - from repoze.bfg.tests import test_path - if hasattr(test_path, '__bfg_abspath__'): - del test_path.__bfg_abspath__ - - def _callFUT(self, path, level=2): - from repoze.bfg.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 - here = os.path.abspath(os.path.dirname(__file__)) - result = self._callFUT('a/b/c') - self.assertEqual(result, os.path.join(here, 'a/b/c')) - - def test_memoization_has_bfg_abspath(self): - import os - from repoze.bfg.tests import test_path - test_path.__bfg_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 - here = os.path.abspath(os.path.dirname(__file__)) - from repoze.bfg.tests import test_path - result = self._callFUT('a/b/c') - self.assertEqual(result, os.path.join(here, 'a/b/c')) - self.assertEqual(test_path.__bfg_abspath__, here) - -class TestCallerModule(unittest.TestCase): - def _callFUT(self, level=2): - from repoze.bfg.path import caller_module - return caller_module(level) - - def test_it_level_1(self): - from repoze.bfg.tests import test_path - result = self._callFUT(1) - self.assertEqual(result, test_path) - - def test_it_level_2(self): - from repoze.bfg.tests import test_path - result = self._callFUT(2) - self.assertEqual(result, test_path) - - def test_it_level_3(self): - from repoze.bfg.tests import test_path - result = self._callFUT(3) - self.failIfEqual(result, test_path) - -class TestCallerPackage(unittest.TestCase): - def _callFUT(self, *arg, **kw): - from repoze.bfg.path import caller_package - return caller_package(*arg, **kw) - - def test_it_level_1(self): - from repoze.bfg import tests - result = self._callFUT(1) - self.assertEqual(result, tests) - - def test_it_level_2(self): - from repoze.bfg 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 repoze.bfg.tests - def dummy_caller_module(*arg): - return repoze.bfg.tests - result = self._callFUT(1, caller_module=dummy_caller_module) - self.assertEqual(result, repoze.bfg.tests) - -class TestPackagePath(unittest.TestCase): - def _callFUT(self, package): - from repoze.bfg.path import package_path - return package_path(package) - - def test_it_package(self): - from repoze.bfg import tests - package = DummyPackageOrModule(tests) - result = self._callFUT(package) - self.assertEqual(result, package.package_path) - - def test_it_module(self): - from repoze.bfg.tests import test_path - module = DummyPackageOrModule(test_path) - result = self._callFUT(module) - self.assertEqual(result, module.package_path) - - def test_memoization_success(self): - from repoze.bfg.tests import test_path - module = DummyPackageOrModule(test_path) - self._callFUT(module) - self.assertEqual(module.__bfg_abspath__, module.package_path) - - def test_memoization_fail(self): - from repoze.bfg.tests import test_path - module = DummyPackageOrModule(test_path, raise_exc=TypeError) - result = self._callFUT(module) - self.failIf(hasattr(module, '__bfg_abspath__')) - self.assertEqual(result, module.package_path) - -class TestPackageOf(unittest.TestCase): - def _callFUT(self, package): - from repoze.bfg.path import package_of - return package_of(package) - - def test_it_package(self): - from repoze.bfg import tests - package = DummyPackageOrModule(tests) - result = self._callFUT(package) - self.assertEqual(result, tests) - - def test_it_module(self): - import repoze.bfg.tests.test_configuration - from repoze.bfg import tests - package = DummyPackageOrModule(repoze.bfg.tests.test_configuration) - result = self._callFUT(package) - self.assertEqual(result, tests) - -class TestPackageName(unittest.TestCase): - def _callFUT(self, package): - from repoze.bfg.path import package_name - return package_name(package) - - def test_it_package(self): - from repoze.bfg import tests - package = DummyPackageOrModule(tests) - result = self._callFUT(package) - self.assertEqual(result, 'repoze.bfg.tests') - - def test_it_module(self): - from repoze.bfg.tests import test_path - module = DummyPackageOrModule(test_path) - result = self._callFUT(module) - self.assertEqual(result, 'repoze.bfg.tests') - - def test_it_None(self): - result = self._callFUT(None) - self.assertEqual(result, '__main__') - -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 - - - - - - diff --git a/repoze/bfg/tests/test_registry.py b/repoze/bfg/tests/test_registry.py deleted file mode 100644 index b6185964a..000000000 --- a/repoze/bfg/tests/test_registry.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest - -class TestRegistry(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.registry import Registry - return Registry - - def _makeOne(self): - return self._getTargetClass()() - - 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) - -class DummyModule: - __path__ = "foo" - __name__ = "dummy" - __file__ = '' - -from zope.interface import Interface -from zope.interface import implements -class IDummyEvent(Interface): - pass - -class DummyEvent(object): - implements(IDummyEvent) diff --git a/repoze/bfg/tests/test_renderers.py b/repoze/bfg/tests/test_renderers.py deleted file mode 100644 index 264b4a547..000000000 --- a/repoze/bfg/tests/test_renderers.py +++ /dev/null @@ -1,499 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp -from repoze.bfg import testing - -class TestTemplateRendererFactory(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, path, factory): - from repoze.bfg.renderers import template_renderer_factory - return template_renderer_factory(path, factory) - - def test_abspath_notfound(self): - from repoze.bfg.interfaces import ITemplateRenderer - abspath = '/wont/exist' - testing.registerUtility({}, ITemplateRenderer, name=abspath) - self.assertRaises(ValueError, self._callFUT, abspath, None) - - def test_abspath_alreadyregistered(self): - from repoze.bfg.interfaces import ITemplateRenderer - import os - abspath = os.path.abspath(__file__) - renderer = {} - testing.registerUtility(renderer, ITemplateRenderer, name=abspath) - result = self._callFUT(abspath, None) - self.failUnless(result is renderer) - - def test_abspath_notyetregistered(self): - from repoze.bfg.interfaces import ITemplateRenderer - import os - abspath = os.path.abspath(__file__) - renderer = {} - testing.registerUtility(renderer, ITemplateRenderer, name=abspath) - result = self._callFUT(abspath, None) - self.failUnless(result is renderer) - - def test_relpath_path_registered(self): - renderer = {} - from repoze.bfg.interfaces import ITemplateRenderer - testing.registerUtility(renderer, ITemplateRenderer, name='foo/bar') - result = self._callFUT('foo/bar', None) - self.failUnless(renderer is result) - - def test_relpath_is_package_registered(self): - renderer = {} - from repoze.bfg.interfaces import ITemplateRenderer - testing.registerUtility(renderer, ITemplateRenderer, name='foo:bar/baz') - result = self._callFUT('foo:bar/baz', None) - self.failUnless(renderer is result) - - def test_spec_notfound(self): - self.assertRaises(ValueError, self._callFUT, - 'repoze.bfg.tests:wont/exist', None) - - def test_spec_alreadyregistered(self): - from repoze.bfg.interfaces import ITemplateRenderer - from repoze.bfg import tests - module_name = tests.__name__ - relpath = 'test_renderers.py' - spec = '%s:%s' % (module_name, relpath) - renderer = {} - testing.registerUtility(renderer, ITemplateRenderer, name=spec) - result = self._callFUT(spec, None) - self.failUnless(result is renderer) - - def test_spec_notyetregistered(self): - import os - from repoze.bfg import tests - module_name = tests.__name__ - relpath = 'test_renderers.py' - renderer = {} - factory = DummyFactory(renderer) - spec = '%s:%s' % (module_name, relpath) - result = self._callFUT(spec, factory) - self.failUnless(result is renderer) - path = os.path.abspath(__file__) - if path.endswith('pyc'): # pragma: no cover - path = path[:-1] - self.assertEqual(factory.path, path) - self.assertEqual(factory.kw, {}) - - def test_reload_resources_true(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ISettings - from repoze.bfg.interfaces import ITemplateRenderer - settings = {'reload_resources':True} - testing.registerUtility(settings, ISettings) - renderer = {} - factory = DummyFactory(renderer) - result = self._callFUT('repoze.bfg.tests:test_renderers.py', factory) - self.failUnless(result is renderer) - spec = '%s:%s' % ('repoze.bfg.tests', 'test_renderers.py') - reg = get_current_registry() - self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec), - None) - - def test_reload_resources_false(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ISettings - from repoze.bfg.interfaces import ITemplateRenderer - settings = {'reload_resources':False} - testing.registerUtility(settings, ISettings) - renderer = {} - factory = DummyFactory(renderer) - result = self._callFUT('repoze.bfg.tests:test_renderers.py', factory) - self.failUnless(result is renderer) - spec = '%s:%s' % ('repoze.bfg.tests', 'test_renderers.py') - reg = get_current_registry() - self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec), - None) - -class TestRendererFromName(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, path, package=None): - from repoze.bfg.renderers import renderer_from_name - return renderer_from_name(path, package) - - def test_it(self): - from repoze.bfg.interfaces import IRendererFactory - import os - here = os.path.dirname(os.path.abspath(__file__)) - fixture = os.path.join(here, 'fixtures/minimal.pt') - def factory(path, **kw): - return path - testing.registerUtility(factory, IRendererFactory, name='.pt') - result = self._callFUT(fixture) - self.assertEqual(result, fixture) - - def test_with_package(self): - from repoze.bfg.interfaces import IRendererFactory - def factory(path, **kw): - return path - testing.registerUtility(factory, IRendererFactory, name='.pt') - import repoze.bfg.tests - result = self._callFUT('fixtures/minimal.pt', repoze.bfg.tests) - self.assertEqual(result, 'repoze.bfg.tests:fixtures/minimal.pt') - - def test_it_no_renderer(self): - self.assertRaises(ValueError, self._callFUT, 'foo') - - -class Test_json_renderer_factory(unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.renderers import json_renderer_factory - return json_renderer_factory(name) - - def test_it(self): - renderer = self._callFUT(None) - result = renderer({'a':1}, {}) - self.assertEqual(result, '{"a": 1}') - - def test_with_request_content_type_notset(self): - request = testing.DummyRequest() - renderer = self._callFUT(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._callFUT(None) - renderer({'a':1}, {'request':request}) - self.assertEqual(request.response_content_type, 'text/mishmash') - -class Test_string_renderer_factory(unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.renderers import string_renderer_factory - return string_renderer_factory(name) - - def test_it_unicode(self): - renderer = self._callFUT(None) - value = unicode('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(None, {'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(None, {'request':request}) - self.assertEqual(request.response_content_type, 'text/mishmash') - -class Test_rendered_response(unittest.TestCase): - def setUp(self): - testing.setUp() - from zope.deprecation import __show__ - __show__.off() - - def tearDown(self): - testing.tearDown() - from zope.deprecation import __show__ - __show__.on() - - def _callFUT(self, renderer, response, view=None, - context=None, request=None, renderer_name=None): - from repoze.bfg.renderers import rendered_response - if request is None: - request = testing.DummyRequest() - return rendered_response(renderer, response, view, - context, request, renderer_name) - - def _makeRenderer(self): - def renderer(*arg): - return 'Hello!' - return renderer - - def test_is_response(self): - renderer = self._makeRenderer() - response = DummyResponse() - result = self._callFUT(renderer, response) - self.assertEqual(result, response) - - def test_calls_renderer(self): - renderer = self._makeRenderer() - response = {'a':'1'} - result = self._callFUT(renderer, response) - self.assertEqual(result.body, 'Hello!') - - -class TestRendererHelper(unittest.TestCase): - def setUp(self): - self.config = cleanUp() - - def tearDown(self): - cleanUp() - - def _makeOne(self, *arg, **kw): - from repoze.bfg.renderers import RendererHelper - return RendererHelper(*arg, **kw) - - def _registerRendererFactory(self): - from repoze.bfg.interfaces import IRendererFactory - def renderer(*arg): - def respond(*arg): - return arg - return respond - self.config.registry.registerUtility(renderer, IRendererFactory, - name='.foo') - return renderer - - def test_resolve_spec_path_is_None(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec(None) - self.assertEqual(result, None) - - def test_resolve_spec_package_is_None(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec('/foo/bar') - self.assertEqual(result, '/foo/bar') - - def test_resolve_spec_absolute(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec('repoze.bfg:flub') - self.assertEqual(result, 'repoze.bfg:flub') - - def test_resolve_spec_relative(self): - helper = self._makeOne('loo.foo', package='repoze.bfg') - result = helper.resolve_spec('flub') - self.assertEqual(result, 'repoze.bfg:flub') - - def test_render_to_response(self): - self._registerRendererFactory() - request = Dummy() - helper = self._makeOne('loo.foo') - response = helper.render_to_response('values', 'system_values', - request=request) - self.assertEqual(response.body, ('values', 'system_values')) - - def test_render_explicit_registry(self): - factory = self._registerRendererFactory() - class DummyRegistry(object): - def __init__(self): - self.responses = [factory, lambda *arg: {}] - def queryUtility(self, iface, name=None): - self.queried = True - return self.responses.pop(0) - reg = DummyRegistry() - helper = self._makeOne('loo.foo', registry=reg) - result = helper.render('value', {}) - self.assertEqual(result, ('value', {})) - self.failUnless(reg.queried) - - def test_render_system_values_is_None(self): - self._registerRendererFactory() - request = Dummy() - context = Dummy() - request.context = context - helper = self._makeOne('loo.foo') - result = helper.render('values', None, request=request) - system = {'request':request, 'context':context, - 'renderer_name':'loo.foo', 'view':None} - self.assertEqual(result, ('values', system)) - - def test_render_renderer_globals_factory_active(self): - self._registerRendererFactory() - from repoze.bfg.interfaces import IRendererGlobalsFactory - def rg(system): - return {'a':1} - self.config.registry.registerUtility(rg, IRendererGlobalsFactory) - helper = self._makeOne('loo.foo') - result = helper.render('values', None) - self.assertEqual(result[1]['a'], 1) - - def test__make_response_with_content_type(self): - request = testing.DummyRequest() - attrs = {'response_content_type':'text/nonsense'} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.content_type, 'text/nonsense') - self.assertEqual(response.body, 'abc') - - def test__make_response_with_headerlist(self): - request = testing.DummyRequest() - attrs = {'response_headerlist':[('a', '1'), ('b', '2')]} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.headerlist, - [('Content-Type', 'text/html; charset=UTF-8'), - ('Content-Length', '3'), - ('a', '1'), - ('b', '2')]) - self.assertEqual(response.body, 'abc') - - def test__make_response_with_status(self): - request = testing.DummyRequest() - attrs = {'response_status':'406 You Lose'} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.status, '406 You Lose') - self.assertEqual(response.body, 'abc') - - def test__make_response_with_charset(self): - request = testing.DummyRequest() - attrs = {'response_charset':'UTF-16'} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.charset, 'UTF-16') - - def test__make_response_with_cache_for(self): - request = testing.DummyRequest() - attrs = {'response_cache_for':100} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.cache_control.max_age, 100) - - def test_with_alternate_response_factory(self): - from repoze.bfg.interfaces import IResponseFactory - class ResponseFactory(object): - def __init__(self, result): - self.result = result - self.config.registry.registerUtility(ResponseFactory, IResponseFactory) - request = testing.DummyRequest() - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.__class__, ResponseFactory) - self.assertEqual(response.result, 'abc') - - def test__make_response_with_real_request(self): - # functional - from repoze.bfg.request import Request - request = Request({}) - attrs = {'response_status':'406 You Lose'} - request.__dict__.update(attrs) - helper = self._makeOne('loo.foo') - response = helper._make_response('abc', request) - self.assertEqual(response.status, '406 You Lose') - self.assertEqual(response.body, 'abc') - -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 repoze.bfg.renderers import render - return render(renderer_name, value, request=request, package=package) - - def test_it_no_request(self): - renderer = self.config.testing_add_renderer( - 'repoze.bfg.tests:abc/def.pt') - renderer.string_response = 'abc' - result = self._callFUT('abc/def.pt', dict(a=1)) - self.assertEqual(result, 'abc') - renderer.assert_(a=1) - renderer.assert_(request=None) - - def test_it_with_request(self): - renderer = self.config.testing_add_renderer( - 'repoze.bfg.tests:abc/def.pt') - renderer.string_response = 'abc' - request = testing.DummyRequest() - result = self._callFUT('abc/def.pt', - dict(a=1), request=request) - self.assertEqual(result, 'abc') - renderer.assert_(a=1) - renderer.assert_(request=request) - -class Test_render_to_response(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, renderer_name, value, request=None, package=None): - from repoze.bfg.renderers import render_to_response - return render_to_response(renderer_name, value, request=request, - package=package) - - def test_it_no_request(self): - renderer = self.config.testing_add_renderer( - 'repoze.bfg.tests:abc/def.pt') - renderer.string_response = 'abc' - response = self._callFUT('abc/def.pt', dict(a=1)) - self.assertEqual(response.body, 'abc') - renderer.assert_(a=1) - renderer.assert_(request=None) - - def test_it_with_request(self): - renderer = self.config.testing_add_renderer( - 'repoze.bfg.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, 'abc') - renderer.assert_(a=1) - renderer.assert_(request=request) - -class Test_get_renderer(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, renderer_name, **kw): - from repoze.bfg.renderers import get_renderer - return get_renderer(renderer_name) - - def test_it(self): - renderer = self.config.testing_add_renderer( - 'repoze.bfg.tests:abc/def.pt') - result = self._callFUT('abc/def.pt') - self.assertEqual(result, renderer) - -class Dummy: - pass - -class DummyResponse: - status = '200 OK' - headerlist = () - app_iter = () - body = '' - -class DummyFactory: - def __init__(self, renderer): - self.renderer = renderer - - def __call__(self, path, **kw): - self.path = path - self.kw = kw - return self.renderer - - diff --git a/repoze/bfg/tests/test_request.py b/repoze/bfg/tests/test_request.py deleted file mode 100644 index f1874d60a..000000000 --- a/repoze/bfg/tests/test_request.py +++ /dev/null @@ -1,231 +0,0 @@ -import unittest - -class TestRequest(unittest.TestCase): - def _makeOne(self, environ): - return self._getTargetClass()(environ) - - def _getTargetClass(self): - from repoze.bfg.request import Request - return Request - - 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_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'], u'La Pe\xf1a') - - def test_class_implements(self): - from repoze.bfg.interfaces import IRequest - klass = self._getTargetClass() - self.assertTrue(IRequest.implementedBy(klass)) - - def test_instance_provides(self): - from repoze.bfg.interfaces import IRequest - inst = self._makeOne({}) - self.assertTrue(IRequest.providedBy(inst)) - - def test_setattr_and_getattr_dotnotation(self): - inst = self._makeOne({}) - inst.foo = 1 - self.assertEqual(inst.foo, 1) - - def test_setattr_and_getattr(self): - inst = self._makeOne({}) - setattr(inst, 'bar', 1) - self.assertEqual(getattr(inst, 'bar'), 1) - - def test___contains__(self): - environ ={'zooma':1} - inst = self._makeOne(environ) - self.failUnless('zooma' in inst) - - def test___delitem__(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - del inst['zooma'] - self.failIf('zooma' in environ) - - def test___getitem__(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(inst['zooma'], 1) - - def test___iter__(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - iterator = iter(inst) - self.assertEqual(list(iterator), list(iter(environ))) - - def test___setitem__(self): - environ = {} - inst = self._makeOne(environ) - inst['zooma'] = 1 - self.assertEqual(environ, {'zooma':1}) - - def test_get(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(inst.get('zooma'), 1) - - def test_has_key(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(inst.has_key('zooma'), True) - - def test_items(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(inst.items(), environ.items()) - - def test_iteritems(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(list(inst.iteritems()), list(environ.iteritems())) - - def test_iterkeys(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(list(inst.iterkeys()), list(environ.iterkeys())) - - def test_itervalues(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(list(inst.itervalues()), list(environ.itervalues())) - - def test_keys(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - self.assertEqual(inst.keys(), environ.keys()) - - def test_pop(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - popped = inst.pop('zooma') - self.assertEqual(environ, {}) - self.assertEqual(popped, 1) - - def test_popitem(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - popped = inst.popitem() - self.assertEqual(environ, {}) - self.assertEqual(popped, ('zooma', 1)) - - def test_setdefault(self): - environ = {} - inst = self._makeOne(environ) - marker = [] - result = inst.setdefault('a', marker) - self.assertEqual(environ, {'a':marker}) - self.assertEqual(result, marker) - - def test_update(self): - environ = {} - inst = self._makeOne(environ) - inst.update({'a':1}, b=2) - self.assertEqual(environ, {'a':1, 'b':2}) - - def test_values(self): - environ = {'zooma':1} - inst = self._makeOne(environ) - result = inst.values() - self.assertEqual(result, environ.values()) - - def test_add_response_callback(self): - inst = self._makeOne({}) - self.assertEqual(inst.response_callbacks, ()) - def callback(request, response): - """ """ - inst.add_response_callback(callback) - self.assertEqual(inst.response_callbacks, [callback]) - inst.add_response_callback(callback) - self.assertEqual(inst.response_callbacks, [callback, callback]) - - def test__process_response_callbacks(self): - inst = self._makeOne({}) - def callback1(request, response): - request.called1 = True - response.called1 = True - def callback2(request, response): - request.called2 = True - response.called2 = True - inst.response_callbacks = [callback1, callback2] - response = DummyResponse() - inst._process_response_callbacks(response) - self.assertEqual(inst.called1, True) - self.assertEqual(inst.called2, True) - self.assertEqual(response.called1, True) - self.assertEqual(response.called2, True) - self.assertEqual(inst.response_callbacks, []) - - def test_add_finished_callback(self): - inst = self._makeOne({}) - self.assertEqual(inst.finished_callbacks, ()) - def callback(request): - """ """ - inst.add_finished_callback(callback) - self.assertEqual(inst.finished_callbacks, [callback]) - inst.add_finished_callback(callback) - self.assertEqual(inst.finished_callbacks, [callback, callback]) - - def test__process_finished_callbacks(self): - inst = self._makeOne({}) - def callback1(request): - request.called1 = True - def callback2(request): - request.called2 = True - inst.finished_callbacks = [callback1, callback2] - inst._process_finished_callbacks() - self.assertEqual(inst.called1, True) - self.assertEqual(inst.called2, True) - self.assertEqual(inst.finished_callbacks, []) - -class Test_route_request_iface(unittest.TestCase): - def _callFUT(self, name): - from repoze.bfg.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') - -class Test_add_global_response_headers(unittest.TestCase): - def _callFUT(self, request, headerlist): - from repoze.bfg.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 DummyRequest: - def __init__(self, environ=None): - if environ is None: - environ = {} - self.environ = environ - - def add_response_callback(self, callback): - self.response_callbacks = [callback] - -class DummyResponse: - def __init__(self): - self.headerlist = [] - - diff --git a/repoze/bfg/tests/test_resource.py b/repoze/bfg/tests/test_resource.py deleted file mode 100644 index 191d72c8b..000000000 --- a/repoze/bfg/tests/test_resource.py +++ /dev/null @@ -1,431 +0,0 @@ -import unittest -from repoze.bfg.testing import cleanUp - -class TestOverrideProvider(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _getTargetClass(self): - from repoze.bfg.resource import OverrideProvider - return OverrideProvider - - def _makeOne(self, module): - klass = self._getTargetClass() - return klass(module) - - def _registerOverrides(self, overrides, name='repoze.bfg.tests'): - from repoze.bfg.interfaces import IPackageOverrides - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - reg.registerUtility(overrides, IPackageOverrides, name=name) - - def test_get_resource_filename_no_overrides(self): - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, resource_name) - result = provider.get_resource_filename(None, resource_name) - self.assertEqual(result, expected) - - def test_get_resource_stream_no_overrides(self): - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = open(os.path.join(here, resource_name)).read() - result = provider.get_resource_stream(None, resource_name) - self.assertEqual(result.read(), expected) - - def test_get_resource_string_no_overrides(self): - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = open(os.path.join(here, resource_name)).read() - result = provider.get_resource_string(None, resource_name) - self.assertEqual(result, expected) - - def test_has_resource_no_overrides(self): - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - result = provider.has_resource(resource_name) - self.assertEqual(result, True) - - def test_resource_isdir_no_overrides(self): - file_resource_name = 'test_resource.py' - directory_resource_name = 'fixtures' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - 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 = 'fixtures' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - result = provider.resource_isdir(resource_name) - self.failUnless(result) - - def test_get_resource_filename_override_returns_None(self): - overrides = DummyOverrides(None) - self._registerOverrides(overrides) - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, resource_name) - result = provider.get_resource_filename(None, resource_name) - self.assertEqual(result, expected) - - def test_get_resource_stream_override_returns_None(self): - overrides = DummyOverrides(None) - self._registerOverrides(overrides) - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, resource_name) - result = provider.get_resource_filename(None, resource_name) - self.assertEqual(result, expected) - - def test_get_resource_string_override_returns_None(self): - overrides = DummyOverrides(None) - self._registerOverrides(overrides) - import os - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, resource_name) - result = provider.get_resource_filename(None, resource_name) - self.assertEqual(result, expected) - - def test_has_resource_override_returns_None(self): - overrides = DummyOverrides(None) - self._registerOverrides(overrides) - resource_name = 'test_resource.py' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - 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 = 'fixtures' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - 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 = 'fixtures' - import repoze.bfg.tests - provider = self._makeOne(repoze.bfg.tests) - result = provider.resource_listdir(resource_name) - self.failUnless(result) - - def test_get_resource_filename_override_returns_value(self): - overrides = DummyOverrides('value') - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.get_resource_filename(None, 'test_resource.py') - self.assertEqual(result, 'value') - - def test_get_resource_stream_override_returns_value(self): - overrides = DummyOverrides('value') - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.get_resource_stream(None, 'test_resource.py') - self.assertEqual(result, 'value') - - def test_get_resource_string_override_returns_value(self): - overrides = DummyOverrides('value') - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.get_resource_string(None, 'test_resource.py') - self.assertEqual(result, 'value') - - def test_has_resource_override_returns_True(self): - overrides = DummyOverrides(True) - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.has_resource('test_resource.py') - self.assertEqual(result, True) - - def test_resource_isdir_override_returns_False(self): - overrides = DummyOverrides(False) - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.resource_isdir('fixtures') - self.assertEqual(result, False) - - def test_resource_listdir_override_returns_values(self): - overrides = DummyOverrides(['a']) - import repoze.bfg.tests - self._registerOverrides(overrides) - provider = self._makeOne(repoze.bfg.tests) - result = provider.resource_listdir('fixtures') - self.assertEqual(result, ['a']) - -class TestPackageOverrides(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.resource import PackageOverrides - return PackageOverrides - - def _makeOne(self, package, pkg_resources=None): - klass = self._getTargetClass() - if pkg_resources is None: - pkg_resources = DummyPkgResources() - return klass(package, pkg_resources=pkg_resources) - - def test_ctor_package_already_has_loader_of_different_type(self): - package = DummyPackage('package') - package.__loader__ = True - self.assertRaises(TypeError, self._makeOne, package) - - 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 repoze.bfg.resource 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 repoze.bfg.resource import DirectoryOverride - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= [None] - po.insert('foo/', 'package', 'bar/') - self.assertEqual(len(po.overrides), 2) - override = po.overrides[0] - self.assertEqual(override.__class__, DirectoryOverride) - - def test_insert_file(self): - from repoze.bfg.resource import FileOverride - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= [None] - po.insert('foo.pt', 'package', 'bar.pt') - self.assertEqual(len(po.overrides), 2) - override = po.overrides[0] - self.assertEqual(override.__class__, FileOverride) - - def test_search_path(self): - overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - self.assertEqual(list(po.search_path('whatever')), - [('package', 'name')]) - - def test_get_filename(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('repoze.bfg.tests', 'test_resource.py'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, 'test_resource.py') - self.assertEqual(po.get_filename('whatever'), expected) - - def test_get_stream(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('repoze.bfg.tests', 'test_resource.py'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - expected = open(os.path.join(here, 'test_resource.py')).read() - self.assertEqual(po.get_stream('whatever').read(), expected) - - def test_get_string(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('repoze.bfg.tests', 'test_resource.py'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - expected = open(os.path.join(here, 'test_resource.py')).read() - self.assertEqual(po.get_string('whatever'), expected) - - def test_has_resource(self): - overrides = [ DummyOverride(None), DummyOverride( - ('repoze.bfg.tests', 'test_resource.py'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - self.assertEqual(po.has_resource('whatever'), True) - - def test_isdir_false(self): - overrides = [ DummyOverride( - ('repoze.bfg.tests', 'test_resource.py'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - self.assertEqual(po.isdir('whatever'), False) - - def test_isdir_true(self): - overrides = [ DummyOverride( - ('repoze.bfg.tests', 'fixtures'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - self.assertEqual(po.isdir('whatever'), True) - - def test_listdir(self): - overrides = [ DummyOverride( - ('repoze.bfg.tests', 'fixtures'))] - package = DummyPackage('package') - po = self._makeOne(package) - po.overrides= overrides - self.failUnless(po.listdir('whatever')) - -class TestDirectoryOverride(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.resource import DirectoryOverride - return DirectoryOverride - - def _makeOne(self, path, package, prefix): - klass = self._getTargetClass() - return klass(path, package, prefix) - - def test_it_match(self): - o = self._makeOne('foo/', 'package', 'bar/') - result = o('foo/something.pt') - self.assertEqual(result, ('package', 'bar/something.pt')) - - def test_it_no_match(self): - o = self._makeOne('foo/', 'package', 'bar/') - result = o('baz/notfound.pt') - self.assertEqual(result, None) - -class Test_resolve_resource_spec(unittest.TestCase): - def _callFUT(self, spec, package_name='__main__'): - from repoze.bfg.resource import resolve_resource_spec - return resolve_resource_spec(spec, package_name) - - def test_abspath(self): - import os - here = os.path.dirname(__file__) - path = os.path.abspath(here) - package_name, filename = self._callFUT(path, 'apackage') - self.assertEqual(filename, path) - self.assertEqual(package_name, None) - - def test_rel_spec(self): - pkg = 'repoze.bfg.tests' - path = 'test_resource.py' - package_name, filename = self._callFUT(path, pkg) - self.assertEqual(package_name, 'repoze.bfg.tests') - self.assertEqual(filename, 'test_resource.py') - - def test_abs_spec(self): - pkg = 'repoze.bfg.tests' - path = 'repoze.bfg.nottests:test_resource.py' - package_name, filename = self._callFUT(path, pkg) - self.assertEqual(package_name, 'repoze.bfg.nottests') - self.assertEqual(filename, 'test_resource.py') - - def test_package_name_is_None(self): - pkg = None - path = 'test_resource.py' - package_name, filename = self._callFUT(path, pkg) - self.assertEqual(package_name, None) - self.assertEqual(filename, 'test_resource.py') - - def test_package_name_is_package_object(self): - import repoze.bfg.tests - pkg = repoze.bfg.tests - path = 'test_resource.py' - package_name, filename = self._callFUT(path, pkg) - self.assertEqual(package_name, 'repoze.bfg.tests') - self.assertEqual(filename, 'test_resource.py') - - -class TestFileOverride(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.resource import FileOverride - return FileOverride - - def _makeOne(self, path, package, prefix): - klass = self._getTargetClass() - return klass(path, package, prefix) - - def test_it_match(self): - o = self._makeOne('foo.pt', 'package', 'bar.pt') - result = o('foo.pt') - self.assertEqual(result, ('package', 'bar.pt')) - - def test_it_no_match(self): - o = self._makeOne('foo.pt', 'package', 'bar.pt') - result = o('notfound.pt') - self.assertEqual(result, None) - -class DummyOverride: - def __init__(self, result): - self.result = result - - def __call__(self, resource_name): - return self.result - -class DummyOverrides: - def __init__(self, result): - self.result = result - - def get_filename(self, resource_name): - return self.result - - listdir = isdir = has_resource = get_stream = get_string = get_filename - -class 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 - diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py deleted file mode 100644 index fade0679b..000000000 --- a/repoze/bfg/tests/test_router.py +++ /dev/null @@ -1,1034 +0,0 @@ -import unittest - -from repoze.bfg import testing - -class TestRouter(unittest.TestCase): - def setUp(self): - testing.setUp() - from repoze.bfg.threadlocal import get_current_registry - self.registry = get_current_registry() - - def tearDown(self): - testing.tearDown() - - def _registerRouteRequest(self, name): - from repoze.bfg.interfaces import IRouteRequest - from repoze.bfg.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 repoze.bfg.interfaces import IRoutesMapper - from repoze.bfg.urldispatch import RoutesMapper - mapper = self.registry.queryUtility(IRoutesMapper) - if mapper is None: - mapper = RoutesMapper() - self.registry.registerUtility(mapper, IRoutesMapper) - mapper.connect(name, path, factory) - - def _registerLogger(self): - from repoze.bfg.interfaces import IDebugLogger - logger = DummyLogger() - self.registry.registerUtility(logger, IDebugLogger) - return logger - - def _registerSettings(self, **kw): - from repoze.bfg.interfaces import ISettings - settings = {'debug_authorization':False, 'debug_notfound':False} - settings.update(kw) - self.registry.registerUtility(settings, ISettings) - - def _registerTraverserFactory(self, context, view_name='', subpath=None, - traversed=None, virtual_root=None, - virtual_root_path=None, raise_error=None, - **kw): - from repoze.bfg.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 repoze.bfg.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 repoze.bfg.interfaces import IRootFactory - self.registry.registerUtility(rootfactory, IRootFactory) - return rootfactory - - def _getTargetClass(self): - from repoze.bfg.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_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 repoze.bfg.interfaces import IRequestFactory - class DummyRequestFactory(object): - pass - self.registry.registerUtility(DummyRequestFactory, IRequestFactory) - router = self._makeOne() - self.assertEqual(router.request_factory, DummyRequestFactory) - - def test_call_traverser_default(self): - from repoze.bfg.exceptions import NotFound - environ = self._makeEnviron() - logger = self._registerLogger() - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(NotFound, router, environ, start_response) - self.failUnless('/' in why[0], why) - self.failIf('debug_notfound' in why[0]) - self.assertEqual(len(logger.messages), 0) - - def test_traverser_raises_notfound_class(self): - from repoze.bfg.exceptions import NotFound - environ = self._makeEnviron() - context = DummyContext() - self._registerTraverserFactory(context, raise_error=NotFound) - router = self._makeOne() - start_response = DummyStartResponse() - self.assertRaises(NotFound, router, environ, start_response) - - def test_traverser_raises_notfound_instance(self): - from repoze.bfg.exceptions import NotFound - environ = self._makeEnviron() - context = DummyContext() - self._registerTraverserFactory(context, raise_error=NotFound('foo')) - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(NotFound, router, environ, start_response) - self.failUnless('foo' in why[0], why) - - def test_traverser_raises_forbidden_class(self): - from repoze.bfg.exceptions import Forbidden - environ = self._makeEnviron() - context = DummyContext() - self._registerTraverserFactory(context, raise_error=Forbidden) - router = self._makeOne() - start_response = DummyStartResponse() - self.assertRaises(Forbidden, router, environ, start_response) - - def test_traverser_raises_forbidden_instance(self): - from repoze.bfg.exceptions import Forbidden - environ = self._makeEnviron() - context = DummyContext() - self._registerTraverserFactory(context, raise_error=Forbidden('foo')) - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(Forbidden, router, environ, start_response) - self.failUnless('foo' in why[0], why) - - def test_call_no_view_registered_no_isettings(self): - from repoze.bfg.exceptions import NotFound - environ = self._makeEnviron() - context = DummyContext() - self._registerTraverserFactory(context) - logger = self._registerLogger() - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(NotFound, router, environ, start_response) - self.failUnless('/' in why[0], why) - self.failIf('debug_notfound' in why[0]) - self.assertEqual(len(logger.messages), 0) - - def test_call_no_view_registered_debug_notfound_false(self): - from repoze.bfg.exceptions import NotFound - 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(NotFound, router, environ, start_response) - self.failUnless('/' in why[0], why) - self.failIf('debug_notfound' in why[0]) - self.assertEqual(len(logger.messages), 0) - - def test_call_no_view_registered_debug_notfound_true(self): - from repoze.bfg.exceptions import NotFound - 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(NotFound, router, environ, start_response) - self.failUnless( - "debug_notfound of url http://localhost:8080/; path_info: '/', " - "context:" in why[0]) - self.failUnless("view_name: '', subpath: []" in why[0]) - self.failUnless('http://localhost:8080' in why[0], why) - - self.assertEqual(len(logger.messages), 1) - message = logger.messages[0] - self.failUnless('of url http://localhost:8080' in message) - self.failUnless("path_info: '/'" in message) - self.failUnless('DummyContext instance at' in message) - self.failUnless("view_name: ''" in message) - self.failUnless("subpath: []" in message) - - def test_call_view_returns_nonresponse(self): - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - self._registerTraverserFactory(context) - environ = self._makeEnviron() - view = DummyView('abc') - self._registerView(view, '', IViewClassifier, None, None) - router = self._makeOne() - start_response = DummyStartResponse() - self.assertRaises(ValueError, router, environ, start_response) - - def test_call_view_registered_nonspecific_default_path(self): - from repoze.bfg.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) - 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 repoze.bfg.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 repoze.bfg.interfaces import IRequest - from repoze.bfg.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 repoze.bfg.exceptions import NotFound - from repoze.bfg.interfaces import IViewClassifier - class IContext(Interface): - pass - class INotContext(Interface): - pass - from repoze.bfg.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(NotFound, router, environ, start_response) - - def test_call_view_raises_forbidden(self): - from zope.interface import Interface - from zope.interface import directlyProvides - from repoze.bfg.exceptions import Forbidden - class IContext(Interface): - pass - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - response = DummyResponse() - view = DummyView(response, raise_exception=Forbidden("unauthorized")) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(Forbidden, router, environ, start_response) - self.assertEqual(why[0], 'unauthorized') - - def test_call_view_raises_notfound(self): - from zope.interface import Interface - from zope.interface import directlyProvides - class IContext(Interface): - pass - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.exceptions import NotFound - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - response = DummyResponse() - view = DummyView(response, raise_exception=NotFound("notfound")) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - why = exc_raised(NotFound, router, environ, start_response) - self.assertEqual(why[0], 'notfound') - - def test_call_request_has_response_callbacks(self): - from zope.interface import Interface - from zope.interface import directlyProvides - class IContext(Interface): - pass - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - response = DummyResponse('200 OK') - def view(context, request): - def callback(request, response): - response.called_back = True - request.response_callbacks = [callback] - return response - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - router(environ, start_response) - self.assertEqual(response.called_back, True) - - def test_call_request_has_finished_callbacks_when_view_succeeds(self): - from zope.interface import Interface - from zope.interface import directlyProvides - class IContext(Interface): - pass - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - response = DummyResponse('200 OK') - def view(context, request): - def callback(request): - request.environ['called_back'] = True - request.finished_callbacks = [callback] - return response - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - router(environ, start_response) - self.assertEqual(environ['called_back'], True) - - def test_call_request_has_finished_callbacks_when_view_raises(self): - from zope.interface import Interface - from zope.interface import directlyProvides - class IContext(Interface): - pass - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - def view(context, request): - def callback(request): - request.environ['called_back'] = True - request.finished_callbacks = [callback] - raise NotImplementedError - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - exc_raised(NotImplementedError, router, environ, start_response) - self.assertEqual(environ['called_back'], True) - - def test_call_request_factory_raises(self): - # making sure finally doesnt barf when a request cannot be created - environ = self._makeEnviron() - router = self._makeOne() - def dummy_request_factory(environ): - raise NotImplementedError - router.request_factory = dummy_request_factory - start_response = DummyStartResponse() - exc_raised(NotImplementedError, router, environ, start_response) - - def test_call_eventsends(self): - from repoze.bfg.interfaces import INewRequest - from repoze.bfg.interfaces import INewResponse - from repoze.bfg.interfaces import IContextFound - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - self._registerTraverserFactory(context) - response = DummyResponse() - response.app_iter = ['Hello world'] - view = DummyView(response) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, None, None) - request_events = self._registerEventListener(INewRequest) - aftertraversal_events = self._registerEventListener(IContextFound) - response_events = self._registerEventListener(INewResponse) - router = self._makeOne() - start_response = DummyStartResponse() - result = router(environ, start_response) - self.assertEqual(len(request_events), 1) - self.assertEqual(request_events[0].request.environ, environ) - self.assertEqual(len(aftertraversal_events), 1) - self.assertEqual(aftertraversal_events[0].request.environ, environ) - self.assertEqual(len(response_events), 1) - self.assertEqual(response_events[0].response, response) - self.assertEqual(result, response.app_iter) - - def test_call_pushes_and_pops_threadlocal_manager(self): - from repoze.bfg.interfaces import IViewClassifier - context = DummyContext() - self._registerTraverserFactory(context) - response = DummyResponse() - response.app_iter = ['Hello world'] - view = DummyView(response) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, None, None) - router = self._makeOne() - start_response = DummyStartResponse() - router.threadlocal_manager = DummyThreadLocalManager() - router(environ, start_response) - self.assertEqual(len(router.threadlocal_manager.pushed), 1) - self.assertEqual(len(router.threadlocal_manager.popped), 1) - - def test_call_route_matches_and_has_factory(self): - from repoze.bfg.interfaces import IViewClassifier - self._registerRouteRequest('foo') - 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(environ['bfg.routes.matchdict'], matchdict) - self.assertEqual(environ['bfg.routes.route'].name, 'foo') - self.assertEqual(request.matchdict, matchdict) - self.assertEqual(request.matched_route.name, 'foo') - - def test_call_route_matches_doesnt_overwrite_subscriber_iface(self): - from repoze.bfg.interfaces import INewRequest - from repoze.bfg.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(environ['bfg.routes.matchdict'], matchdict) - self.assertEqual(environ['bfg.routes.route'].name, 'foo') - self.assertEqual(request.matchdict, matchdict) - self.assertEqual(request.matched_route.name, 'foo') - self.failUnless(IFoo.providedBy(request)) - - def test_root_factory_raises_notfound(self): - from repoze.bfg.interfaces import IRootFactory - from repoze.bfg.exceptions import NotFound - from zope.interface import Interface - from zope.interface import directlyProvides - def rootfactory(request): - raise NotFound('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(NotFound, router, environ, start_response) - self.failUnless('from root factory' in why[0]) - - def test_root_factory_raises_forbidden(self): - from repoze.bfg.interfaces import IRootFactory - from repoze.bfg.exceptions import Forbidden - from zope.interface import Interface - from zope.interface import directlyProvides - def rootfactory(request): - raise Forbidden('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(Forbidden, router, environ, start_response) - self.failUnless('from root factory' in why[0]) - - def test_root_factory_exception_propagating(self): - from repoze.bfg.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 repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequestFactory - def rfactory(environ): - return request - self.registry.registerUtility(rfactory, IRequestFactory) - from repoze.bfg.request import Request - request = Request.blank('/') - context = DummyContext() - directlyProvides(context, IContext) - self._registerTraverserFactory(context, subpath=['']) - response = DummyResponse() - view = DummyView(response, raise_exception=RuntimeError) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, IContext) - router = self._makeOne() - start_response = DummyStartResponse() - self.assertRaises(RuntimeError, router, environ, start_response) - # ``exception`` must be attached to request even if a suitable - # exception view cannot be found - self.assertEqual(request.exception.__class__, RuntimeError) - - def test_call_view_raises_exception_view(self): - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.interfaces import IRequest - 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, - IRequest, RuntimeError) - router = self._makeOne() - start_response = DummyStartResponse() - result = router(environ, start_response) - self.assertEqual(result, ["Hello, world"]) - self.assertEqual(view.request.exception.__class__, RuntimeError) - - def test_call_view_raises_super_exception_sub_exception_view(self): - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IRootFactory - from repoze.bfg.interfaces import IRequest - from repoze.bfg.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 repoze.bfg.interfaces import IRequest - from repoze.bfg.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_response(self): - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - environ = self._makeEnviron() - response = DummyResponse() - view = DummyView(response, raise_exception=RuntimeError) - self._registerView(view, '', IViewClassifier, IRequest, None) - exception_view = DummyView(None) - self._registerView(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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.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 repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IExceptionViewClassifier - req_iface = self._registerRouteRequest('foo') - response = DummyResponse() - exception_response = DummyResponse() - exception_response.app_iter = ["Hello, world"] - view = DummyView(response, raise_exception=RuntimeError) - exception_view = DummyView(exception_response) - environ = self._makeEnviron() - self._registerView(view, '', IViewClassifier, IRequest, None) - self._registerView(exception_view, '', IExceptionViewClassifier, - req_iface, RuntimeError) - router = self._makeOne() - start_response = DummyStartResponse() - self.assertRaises(RuntimeError, router, environ, start_response) - -class DummyContext: - pass - -class DummyView: - def __init__(self, response, raise_exception=None): - self.response = response - self.raise_exception = raise_exception - - def __call__(self, context, request): - self.context = context - self.request = request - if not self.raise_exception is None: - raise self.raise_exception - return self.response - -class DummyRootFactory: - def __init__(self, root): - self.root = root - - def __call__(self, environ): - return self.root - -class DummyStartResponse: - status = () - headers = () - def __call__(self, status, headers): - self.status = status - self.headers = headers - -class DummyResponse: - headerlist = () - app_iter = () - def __init__(self, status='200 OK'): - self.status = status - -class DummyThreadLocalManager: - def __init__(self): - self.pushed = [] - self.popped = [] - - def push(self, val): - self.pushed.append(val) - - def pop(self): - self.popped.append(True) - -class DummyAuthenticationPolicy: - pass - -class DummyLogger: - def __init__(self): - self.messages = [] - def info(self, msg): - self.messages.append(msg) - warn = info - debug = info - -def exc_raised(exc, func, *arg, **kw): - try: - func(*arg, **kw) - except exc, e: - return e - else: - raise AssertionError('%s not raised' % exc) # pragma: no cover - - diff --git a/repoze/bfg/tests/test_scripting.py b/repoze/bfg/tests/test_scripting.py deleted file mode 100644 index 2663c4a0f..000000000 --- a/repoze/bfg/tests/test_scripting.py +++ /dev/null @@ -1,81 +0,0 @@ -import unittest - -class TestGetRoot(unittest.TestCase): - def _callFUT(self, app, request=None): - from repoze.bfg.paster import get_root - return get_root(app, request) - - def test_it_norequest(self): - app = DummyApp() - root, closer = self._callFUT(app) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['registry'], dummy_registry) - self.assertEqual(pushed['request'].registry, app.registry) - self.assertEqual(len(app.threadlocal_manager.popped), 0) - closer() - self.assertEqual(len(app.threadlocal_manager.popped), 1) - - def test_it_withrequest(self): - app = DummyApp() - request = DummyRequest({}) - root, closer = self._callFUT(app, request) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['registry'], dummy_registry) - self.assertEqual(pushed['request'], request) - self.assertEqual(len(app.threadlocal_manager.popped), 0) - closer() - self.assertEqual(len(app.threadlocal_manager.popped), 1) - - def test_it_requestfactory_overridden(self): - app = DummyApp() - request = Dummy() - class DummyFactory(object): - @classmethod - def blank(cls, path): - return request - registry = DummyRegistry(DummyFactory) - app.registry = registry - root, closer = self._callFUT(app) - self.assertEqual(len(app.threadlocal_manager.pushed), 1) - pushed = app.threadlocal_manager.pushed[0] - self.assertEqual(pushed['request'], request) - -class Dummy: - pass - -dummy_root = Dummy() - -class DummyRegistry(object): - def __init__(self, result=None): - self.result = result - - def queryUtility(self, iface, default=None): - return self.result or default - -dummy_registry = DummyRegistry() - -class DummyApp: - def __init__(self): - self.registry = dummy_registry - self.threadlocal_manager = DummyThreadLocalManager() - - def root_factory(self, environ): - return dummy_root - -class DummyThreadLocalManager: - def __init__(self): - self.pushed = [] - self.popped = [] - - def push(self, item): - self.pushed.append(item) - - def pop(self): - self.popped.append(True) - -class DummyRequest: - def __init__(self, environ): - self.environ = environ - diff --git a/repoze/bfg/tests/test_security.py b/repoze/bfg/tests/test_security.py deleted file mode 100644 index 13a0e2d9b..000000000 --- a/repoze/bfg/tests/test_security.py +++ /dev/null @@ -1,395 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp - - -class TestAllPermissionsList(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _getTargetClass(self): - from repoze.bfg.security import AllPermissionsList - return AllPermissionsList - - def _makeOne(self): - return self._getTargetClass()() - - def test_it(self): - thing = self._makeOne() - self.failUnless(thing.__eq__(thing)) - self.assertEqual(thing.__iter__(), ()) - self.failUnless('anything' in thing) - - def test_singleton(self): - from repoze.bfg.security import ALL_PERMISSIONS - self.assertEqual(ALL_PERMISSIONS.__class__, self._getTargetClass()) - -class TestAllowed(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.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.failUnless(allowed) - self.assertEqual(str(allowed), 'hello') - self.failUnless('" in repr(allowed)) - -class TestDenied(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.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.failIf(denied) - self.assertEqual(str(denied), 'hello') - self.failUnless('" in repr(denied)) - -class TestACLAllowed(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.security import ACLAllowed - return ACLAllowed - - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) - - def test_it(self): - msg = ("ACLAllowed permission 'permission' via ACE 'ace' in ACL 'acl' " - "on context 'ctx' for principals 'principals'") - allowed = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') - self.failUnless(msg in allowed.msg) - self.assertEqual(allowed, True) - self.failUnless(allowed) - self.assertEqual(str(allowed), msg) - self.failUnless('" % msg in repr(allowed)) - -class TestACLDenied(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.security import ACLDenied - return ACLDenied - - def _makeOne(self, *arg, **kw): - klass = self._getTargetClass() - return klass(*arg, **kw) - - def test_it(self): - msg = ("ACLDenied permission 'permission' via ACE 'ace' in ACL 'acl' " - "on context 'ctx' for principals 'principals'") - denied = self._makeOne('ace', 'acl', 'permission', 'principals', 'ctx') - self.failUnless(msg in denied.msg) - self.assertEqual(denied, False) - self.failIf(denied) - self.assertEqual(str(denied), msg) - self.failUnless('" % msg in repr(denied)) - -class TestViewExecutionPermitted(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.security import view_execution_permitted - return view_execution_permitted(*arg, **kw) - - def _registerSecuredView(self, view_name, allow=True): - from repoze.bfg.threadlocal import get_current_registry - from zope.interface import Interface - from repoze.bfg.interfaces import ISecuredView - from repoze.bfg.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 repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import ISettings - settings = dict(debug_authorization=True) - reg = get_current_registry() - reg.registerUtility(settings, ISettings) - context = DummyContext() - request = DummyRequest({}) - result = self._callFUT(context, request, '') - msg = result.msg - self.failUnless("Allowed: view name '' in context" in msg) - self.failUnless('(no permission defined)' in msg) - self.assertEqual(result, True) - - def test_with_permission(self): - from zope.interface import Interface - from zope.interface import directlyProvides - from repoze.bfg.interfaces import IRequest - class IContext(Interface): - pass - context = DummyContext() - directlyProvides(context, IContext) - self._registerSecuredView('', True) - request = DummyRequest({}) - directlyProvides(request, IRequest) - result = self._callFUT(context, request, '') - self.failUnless(result is True) - -class TestHasPermission(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg): - from repoze.bfg.security import has_permission - return has_permission(*arg) - - def test_no_authentication_policy(self): - request = _makeRequest() - result = self._callFUT('view', None, request) - self.assertEqual(result, True) - self.assertEqual(result.msg, 'No authentication policy in use.') - - def test_authentication_policy_no_authorization_policy(self): - request = _makeRequest() - _registerAuthenticationPolicy(request.registry, None) - self.assertRaises(ValueError, self._callFUT, 'view', None, request) - - def test_authn_and_authz_policies_registered(self): - request = _makeRequest() - _registerAuthenticationPolicy(request.registry, None) - _registerAuthorizationPolicy(request.registry, 'yo') - self.assertEqual(self._callFUT('view', None, request), 'yo') - - def test_no_registry_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - request = DummyRequest({}) - registry = get_current_registry() - _registerAuthenticationPolicy(registry, None) - _registerAuthorizationPolicy(registry, 'yo') - self.assertEqual(self._callFUT('view', None, request), 'yo') - -class TestAuthenticatedUserId(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.security import authenticated_userid - return authenticated_userid(request) - - def test_no_authentication_policy(self): - request = _makeRequest() - result = self._callFUT(request) - self.assertEqual(result, None) - - def test_with_authentication_policy(self): - request = _makeRequest() - _registerAuthenticationPolicy(request.registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - - def test_with_authentication_policy_no_reg_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - request = DummyRequest({}) - registry = get_current_registry() - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - -class TestEffectivePrincipals(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, request): - from repoze.bfg.security import effective_principals - return effective_principals(request) - - def test_no_authentication_policy(self): - request = _makeRequest() - result = self._callFUT(request) - self.assertEqual(result, []) - - def test_with_authentication_policy(self): - request = _makeRequest() - _registerAuthenticationPolicy(request.registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - - def test_with_authentication_policy_no_reg_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - request = DummyRequest({}) - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - -class TestPrincipalsAllowedByPermission(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg): - from repoze.bfg.security import principals_allowed_by_permission - return principals_allowed_by_permission(*arg) - - def test_no_authorization_policy(self): - from repoze.bfg.security import Everyone - context = DummyContext() - result = self._callFUT(context, 'view') - self.assertEqual(result, [Everyone]) - - def test_with_authorization_policy(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - _registerAuthorizationPolicy(registry, 'yo') - context = DummyContext() - result = self._callFUT(context, 'view') - self.assertEqual(result, 'yo') - -class TestRemember(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg): - from repoze.bfg.security import remember - return remember(*arg) - - def test_no_authentication_policy(self): - request = _makeRequest() - result = self._callFUT(request, 'me') - self.assertEqual(result, []) - - def test_with_authentication_policy(self): - request = _makeRequest() - registry = request.registry - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request, 'me') - self.assertEqual(result, 'yo') - - def test_with_authentication_policy_no_reg_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - request = DummyRequest({}) - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request, 'me') - self.assertEqual(result, 'yo') - -class TestForget(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg): - from repoze.bfg.security import forget - return forget(*arg) - - def test_no_authentication_policy(self): - request = _makeRequest() - result = self._callFUT(request) - self.assertEqual(result, []) - - def test_with_authentication_policy(self): - request = _makeRequest() - _registerAuthenticationPolicy(request.registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - - def test_with_authentication_policy_no_reg_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - registry = get_current_registry() - request = DummyRequest({}) - _registerAuthenticationPolicy(registry, 'yo') - result = self._callFUT(request) - self.assertEqual(result, 'yo') - -class DummyContext: - def __init__(self, *arg, **kw): - self.__dict__.update(kw) - -class DummyRequest: - def __init__(self, environ): - self.environ = environ - -class DummyAuthenticationPolicy: - def __init__(self, result): - self.result = result - - def effective_principals(self, request): - return self.result - - def authenticated_userid(self, request): - return self.result - - def remember(self, request, principal, **kw): - return self.result - - def forget(self, request): - return self.result - -class DummyAuthorizationPolicy: - def __init__(self, result): - self.result = result - - def permits(self, context, principals, permission): - return self.result - - def principals_allowed_by_permission(self, context, permission): - return self.result - -def _registerAuthenticationPolicy(reg, result): - from repoze.bfg.interfaces import IAuthenticationPolicy - policy = DummyAuthenticationPolicy(result) - reg.registerUtility(policy, IAuthenticationPolicy) - return policy - -def _registerAuthorizationPolicy(reg, result): - from repoze.bfg.interfaces import IAuthorizationPolicy - policy = DummyAuthorizationPolicy(result) - reg.registerUtility(policy, IAuthorizationPolicy) - return policy - -def _makeRequest(): - from repoze.bfg.registry import Registry - request = DummyRequest({}) - request.registry = Registry() - return request - - diff --git a/repoze/bfg/tests/test_settings.py b/repoze/bfg/tests/test_settings.py deleted file mode 100644 index be73f9dce..000000000 --- a/repoze/bfg/tests/test_settings.py +++ /dev/null @@ -1,203 +0,0 @@ -import unittest - -class TestSettings(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.settings import Settings - return Settings - - def _makeOne(self, d=None, environ=None): - if environ is None: - environ = {} - klass = self._getTargetClass() - return klass(d, _environ_=environ) - - def test_getattr(self): - settings = self._makeOne({'reload_templates':False}) - self.assertEqual(settings.reload_templates, False) - - def test_getattr_raises_attribute_error(self): - settings = self._makeOne() - self.assertRaises(AttributeError, settings.__getattr__, 'mykey') - - def test_noargs(self): - settings = self._makeOne() - self.assertEqual(settings['debug_authorization'], False) - self.assertEqual(settings['debug_notfound'], False) - self.assertEqual(settings['reload_templates'], False) - self.assertEqual(settings['reload_resources'], False) - self.assertEqual(settings['configure_zcml'], '') - - def test_reload_templates(self): - settings = self._makeOne({}) - self.assertEqual(settings['reload_templates'], False) - result = self._makeOne({'reload_templates':'false'}) - self.assertEqual(result['reload_templates'], False) - result = self._makeOne({'reload_templates':'t'}) - self.assertEqual(result['reload_templates'], True) - result = self._makeOne({'reload_templates':'1'}) - self.assertEqual(result['reload_templates'], True) - result = self._makeOne({}, {'BFG_RELOAD_TEMPLATES':'1'}) - self.assertEqual(result['reload_templates'], True) - result = self._makeOne({'reload_templates':'false'}, - {'BFG_RELOAD_TEMPLATES':'1'}) - self.assertEqual(result['reload_templates'], True) - - def test_reload_resources(self): - result = self._makeOne({}) - self.assertEqual(result['reload_resources'], False) - result = self._makeOne({'reload_resources':'false'}) - self.assertEqual(result['reload_resources'], False) - result = self._makeOne({'reload_resources':'t'}) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({'reload_resources':'1'}) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({}, {'BFG_RELOAD_RESOURCES':'1'}) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({'reload_resources':'false'}, - {'BFG_RELOAD_RESOURCES':'1'}) - self.assertEqual(result['reload_resources'], True) - - def test_reload_all(self): - result = self._makeOne({}) - self.assertEqual(result['reload_templates'], False) - self.assertEqual(result['reload_resources'], False) - result = self._makeOne({'reload_all':'false'}) - self.assertEqual(result['reload_templates'], False) - self.assertEqual(result['reload_resources'], False) - result = self._makeOne({'reload_all':'t'}) - self.assertEqual(result['reload_templates'], True) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({'reload_all':'1'}) - self.assertEqual(result['reload_templates'], True) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({}, {'BFG_RELOAD_ALL':'1'}) - self.assertEqual(result['reload_templates'], True) - self.assertEqual(result['reload_resources'], True) - result = self._makeOne({'reload_all':'false'}, - {'BFG_RELOAD_ALL':'1'}) - self.assertEqual(result['reload_templates'], True) - self.assertEqual(result['reload_resources'], True) - - def test_debug_authorization(self): - result = self._makeOne({}) - self.assertEqual(result['debug_authorization'], False) - result = self._makeOne({'debug_authorization':'false'}) - self.assertEqual(result['debug_authorization'], False) - result = self._makeOne({'debug_authorization':'t'}) - self.assertEqual(result['debug_authorization'], True) - result = self._makeOne({'debug_authorization':'1'}) - self.assertEqual(result['debug_authorization'], True) - result = self._makeOne({}, {'BFG_DEBUG_AUTHORIZATION':'1'}) - self.assertEqual(result['debug_authorization'], True) - result = self._makeOne({'debug_authorization':'false'}, - {'BFG_DEBUG_AUTHORIZATION':'1'}) - self.assertEqual(result['debug_authorization'], True) - - def test_debug_notfound(self): - result = self._makeOne({}) - self.assertEqual(result['debug_notfound'], False) - result = self._makeOne({'debug_notfound':'false'}) - self.assertEqual(result['debug_notfound'], False) - result = self._makeOne({'debug_notfound':'t'}) - self.assertEqual(result['debug_notfound'], True) - result = self._makeOne({'debug_notfound':'1'}) - self.assertEqual(result['debug_notfound'], True) - result = self._makeOne({}, {'BFG_DEBUG_NOTFOUND':'1'}) - self.assertEqual(result['debug_notfound'], True) - result = self._makeOne({'debug_notfound':'false'}, - {'BFG_DEBUG_NOTFOUND':'1'}) - self.assertEqual(result['debug_notfound'], True) - - def test_debug_templates(self): - result = self._makeOne({}) - self.assertEqual(result['debug_templates'], False) - result = self._makeOne({'debug_templates':'false'}) - self.assertEqual(result['debug_templates'], False) - result = self._makeOne({'debug_templates':'t'}) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({'debug_templates':'1'}) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({}, {'BFG_DEBUG_TEMPLATES':'1'}) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({'debug_templates':'false'}, - {'BFG_DEBUG_TEMPLATES':'1'}) - self.assertEqual(result['debug_templates'], True) - - def test_debug_all(self): - result = self._makeOne({}) - self.assertEqual(result['debug_notfound'], False) - self.assertEqual(result['debug_authorization'], False) - self.assertEqual(result['debug_templates'], False) - result = self._makeOne({'debug_all':'false'}) - self.assertEqual(result['debug_notfound'], False) - self.assertEqual(result['debug_authorization'], False) - self.assertEqual(result['debug_templates'], False) - result = self._makeOne({'debug_all':'t'}) - self.assertEqual(result['debug_notfound'], True) - self.assertEqual(result['debug_authorization'], True) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({'debug_all':'1'}) - self.assertEqual(result['debug_notfound'], True) - self.assertEqual(result['debug_authorization'], True) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({}, {'BFG_DEBUG_ALL':'1'}) - self.assertEqual(result['debug_notfound'], True) - self.assertEqual(result['debug_authorization'], True) - self.assertEqual(result['debug_templates'], True) - result = self._makeOne({'debug_all':'false'}, - {'BFG_DEBUG_ALL':'1'}) - self.assertEqual(result['debug_notfound'], True) - self.assertEqual(result['debug_authorization'], True) - self.assertEqual(result['debug_templates'], True) - - def test_configure_zcml(self): - result = self._makeOne({}) - self.assertEqual(result['configure_zcml'], '') - result = self._makeOne({'configure_zcml':'abc'}) - self.assertEqual(result['configure_zcml'], 'abc') - result = self._makeOne({}, {'BFG_CONFIGURE_ZCML':'abc'}) - self.assertEqual(result['configure_zcml'], 'abc') - result = self._makeOne({'configure_zcml':'def'}, - {'BFG_CONFIGURE_ZCML':'abc'}) - self.assertEqual(result['configure_zcml'], 'abc') - - def test_default_locale_name(self): - result = self._makeOne({}) - self.assertEqual(result['default_locale_name'], 'en') - result = self._makeOne({'default_locale_name':'abc'}) - self.assertEqual(result['default_locale_name'], 'abc') - result = self._makeOne({}, {'BFG_DEFAULT_LOCALE_NAME':'abc'}) - self.assertEqual(result['default_locale_name'], 'abc') - result = self._makeOne({'default_locale_name':'def'}, - {'BFG_DEFAULT_LOCALE_NAME':'abc'}) - self.assertEqual(result['default_locale_name'], 'abc') - - def test_originals_kept(self): - result = self._makeOne({'a':'i am so a'}) - self.assertEqual(result['a'], 'i am so a') - - -class TestGetSettings(unittest.TestCase): - def setUp(self): - from repoze.bfg.configuration import Configurator - from repoze.bfg.registry import Registry - registry = Registry('testing') - self.config = Configurator(registry=registry) - self.config.begin() - - def tearDown(self): - self.config.end() - - def _callFUT(self): - from repoze.bfg.settings import get_settings - return get_settings() - - def test_it_nosettings(self): - self.assertEqual(self._callFUT(), None) - - def test_it_withsettings(self): - from repoze.bfg.interfaces import ISettings - settings = {'a':1} - self.config.registry.registerUtility(settings, ISettings) - self.assertEqual(self._callFUT(), settings) - diff --git a/repoze/bfg/tests/test_static.py b/repoze/bfg/tests/test_static.py deleted file mode 100644 index 449860883..000000000 --- a/repoze/bfg/tests/test_static.py +++ /dev/null @@ -1,343 +0,0 @@ -import unittest -from repoze.bfg.testing import cleanUp - -class TestPackageURLParser(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.static import PackageURLParser - return PackageURLParser - - def _makeOne(self, *arg, **kw): - return self._getTargetClass()(*arg, **kw) - - - def _makeEnviron(self, **kw): - environ = { - 'wsgi.url_scheme':'http', - 'wsgi.version':(1,0), - 'SERVER_NAME':'example.com', - 'SERVER_PORT':'6543', - 'PATH_INFO':'/', - 'SCRIPT_NAME':'', - 'REQUEST_METHOD':'GET', - } - environ.update(kw) - return environ - - def test_ctor_allargs(self): - inst = self._makeOne('package', 'resource/name', root_resource='root', - cache_max_age=100) - self.assertEqual(inst.package_name, 'package') - self.assertEqual(inst.resource_name, 'resource/name') - self.assertEqual(inst.root_resource, 'root') - self.assertEqual(inst.cache_max_age, 100) - - def test_ctor_defaultargs(self): - inst = self._makeOne('package', 'resource/name') - self.assertEqual(inst.package_name, 'package') - self.assertEqual(inst.resource_name, 'resource/name') - self.assertEqual(inst.root_resource, 'resource/name') - self.assertEqual(inst.cache_max_age, None) - - def test_call_adds_slash_path_info_empty(self): - environ = self._makeEnviron(PATH_INFO='') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('301 Moved Permanently' in body) - self.failUnless('http://example.com:6543/' in body) - - def test_path_info_slash_means_index_html(self): - environ = self._makeEnviron() - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('static' in body) - - def test_resource_out_of_bounds(self): - environ = self._makeEnviron() - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - inst.root_resource = 'abcdef' - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('404 Not Found' in body) - self.failUnless('http://example.com:6543/' in body) - - def test_resource_doesnt_exist(self): - environ = self._makeEnviron(PATH_INFO='/notthere') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('404 Not Found' in body) - self.failUnless('http://example.com:6543/' in body) - - def test_resource_isdir(self): - environ = self._makeEnviron(PATH_INFO='/subdir/') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('subdir' in body) - - def test_resource_is_file(self): - environ = self._makeEnviron(PATH_INFO='/index.html') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('static' in body) - - def test_resource_is_file_with_cache_max_age(self): - environ = self._makeEnviron(PATH_INFO='/index.html') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static', - cache_max_age=600) - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('static' in body) - self.assertEqual(len(sr.headerlist), 8) - header_names = [ x[0] for x in sr.headerlist ] - header_names.sort() - self.assertEqual(header_names, - ['Accept-Ranges', 'Cache-Control', - 'Content-Length', 'Content-Range', - 'Content-Type', 'ETag', 'Expires', 'Last-Modified']) - - def test_resource_is_file_with_no_cache_max_age(self): - environ = self._makeEnviron(PATH_INFO='/index.html') - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - body = response[0] - self.failUnless('static' in body) - self.assertEqual(len(sr.headerlist), 6) - header_names = [ x[0] for x in sr.headerlist ] - header_names.sort() - self.assertEqual(header_names, - ['Accept-Ranges', 'Content-Length', 'Content-Range', - 'Content-Type', 'ETag', 'Last-Modified']) - - def test_if_none_match(self): - class DummyEq(object): - def __eq__(self, other): - return True - dummy_eq = DummyEq() - environ = self._makeEnviron(HTTP_IF_NONE_MATCH=dummy_eq) - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - sr = DummyStartResponse() - response = inst(environ, sr) - self.assertEqual(len(sr.headerlist), 1) - self.assertEqual(sr.status, '304 Not Modified') - self.assertEqual(sr.headerlist[0][0], 'ETag') - self.assertEqual(response[0], '') - - def test_repr(self): - inst = self._makeOne('repoze.bfg.tests', 'fixtures/static') - self.failUnless( - repr(inst).startswith( - ''%(self.__name__, id(self)) - -class DummyRequest: - application_url = 'http://example.com:5432' # app_url never ends with slash - def __init__(self, environ=None): - if environ is None: - environ = {} - self.environ = environ - -class DummyContextURL: - def __init__(self, context, request): - pass - - def virtual_root(self): - return '123' - -def _makeRequest(environ=None): - from repoze.bfg.registry import Registry - request = DummyRequest() - request.registry = Registry() - return request diff --git a/repoze/bfg/tests/test_url.py b/repoze/bfg/tests/test_url.py deleted file mode 100644 index 1b6f7814c..000000000 --- a/repoze/bfg/tests/test_url.py +++ /dev/null @@ -1,304 +0,0 @@ -import unittest - -from repoze.bfg.testing import cleanUp - -class ModelURLTests(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, model, request, *elements, **kw): - from repoze.bfg.url import model_url - return model_url(model, request, *elements, **kw) - - def _registerContextURL(self, reg): - from repoze.bfg.interfaces import IContextURL - from zope.interface import Interface - class DummyContextURL(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'http://example.com/context/' - reg.registerAdapter(DummyContextURL, (Interface, Interface), - IContextURL) - - def test_root_default(self): - request = _makeRequest() - self._registerContextURL(request.registry) - root = DummyContext() - result = self._callFUT(root, request) - self.assertEqual(result, 'http://example.com/context/') - - def test_extra_args(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, 'this/theotherthing', 'that') - self.assertEqual( - result, - 'http://example.com/context/this%2Ftheotherthing/that') - - def test_unicode_in_element_names(self): - request = _makeRequest() - self._registerContextURL(request.registry) - uc = unicode('La Pe\xc3\xb1a', 'utf-8') - context = DummyContext() - result = self._callFUT(context, request, uc) - self.assertEqual(result, - 'http://example.com/context/La%20Pe%C3%B1a') - - def test_element_names_url_quoted(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, 'a b c') - self.assertEqual(result, 'http://example.com/context/a%20b%20c') - - def test_with_query_dict(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, 'a', query={'a':uc}) - self.assertEqual(result, - 'http://example.com/context/a?a=La+Pe%C3%B1a') - - def test_with_query_seq(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, 'a', query=[('a', 'hi there'), - ('b', uc)]) - self.assertEqual(result, - 'http://example.com/context/a?a=hi+there&b=La+Pe%C3%B1a') - - def test_anchor_is_after_root_when_no_elements(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, anchor='a') - self.assertEqual(result, - 'http://example.com/context/#a') - - def test_anchor_is_after_elements_when_no_qs(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, 'a', anchor='b') - self.assertEqual(result, - 'http://example.com/context/a#b') - - def test_anchor_is_after_qs_when_qs_is_present(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, 'a', - query={'b':'c'}, anchor='d') - self.assertEqual(result, - 'http://example.com/context/a?b=c#d') - - def test_anchor_is_encoded_utf8_if_unicode(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, anchor=uc) - self.assertEqual(result, - 'http://example.com/context/#La Pe\xc3\xb1a') - - def test_anchor_is_not_urlencoded(self): - request = _makeRequest() - self._registerContextURL(request.registry) - context = DummyContext() - result = self._callFUT(context, request, anchor=' /#') - self.assertEqual(result, - 'http://example.com/context/# /#') - - def test_no_IContextURL_registered(self): - # falls back to TraversalContextURL - root = DummyContext() - root.__name__ = '' - root.__parent__ = None - request = _makeRequest() - request.environ = {} - result = self._callFUT(root, request) - self.assertEqual(result, 'http://example.com:5432/') - - def test_no_registry_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - request = DummyRequest() - self._registerContextURL(reg) - root = DummyContext() - result = self._callFUT(root, request) - self.assertEqual(result, 'http://example.com/context/') - -class TestRouteUrl(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.url import route_url - return route_url(*arg, **kw) - - def test_with_elements(self): - from repoze.bfg.interfaces import IRoutesMapper - request = _makeRequest() - mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) - request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2', - a=1, b=2, c=3, _query={'a':1}, - _anchor=u"foo") - self.assertEqual(result, - 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') - - def test_no_elements(self): - from repoze.bfg.interfaces import IRoutesMapper - request = _makeRequest() - mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) - request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, a=1, b=2, c=3, _query={'a':1}, - _anchor=u"foo") - self.assertEqual(result, - 'http://example.com:5432/1/2/3?a=1#foo') - - def test_it_generation_error(self): - from repoze.bfg.interfaces import IRoutesMapper - request = _makeRequest() - mapper = DummyRoutesMapper(raise_exc=KeyError) - request.registry.registerUtility(mapper, IRoutesMapper) - mapper.raise_exc = KeyError - self.assertRaises(KeyError, self._callFUT, 'flub', request, a=1) - - def test_generate_doesnt_receive_query_or_anchor(self): - from repoze.bfg.interfaces import IRoutesMapper - route = DummyRoute(result='') - mapper = DummyRoutesMapper(route=route) - from zope.component import getSiteManager - sm = getSiteManager() - sm.registerUtility(mapper, IRoutesMapper) - request = DummyRequest() - result = self._callFUT('flub', request, _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_with_app_url(self): - from repoze.bfg.interfaces import IRoutesMapper - request = _makeRequest() - mapper = DummyRoutesMapper(route=DummyRoute(result='/1/2/3')) - request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, _app_url='http://example2.com') - self.assertEqual(result, 'http://example2.com/1/2/3') - - def test_with_pregenerator(self): - from repoze.bfg.interfaces import IRoutesMapper - request = _makeRequest() - 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 = self._callFUT('flub', request) - self.assertEqual(result, 'http://example2.com/1/2/3/a') - self.assertEqual(route.kw, {}) # shouldnt have anchor/query - -class TestStaticUrl(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.url import static_url - return static_url(*arg, **kw) - - def test_staticurlinfo_notfound(self): - request = _makeRequest() - self.assertRaises(ValueError, self._callFUT, 'static/foo.css', request) - - def test_abspath(self): - request = _makeRequest() - self.assertRaises(ValueError, self._callFUT, '/static/foo.css', request) - - def test_found_rel(self): - from repoze.bfg.interfaces import IStaticURLInfo - request = _makeRequest() - info = DummyStaticURLInfo('abc') - request.registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('static/foo.css', request) - self.assertEqual(result, 'abc') - self.assertEqual(info.args, - ('repoze.bfg.tests:static/foo.css', request, {}) ) - - def test_found_abs(self): - from repoze.bfg.interfaces import IStaticURLInfo - request = _makeRequest() - info = DummyStaticURLInfo('abc') - request.registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('repoze.bfg.tests:static/foo.css', request) - self.assertEqual(result, 'abc') - self.assertEqual(info.args, - ('repoze.bfg.tests:static/foo.css', request, {}) ) - - def test_found_abs_no_registry_on_request(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IStaticURLInfo - request = DummyRequest() - registry = get_current_registry() - info = DummyStaticURLInfo('abc') - registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('repoze.bfg.tests:static/foo.css', request) - self.assertEqual(result, 'abc') - self.assertEqual(info.args, - ('repoze.bfg.tests:static/foo.css', request, {}) ) - -class DummyContext(object): - def __init__(self, next=None): - self.next = next - -class DummyRequest: - application_url = 'http://example.com:5432' # app_url never ends with slash - def __init__(self, environ=None): - if environ is None: - environ = {} - self.environ = environ - -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 - -def _makeRequest(environ=None): - from repoze.bfg.registry import Registry - request = DummyRequest(environ) - request.registry = Registry() - return request - -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/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py deleted file mode 100644 index bf43750ea..000000000 --- a/repoze/bfg/tests/test_urldispatch.py +++ /dev/null @@ -1,318 +0,0 @@ -import unittest -from repoze.bfg import testing - -class TestRoute(unittest.TestCase): - def _getTargetClass(self): - from repoze.bfg.urldispatch import Route - return Route - - def _makeOne(self, *arg): - return self._getTargetClass()(*arg) - - def test_provides_IRoute(self): - from repoze.bfg.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.failUnless(route.generate.__class__ is types.FunctionType) - self.failUnless(route.match.__class__ is types.FunctionType) - - def test_ctor_defaults(self): - import types - route = self._makeOne('name', ':path') - self.assertEqual(route.pattern, ':path') - self.assertEqual(route.path, ':path') - self.assertEqual(route.name, 'name') - self.assertEqual(route.factory, None) - self.failUnless(route.generate.__class__ is types.FunctionType) - self.failUnless(route.match.__class__ is types.FunctionType) - - def test_match(self): - route = self._makeOne('name', ':path') - self.assertEqual(route.match('/whatever'), {'path':'whatever'}) - - def test_generate(self): - route = self._makeOne('name', ':path') - self.assertEqual(route.generate({'path':'abc'}), '/abc') - -class RoutesMapperTests(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _getRequest(self, **kw): - from repoze.bfg.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 repoze.bfg.urldispatch import RoutesMapper - return RoutesMapper - - def _makeOne(self): - klass = self._getTargetClass() - return klass() - - def test_provides_IRoutesMapper(self): - from repoze.bfg.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___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':u'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__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 repoze.bfg.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 repoze.bfg.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':u'/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_url_decode_error(self): - from repoze.bfg.exceptions import URLDecodeError - matcher, generator = self._callFUT('/:foo') - self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00') - -class TestCompileRouteMatchFunctional(unittest.TestCase): - def matches(self, pattern, path, expected): - from repoze.bfg.urldispatch import _compile_route - matcher = _compile_route(pattern)[0] - result = matcher(path) - self.assertEqual(result, expected) - - def generates(self, pattern, dict, result): - from repoze.bfg.urldispatch import _compile_route - self.assertEqual(_compile_route(pattern)[1](dict), result) - - def test_matcher_functional(self): - self.matches('/', '', None) - self.matches('', '', None) - self.matches('/', '/foo', None) - self.matches('/foo/', '/foo', None) - self.matches('/:x', '', None) - self.matches('/:x', '/', None) - self.matches('/abc/:def', '/abc/', None) - self.matches('/abc/:def:baz', '/abc/bleep', None) # bad pattern - self.matches('', '/', {}) - self.matches('/', '/', {}) - 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/%20abc', {'traverse':('zzz', ' abc')}) - self.matches(':x', '/La%20Pe%C3%B1a', {'x':u'La Pe\xf1a'}) - self.matches('*traverse', '/La%20Pe%C3%B1a/x', - {'traverse':(u'La Pe\xf1a', 'x')}) - self.matches('/foo/:id.html', '/foo/bar.html', {'id':'bar'}) - - def test_generator_functional(self): - self.generates('', {}, '/') - self.generates('/', {}, '/') - 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':unicode('/La Pe\xc3\xb1a', 'utf-8')}, - '/%2FLa%20Pe%C3%B1a') - self.generates('/:x*y', {'x':unicode('/La Pe\xc3\xb1a', 'utf-8'), - 'y':'/rest/of/path'}, - '/%2FLa%20Pe%C3%B1a/rest/of/path') - self.generates('*traverse', {'traverse':('a', u'La Pe\xf1a')}, - '/a/La%20Pe%C3%B1a') - self.generates('/foo/:id.html', {'id':'bar'}, '/foo/bar.html') - -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/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py deleted file mode 100644 index e5adc8de1..000000000 --- a/repoze/bfg/tests/test_view.py +++ /dev/null @@ -1,499 +0,0 @@ -import unittest -import sys - -from repoze.bfg.testing import cleanUp - -class BaseTest(object): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _registerView(self, reg, app, name): - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IViewClassifier - for_ = (IViewClassifier, IRequest, IContext) - from repoze.bfg.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', - } - environ.update(extras) - return environ - - def _makeRequest(self, **environ): - from repoze.bfg.interfaces import IRequest - from zope.interface import directlyProvides - from webob import Request - from repoze.bfg.registry import Registry - environ = self._makeEnviron(**environ) - request = Request(environ) - request.registry = Registry() - directlyProvides(request, IRequest) - return request - - def _makeContext(self): - from zope.interface import directlyProvides - context = DummyContext() - directlyProvides(context, IContext) - return context - - -class RenderViewToResponseTests(BaseTest, unittest.TestCase): - def _callFUT(self, *arg, **kw): - from repoze.bfg.view import render_view_to_response - return render_view_to_response(*arg, **kw) - - def test_call_no_view_registered(self): - request = self._makeRequest() - context = self._makeContext() - result = self._callFUT(context, request, name='notregistered') - self.assertEqual(result, None) - - def test_call_no_registry_on_request(self): - request = self._makeRequest() - del request.registry - context = self._makeContext() - result = self._callFUT(context, request, name='notregistered') - self.assertEqual(result, None) - - def test_call_view_registered_secure(self): - request = self._makeRequest() - context = self._makeContext() - response = DummyResponse() - view = make_view(response) - self._registerView(request.registry, view, 'registered') - response = self._callFUT(context, request, name='registered', - secure=True) - self.assertEqual(response.status, '200 OK') - - def test_call_view_registered_insecure_no_call_permissive(self): - context = self._makeContext() - request = self._makeRequest() - response = DummyResponse() - view = make_view(response) - self._registerView(request.registry, view, 'registered') - response = self._callFUT(context, request, name='registered', - secure=False) - self.assertEqual(response.status, '200 OK') - - def test_call_view_registered_insecure_with_call_permissive(self): - context = self._makeContext() - request = self._makeRequest() - response = DummyResponse() - view = make_view(response) - def anotherview(context, request): - return DummyResponse('anotherview') - view.__call_permissive__ = anotherview - self._registerView(request.registry, view, 'registered') - response = self._callFUT(context, request, name='registered', - secure=False) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.app_iter, ['anotherview']) - -class RenderViewToIterableTests(BaseTest, unittest.TestCase): - def _callFUT(self, *arg, **kw): - from repoze.bfg.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('anotherview') - view.__call_permissive__ = anotherview - self._registerView(request.registry, view, 'registered') - iterable = self._callFUT(context, request, name='registered', - secure=False) - self.assertEqual(iterable, ['anotherview']) - -class RenderViewTests(BaseTest, unittest.TestCase): - def _callFUT(self, *arg, **kw): - from repoze.bfg.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, '') - - 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, '') - - 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') - s = self._callFUT(context, request, name='registered', secure=False) - self.assertEqual(s, 'anotherview') - -class TestIsResponse(unittest.TestCase): - def _callFUT(self, *arg, **kw): - from repoze.bfg.view import is_response - return is_response(*arg, **kw) - - def test_is(self): - response = DummyResponse() - self.assertEqual(self._callFUT(response), True) - - def test_isnt(self): - response = None - self.assertEqual(self._callFUT(response), False) - - def test_partial_inst(self): - response = DummyResponse() - response.app_iter = None - self.assertEqual(self._callFUT(response), False) - - def test_status_not_string(self): - response = DummyResponse() - response.status = None - self.assertEqual(self._callFUT(response), False) - -class TestBFGViewDecorator(unittest.TestCase): - def setUp(self): - cleanUp() - - def tearDown(self): - cleanUp() - - def _getTargetClass(self): - from repoze.bfg.view import bfg_view - return bfg_view - - def _makeOne(self, *arg, **kw): - return self._getTargetClass()(*arg, **kw) - - def test_create_defaults(self): - decorator = self._makeOne() - self.assertEqual(decorator.name, '') - self.assertEqual(decorator.request_type, None) - self.assertEqual(decorator.context, None) - self.assertEqual(decorator.permission, None) - - def test_create_nondefaults(self): - decorator = self._makeOne(name=None, request_type=None, for_=None, - permission='foo') - self.assertEqual(decorator.name, None) - self.assertEqual(decorator.request_type, None) - self.assertEqual(decorator.context, None) - self.assertEqual(decorator.permission, 'foo') - - def test_call_function(self): - decorator = self._makeOne() - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(): pass - wrapped = decorator(foo) - self.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['permission'], None) - self.assertEqual(settings[0]['context'], None) - self.assertEqual(settings[0]['request_type'], None) - - 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.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['permission'], None) - self.assertEqual(settings[0]['context'], None) - self.assertEqual(settings[0]['request_type'], None) - - def test_stacking(self): - decorator1 = self._makeOne(name='1') - venusian1 = DummyVenusian() - decorator1.venusian = venusian1 - venusian2 = DummyVenusian() - decorator2 = self._makeOne(name='2') - decorator2.venusian = venusian2 - def foo(): pass - wrapped1 = decorator1(foo) - wrapped2 = decorator2(wrapped1) - self.failUnless(wrapped1 is foo) - self.failUnless(wrapped2 is foo) - settings1 = call_venusian(venusian1) - self.assertEqual(len(settings1), 1) - self.assertEqual(settings1[0]['name'], '1') - settings2 = call_venusian(venusian2) - self.assertEqual(len(settings2), 1) - self.assertEqual(settings2[0]['name'], '2') - - def test_call_as_method(self): - decorator = self._makeOne() - venusian = DummyVenusian() - decorator.venusian = venusian - decorator.venusian.info.scope = 'class' - def foo(self): pass - def bar(self): pass - class foo(object): - foomethod = decorator(foo) - barmethod = decorator(bar) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 2) - self.assertEqual(settings[0]['attr'], 'foo') - self.assertEqual(settings[1]['attr'], 'bar') - - def test_with_custom_predicates(self): - decorator = self._makeOne(custom_predicates=(1,)) - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(context, request): pass - decorated = decorator(foo) - self.failUnless(decorated is foo) - settings = call_venusian(venusian) - self.assertEqual(settings[0]['custom_predicates'], (1,)) - - def test_call_with_renderer_nodot(self): - decorator = self._makeOne(renderer='json') - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(): pass - wrapped = decorator(foo) - self.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], 'json') - - def test_call_with_renderer_relpath(self): - decorator = self._makeOne(renderer='fixtures/minimal.pt') - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(): pass - wrapped = decorator(foo) - self.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], - 'repoze.bfg.tests:fixtures/minimal.pt') - - def test_call_with_renderer_pkgpath(self): - decorator = self._makeOne( - renderer='repoze.bfg.tests:fixtures/minimal.pt') - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(): pass - wrapped = decorator(foo) - self.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], - 'repoze.bfg.tests:fixtures/minimal.pt') - -class Test_append_slash_notfound_view(BaseTest, unittest.TestCase): - def _callFUT(self, context, request): - from repoze.bfg.view import append_slash_notfound_view - return append_slash_notfound_view(context, request) - - def _registerMapper(self, reg, match=True): - from repoze.bfg.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_matches(self): - request = self._makeRequest(PATH_INFO='/abc') - context = ExceptionResponse() - self._registerMapper(request.registry, True) - response = self._callFUT(context, request) - self.assertEqual(response.status, '302 Found') - self.assertEqual(response.location, '/abc/') - -class TestAppendSlashNotFoundViewFactory(BaseTest, unittest.TestCase): - def _makeOne(self, notfound_view): - from repoze.bfg.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 repoze.bfg.view import default_exceptionresponse_view - return default_exceptionresponse_view(context, request) - - def test_is_exception(self): - context = Exception() - result = self._callFUT(context, None) - self.failUnless(result is context) - - def test_is_not_exception_no_request_exception(self): - context = object() - request = DummyRequest() - result = self._callFUT(context, request) - self.failUnless(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 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 - -class DummyResponse: - status = '200 OK' - headerlist = () - def __init__(self, body=None): - if body is None: - self.app_iter = () - else: - self.app_iter = [body] - -from zope.interface import Interface -class IContext(Interface): - pass - -class DummyVenusianInfo(object): - scope = 'notaclass' - module = sys.modules['repoze.bfg.tests'] - -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): - self.attachments.append((wrapped, callback, category)) - return self.info - -class DummyConfig(object): - def __init__(self): - self.settings = [] - - def add_view(self, **kw): - self.settings.append(kw) - -class DummyVenusianContext(object): - def __init__(self): - self.config = DummyConfig() - -def call_venusian(venusian): - context = DummyVenusianContext() - for wrapped, callback, category in venusian.attachments: - callback(context, None, None) - return context.config.settings - diff --git a/repoze/bfg/tests/test_wsgi.py b/repoze/bfg/tests/test_wsgi.py deleted file mode 100644 index 5d3ec1faa..000000000 --- a/repoze/bfg/tests/test_wsgi.py +++ /dev/null @@ -1,112 +0,0 @@ -import unittest - -class WSGIAppTests(unittest.TestCase): - def _callFUT(self, app): - from repoze.bfg.wsgi import wsgiapp - return wsgiapp(app) - - def test_decorator(self): - context = DummyContext() - request = DummyRequest() - decorator = self._callFUT(dummyapp) - response = decorator(context, request) - self.assertEqual(response, dummyapp) - -class WSGIApp2Tests(unittest.TestCase): - def _callFUT(self, app): - from repoze.bfg.wsgi import wsgiapp2 - return wsgiapp2(app) - - def test_decorator_with_subpath_and_view_name(self): - context = DummyContext() - request = DummyRequest() - request.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = ['subpath'] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} - 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.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = ['subpath'] - request.view_name = '' - request.environ = {'SCRIPT_NAME':'/foo'} - 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.traversed = ['a', 'b'] - request.virtual_root_path = ['a'] - request.subpath = [] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} - 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.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = 'view_name' - request.environ = {'SCRIPT_NAME':'/foo'} - 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.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = '' - request.environ = {'SCRIPT_NAME':'/foo'} - 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.traversed = [] - request.virtual_root_path = [] - request.subpath = [] - request.view_name = '' - request.environ = {'SCRIPT_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'], '') - -def dummyapp(environ, start_response): - """ """ - -class DummyContext: - pass - -class DummyRequest: - def get_response(self, application): - return application diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py deleted file mode 100644 index 131122d7b..000000000 --- a/repoze/bfg/tests/test_zcml.py +++ /dev/null @@ -1,1283 +0,0 @@ -import logging - -logging.basicConfig() - -import unittest - -from repoze.bfg import testing - -from zope.interface import Interface -from zope.interface import implements - -class TestViewDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import view - return view(*arg, **kw) - - def test_request_type_ashttpmethod(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequest - context = DummyContext() - view = lambda *arg: None - self._callFUT(context, 'repoze.view', IDummy, view=view, - request_type='GET') - actions = context.actions - self.assertEqual(len(actions), 1) - action = actions[0] - discrim = ('view', IDummy, '', None, IView, None, None, 'GET', None, - None, False, None, None, None) - self.assertEqual(action['discriminator'], discrim) - register = action['callable'] - register() - reg = get_current_registry() - wrapper = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - request = DummyRequest() - request.method = 'GET' - self.assertEqual(wrapper.__predicated__(None, request), True) - request.method = 'POST' - self.assertEqual(wrapper.__predicated__(None, request), False) - - def test_request_type_asinterfacestring(self): - from zope.interface import directlyProvides - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequest - context = DummyContext(IDummy) - view = lambda *arg: 'OK' - self._callFUT(context, 'repoze.view', IDummy, view=view, - request_type='whatever') - actions = context.actions - self.assertEqual(len(actions), 1) - discrim = ('view', IDummy, '', IDummy, IView, None, None, None, None, - None, False, None, None, None) - self.assertEqual(actions[0]['discriminator'], discrim) - register = actions[0]['callable'] - register() - reg = get_current_registry() - regview = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - self.assertNotEqual(view, regview) - request = DummyRequest() - directlyProvides(request, IDummy) - result = regview(None, request) - self.assertEqual(result, 'OK') - self.failIf(hasattr(view, '__call_permissive__')) - - def test_request_type_asnoninterfacestring(self): - from repoze.bfg.exceptions import ConfigurationError - context = DummyContext('notaninterface') - view = lambda *arg: 'OK' - self.assertRaises(ConfigurationError, - self._callFUT, - context, 'repoze.view', IDummy, view=view, - request_type='whatever') - - def test_with_dotted_renderer(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRendererFactory - from repoze.bfg.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - def factory(path): - def foo(*arg): - return 'OK' - return foo - reg.registerUtility(factory, IRendererFactory, name='.pt') - view = lambda *arg: None - self._callFUT(context, 'repoze.view', IDummy, view=view, - renderer='foo/template.pt') - actions = context.actions - self.assertEqual(len(actions), 1) - discrim = ('view', IDummy, '', None, IView, None, None, None, None, - None, False, None, None, None) - self.assertEqual(actions[0]['discriminator'], discrim) - register = actions[0]['callable'] - register() - regview = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - self.assertEqual(regview(None, None).body, 'OK') - - def test_with_custom_predicates(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - view = lambda *arg: 'OK' - def pred1(context, request): - return True - def pred2(context, request): - return True - preds = (pred1, pred2) - self._callFUT(context, 'repoze.view', IDummy, view=view, - custom_predicates=preds) - actions = context.actions - self.assertEqual(len(actions), 1) - discrim = ('view', IDummy, '', None, IView, None, None, None, None, - None, False, None, None, None) - discrim = discrim + tuple(sorted(preds)) - self.assertEqual(actions[0]['discriminator'], discrim) - register = actions[0]['callable'] - register() - regview = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - self.assertEqual(regview(None, None), 'OK') - - def test_context_trumps_for(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - view = lambda *arg: 'OK' - class Foo: - pass - self._callFUT(context, 'repoze.view', for_=Foo, view=view, - context=IDummy) - actions = context.actions - self.assertEqual(len(actions), 1) - discrim = ('view', IDummy, '', None, IView, None, None, None, None, - None, False, None, None, None) - self.assertEqual(actions[0]['discriminator'], discrim) - register = actions[0]['callable'] - register() - regview = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - self.assertEqual(regview(None, None), 'OK') - - def test_with_for(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - view = lambda *arg: 'OK' - class Foo: - pass - self._callFUT(context, 'repoze.view', for_=IDummy, view=view) - actions = context.actions - self.assertEqual(len(actions), 1) - discrim = ('view', IDummy, '', None, IView, None, None, None, None, - None, False, None, None, None) - self.assertEqual(actions[0]['discriminator'], discrim) - register = actions[0]['callable'] - register() - regview = reg.adapters.lookup( - (IViewClassifier, IRequest, IDummy), IView, name='') - self.assertEqual(regview(None, None), 'OK') - -class TestNotFoundDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, view, **kw): - from repoze.bfg.zcml import notfound - return notfound(context, view, **kw) - - def test_it(self): - from zope.interface import implementedBy - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.exceptions import NotFound - - context = DummyContext() - def view(request): - return 'OK' - self._callFUT(context, view) - actions = context.actions - self.assertEqual(len(actions), 1) - - discrim = ('view', NotFound, '', None, IView, None, None, None, None, - None, False, None, None, None) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], discrim) - register = regadapt['callable'] - register() - reg = get_current_registry() - derived_view = reg.adapters.lookup( - (IViewClassifier, IRequest, implementedBy(NotFound)), - IView, default=None) - - self.assertNotEqual(derived_view, None) - self.assertEqual(derived_view(None, None), 'OK') - self.assertEqual(derived_view.__name__, 'bwcompat_view') - - def test_it_with_dotted_renderer(self): - from zope.interface import implementedBy - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.exceptions import NotFound - from repoze.bfg.configuration import Configurator - context = DummyContext() - reg = get_current_registry() - config = Configurator(reg) - def dummy_renderer_factory(*arg, **kw): - return lambda *arg, **kw: 'OK' - config.add_renderer('.pt', dummy_renderer_factory) - def view(request): - return {} - self._callFUT(context, view, renderer='fake.pt') - actions = context.actions - regadapt = actions[0] - register = regadapt['callable'] - register() - derived_view = reg.adapters.lookup( - (IViewClassifier, IRequest, implementedBy(NotFound)), - IView, default=None) - self.assertNotEqual(derived_view, None) - self.assertEqual(derived_view(None, None).body, 'OK') - self.assertEqual(derived_view.__name__, 'bwcompat_view') - -class TestForbiddenDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, view, **kw): - from repoze.bfg.zcml import forbidden - return forbidden(context, view, **kw) - - def test_it(self): - from zope.interface import implementedBy - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.exceptions import Forbidden - context = DummyContext() - def view(request): - return 'OK' - self._callFUT(context, view) - actions = context.actions - - self.assertEqual(len(actions), 1) - - discrim = ('view', Forbidden, '', None, IView, None, None, None, None, - None, False, None, None, None) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], discrim) - register = regadapt['callable'] - register() - reg = get_current_registry() - derived_view = reg.adapters.lookup( - (IViewClassifier, IRequest, implementedBy(Forbidden)), - IView, default=None) - - self.assertNotEqual(derived_view, None) - self.assertEqual(derived_view(None, None), 'OK') - self.assertEqual(derived_view.__name__, 'bwcompat_view') - - def test_it_with_dotted_renderer(self): - from zope.interface import implementedBy - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRequest - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.exceptions import Forbidden - from repoze.bfg.configuration import Configurator - context = DummyContext() - reg = get_current_registry() - config = Configurator(reg) - def dummy_renderer_factory(*arg, **kw): - return lambda *arg, **kw: 'OK' - config.add_renderer('.pt', dummy_renderer_factory) - def view(request): - return {} - self._callFUT(context, view, renderer='fake.pt') - actions = context.actions - regadapt = actions[0] - register = regadapt['callable'] - register() - derived_view = reg.adapters.lookup( - (IViewClassifier, IRequest, implementedBy(Forbidden)), - IView, default=None) - self.assertNotEqual(derived_view, None) - self.assertEqual(derived_view(None, None).body, 'OK') - self.assertEqual(derived_view.__name__, 'bwcompat_view') - -class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, **kw): - from repoze.bfg.zcml import repozewho1authenticationpolicy - return repozewho1authenticationpolicy(context, **kw) - - def test_it_defaults(self): - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - from repoze.bfg.interfaces import IAuthenticationPolicy - context = DummyContext() - self._callFUT(context) - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.callback, None) - self.assertEqual(policy.identifier_name, 'auth_tkt') - - def test_it(self): - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - from repoze.bfg.interfaces import IAuthenticationPolicy - context = DummyContext() - def callback(identity, request): - """ """ - self._callFUT(context, identifier_name='something', callback=callback) - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.callback, callback) - self.assertEqual(policy.identifier_name, 'something') - -class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, **kw): - from repoze.bfg.zcml import remoteuserauthenticationpolicy - return remoteuserauthenticationpolicy(context, **kw) - - def test_defaults(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - context = DummyContext() - def callback(identity, request): - """ """ - self._callFUT(context) - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.environ_key, 'REMOTE_USER') - self.assertEqual(policy.callback, None) - - def test_it(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.threadlocal import get_current_registry - context = DummyContext() - def callback(identity, request): - """ """ - self._callFUT(context, environ_key='BLAH', callback=callback) - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - reg = get_current_registry() - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.environ_key, 'BLAH') - self.assertEqual(policy.callback, callback) - -class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, secret, **kw): - from repoze.bfg.zcml import authtktauthenticationpolicy - return authtktauthenticationpolicy(context, secret, **kw) - - def test_it_defaults(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - context = DummyContext() - self._callFUT(context, 'sosecret') - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.cookie.secret, 'sosecret') - self.assertEqual(policy.callback, None) - - def test_it_noconfigerror(self): - from repoze.bfg.interfaces import IAuthenticationPolicy - from repoze.bfg.threadlocal import get_current_registry - reg = get_current_registry() - context = DummyContext() - def callback(identity, request): - """ """ - self._callFUT(context, 'sosecret', callback=callback, - cookie_name='repoze.bfg.auth_tkt', - secure=True, include_ip=True, timeout=100, - reissue_time=60, http_only=True, path="/sub/") - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy.cookie.path, '/sub/') - self.assertEqual(policy.cookie.http_only, True) - self.assertEqual(policy.cookie.secret, 'sosecret') - self.assertEqual(policy.callback, callback) - - def test_it_configerror(self): - from repoze.bfg.exceptions import ConfigurationError - context = DummyContext() - def callback(identity, request): - """ """ - self.assertRaises(ConfigurationError, - self._callFUT, - context, 'sosecret', callback=callback, - cookie_name='repoze.bfg.auth_tkt', - secure=True, include_ip=True, timeout=100, - reissue_time=500, http_only=True, - path="/cgi-bin/bfg.cgi/") - -class TestACLAuthorizationPolicyDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, **kw): - from repoze.bfg.zcml import aclauthorizationpolicy - return aclauthorizationpolicy(context, **kw) - - def test_it(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.authorization import ACLAuthorizationPolicy - from repoze.bfg.interfaces import IAuthorizationPolicy - reg = get_current_registry() - context = DummyContext() - def callback(identity, request): - """ """ - self._callFUT(context) - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IAuthorizationPolicy) - self.assertEqual(regadapt['callable'], None) - self.assertEqual(regadapt['args'], ()) - policy = reg.getUtility(IAuthorizationPolicy) - self.assertEqual(policy.__class__, ACLAuthorizationPolicy) - -class TestRouteDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import route - return route(*arg, **kw) - - def _assertRoute(self, name, pattern, num_predicates=0): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRoutesMapper - reg = get_current_registry() - mapper = reg.getUtility(IRoutesMapper) - routes = mapper.get_routes() - route = routes[0] - self.assertEqual(len(routes), 1) - self.assertEqual(route.name, name) - self.assertEqual(route.pattern, pattern) - self.assertEqual(len(routes[0].predicates), num_predicates) - return route - - def test_with_view(self): - from repoze.bfg.threadlocal import get_current_registry - from zope.interface import Interface - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRouteRequest - context = DummyContext() - view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'pattern', view=view) - actions = context.actions - self.assertEqual(len(actions), 2) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - view_action = actions[1] - reg = get_current_registry() - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', None, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) - wrapped = reg.adapters.lookup( - (IViewClassifier, request_type, Interface), IView, name='') - self.failUnless(wrapped) - - def test_with_view_and_view_context(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRouteRequest - context = DummyContext() - view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'pattern', view=view, - view_context=IDummy) - actions = context.actions - self.assertEqual(len(actions), 2) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - view_action = actions[1] - reg = get_current_registry() - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', IDummy, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) - wrapped = reg.adapters.lookup( - (IViewClassifier, request_type, IDummy), IView, name='') - self.failUnless(wrapped) - - def test_with_view_context_trumps_view_for(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRouteRequest - context = DummyContext() - view = lambda *arg: 'OK' - class Foo: - pass - self._callFUT(context, 'name', 'pattern', view=view, - view_context=IDummy, view_for=Foo) - actions = context.actions - self.assertEqual(len(actions), 2) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - view_action = actions[1] - reg = get_current_registry() - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', IDummy, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) - wrapped = reg.adapters.lookup( - (IViewClassifier, request_type, IDummy), IView, name='') - self.failUnless(wrapped) - - def test_with_dotted_renderer(self): - - from repoze.bfg.threadlocal import get_current_registry - from zope.interface import Interface - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRouteRequest - from repoze.bfg.interfaces import IRendererFactory - reg = get_current_registry() - def renderer(path): - return lambda *arg: 'OK' - reg.registerUtility(renderer, IRendererFactory, name='.pt') - - context = DummyContext() - view = lambda *arg: 'OK' - self._callFUT(context, 'name', 'pattern', view=view, - renderer='fixtureapp/templates/foo.pt') - actions = context.actions - self.assertEqual(len(actions), 2) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - view_action = actions[1] - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', None, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) - wrapped = reg.adapters.lookup( - (IViewClassifier, request_type, Interface), IView, name='') - self.failUnless(wrapped) - request = DummyRequest() - result = wrapped(None, request) - self.assertEqual(result.body, 'OK') - - def test_with_custom_predicates(self): - def pred1(context, request): pass - def pred2(context, request): pass - preds = tuple(sorted([pred1, pred2])) - - context = DummyContext() - self._callFUT(context, 'name', 'pattern', - custom_predicates=(pred1, pred2)) - actions = context.actions - self.assertEqual(len(actions), 1) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual( - route_discriminator, - ('route', 'name', False, None, None, None, None,None) + preds) - self._assertRoute('name', 'pattern', 2) - - def test_with_path_argument_no_pattern(self): - context = DummyContext() - self._callFUT(context, 'name', path='pattern') - actions = context.actions - self.assertEqual(len(actions), 1) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - def test_with_path_argument_and_pattern(self): - context = DummyContext() - self._callFUT(context, 'name', pattern='pattern', path='path') - actions = context.actions - self.assertEqual(len(actions), 1) - - route_action = actions[0] - route_action['callable']() - route_discriminator = route_action['discriminator'] - self.assertEqual(route_discriminator, - ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - - - def test_with_neither_path_nor_pattern(self): - from repoze.bfg.exceptions import ConfigurationError - context = DummyContext() - self.assertRaises(ConfigurationError, self._callFUT, context, 'name') - -class TestStaticDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import static - return static(*arg, **kw) - - def test_it_with_slash(self): - from repoze.bfg.static import PackageURLParser - from repoze.bfg.threadlocal import get_current_registry - from zope.interface import implementedBy - from repoze.bfg.static import StaticURLInfo - from repoze.bfg.interfaces import IView - from repoze.bfg.interfaces import IViewClassifier - from repoze.bfg.interfaces import IRouteRequest - from repoze.bfg.interfaces import IRoutesMapper - context = DummyContext() - self._callFUT(context, 'name', 'fixtures/static') - actions = context.actions - self.assertEqual(len(actions), 2) - - reg = get_current_registry() - - route_action = actions[0] - discriminator = route_action['discriminator'] - self.assertEqual(discriminator, ('static', 'name')) - route_action['callable'](*route_action['args'], **route_action['kw']) - mapper = reg.getUtility(IRoutesMapper) - routes = mapper.get_routes() - self.assertEqual(len(routes), 1) - self.assertEqual(routes[0].pattern, 'name/*subpath') - self.assertEqual(routes[0].name, 'name/') - - view_action = actions[1] - discriminator = view_action['discriminator'] - self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) - self.assertEqual(discriminator[4], IView) - iface = implementedBy(StaticURLInfo) - request_type = reg.getUtility(IRouteRequest, 'name/') - view = reg.adapters.lookup( - (IViewClassifier, request_type, iface), IView, name='') - request = DummyRequest() - self.assertEqual(view(None, request).__class__, PackageURLParser) - -class TestResourceDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import resource - return resource(*arg, **kw) - - def test_it(self): - from repoze.bfg.configuration import Configurator - context = DummyContext() - self._callFUT(context, 'a', 'b') - actions = context.actions - self.assertEqual(len(actions), 1) - action = actions[0] - self.assertEqual(action['callable'].im_func, - Configurator.override_resource.im_func) - self.assertEqual(action['discriminator'], None) - self.assertEqual(action['args'], ('a', 'b', None)) - - -class TestRendererDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import renderer - return renderer(*arg, **kw) - - def test_it(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IRendererFactory - context = DummyContext() - renderer = lambda *arg, **kw: None - self._callFUT(context, renderer, 'r') - actions = context.actions - self.assertEqual(len(actions), 1) - action = actions[0] - self.assertEqual(action['discriminator'], (IRendererFactory, 'r')) - reg = get_current_registry() - self.failUnless(reg.getUtility(IRendererFactory, 'r'), renderer) - -class TestZCMLConfigure(unittest.TestCase): - i = 0 - def _callFUT(self, path, package): - from repoze.bfg.zcml import zcml_configure - return zcml_configure(path, package) - - def setUp(self): - testing.setUp() - self.tempdir = None - import sys - import os - import tempfile - from repoze.bfg.path import package_path - from repoze.bfg.tests import fixtureapp as package - import shutil - tempdir = tempfile.mkdtemp() - modname = 'myfixture%s' % self.i - self.i += 1 - self.packagepath = os.path.join(tempdir, modname) - fixturedir = package_path(package) - shutil.copytree(fixturedir, self.packagepath) - sys.path.insert(0, tempdir) - self.module = __import__(modname) - self.tempdir = tempdir - - def tearDown(self): - testing.tearDown() - import sys - import shutil - if self.module is not None: - del sys.modules[self.module.__name__] - if self.tempdir is not None: - sys.path.pop(0) - shutil.rmtree(self.tempdir) - - def test_zcml_configure(self): - actions = self._callFUT('configure.zcml', self.module) - self.failUnless(actions) - self.failUnless(isinstance(actions, list)) - - def test_zcml_configure_nonexistent_configure_dot_zcml(self): - import os - os.remove(os.path.join(self.packagepath, 'configure.zcml')) - self.assertRaises(IOError, self._callFUT, 'configure.zcml', - self.module) - -class TestZCMLScanDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, package): - from repoze.bfg.zcml import scan - return scan(context, package) - - def test_it(self): - from repoze.bfg.configuration import Configurator - dummy_module = DummyModule() - context = DummyContext() - self._callFUT(context, dummy_module) - actions = context.actions - self.assertEqual(len(actions), 1) - action = actions[0] - self.assertEqual(action['callable'].im_func, Configurator.scan.im_func) - self.assertEqual(action['discriminator'], None) - self.assertEqual(action['args'], (dummy_module, None, None)) - -class TestAdapterDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import adapter - return adapter(*arg, **kw) - - def test_for_is_None_no_adaptedBy(self): - context = DummyContext() - factory = DummyFactory() - self.assertRaises(TypeError, self._callFUT, context, [factory], - provides=None, for_=None) - - def test_for_is_None_adaptedBy_still_None(self): - context = DummyContext() - factory = DummyFactory() - factory.__component_adapts__ = None - self.assertRaises(TypeError, self._callFUT, context, [factory], - provides=None, for_=None) - - def test_for_is_None_adaptedBy_set(self): - from repoze.bfg.registry import Registry - context = DummyContext() - factory = DummyFactory() - factory.__component_adapts__ = (IDummy,) - self._callFUT(context, [factory], provides=IFactory, for_=None) - self.assertEqual(len(context.actions), 1) - regadapt = context.actions[0] - self.assertEqual(regadapt['discriminator'], - ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'].im_func, - Registry.registerAdapter.im_func) - self.assertEqual(regadapt['args'], - (factory, (IDummy,), IFactory, '', None)) - - def test_provides_missing(self): - context = DummyContext() - factory = DummyFactory() - self.assertRaises(TypeError, self._callFUT, context, [factory], - provides=None, for_=(IDummy,)) - - def test_provides_obtained_via_implementedBy(self): - from repoze.bfg.registry import Registry - context = DummyContext() - self._callFUT(context, [DummyFactory], for_=(IDummy,)) - regadapt = context.actions[0] - self.assertEqual(regadapt['discriminator'], - ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'].im_func, - Registry.registerAdapter.im_func) - self.assertEqual(regadapt['args'], - (DummyFactory, (IDummy,), IFactory, '', None)) - - def test_multiple_factories_multiple_for(self): - context = DummyContext() - factory = DummyFactory() - self.assertRaises(ValueError, self._callFUT, context, - [factory, factory], - provides=IFactory, - for_=(IDummy, IDummy)) - - def test_no_factories_multiple_for(self): - context = DummyContext() - self.assertRaises(ValueError, self._callFUT, context, - factory=[], - provides=IFactory, - for_=(IDummy, IDummy)) - - def test_rolled_up_factories(self): - from repoze.bfg.registry import Registry - context = DummyContext() - factory = DummyFactory() - self._callFUT(context, - [factory, factory], - provides=IFactory, - for_=(IDummy,)) - regadapt = context.actions[0] - self.assertEqual(regadapt['discriminator'], - ('adapter', (IDummy,), IFactory, '')) - self.assertEqual(regadapt['callable'].im_func, - Registry.registerAdapter.im_func) - self.assertEqual(regadapt['args'][0].__module__, 'repoze.bfg.zcml') - -class TestSubscriberDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import subscriber - return subscriber(*arg, **kw) - - def test_no_factory_no_handler(self): - context = DummyContext() - self.assertRaises(TypeError, - self._callFUT, context, for_=None, factory=None, - handler=None, - provides=None) - - def test_handler_with_provides(self): - context = DummyContext() - self.assertRaises(TypeError, - self._callFUT, context, for_=None, factory=None, - handler=1, provides=1) - - def test_handler_and_factory(self): - context = DummyContext() - self.assertRaises(TypeError, - self._callFUT, context, for_=None, factory=1, - handler=1, provides=None) - - def test_no_provides_with_factory(self): - context = DummyContext() - self.assertRaises(TypeError, - self._callFUT, context, for_=None, factory=1, - handler=None, provides=None) - - def test_adapted_by_as_for_is_None(self): - context = DummyContext() - factory = DummyFactory() - factory.__component_adapts__ = None - self.assertRaises(TypeError, self._callFUT, context, for_=None, - factory=factory, handler=None, provides=IFactory) - - def test_register_with_factory(self): - from repoze.bfg.registry import Registry - context = DummyContext() - factory = DummyFactory() - self._callFUT(context, for_=(IDummy,), - factory=factory, handler=None, provides=IFactory) - self.assertEqual(len(context.actions), 1) - subadapt = context.actions[0] - self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'].im_func, - Registry.registerSubscriptionAdapter.im_func) - self.assertEqual(subadapt['args'], - (factory, (IDummy,), IFactory, None, None) ) - - def test_register_with_handler(self): - from repoze.bfg.configuration import Configurator - context = DummyContext() - factory = DummyFactory() - self._callFUT(context, for_=(IDummy,), - factory=None, handler=factory) - self.assertEqual(len(context.actions), 1) - subadapt = context.actions[0] - self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'].im_func, - Configurator.add_subscriber.im_func) - self.assertEqual(subadapt['args'], (factory, (IDummy,), None) ) - -class TestUtilityDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import utility - return utility(*arg, **kw) - - def test_factory_and_component(self): - context = DummyContext() - self.assertRaises(TypeError, self._callFUT, - context, factory=1, component=1) - - def test_missing_provides(self): - context = DummyContext() - self.assertRaises(TypeError, self._callFUT, context, provides=None) - - def test_provides_from_factory_implements(self): - from repoze.bfg.registry import Registry - context = DummyContext() - self._callFUT(context, factory=DummyFactory) - self.assertEqual(len(context.actions), 1) - utility = context.actions[0] - self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) - self.assertEqual(utility['callable'].im_func, - Registry.registerUtility.im_func) - self.assertEqual(utility['args'], (None, IFactory, '', None)) - self.assertEqual(utility['kw'], {'factory':DummyFactory}) - - def test_provides_from_component_provides(self): - from repoze.bfg.registry import Registry - context = DummyContext() - component = DummyFactory() - self._callFUT(context, component=component) - self.assertEqual(len(context.actions), 1) - utility = context.actions[0] - self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) - self.assertEqual(utility['callable'].im_func, - Registry.registerUtility.im_func) - self.assertEqual(utility['args'], (component, IFactory, '', None)) - self.assertEqual(utility['kw'], {}) - -class TestTranslationDirDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import translationdir - return translationdir(*arg, **kw) - - def test_it(self): - from repoze.bfg.configuration import Configurator - context = DummyContext() - tdir = 'repoze.bfg.tests.localeapp:locale' - self._callFUT(context, tdir) - actions = context.actions - self.assertEqual(len(actions), 1) - action = context.actions[0] - self.assertEqual(action['discriminator'], ('tdir', tdir)) - self.assertEqual(action['callable'].im_func, - Configurator.add_translation_dirs.im_func) - self.assertEqual(action['args'], (tdir,)) - action['callable'](*action['args']) # doesn't blow up - -class TestLocaleNegotiatorDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from repoze.bfg.zcml import localenegotiator - return localenegotiator(*arg, **kw) - - def test_it(self): - from repoze.bfg.configuration import Configurator - context = DummyContext() - dummy_negotiator = object() - self._callFUT(context, dummy_negotiator) - actions = context.actions - self.assertEqual(len(actions), 1) - action = context.actions[0] - self.assertEqual(action['discriminator'], 'lnegotiator') - self.assertEqual(action['callable'].im_func, - Configurator.set_locale_negotiator.im_func) - self.assertEqual(action['args'], (dummy_negotiator,)) - action['callable'](*action['args']) # doesn't blow up - -class TestDefaultPermissionDirective(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, context, name): - from repoze.bfg.zcml import default_permission - return default_permission(context, name) - - def test_it(self): - from repoze.bfg.threadlocal import get_current_registry - from repoze.bfg.interfaces import IDefaultPermission - reg = get_current_registry() - context = DummyContext() - self._callFUT(context, 'view') - actions = context.actions - self.assertEqual(len(actions), 1) - regadapt = actions[0] - self.assertEqual(regadapt['discriminator'], IDefaultPermission) - perm = reg.getUtility(IDefaultPermission) - self.assertEqual(perm, 'view') - -class TestLoadZCML(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_it(self): - from zope.configuration import xmlconfig - import repoze.bfg.includes - xmlconfig.file('configure.zcml', package=repoze.bfg.includes) - -class TestRolledUpFactory(unittest.TestCase): - def _callFUT(self, *factories): - from repoze.bfg.zcml import _rolledUpFactory - return _rolledUpFactory(factories) - - def test_it(self): - def foo(ob): - return ob - factory = self._callFUT(foo, foo) - result = factory(True) - self.assertEqual(result, True) - -class Test_path_spec(unittest.TestCase): - def _callFUT(self, context, path): - from repoze.bfg.zcml import path_spec - return path_spec(context, path) - - def test_no_package_attr(self): - context = DummyContext() - path = '/thepath' - result = self._callFUT(context, path) - self.assertEqual(result, path) - - def test_package_attr_None(self): - context = DummyContext() - context.package = None - path = '/thepath' - result = self._callFUT(context, path) - self.assertEqual(result, path) - - def test_package_path_doesnt_start_with_abspath(self): - context = DummyContext() - context.package = DummyPackage('repoze.bfg.tests') - path = '/thepath' - result = self._callFUT(context, path) - self.assertEqual(result, path) - - def test_package_path_starts_with_abspath(self): - import pkg_resources - import os - context = DummyContext() - package = DummyPackage('repoze.bfg.tests') - package_path = pkg_resources.resource_filename('repoze.bfg.tests', '') - template_path = os.path.join(package_path, 'templates/foo.pt') - context.package = package - result = self._callFUT(context, template_path) - self.assertEqual(result, 'repoze.bfg.tests:templates/foo.pt') - - def test_package_name_is___main__(self): - context = DummyContext() - package = DummyPackage('__main__') - context.package = package - result = self._callFUT(context, '/foo.pt') - self.assertEqual(result, '/foo.pt') - - def test_path_is_already_resource_spec(self): - context = DummyContext() - result = self._callFUT(context, 'repoze.bfg.tests:foo.pt') - self.assertEqual(result, 'repoze.bfg.tests:foo.pt') - -class IDummy(Interface): - pass - -class IFactory(Interface): - pass - -class DummyFactory(object): - implements(IFactory) - def __call__(self): - """ """ - -class DummyModule: - __path__ = "foo" - __name__ = "dummy" - __file__ = '' - -class DummyContext: - def __init__(self, resolved=DummyModule): - self.actions = [] - self.info = None - self.resolved = resolved - self.package = None - - def action(self, discriminator, callable=None, args=(), kw={}, order=0): - self.actions.append( - {'discriminator':discriminator, - 'callable':callable, - 'args':args, - 'kw':kw} - ) - - def path(self, path): - return path - - def resolve(self, dottedname): - return self.resolved - -class Dummy: - pass - -class DummyRoute: - pass - -class DummyRequest: - subpath = () - def __init__(self, environ=None): - if environ is None: - environ = {} - self.environ = environ - self.path_info = environ.get('PATH_INFO', None) - - def get_response(self, app): - return app - - def copy(self): - return self - -class DummyPackage(object): - def __init__(self, name): - self.__name__ = name - self.__file__ = '/__init__.py' - diff --git a/repoze/bfg/tests/viewdecoratorapp/__init__.py b/repoze/bfg/tests/viewdecoratorapp/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/repoze/bfg/tests/viewdecoratorapp/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/repoze/bfg/tests/viewdecoratorapp/configure.zcml b/repoze/bfg/tests/viewdecoratorapp/configure.zcml deleted file mode 100644 index 6867046df..000000000 --- a/repoze/bfg/tests/viewdecoratorapp/configure.zcml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/repoze/bfg/tests/viewdecoratorapp/views/__init__.py b/repoze/bfg/tests/viewdecoratorapp/views/__init__.py deleted file mode 100644 index 5bb534f79..000000000 --- a/repoze/bfg/tests/viewdecoratorapp/views/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt b/repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt deleted file mode 100644 index 6a2f701b6..000000000 --- a/repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt +++ /dev/null @@ -1,3 +0,0 @@ - -${result} - diff --git a/repoze/bfg/tests/viewdecoratorapp/views/views.py b/repoze/bfg/tests/viewdecoratorapp/views/views.py deleted file mode 100644 index 29f8c7fd4..000000000 --- a/repoze/bfg/tests/viewdecoratorapp/views/views.py +++ /dev/null @@ -1,17 +0,0 @@ -import os -from repoze.bfg.view import bfg_view - -@bfg_view(renderer='templates/foo.pt', name='first') -def first(request): - return {'result':'OK1'} - -@bfg_view(renderer='repoze.bfg.tests.viewdecoratorapp.views:templates/foo.pt', - name='second') -def second(request): - return {'result':'OK2'} - -here = os.path.normpath(os.path.dirname(os.path.abspath(__file__))) -foo = os.path.join(here, 'templates', 'foo.pt') -@bfg_view(renderer=foo, name='third') -def third(request): - return {'result':'OK3'} diff --git a/repoze/bfg/threadlocal.py b/repoze/bfg/threadlocal.py deleted file mode 100644 index 631cca62a..000000000 --- a/repoze/bfg/threadlocal.py +++ /dev/null @@ -1,61 +0,0 @@ -import threading - -from repoze.bfg.registry import global_registry - -class ThreadLocalManager(threading.local): - def __init__(self, default=None): - # http://code.google.com/p/google-app-engine-django/issues/detail?id=119 - # we *must* use a keword argument for ``default`` here instead - # of a positional argument to work around a bug in the - # implementation of _threading_local.local in Python, which is - # used by GAE instead of _thread.local - self.stack = [] - self.default = default - - def push(self, info): - self.stack.append(info) - - set = push # b/c - - def pop(self): - if self.stack: - return self.stack.pop() - - def get(self): - try: - return self.stack[-1] - except IndexError: - return self.default() - - def clear(self): - self.stack[:] = [] - -def defaults(): - return {'request':None, 'registry':global_registry} - -manager = ThreadLocalManager(default=defaults) - -def get_current_request(): - """Return the currently active request or ``None`` if no request - is currently active. - - This function should be used *extremely sparingly*, usually only - in unit testing code. it's almost always usually a mistake to use - ``get_current_request`` outside a testing context because its - usage makes it possible to write code that can be neither easily - tested nor scripted. - """ - return manager.get()['request'] - -def get_current_registry(context=None): # context required by getSiteManager API - """Return the currently active :term:`application registry` or the - global application registry if no request is currently active. - - This function should be used *extremely sparingly*, usually only - in unit testing code. it's almost always usually a mistake to use - ``get_current_registry`` outside a testing context because its - usage makes it possible to write code that can be neither easily - tested nor scripted. - """ - return manager.get()['registry'] - diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py deleted file mode 100644 index 84365e2ff..000000000 --- a/repoze/bfg/traversal.py +++ /dev/null @@ -1,674 +0,0 @@ -import urllib - -from zope.interface import implements -from zope.interface.interfaces import IInterface - -from repoze.lru import lru_cache - -from repoze.bfg.interfaces import IContextURL -from repoze.bfg.interfaces import IRequestFactory -from repoze.bfg.interfaces import ITraverser -from repoze.bfg.interfaces import VH_ROOT_KEY - -from repoze.bfg.encode import url_quote -from repoze.bfg.exceptions import URLDecodeError -from repoze.bfg.location import lineage -from repoze.bfg.request import Request -from repoze.bfg.threadlocal import get_current_registry - -def find_root(model): - """ Find the root node in the graph to which ``model`` - belongs. Note that ``model`` should be :term:`location`-aware. - Note that the root node is available in the request object by - accessing the ``request.root`` attribute. - """ - for location in lineage(model): - if location.__parent__ is None: - model = location - break - return model - -def find_model(model, path): - """ Given a model object and a string or tuple representing a path - (such as the return value of - :func:`repoze.bfg.traversal.model_path` or - :func:`repoze.bfg.traversal.model_path_tuple`), return a context - in this application's model graph at the specified path. The - model passed in *must* be :term:`location`-aware. If the path - cannot be resolved (if the respective node in the graph does not - exist), a :exc:`KeyError` will be raised. - - This function is the logical inverse of - :func:`repoze.bfg.traversal.model_path` and - :func:`repoze.bfg.traversal.model_path_tuple`; it can resolve any - path string or tuple generated by either of those functions. - - Rules for passing a *string* as the ``path`` argument: if the - first character in the path string is the with the ``/`` - character, the path will considered absolute and the graph - traversal will start at the root object. If the first character - of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following - manner: each Unicode path segment must be encoded as UTF-8 and as - each path segment must escaped via Python's :mod:`urllib.quote`. - For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or - ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`repoze.bfg.traversal.model_path` function generates strings - which follow these rules (albeit only absolute ones). - - Rules for passing a *tuple* as the ``path`` argument: if the first - element in the path tuple is the empty string (for example ``('', - 'a', 'b', 'c')``, the path is considered absolute and the graph - traversal will start at the graph root object. If the first - element in the path tuple is not the empty string (for example - ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No - URL-quoting or UTF-8-encoding of individual path segments within - the tuple is required (each segment may be any string or unicode - object representing a model name). Model path tuples generated by - :func:`repoze.bfg.traversal.model_path_tuple` can always be - resolved by ``find_model``. - """ - D = traverse(model, path) - view_name = D['view_name'] - context = D['context'] - if view_name: - raise KeyError('%r has no subelement %s' % (context, view_name)) - return context - -def find_interface(model, class_or_interface): - """ - Return the first object found in the parent chain of ``model`` - which, a) if ``class_or_interface`` is a Python class object, is - an instance of the class or any subclass of that class or b) if - ``class_or_interface`` is a :term:`interface`, provides the - specified interface. Return ``None`` if no object providing - ``interface_or_class`` can be found in the parent chain. The - ``model`` passed in *must* be :term:`location`-aware. - """ - if IInterface.providedBy(class_or_interface): - test = class_or_interface.providedBy - else: - test = lambda arg: isinstance(arg, class_or_interface) - for location in lineage(model): - if test(location): - return location - -def model_path(model, *elements): - """ Return a string object representing the absolute physical path - of the model object based on its position in the model graph, e.g - ``/foo/bar``. Any positional arguments passed in as ``elements`` - will be appended as path segments to the end of the model path. - For instance, if the model's path is ``/foo/bar`` and ``elements`` - equals ``('a', 'b')``, the returned string will be - ``/foo/bar/a/b``. The first character in the string will always - be the ``/`` character (a leading ``/`` character in a path string - represents that the path is absolute). - - Model path strings returned will be escaped in the following - manner: each unicode path segment will be encoded as UTF-8 and - each path segment will be escaped via Python's :mod:`urllib.quote`. - For example, ``/path/to%20the/La%20Pe%C3%B1a``. - - This function is a logical inverse of - :mod:`repoze.bfg.traversal.find_model`: it can be used to generate - path references that can later be resolved via that function. - - The ``model`` passed in *must* be :term:`location`-aware. - - .. note:: Each segment in the path string returned will use the - ``__name__`` attribute of the model it represents within - the graph. Each of these segments *should* be a unicode - or string object (as per the contract of - :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a - ``__name__`` which (by error) is a dictionary, the - :func:`repoze.bfg.traversal.model_path` function will - attempt to append it to a string and it will cause a - :exc:`repoze.bfg.exceptions.URLDecodeError`. - - .. note:: The :term:`root` model *must* have a ``__name__`` - attribute with a value of either ``None`` or the empty - string for paths to be generated properly. If the root - model has a non-null ``__name__`` attribute, its name - will be prepended to the generated path rather than a - single leading '/' character. - """ - # joining strings is a bit expensive so we delegate to a function - # which caches the joined result for us - return _join_path_tuple(model_path_tuple(model, *elements)) - -def traverse(model, path): - """Given a model object as ``model`` and a string or tuple - representing a path as ``path`` (such as the return value of - :func:`repoze.bfg.traversal.model_path` or - :func:`repoze.bfg.traversal.model_path_tuple` or the value of - ``request.environ['PATH_INFO']``), return a dictionary with the - keys ``context``, ``root``, ``view_name``, ``subpath``, - ``traversed``, ``virtual_root``, and ``virtual_root_path``. - - A definition of each value in the returned dictionary: - - - ``context``: The :term:`context` (a :term:`model` object) found - via traversal or url dispatch. If the ``path`` passed in is the - empty string, the value of the ``model`` argument passed to this - function is returned. - - - ``root``: The model object at which :term:`traversal` begins. - If the ``model`` passed in was found via url dispatch or if the - ``path`` passed in was relative (non-absolute), the value of the - ``model`` argument passed to this function is returned. - - - ``view_name``: The :term:`view name` found during - :term:`traversal` or :term:`url dispatch`; if the ``model`` was - found via traversal, this is usually a representation of the - path segment which directly follows the path to the ``context`` - in the ``path``. The ``view_name`` will be a Unicode object or - the empty string. The ``view_name`` will be the empty string if - there is no element which follows the ``context`` path. An - example: if the path passed is ``/foo/bar``, and a context - object is found at ``/foo`` (but not at ``/foo/bar``), the 'view - name' will be ``u'bar'``. If the ``model`` was found via - urldispatch, the view_name will be the name the route found was - registered with. - - - ``subpath``: For a ``model`` found via :term:`traversal`, this - is a sequence of path segments found in the ``path`` that follow - the ``view_name`` (if any). Each of these items is a Unicode - object. If no path segments follow the ``view_name``, the - subpath will be the empty sequence. An example: if the path - passed is ``/foo/bar/baz/buz``, and a context object is found at - ``/foo`` (but not ``/foo/bar``), the 'view name' will be - ``u'bar'`` and the :term:`subpath` will be ``[u'baz', u'buz']``. - For a ``model`` found via url dispatch, the subpath will be a - sequence of values discerned from ``*subpath`` in the route - pattern matched or the empty sequence. - - - ``traversed``: The sequence of path elements traversed from the - root to find the ``context`` object during :term:`traversal`. - Each of these items is a Unicode object. If no path segments - were traversed to find the ``context`` object (e.g. if the - ``path`` provided is the empty string), the ``traversed`` value - will be the empty sequence. If the ``model`` is a model found - via :term:`url dispatch`, traversed will be None. - - - ``virtual_root``: A model object representing the 'virtual' root - of the object graph being traversed during :term:`traversal`. - See :ref:`vhosting_chapter` for a definition of the virtual root - object. If no virtual hosting is in effect, and the ``path`` - passed in was absolute, the ``virtual_root`` will be the - *physical* root object (the object at which :term:`traversal` - begins). If the ``model`` passed in was found via :term:`URL - dispatch` or if the ``path`` passed in was relative, the - ``virtual_root`` will always equal the ``root`` object (the - model passed in). - - - ``virtual_root_path`` -- If :term:`traversal` was used to find - the ``model``, this will be the sequence of path elements - traversed to find the ``virtual_root`` object. Each of these - items is a Unicode object. If no path segments were traversed - to find the ``virtual_root`` object (e.g. if virtual hosting is - not in effect), the ``traversed`` value will be the empty list. - If url dispatch was used to find the ``model``, this will be - ``None``. - - If the path cannot be resolved, a :exc:`KeyError` will be raised. - - Rules for passing a *string* as the ``path`` argument: if the - first character in the path string is the with the ``/`` - character, the path will considered absolute and the graph - traversal will start at the root object. If the first character - of the path string is *not* the ``/`` character, the path is - considered relative and graph traversal will begin at the model - object supplied to the function as the ``model`` argument. If an - empty string is passed as ``path``, the ``model`` passed in will - be returned. Model path strings must be escaped in the following - manner: each Unicode path segment must be encoded as UTF-8 and as - each path segment must escaped via Python's :mod:`urllib.quote`. - For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or - ``to%20the/La%20Pe%C3%B1a`` (relative). The - :func:`repoze.bfg.traversal.model_path` function generates strings - which follow these rules (albeit only absolute ones). - - Rules for passing a *tuple* as the ``path`` argument: if the first - element in the path tuple is the empty string (for example ``('', - 'a', 'b', 'c')``, the path is considered absolute and the graph - traversal will start at the graph root object. If the first - element in the path tuple is not the empty string (for example - ``('a', 'b', 'c')``), the path is considered relative and graph - traversal will begin at the model object supplied to the function - as the ``model`` argument. If an empty sequence is passed as - ``path``, the ``model`` passed in itself will be returned. No - URL-quoting or UTF-8-encoding of individual path segments within - the tuple is required (each segment may be any string or unicode - object representing a model name). - - Explanation of the conversion of ``path`` segment values to - Unicode during traversal: Each segment is URL-unquoted, and - decoded into Unicode. Each segment is assumed to be encoded using - the UTF-8 encoding (or a subset, such as ASCII); a - :exc:`repoze.bfg.exceptions.URLDecodeError` is raised if a segment - cannot be decoded. If a segment name is empty or if it is ``.``, - it is ignored. If a segment name is ``..``, the previous segment - is deleted, and the ``..`` is ignored. As a result of this - process, the return values ``view_name``, each element in the - ``subpath``, each element in ``traversed``, and each element in - the ``virtual_root_path`` will be Unicode as opposed to a string, - and will be URL-decoded. - """ - - if hasattr(path, '__iter__'): - # the traverser factory expects PATH_INFO to be a string, not - # unicode and it expects path segments to be utf-8 and - # urlencoded (it's the same traverser which accepts PATH_INFO - # from user agents; user agents always send strings). - if path: - path = _join_path_tuple(tuple(path)) - else: - path = '' - - if path and path[0] == '/': - model = find_root(model) - - reg = get_current_registry() - request_factory = reg.queryUtility(IRequestFactory, default=Request) - request = request_factory.blank(path) - request.registry = reg - traverser = reg.queryAdapter(model, ITraverser) - if traverser is None: - traverser = ModelGraphTraverser(model) - - return traverser(request) - -def model_path_tuple(model, *elements): - """ - Return a tuple representing the absolute physical path of the - ``model`` object based on its position in an object graph, e.g - ``('', 'foo', 'bar')``. Any positional arguments passed in as - ``elements`` will be appended as elements in the tuple - representing the model path. For instance, if the model's - path is ``('', 'foo', 'bar')`` and elements equals ``('a', 'b')``, - the returned tuple will be ``('', 'foo', 'bar', 'a', b')``. The - first element of this tuple will always be the empty string (a - leading empty string element in a path tuple represents that the - path is absolute). - - This function is a logical inverse of - :func:`repoze.bfg.traversal.find_model`: it can be used to - generate path references that can later be resolved that function. - - The ``model`` passed in *must* be :term:`location`-aware. - - .. note:: Each segment in the path tuple returned will equal the - ``__name__`` attribute of the model it represents within - the graph. Each of these segments *should* be a unicode - or string object (as per the contract of - :term:`location`-awareness). However, no conversion or - safety checking of model names is performed. For - instance, if one of the models in your graph has a - ``__name__`` which (by error) is a dictionary, that - dictionary will be placed in the path tuple; no warning - or error will be given. - - .. note:: The :term:`root` model *must* have a ``__name__`` - attribute with a value of either ``None`` or the empty - string for path tuples to be generated properly. If - the root model has a non-null ``__name__`` attribute, - its name will be the first element in the generated - path tuple rather than the empty string. - """ - return tuple(_model_path_list(model, *elements)) - -def _model_path_list(model, *elements): - """ Implementation detail shared by model_path and model_path_tuple """ - path = [loc.__name__ or '' for loc in lineage(model)] - path.reverse() - path.extend(elements) - return path - -def virtual_root(model, request): - """ - Provided any :term:`model` and a :term:`request` object, return - the model object representing the :term:`virtual root` of the - current :term:`request`. Using a virtual root in a - :term:`traversal` -based :mod:`repoze.bfg` application permits - rooting, for example, the object at the traversal path ``/cms`` at - ``http://example.com/`` instead of rooting it at - ``http://example.com/cms/``. - - If the ``model`` passed in is a context obtained via - :term:`traversal`, and if the ``HTTP_X_VHM_ROOT`` key is in the - WSGI environment, the value of this key will be treated as a - 'virtual root path': the :func:`repoze.bfg.traversal.find_model` - API will be used to find the virtual root object using this path; - if the object is found, it will be returned. If the - ``HTTP_X_VHM_ROOT`` key is is not present in the WSGI environment, - the physical :term:`root` of the graph will be returned instead. - - Virtual roots are not useful at all in applications that use - :term:`URL dispatch`. Contexts obtained via URL dispatch don't - really support being virtually rooted (each URL dispatch context - is both its own physical and virtual root). However if this API - is called with a ``model`` argument which is a context obtained - via URL dispatch, the model passed in will be returned - unconditionally.""" - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - urlgenerator = reg.queryMultiAdapter((model, request), IContextURL) - if urlgenerator is None: - urlgenerator = TraversalContextURL(model, request) - return urlgenerator.virtual_root() - -@lru_cache(1000) -def traversal_path(path): - """ Given a ``PATH_INFO`` string (slash-separated path segments), - return a tuple representing that path which can be used to - traverse a graph. - - The ``PATH_INFO`` is split on slashes, creating a list of - segments. Each segment is URL-unquoted, and subsequently decoded - into Unicode. Each segment is assumed to be encoded using the - UTF-8 encoding (or a subset, such as ASCII); a - :exc:`repoze.bfg.exceptions.URLDecodeError` is raised if a segment - cannot be decoded. If a segment name is empty or if it is ``.``, - it is ignored. If a segment name is ``..``, the previous segment - is deleted, and the ``..`` is ignored. - - If this function is passed a Unicode object instead of a string, - that Unicode object *must* directly encodeable to ASCII. For - example, u'/foo' will work but u'/' (a - Unicode object with characters that cannot be encoded to ascii) - will not. - - .. note: New in version 1.3, this API eagerly attempts to encode a - Unicode ``path`` into ASCII before attempting to split it and - decode its segments. This is for convenience. In version 1.2 - and prior, if the path was Unicode, an inappropriate call to - the ``decode()`` method of a Unicode path segment could cause a - ``UnicodeDecodeError`` to occur even if the Unicode - representation of the path contained no 'high order' - characters. - - Examples: - - ``/`` - - () - - ``/foo/bar/baz`` - - (u'foo', u'bar', u'baz') - - ``foo/bar/baz`` - - (u'foo', u'bar', u'baz') - - ``/foo/bar/baz/`` - - (u'foo', u'bar', u'baz') - - ``/foo//bar//baz/`` - - (u'foo', u'bar', u'baz') - - ``/foo/bar/baz/..`` - - (u'foo', u'bar') - - ``/my%20archives/hello`` - - (u'my archives', u'hello') - - ``/archives/La%20Pe%C3%B1a`` - - (u'archives', u'') - - .. note:: This function does not generate the same type of tuples - that :func:`repoze.bfg.traversal.model_path_tuple` does. - In particular, the leading empty string is not present - in the tuple it returns, unlike tuples returned by - :func:`repoze.bfg.traversal.model_path_tuple`. As a - result, tuples generated by ``traversal_path`` are not - resolveable by the - :func:`repoze.bfg.traversal.find_model` API. - ``traversal_path`` is a function mostly used by the - internals of :mod:`repoze.bfg` and by people writing - their own traversal machinery, as opposed to users - writing applications in :mod:`repoze.bfg`. - """ - if isinstance(path, unicode): - path = path.encode('ascii') - path = path.strip('/') - clean = [] - for segment in path.split('/'): - segment = urllib.unquote(segment) # deal with spaces in path segment - if not segment or segment=='.': - continue - elif segment == '..': - del clean[-1] - else: - try: - segment = segment.decode('utf-8') - except UnicodeDecodeError, e: - raise URLDecodeError( - e.encoding, e.object, e.start, e.end, e.reason - ) - clean.append(segment) - return tuple(clean) - -_segment_cache = {} - -def quote_path_segment(segment): - """ Return a quoted representation of a 'path segment' (such as - the string ``__name__`` attribute of a model) as a string. If the - ``segment`` passed in is a unicode object, it is converted to a - UTF-8 string, then it is URL-quoted using Python's - ``urllib.quote``. If the ``segment`` passed in is a string, it is - URL-quoted using Python's :mod:`urllib.quote`. If the segment - passed in is not a string or unicode object, an error will be - raised. The return value of ``quote_path_segment`` is always a - string, never Unicode. - - .. note:: The return value for each segment passed to this - function is cached in a module-scope dictionary for - speed: the cached version is returned when possible - rather than recomputing the quoted version. No cache - emptying is ever done for the lifetime of an - application, however. If you pass arbitrary - user-supplied strings to this function (as opposed to - some bounded set of values from a 'working set' known to - your application), it may become a memory leak. - """ - # The bit of this code that deals with ``_segment_cache`` is an - # optimization: we cache all the computation of URL path segments - # in this module-scope dictionary with the original string (or - # unicode value) as the key, so we can look it up later without - # needing to reencode or re-url-quote it - try: - return _segment_cache[segment] - except KeyError: - if segment.__class__ is unicode: # isinstance slighly slower (~15%) - result = url_quote(segment.encode('utf-8')) - else: - result = url_quote(segment) - # we don't need a lock to mutate _segment_cache, as the below - # will generate exactly one Python bytecode (STORE_SUBSCR) - _segment_cache[segment] = result - return result - -class ModelGraphTraverser(object): - """ A model graph traverser that should be used (for speed) when - every object in the graph supplies a ``__name__`` and - ``__parent__`` attribute (ie. every object in the graph is - :term:`location` aware) .""" - - implements(ITraverser) - - VIEW_SELECTOR = '@@' - - def __init__(self, root): - self.root = root - - def __call__(self, request): - try: - environ = request.environ - except AttributeError: - # In BFG 1.0 and before, this API expected an environ - # rather than a request; some bit of code may still be - # passing us an environ. If so, deal. - environ = request - - if 'bfg.routes.matchdict' in environ: - matchdict = environ['bfg.routes.matchdict'] - - path = matchdict.get('traverse', '/') - if hasattr(path, '__iter__'): - # this is a *traverse stararg (not a :traverse) - path = '/'.join([quote_path_segment(x) for x in path]) or '/' - - subpath = matchdict.get('subpath', ()) - if not hasattr(subpath, '__iter__'): - # this is not a *subpath stararg (just a :subpath) - subpath = traversal_path(subpath) - - else: - # this request did not match a route - subpath = () - try: - path = environ['PATH_INFO'] or '/' - except KeyError: - path = '/' - - if VH_ROOT_KEY in environ: - vroot_path = environ[VH_ROOT_KEY] - vroot_tuple = traversal_path(vroot_path) - vpath = vroot_path + path - vroot_idx = len(vroot_tuple) -1 - else: - vroot_tuple = () - vpath = path - vroot_idx = -1 - - root = self.root - ob = vroot = root - - if vpath == '/' or (not vpath): - # prevent a call to traversal_path if we know it's going - # to return the empty tuple - vpath_tuple = () - else: - # we do dead reckoning here via tuple slicing instead of - # pushing and popping temporary lists for speed purposes - # and this hurts readability; apologies - i = 0 - view_selector = self.VIEW_SELECTOR - vpath_tuple = traversal_path(vpath) - for segment in vpath_tuple: - if segment[:2] == view_selector: - return {'context':ob, - 'view_name':segment[2:], - 'subpath':vpath_tuple[i+1:], - 'traversed':vpath_tuple[:vroot_idx+i+1], - 'virtual_root':vroot, - 'virtual_root_path':vroot_tuple, - 'root':root} - try: - getitem = ob.__getitem__ - except AttributeError: - return {'context':ob, - 'view_name':segment, - 'subpath':vpath_tuple[i+1:], - 'traversed':vpath_tuple[:vroot_idx+i+1], - 'virtual_root':vroot, - 'virtual_root_path':vroot_tuple, - 'root':root} - - try: - next = getitem(segment) - except KeyError: - return {'context':ob, - 'view_name':segment, - 'subpath':vpath_tuple[i+1:], - 'traversed':vpath_tuple[:vroot_idx+i+1], - 'virtual_root':vroot, - 'virtual_root_path':vroot_tuple, - 'root':root} - if i == vroot_idx: - vroot = next - ob = next - i += 1 - - return {'context':ob, 'view_name':u'', 'subpath':subpath, - 'traversed':vpath_tuple, 'virtual_root':vroot, - 'virtual_root_path':vroot_tuple, 'root':root} - -class TraversalContextURL(object): - """ The IContextURL adapter used to generate URLs for a context - object obtained via graph traversal""" - implements(IContextURL) - - vroot_varname = VH_ROOT_KEY - - def __init__(self, context, request): - self.context = context - self.request = request - - def virtual_root(self): - environ = self.request.environ - vroot_varname = self.vroot_varname - if vroot_varname in environ: - return find_model(self.context, environ[vroot_varname]) - # shortcut instead of using find_root; we probably already - # have it on the request - try: - return self.request.root - except AttributeError: - return find_root(self.context) - - def __call__(self): - """ Generate a URL based on the :term:`lineage` of a - :term:`model` object obtained via :term:`traversal`. If any - model in the context lineage has a Unicode name, it will be - converted to a UTF-8 string before being attached to the URL. - If a ``HTTP_X_VHM_ROOT`` key is present in the WSGI - environment, its value will be treated as a 'virtual root - path': the path of the URL generated by this will be - left-stripped of this virtual root path value. - """ - path = model_path(self.context) - if path != '/': - path = path + '/' - request = self.request - environ = request.environ - vroot_varname = self.vroot_varname - - # if the path starts with the virtual root path, trim it out - if vroot_varname in environ: - vroot_path = environ[vroot_varname] - if path.startswith(vroot_path): - path = path[len(vroot_path):] - - app_url = request.application_url # never ends in a slash - return app_url + path - -@lru_cache(1000) -def _join_path_tuple(tuple): - return tuple and '/'.join([quote_path_segment(x) for x in tuple]) or '/' - -class DefaultRootFactory: - __parent__ = None - __name__ = None - def __init__(self, request): - matchdict = getattr(request, 'matchdict', {}) - # provide backwards compatibility for applications which - # used routes (at least apps without any custom "context - # factory") in BFG 0.9.X and before - self.__dict__.update(matchdict) diff --git a/repoze/bfg/url.py b/repoze/bfg/url.py deleted file mode 100644 index d1532f7ef..000000000 --- a/repoze/bfg/url.py +++ /dev/null @@ -1,327 +0,0 @@ -""" Utility functions for dealing with URLs in repoze.bfg """ - -import os - -from repoze.lru import lru_cache - -from repoze.bfg.interfaces import IContextURL -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import IStaticURLInfo - -from repoze.bfg.encode import urlencode -from repoze.bfg.path import caller_package -from repoze.bfg.threadlocal import get_current_registry -from repoze.bfg.traversal import TraversalContextURL -from repoze.bfg.traversal import quote_path_segment - -def route_url(route_name, request, *elements, **kw): - """Generates a fully qualified URL for a named :mod:`repoze.bfg` - :term:`route configuration`. - - Use the route's ``name`` as the first positional argument. Use a - request object as the second positional argument. Additional - positional arguments are appended to the URL as path segments - after it is generated. - - Use keyword arguments to supply values which match any dynamic - path elements in the route definition. Raises a :exc:`KeyError` - exception if the URL cannot be generated for any reason (not - enough arguments, for example). - - For example, if you've defined a route named "foobar" with the path - ``:foo/:bar/*traverse``:: - - route_url('foobar', request, foo='1') => - route_url('foobar', request, foo='1', bar='2') => - route_url('foobar', request, foo='1', bar='2', - 'traverse=('a','b')) => http://e.com/1/2/a/b - route_url('foobar', request, foo='1', bar='2', - 'traverse=('/a/b')) => http://e.com/1/2/a/b - - Values replacing ``:segment`` arguments can be passed as strings - or Unicode objects. They will be encoded to UTF-8 and URL-quoted - before being placed into the generated URL. - - Values replacing ``*remainder`` arguments can be passed as strings - *or* tuples of Unicode/string values. If a tuple is passed as a - ``*remainder`` replacement value, its values are URL-quoted and - encoded to UTF-8. The resulting strings are joined with slashes - and rendered into the URL. If a string is passed as a - ``*remainder`` replacement value, it is tacked on to the URL - untouched. - - If a keyword argument ``_query`` is present, it will used to - compose a query string that will be tacked on to the end of the - URL. The value of ``_query`` must be a sequence of two-tuples - *or* a data structure with an ``.items()`` method that returns a - sequence of two-tuples (presumably a dictionary). This data - structure will be turned into a query string per the documentation - of :func:`repoze.bfg.encode.urlencode` function. After the query - data is turned into a query string, a leading ``?`` is prepended, - and the resulting string is appended to the generated URL. - - .. note:: Python data structures that are passed as ``_query`` - which are sequences or dictionaries are turned into a - string under the same rules as when run through - :func:`urllib.urlencode` with the ``doseq`` argument - equal to ``True``. This means that sequences can be - passed as values, and a k=v pair will be placed into the - query string for each value. - - If a keyword argument ``_anchor`` is present, its string - representation will be used as a named anchor in the generated URL - (e.g. if ``_anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). - - .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 - encoded. If ``_anchor`` is passed as a Unicode object, it - will be converted to UTF-8 before being appended to the - URL. The anchor value is not quoted in any way before - being appended to the generated URL. - - If both ``_anchor`` and ``_query`` are specified, the anchor - element will always follow the query element, - e.g. ``http://example.com?foo=1#bar``. - - If a keyword ``_app_url`` is present, it will be used as the - protocol/hostname/port/leading path prefix of the generated URL. - For example, using an ``_app_url`` of - ``http://example.com:8080/foo`` would cause the URL - ``http://example.com:8080/foo/fleeb/flub`` to be returned from - this function if the expansion of the route pattern associated - with the ``route_name`` expanded to ``/fleeb/flub``. If - ``_app_url`` is not specified, the result of - ``request.application_url`` will be used as the prefix (the - default). - - .. note:: Special treatment of ``_app_url`` was added in - :mod:`repoze.bfg` 1.3. - - This function raises a :exc:`KeyError` if the URL cannot be - generated due to missing replacement names. Extra replacement - names are ignored. - - If the route object which matches the ``route_name`` argument has - a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments - arguments passed to this function might be augmented or changed. - """ - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - mapper = reg.getUtility(IRoutesMapper) - route = mapper.get_route(route_name) - - if route is None: - raise KeyError('No such route named %s' % route_name) - - if route.pregenerator is not None: - elements, kw = route.pregenerator(request, elements, kw) - - anchor = '' - qs = '' - app_url = None - - if '_query' in kw: - qs = '?' + urlencode(kw.pop('_query'), doseq=True) - - if '_anchor' in kw: - anchor = kw.pop('_anchor') - if isinstance(anchor, unicode): - anchor = anchor.encode('utf-8') - anchor = '#' + anchor - - if '_app_url' in kw: - app_url = kw.pop('_app_url') - - path = route.generate(kw) # raises KeyError if generate fails - - if elements: - suffix = _join_elements(elements) - if not path.endswith('/'): - suffix = '/' + suffix - else: - suffix = '' - - if app_url is None: - # we only defer lookup of application_url until here because - # it's somewhat expensive; we won't need to do it if we've - # been passed _app_url - app_url = request.application_url - - return app_url + path + suffix + qs + anchor - -def model_url(model, request, *elements, **kw): - """ - Generate a string representing the absolute URL of the ``model`` - object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or - ``SERVER_NAME`` in the ``request``, plus any ``SCRIPT_NAME``. The - overall result of this function is always a UTF-8 encoded string - (never Unicode). - - Examples:: - - model_url(context, request) => - - http://example.com/ - - model_url(context, request, 'a.html') => - - http://example.com/a.html - - model_url(context, request, 'a.html', query={'q':'1'}) => - - http://example.com/a.html?q=1 - - model_url(context, request, 'a.html', anchor='abc') => - - http://example.com/a.html#abc - - Any positional arguments passed in as ``elements`` must be strings - or Unicode objects. These will be joined by slashes and appended - to the generated model URL. Each of the elements passed in is - URL-quoted before being appended; if any element is Unicode, it - will converted to a UTF-8 bytestring before being URL-quoted. - - .. warning:: if no ``elements`` arguments are specified, the model - URL will end with a trailing slash. If any - ``elements`` are used, the generated URL will *not* - end in trailing a slash. - - If a keyword argument ``query`` is present, it will used to - compose a query string that will be tacked on to the end of the - URL. The value of ``query`` must be a sequence of two-tuples *or* - a data structure with an ``.items()`` method that returns a - sequence of two-tuples (presumably a dictionary). This data - structure will be turned into a query string per the documentation - of ``repoze.url.urlencode`` function. After the query data is - turned into a query string, a leading ``?`` is prepended, and the - resulting string is appended to the generated URL. - - .. note:: Python data structures that are passed as ``query`` - which are sequences or dictionaries are turned into a - string under the same rules as when run through - :func:`urllib.urlencode` with the ``doseq`` argument - equal to ``True``. This means that sequences can be - passed as values, and a k=v pair will be placed into the - query string for each value. - - If a keyword argument ``anchor`` is present, its string - representation will be used as a named anchor in the generated URL - (e.g. if ``anchor`` is passed as ``foo`` and the model URL is - ``http://example.com/model/url``, the resulting generated URL will - be ``http://example.com/model/url#foo``). - - .. note:: If ``anchor`` is passed as a string, it should be UTF-8 - encoded. If ``anchor`` is passed as a Unicode object, it - will be converted to UTF-8 before being appended to the - URL. The anchor value is not quoted in any way before - being appended to the generated URL. - - If both ``anchor`` and ``query`` are specified, the anchor element - will always follow the query element, - e.g. ``http://example.com?foo=1#bar``. - - .. note:: If the ``model`` used is the result of a - :term:`traversal`, it must be :term:`location`-aware. - The ``model`` can also be the context of a :term:`URL - dispatch`; contexts found this way do not need to be - location-aware. - - .. note:: If a 'virtual root path' is present in the request - environment (the value of the WSGI environ key - ``HTTP_X_VHM_ROOT``), and the ``model`` was obtained via - :term:`traversal`, the URL path will not include the - virtual root prefix (it will be stripped off the - left hand side of the generated URL). - """ - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - context_url = reg.queryMultiAdapter((model, request), IContextURL) - if context_url is None: - context_url = TraversalContextURL(model, request) - model_url = context_url() - - qs = '' - anchor = '' - - if 'query' in kw: - qs = '?' + urlencode(kw['query'], doseq=True) - - if 'anchor' in kw: - anchor = kw['anchor'] - if isinstance(anchor, unicode): - anchor = anchor.encode('utf-8') - anchor = '#' + anchor - - if elements: - suffix = _join_elements(elements) - else: - suffix = '' - - return model_url + suffix + qs + anchor - -def static_url(path, request, **kw): - """ - Generates a fully qualified URL for a static :term:`resource`. - The resource must live within a location defined via the - :meth:`repoze.bfg.configuration.Configurator.add_static_view` - :term:`configuration declaration` or the ```` ZCML - directive (see :ref:`static_resources_section`). - - Example:: - - static_url('mypackage:static/foo.css', request) => - - http://example.com/static/foo.css - - - The ``path`` argument points at a file or directory on disk which - a URL should be generated for. The ``path`` may be either a - relative path (e.g. ``static/foo.css``) or a :term:`resource - specification` (e.g. ``mypackage:static/foo.css``). A ``path`` - may not be an absolute filesystem path (a :exc:`ValueError` will - be raised if this function is supplied with an absolute path). - - The ``request`` argument should be a :term:`request` object. - - The purpose of the ``**kw`` argument is the same as the purpose of - the :func:`repoze.bfg.url.route_url` ``**kw`` argument. See the - documentation for that function to understand the arguments which - you can provide to it. However, typically, you don't need to pass - anything as ``*kw`` when generating a static resource URL. - - This function raises a :exc:`ValueError` if a static view - definition cannot be found which matches the path specification. - - .. note:: This feature is new in :mod:`repoze.bfg` 1.1. - """ - if os.path.isabs(path): - raise ValueError('Absolute paths cannot be used to generate static ' - 'urls (use a package-relative path or a resource ' - 'specification).') - if not ':' in path: - # if it's not a package:relative/name and it's not an - # /absolute/path it's a relative/path; this means its relative - # to the package in which the caller's module is defined. - package = caller_package() - path = '%s:%s' % (package.__name__, path) - - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - info = reg.queryUtility(IStaticURLInfo) - if info is None: - raise ValueError('No static URL definition matching %s' % path) - - return info.generate(path, request, **kw) - -@lru_cache(1000) -def _join_elements(elements): - return '/'.join([quote_path_segment(s) for s in elements]) diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py deleted file mode 100644 index 8aca2c421..000000000 --- a/repoze/bfg/urldispatch.py +++ /dev/null @@ -1,148 +0,0 @@ -import re -from urllib import unquote -from zope.interface import implements - -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import IRoute - -from repoze.bfg.compat import all -from repoze.bfg.encode import url_quote -from repoze.bfg.exceptions import URLDecodeError -from repoze.bfg.traversal import traversal_path -from repoze.bfg.traversal import quote_path_segment - - -_marker = object() - -class Route(object): - implements(IRoute) - def __init__(self, name, pattern, factory=None, predicates=(), - pregenerator=None): - self.pattern = pattern - self.path = pattern # indefinite b/w compat, not in interface - self.match, self.generate = _compile_route(pattern) - self.name = name - self.factory = factory - self.predicates = predicates - self.pregenerator = pregenerator - -class RoutesMapper(object): - implements(IRoutesMapper) - def __init__(self): - self.routelist = [] - self.routes = {} - - def has_routes(self): - return bool(self.routelist) - - def get_routes(self): - return self.routelist - - def get_route(self, name): - return self.routes.get(name) - - def connect(self, name, pattern, factory=None, predicates=(), - pregenerator=None): - if name in self.routes: - oldroute = self.routes[name] - self.routelist.remove(oldroute) - route = Route(name, pattern, factory, predicates, pregenerator) - self.routelist.append(route) - self.routes[name] = route - return route - - def generate(self, name, kw): - return self.routes[name].generate(kw) - - def __call__(self, request): - environ = request.environ - try: - # empty if mounted under a path in mod_wsgi, for example - path = environ['PATH_INFO'] or '/' - except KeyError: - path = '/' - - for route in self.routelist: - match = route.match(path) - if match is not None: - preds = route.predicates - info = {'match':match, 'route':route} - if preds and not all((p(info, request) for p in preds)): - continue - return info - - return {'route':None, 'match':None} - -# stolen from bobo and modified -route_re = re.compile(r'(/:[a-zA-Z]\w*)') -def _compile_route(route): - if not route.startswith('/'): - route = '/' + route - star = None - if '*' in route: - route, star = route.rsplit('*', 1) - pat = route_re.split(route) - pat.reverse() - rpat = [] - gen = [] - prefix = pat.pop() - if prefix: - rpat.append(re.escape(prefix)) - gen.append(prefix) - while pat: - name = pat.pop() - name = name[2:] - gen.append('/%%(%s)s' % name) - name = '/(?P<%s>[^/]+)' % name - rpat.append(name) - s = pat.pop() - if s: - rpat.append(re.escape(s)) - gen.append(s) - - if star: - rpat.append('(?P<%s>.*?)' % star) - gen.append('%%(%s)s' % star) - - pattern = ''.join(rpat) + '$' - - match = re.compile(pattern).match - def matcher(path): - m = match(path) - if m is None: - return m - d = {} - for k,v in m.groupdict().iteritems(): - if k is not None: - if k == star: - d[k] = traversal_path(v) - else: - encoded = unquote(v) - try: - d[k] = encoded.decode('utf-8') - except UnicodeDecodeError, e: - raise URLDecodeError( - e.encoding, e.object, e.start, e.end, e.reason - ) - - - return d - - - gen = ''.join(gen) - def generator(dict): - newdict = {} - for k, v in dict.items(): - if isinstance(v, unicode): - v = v.encode('utf-8') - if k == star and hasattr(v, '__iter__'): - v = '/'.join([quote_path_segment(x) for x in v]) - elif k != star: - try: - v = url_quote(v) - except TypeError: - pass - newdict[k] = v - return gen % newdict - - return matcher, generator diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py deleted file mode 100644 index c5fd60623..000000000 --- a/repoze/bfg/view.py +++ /dev/null @@ -1,551 +0,0 @@ -import mimetypes -import os - -# See http://bugs.python.org/issue5853 which is a recursion bug -# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix -# has been applied on the Python 2 trunk). This workaround should -# really be in Paste if anywhere, but it's easiest to just do it -# here and get it over with to avoid needing to deal with any -# fallout. - -if hasattr(mimetypes, 'init'): - mimetypes.init() - -from webob.exc import HTTPFound - -import venusian - -from zope.deprecation import deprecated -from zope.interface import providedBy - -from repoze.bfg.interfaces import IRoutesMapper -from repoze.bfg.interfaces import IView -from repoze.bfg.interfaces import IViewClassifier - -from repoze.bfg.path import package_path -from repoze.bfg.resource import resource_spec_from_abspath -from repoze.bfg.static import static_view as static # B/C -from repoze.bfg.threadlocal import get_current_registry - -# b/c imports -from repoze.bfg.security import view_execution_permitted - -view_execution_permitted # prevent PyFlakes from complaining - -deprecated('view_execution_permitted', - "('from repoze.bfg.view import view_execution_permitted' was " - "deprecated as of repoze.bfg 1.0; instead use 'from " - "repoze.bfg.security import view_execution_permitted')", - ) - -deprecated('NotFound', - "('from repoze.bfg.view import NotFound' was " - "deprecated as of repoze.bfg 1.1; instead use 'from " - "repoze.bfg.exceptions import NotFound')", - ) - -static = static # dont yet deprecate this (ever?) - -_marker = object() - -def render_view_to_response(context, request, name='', secure=True): - """ Call the :term:`view callable` configured with a :term:`view - configuration` that matches the :term:`view name` ``name`` - registered against the specified ``context`` and ``request`` and - return a :term:`response` object. This function will return - ``None`` if a corresponding :term:`view callable` cannot be found - (when no :term:`view configuration` matches the combination of - ``name`` / ``context`` / and ``request``). - - If `secure`` is ``True``, and the :term:`view callable` found is - protected by a permission, the permission will be checked before - calling the view function. If the permission check disallows view - execution (based on the current :term:`authorization policy`), a - :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised. - The exception's ``args`` attribute explains why the view access - was disallowed. - - If ``secure`` is ``False``, no permission checking is done.""" - provides = [IViewClassifier] + map(providedBy, (request, context)) - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() - view = reg.adapters.lookup(provides, IView, name=name) - if view is None: - return None - - if not secure: - # the view will have a __call_permissive__ attribute if it's - # secured; otherwise it won't. - view = getattr(view, '__call_permissive__', view) - - # if this view is secured, it will raise a Forbidden - # appropriately if the executing user does not have the proper - # permission - return view(context, request) - -def render_view_to_iterable(context, request, name='', secure=True): - """ Call the :term:`view callable` configured with a :term:`view - configuration` that matches the :term:`view name` ``name`` - registered against the specified ``context`` and ``request`` and - return an iterable object which represents the body of a response. - This function will return ``None`` if a corresponding :term:`view - callable` cannot be found (when no :term:`view configuration` - matches the combination of ``name`` / ``context`` / and - ``request``). Additionally, this function will raise a - :exc:`ValueError` if a view function is found and called but the - view function's result does not have an ``app_iter`` attribute. - - You can usually get the string representation of the return value - of this function by calling ``''.join(iterable)``, or just use - :func:`repoze.bfg.view.render_view` instead. - - If ``secure`` is ``True``, and the view is protected by a - permission, the permission will be checked before the view - function is invoked. If the permission check disallows view - execution (based on the current :term:`authentication policy`), a - :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised; - its ``args`` attribute explains why the view access was - disallowed. - - If ``secure`` is ``False``, no permission checking is - done.""" - response = render_view_to_response(context, request, name, secure) - if response is None: - return None - return response.app_iter - -def render_view(context, request, name='', secure=True): - """ Call the :term:`view callable` configured with a :term:`view - configuration` that matches the :term:`view name` ``name`` - registered against the specified ``context`` and ``request`` - and unwind the view response's ``app_iter`` (see - :ref:`the_response`) into a single string. This function will - return ``None`` if a corresponding :term:`view callable` cannot be - found (when no :term:`view configuration` matches the combination - of ``name`` / ``context`` / and ``request``). Additionally, this - function will raise a :exc:`ValueError` if a view function is - found and called but the view function's result does not have an - ``app_iter`` attribute. This function will return ``None`` if a - corresponding view cannot be found. - - If ``secure`` is ``True``, and the view is protected by a - permission, the permission will be checked before the view is - invoked. If the permission check disallows view execution (based - on the current :term:`authorization policy`), a - :exc:`repoze.bfg.exceptions.Forbidden` exception will be raised; - its ``args`` attribute explains why the view access was - disallowed. - - If ``secure`` is ``False``, no permission checking is done.""" - iterable = render_view_to_iterable(context, request, name, secure) - if iterable is None: - return None - return ''.join(iterable) - -def is_response(ob): - """ Return ``True`` if ``ob`` implements the interface implied by - :ref:`the_response`. ``False`` if not. - - .. note:: this isn't a true interface check (in Zope terms), it's a - duck-typing check, as response objects are not obligated to - actually implement a Zope interface.""" - # response objects aren't obligated to implement a Zope interface, - # so we do it the hard way - if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and - hasattr(ob, 'status') ): - if ( hasattr(ob.app_iter, '__iter__') and - hasattr(ob.headerlist, '__iter__') and - isinstance(ob.status, basestring) ) : - return True - return False - -class bfg_view(object): - """ A function, class or method :term:`decorator` which allows a - developer to create view registrations nearer to a :term:`view - callable` definition than use of :term:`ZCML` or :term:`imperative - configuration` to do the same. - - For example, this code in a module ``views.py``:: - - from models import MyModel - - @bfg_view(name='my_view', context=MyModel, permission='read', - route_name='site1') - def my_view(context, request): - return 'OK' - - Might replace the following call to the - :meth:`repoze.bfg.configuration.Configurator.add_view` method:: - - import views - import models - config.add_view(views.my_view, context=models.MyModel, name='my_view', - permission='read', 'route_name='site1') - - Or might replace the following ZCML ``view`` declaration:: - - - - The following arguments are supported as arguments to - ``bfg_view``: ``context``, ``permission``, ``name``, - ``request_type``, ``route_name``, ``request_method``, - ``request_param``, ``containment``, ``xhr``, ``accept``, - ``header`` and ``path_info``. - - ``context`` should be a Python object or :term:`dotted Python - name` representing the context type that must be found for this - view to be called. If ``context`` is not supplied, the interface - ``zope.interface.Interface`` (matching any context) is used. An - alias for ``context`` is ``for_``. - - If ``permission`` is not supplied, no permission is registered for - this view (it's accessible by any caller). - - If ``name`` is not supplied, the empty string is used (implying - the default view name). - - If ``attr`` is not supplied, ``None`` is used (implying the - function itself if the view is a function, or the ``__call__`` - callable attribute if the view is a class). - - If ``renderer`` is not supplied, ``None`` is used (meaning that no - renderer is associated with this view). - - If ``wrapper`` is not supplied, ``None`` is used (meaning that no - view wrapper is associated with this view). - - If ``request_type`` is not supplied, the interface - :class:`repoze.bfg.interfaces.IRequest` is used, implying the - standard request interface type. - - If ``route_name`` is not supplied, the view configuration is - considered to be made against a URL that doesn't match any defined - :term:`route`. The use of a ``route_name`` is an advanced - feature, useful only if you're also using :term:`url dispatch`. - - If ``request_method`` is not supplied, this view will match a - request with any HTTP ``REQUEST_METHOD`` - (GET/POST/PUT/HEAD/DELETE). If this parameter *is* supplied, it - must be a string naming an HTTP ``REQUEST_METHOD``, indicating - that this view will only match when the current request has a - ``REQUEST_METHOD`` that matches this value. - - If ``request_param`` is not supplied, this view will be called - when a request with any (or no) request GET or POST parameters is - encountered. If the value is present, it must be a string. If - the value supplied to the parameter has no ``=`` sign in it, it - implies that the key must exist in the ``request.params`` - dictionary for this view to 'match' the current request. If the value - supplied to the parameter has a ``=`` sign in it, e.g. - ``request_params="foo=123"``, then the key (``foo``) must both exist - in the ``request.params`` dictionary, and the value must match the - right hand side of the expression (``123``) for the view to "match" the - current request. - - ``containment`` should be a Python object or :term:`dotted Python - name` representing a class or interface type which must be found - as one of the context's location parents for this view to be - called. If ``containment`` is not supplied, this view will be - called when the context of the request has any (or no) - :term:`lineage`. If ``containment`` *is* supplied, it must be a - class or :term:`interface`, denoting that the view'matches' the - current request only if any graph :term:`lineage` node possesses - this class or interface. - - If ``xhr`` is specified, it must be a boolean value. If the value - is ``True``, the view will only be invoked if the request's - ``X-Requested-With`` header has the value ``XMLHttpRequest``. - - If ``accept`` is specified, it must be a mimetype value. If - ``accept`` is specified, the view will only be invoked if the - ``Accept`` HTTP header matches the value requested. See the - description of ``accept`` in :ref:`view_directive` for information - about the allowable composition and matching behavior of this - value. - - If ``header`` is specified, it must be a header name or a - ``headername:headervalue`` pair. If ``header`` is specified, and - possesses a value the view will only be invoked if an HTTP header - matches the value requested. If ``header`` is specified without a - value (a bare header name only), the view will only be invoked if - the HTTP header exists with any value in the request. See the - description of ``header`` in :ref:`view_directive` for information - about the allowable composition and matching behavior of this - value. - - If ``path_info`` is specified, it must be a regular - expression. The view will only be invoked if the ``PATH_INFO`` - WSGI environment variable matches the expression. - - If ``custom_predicates`` is specified, it must be a sequence of - :term:`predicate` callables (a predicate callable accepts two - arguments: ``context`` and ``request`` and returns ``True`` or - ``False``). The view will only be invoked if all custom - predicates return ``True``. - - Any individual or all parameters can be omitted. The simplest - ``bfg_view`` declaration is:: - - @bfg_view() - def my_view(...): - ... - - Such a registration implies that the view name will be - ``my_view``, registered for any :term:`context` object, using no - permission, registered against all non-URL-dispatch-based - requests, with any ``REQUEST_METHOD``, any set of request.params - values, without respect to any object in the :term:`lineage`. - - The ``bfg_view`` decorator can also be used as a class decorator - in Python 2.6 and better (Python 2.5 and below do not support - class decorators):: - - from webob import Response - from repoze.bfg.view import bfg_view - - @bfg_view() - class MyView(object): - def __init__(self, context, request): - self.context = context - self.request = request - def __call__(self): - return Response('hello from %s!' % self.context) - - In Python 2.5 and below, the ``bfg_view`` decorator can still be - used against a class, although not in decorator form:: - - from webob import Response - from repoze.bfg.view import bfg_view - - class MyView(object): - def __init__(self, context, request): - self.context = context - self.request = request - def __call__(self): - return Response('hello from %s!' % self.context) - - MyView = bfg_view()(MyView) - - .. note:: When a view is a class, the calling semantics are - different than when it is a function or another - non-class callable. See :ref:`class_as_view` for more - information. - - .. warning:: Using a class as a view is a new feature in 0.8.1+. - - The bfg_view decorator can also be used against a class method:: - - from webob import Response - from repoze.bfg.view import bfg_view - - class MyView(object): - def __init__(self, context, request): - self.context = context - self.request = request - - @bfg_view(name='hello') - def amethod(self): - return Response('hello from %s!' % self.context) - - When the ``bfg_view`` decorator is used against a class method, a - view is registered for the *class* (as described above), so the - class constructor must accept either ``request`` or ``context, - request``. The method which is decorated must return a response - (or rely on a :term:`renderer` to generate one). Using the - decorator against a particular method of a class is equivalent to - using the ``attr`` parameter in a decorator attached to the class - itself. For example, the above registration implied by the - decorator being used against the ``amethod`` method could be - spelled equivalently as:: - - from webob import Response - from repoze.bfg.view import bfg_view - - @bfg_view(attr='amethod', name='hello') - class MyView(object): - def __init__(self, context, request): - self.context = context - self.request = request - - def amethod(self): - return Response('hello from %s!' % self.context) - - .. warning:: The ability to use the ``bfg_view`` decorator as a - method decorator is new in :mod:`repoze.bfg` version - 1.1. - - To make use of any ``bfg_view`` declaration, you must perform a - :term:`scan`. To do so, either insert the following boilerplate - into your application registry's ZCML:: - - - - See :ref:`scan_directive` for more information about the ZCML - ``scan`` directive. - - Or, if you don't use ZCML, use the - :meth:`repoze.bfg.configuration.Configurator.scan` method:: - - config.scan() - """ - venusian = venusian # for testing injection - def __init__(self, name='', request_type=None, for_=None, permission=None, - route_name=None, request_method=None, request_param=None, - containment=None, attr=None, renderer=None, wrapper=None, - xhr=False, accept=None, header=None, path_info=None, - custom_predicates=(), context=None): - self.name = name - self.request_type = request_type - self.context = context or for_ - self.permission = permission - self.route_name = route_name - self.request_method = request_method - self.request_param = request_param - self.containment = containment - self.attr = attr - self.renderer = renderer - self.wrapper = wrapper - self.xhr = xhr - self.accept = accept - self.header = header - self.path_info = path_info - self.custom_predicates = custom_predicates - - def __call__(self, wrapped): - settings = self.__dict__.copy() - - def callback(context, name, ob): - context.config.add_view(view=ob, **settings) - - info = self.venusian.attach(wrapped, callback, category='bfg') - - if info.scope == 'class': - # if the decorator was attached to a method in a class, or - # otherwise executed at class scope, we need to set an - # 'attr' into the settings if one isn't already in there - if settings['attr'] is None: - settings['attr'] = wrapped.__name__ - - # try to convert the renderer provided into a fully qualified - # resource specification - abspath = settings.get('renderer') - if abspath is not None and '.' in abspath: - isabs = os.path.isabs(abspath) - if not (':' in abspath and not isabs): - # not already a resource spec - if not isabs: - pp = package_path(info.module) - abspath = os.path.join(pp, abspath) - resource = resource_spec_from_abspath(abspath, info.module) - settings['renderer'] = resource - - return wrapped - -def default_exceptionresponse_view(context, request): - if not isinstance(context, Exception): - # backwards compat for an exception response view registered via - # config.set_notfound_view or config.set_forbidden_view - # instead of as a proper exception view - context = request.exception or context - return context - -class AppendSlashNotFoundViewFactory(object): - """ There can only be one :term:`Not Found view` in any - :mod:`repoze.bfg` application. Even if you use - :func:`repoze.bfg.view.append_slash_notfound_view` as the Not - Found view, :mod:`repoze.bfg` still must generate a ``404 Not - Found`` response when it cannot redirect to a slash-appended URL; - this not found response will be visible to site users. - - If you don't care what this 404 response looks like, and you only - need redirections to slash-appended route URLs, you may use the - :func:`repoze.bfg.view.append_slash_notfound_view` object as the - Not Found view. However, if you wish to use a *custom* notfound - view callable when a URL cannot be redirected to a slash-appended - URL, you may wish to use an instance of this class as the Not - Found view, supplying a :term:`view callable` to be used as the - custom notfound view as the first argument to its constructor. - For instance: - - .. code-block:: python - - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import AppendSlashNotFoundViewFactory - - def notfound_view(context, request): - return HTTPNotFound('It aint there, stop trying!') - - custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view) - config.add_view(custom_append_slash, context=NotFound) - - The ``notfound_view`` supplied must adhere to the two-argument - view callable calling convention of ``(context, request)`` - (``context`` will be the exception object). - - .. note:: This class is new as of :mod:`repoze.bfg` version 1.3. - - """ - def __init__(self, notfound_view=None): - if notfound_view is None: - notfound_view = default_exceptionresponse_view - self.notfound_view = notfound_view - - def __call__(self, context, request): - if not isinstance(context, Exception): - # backwards compat for an append_notslash_view registered via - # config.set_notfound_view instead of as a proper exception view - context = request.exception - path = request.environ.get('PATH_INFO', '/') - registry = request.registry - mapper = registry.queryUtility(IRoutesMapper) - if mapper is not None and not path.endswith('/'): - slashpath = path + '/' - for route in mapper.get_routes(): - if route.match(slashpath) is not None: - return HTTPFound(location=slashpath) - return self.notfound_view(context, request) - -append_slash_notfound_view = AppendSlashNotFoundViewFactory() -append_slash_notfound_view.__doc__ = """\ -For behavior like Django's ``APPEND_SLASH=True``, use this view as the -:term:`Not Found view` in your application. - -When this view is the Not Found view (indicating that no view was -found), and any routes have been defined in the configuration of your -application, if the value of the ``PATH_INFO`` WSGI environment -variable does not already end in a slash, and if the value of -``PATH_INFO`` *plus* a slash matches any route's path, do an HTTP -redirect to the slash-appended PATH_INFO. Note that this will *lose* -``POST`` data information (turning it into a GET), so you shouldn't -rely on this to redirect POST requests. - -If you use :term:`ZCML`, add the following to your application's -``configure.zcml`` to use this view as the Not Found view:: - - - -Or use the -:meth:`repoze.bfg.configuration.Configurator.add_view` -method if you don't use ZCML:: - - from repoze.bfg.exceptions import NotFound - from repoze.bfg.view import append_slash_notfound_view - config.add_view(append_slash_notfound_view, context=NotFound) - -See also :ref:`changing_the_notfound_view`. - -.. note:: This function is new as of :mod:`repoze.bfg` version 1.1. -""" - - - diff --git a/repoze/bfg/wsgi.py b/repoze/bfg/wsgi.py deleted file mode 100644 index c576253b3..000000000 --- a/repoze/bfg/wsgi.py +++ /dev/null @@ -1,97 +0,0 @@ -from repoze.bfg.compat import wraps -from repoze.bfg.traversal import quote_path_segment - -def wsgiapp(wrapped): - """ Decorator to turn a WSGI application into a :mod:`repoze.bfg` - :term:`view callable`. This decorator differs from the - :func:`repoze.bfg.wsgi.wsgiapp2` decorator inasmuch as fixups of - ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment *are - not* performed before the application is invoked. - - E.g., the following in a ``views.py`` module:: - - @wsgiapp - def hello_world(environ, start_response): - body = 'Hello world' - start_response('200 OK', [ ('Content-Type', 'text/plain'), - ('Content-Length', len(body)) ] ) - return [body] - - Allows the following ZCML view declaration to be made:: - - - - Or the following call to - :meth:`repoze.bfg.configuration.Configurator.add_view`:: - - from views import hello_world - config.add_view(hello_world, name='hello_world.txt') - - The ``wsgiapp`` decorator will convert the result of the WSGI - application to a :term:`Response` and return it to - :mod:`repoze.bfg` as if the WSGI app were a :mod:`repoze.bfg` - view. - - """ - def decorator(context, request): - return request.get_response(wrapped) - return wraps(wrapped)(decorator) # grokkability - -def wsgiapp2(wrapped): - """ Decorator to turn a WSGI application into a :mod:`repoze.bfg` - view callable. This decorator differs from the - :func:`repoze.bfg.wsgi.wsgiapp` decorator inasmuch as fixups of - ``PATH_INFO`` and ``SCRIPT_NAME`` within the WSGI environment - *are* performed before the application is invoked. - - E.g. the following in a ``views.py`` module:: - - @wsgiapp2 - def hello_world(environ, start_response): - body = 'Hello world' - start_response('200 OK', [ ('Content-Type', 'text/plain'), - ('Content-Length', len(body)) ] ) - return [body] - - Allows the following ZCML view declaration to be made:: - - - - Or the following call to - :meth:`repoze.bfg.configuration.Configurator.add_view`:: - - from views import hello_world - config.add_view(hello_world, name='hello_world.txt') - - The ``wsgiapp2`` decorator will convert the result of the WSGI - application to a Response and return it to :mod:`repoze.bfg` as if - the WSGI app were a :mod:`repoze.bfg` view. The ``SCRIPT_NAME`` - and ``PATH_INFO`` values present in the WSGI environment are fixed - up before the application is invoked. """ - - def decorator(context, request): - traversed = request.traversed - vroot_path = request.virtual_root_path or () - view_name = request.view_name - subpath = request.subpath or () - script_tuple = traversed[len(vroot_path):] - script_list = [ quote_path_segment(name) for name in script_tuple ] - if view_name: - script_list.append(quote_path_segment(view_name)) - script_name = '/' + '/'.join(script_list) - path_list = [ quote_path_segment(name) for name in subpath ] - path_info = '/' + '/'.join(path_list) - request.environ['PATH_INFO'] = path_info - script_name = request.environ['SCRIPT_NAME'] + script_name - if script_name.endswith('/'): - script_name = script_name[:-1] - request.environ['SCRIPT_NAME'] = script_name - return request.get_response(wrapped) - return wraps(wrapped)(decorator) # grokkability - diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py deleted file mode 100644 index 2bf394eb8..000000000 --- a/repoze/bfg/zcml.py +++ /dev/null @@ -1,903 +0,0 @@ -import os - -from zope.configuration import xmlconfig -from zope.configuration.config import ConfigurationMachine -from zope.configuration.fields import GlobalInterface -from zope.configuration.fields import GlobalObject -from zope.configuration.fields import Tokens - -from zope.interface.interfaces import IInterface -from zope.interface import Interface -from zope.interface import implementedBy -from zope.interface import providedBy - -from zope.schema import ASCIILine -from zope.schema import Bool -from zope.schema import Int -from zope.schema import TextLine - -from repoze.bfg.interfaces import IAuthenticationPolicy -from repoze.bfg.interfaces import IAuthorizationPolicy -from repoze.bfg.interfaces import IDefaultPermission -from repoze.bfg.interfaces import IRendererFactory -from repoze.bfg.interfaces import IRouteRequest -from repoze.bfg.interfaces import IView - -from repoze.bfg.authentication import AuthTktAuthenticationPolicy -from repoze.bfg.authentication import RemoteUserAuthenticationPolicy -from repoze.bfg.authentication import RepozeWho1AuthenticationPolicy -from repoze.bfg.authorization import ACLAuthorizationPolicy -from repoze.bfg.configuration import Configurator -from repoze.bfg.exceptions import ConfigurationError -from repoze.bfg.exceptions import NotFound -from repoze.bfg.exceptions import Forbidden -from repoze.bfg.request import route_request_iface -from repoze.bfg.resource import resource_spec_from_abspath -from repoze.bfg.static import StaticURLInfo -from repoze.bfg.threadlocal import get_current_registry - -###################### directives ########################## - -class IViewDirective(Interface): - context = GlobalObject( - title=u"The interface or class this view is for.", - required=False - ) - - for_ = GlobalObject( - title=(u"The interface or class this view is for (alternate spelling " - "of ``context``)."), - required=False - ) - - permission = TextLine( - title=u"Permission", - description=u"The permission needed to use the view.", - required=False - ) - - view = GlobalObject( - title=u"", - description=u"The view function", - required=False, - ) - - name = TextLine( - title=u"The name of the view", - description=u""" - The name shows up in URLs/paths. For example 'foo' or 'foo.html'.""", - required=False, - ) - - attr = TextLine( - title=u'The callable attribute of the view object(default is __call__)', - description=u'', - required=False) - - renderer = TextLine( - title=u'The renderer asssociated with the view', - description=u'', - required=False) - - wrapper = TextLine( - title = u'The *name* of the view that acts as a wrapper for this view.', - description = u'', - required=False) - - request_type = TextLine( - title=u"The request type string or dotted name interface for the view", - description=(u"The view will be called if the interface represented by " - u"'request_type' is implemented by the request. The " - u"default request type is repoze.bfg.interfaces.IRequest"), - required=False - ) - - route_name = TextLine( - title = u'The route that must match for this view to be used', - required = False) - - containment = GlobalObject( - title = u'Dotted name of a containment class or interface', - required=False) - - request_method = TextLine( - title = u'Request method name that must be matched (e.g. GET/POST)', - description = (u'The view will be called if and only if the request ' - 'method (``request.method``) matches this string. This' - 'functionality replaces the older ``request_type`` ' - 'functionality.'), - required=False) - - request_param = TextLine( - title = (u'Request parameter name that must exist in ' - '``request.params`` for this view to match'), - description = (u'The view will be called if and only if the request ' - 'parameter exists which matches this string.'), - required=False) - - xhr = Bool( - title = (u'True if request has an X-Requested-With header with the ' - 'value "XMLHttpRequest"'), - description=(u'Useful for detecting AJAX requests issued from ' - 'jQuery, Protoype and other JavaScript libraries'), - required=False) - - accept = TextLine( - title = (u'Mimetype(s) that must be present in "Accept" HTTP header ' - 'for the view to match a request'), - description=(u'Accepts a mimetype match token in the form ' - '"text/plain", a wildcard mimetype match token in the ' - 'form "text/*" or a match-all wildcard mimetype match ' - 'token in the form "*/*".'), - required = False) - - header = TextLine( - title=u'Header name/value pair in the form "name="', - description=u'Regular expression matching for header values', - required = False) - - path_info = TextLine( - title = (u'Regular expression which must match the ``PATH_INFO`` ' - 'header for the view to match a request'), - description=(u'Accepts a regular expression.'), - required = False) - - custom_predicates = Tokens( - title=u"One or more custom dotted names to custom predicate callables", - description=(u"A list of dotted name references to callables that " - "will be used as predicates for this view configuration"), - required=False, - value_type=GlobalObject() - ) - - -def view( - _context, - permission=None, - for_=None, - view=None, - name="", - request_type=None, - route_name=None, - request_method=None, - request_param=None, - containment=None, - attr=None, - renderer=None, - wrapper=None, - xhr=False, - accept=None, - header=None, - path_info=None, - traverse=None, - custom_predicates=(), - context=None, - cacheable=True, # not used, here for b/w compat < 0.8 - ): - - reg = get_current_registry() - - if request_type in ('GET', 'HEAD', 'PUT', 'POST', 'DELETE'): - # b/w compat for 1.0 - request_method = request_type - request_type = None - - if request_type is not None: - request_type = _context.resolve(request_type) - if not IInterface.providedBy(request_type): - raise ConfigurationError( - 'request_type must be an interface, not %s' % request_type) - - if renderer and '.' in renderer: - renderer = path_spec(_context, renderer) - - context = context or for_ - - def register(): - config = Configurator(reg, package=_context.package) - config.add_view( - permission=permission, context=context, view=view, name=name, - request_type=request_type, route_name=route_name, - request_method=request_method, request_param=request_param, - containment=containment, attr=attr, renderer=renderer, - wrapper=wrapper, xhr=xhr, accept=accept, header=header, - path_info=path_info, custom_predicates=custom_predicates, - _info=_context.info) - - discriminator = ['view', context, name, request_type, IView, containment, - request_param, request_method, route_name, attr, - xhr, accept, header, path_info] - - discriminator.extend(sorted(custom_predicates)) - discriminator = tuple(discriminator) - - _context.action( - discriminator = discriminator, - callable = register, - ) - -_view = view # for directives that take a view arg - -class IRouteDirective(Interface): - """ The interface for the ``route`` ZCML directive - """ - name = TextLine(title=u'name', required=True) - pattern = TextLine(title=u'pattern', required=False) - # alias for pattern - path = TextLine(title=u'path', required=False) - factory = GlobalObject(title=u'context factory', required=False) - view = GlobalObject(title=u'view', required=False) - - view_context = GlobalObject(title=u'view_context', required=False) - # aliases for view_context - for_ = GlobalObject(title=u'for', required=False) - view_for = GlobalObject(title=u'view_for', required=False) - - view_permission = TextLine(title=u'view_permission', required=False) - # alias for view_permission - permission = TextLine(title=u'permission', required=False) - - view_renderer = TextLine(title=u'view_renderer', required=False) - # alias for view_renderer - renderer = TextLine(title=u'renderer', required=False) - - view_attr = TextLine(title=u'view_attr', required=False) - - request_method = TextLine(title=u'request_method', required=False) - request_param = TextLine(title=u'request_param', required=False) - header = TextLine(title=u'header', required=False) - accept = TextLine(title=u'accept', required=False) - xhr = Bool(title=u'xhr', required=False) - path_info = TextLine(title=u'path_info', required=False) - - traverse = TextLine( - title=u'Traverse pattern"', - description=u'A pattern which will compose a traversal path', - required = False) - - custom_predicates = Tokens( - title=u"One or more custom dotted names to custom predicate callables", - description=(u"A list of dotted name references to callables that " - "will be used as predicates for this view configuration"), - required=False, - value_type=GlobalObject() - ) - use_global_views = Bool(title=u'use_global_views', required=False) - -def route(_context, - name, - pattern=None, - view=None, - view_for=None, - permission=None, - factory=None, - for_=None, - header=None, - xhr=False, - accept=None, - path_info=None, - request_method=None, - request_param=None, - custom_predicates=(), - view_permission=None, - view_attr=None, - renderer=None, - view_renderer=None, - view_context=None, - traverse=None, - use_global_views=False, - path=None): - """ Handle ``route`` ZCML directives - """ - # the strange ordering of the request kw args above is for b/w - # compatibility purposes. - - # these are route predicates; if they do not match, the next route - # in the routelist will be tried - reg = get_current_registry() - - if view_context is None: - view_context = view_for or for_ - - view_permission = view_permission or permission - view_renderer = view_renderer or renderer - if view_renderer and '.' in view_renderer: - view_renderer = path_spec(_context, view_renderer) - - if pattern is None: - pattern = path - - if pattern is None: - raise ConfigurationError('route directive must include a "pattern"') - - def register(): - config = Configurator(reg, package=_context.package) - config.add_route( - name, - pattern, - factory=factory, - header=header, - xhr=xhr, - accept=accept, - path_info=path_info, - request_method=request_method, - request_param=request_param, - custom_predicates=custom_predicates, - view=view, - view_context=view_context, - view_permission=view_permission, - view_renderer=view_renderer, - view_attr=view_attr, - use_global_views=use_global_views, - traverse=traverse, - _info=_context.info - ) - - discriminator = ['route', name, xhr, request_method, path_info, - request_param, header, accept] - discriminator.extend(sorted(custom_predicates)) - discriminator = tuple(discriminator) - - _context.action( - discriminator=discriminator, - callable = register, - ) - - if view: - request_iface = reg.queryUtility(IRouteRequest, name=name) - if request_iface is None: - request_iface = route_request_iface(name) - reg.registerUtility(request_iface, IRouteRequest, name=name) - _context.action( - discriminator = ( - 'view', view_context, '', None, IView, name, view_attr), - ) - -class ISystemViewDirective(Interface): - view = GlobalObject( - title=u"", - description=u"The view function", - required=False, - ) - - attr = TextLine( - title=u'The callable attribute of the view object(default is __call__)', - description=u'', - required=False) - - renderer = TextLine( - title=u'The renderer asssociated with the view', - description=u'', - required=False) - - wrapper = TextLine( - title = u'The *name* of the view that acts as a wrapper for this view.', - description = u'', - required=False) - -def notfound(_context, - view=None, - attr=None, - renderer=None, - wrapper=None): - - if renderer and '.' in renderer: - renderer = path_spec(_context, renderer) - - def register(): - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.set_notfound_view(view=view, attr=attr, renderer=renderer, - wrapper=wrapper, _info=_context.info) - - discriminator = ('view', NotFound, '', None, IView, None, None, None, - None, attr, False, None, None, None) - - _context.action( - discriminator = discriminator, - callable = register, - ) - -def forbidden(_context, - view=None, - attr=None, - renderer=None, - wrapper=None): - - if renderer and '.' in renderer: - renderer = path_spec(_context, renderer) - - def register(): - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.set_forbidden_view(view=view, attr=attr, renderer=renderer, - wrapper=wrapper, _info=_context.info) - - discriminator = ('view', Forbidden, '', None, IView, None, None, None, - None, attr, False, None, None, None) - - _context.action( - discriminator = discriminator, - callable = register, - ) - -class IResourceDirective(Interface): - """ - Directive for specifying that one package may override resources from - another package. - """ - to_override = TextLine( - title=u"Override spec", - description=u'The spec of the resource to override.', - required=True) - override_with = TextLine( - title=u"With spec", - description=u"The spec of the resource providing the override.", - required=True) - -def resource(_context, to_override, override_with): - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = None, - callable = config.override_resource, - args = (to_override, override_with, _context.info), - ) - -class IRepozeWho1AuthenticationPolicyDirective(Interface): - identifier_name = TextLine(title=u'identitfier_name', required=False, - default=u'auth_tkt') - callback = GlobalObject(title=u'callback', required=False) - -def repozewho1authenticationpolicy(_context, identifier_name='auth_tkt', - callback=None): - policy = RepozeWho1AuthenticationPolicy(identifier_name=identifier_name, - callback=callback) - # authentication policies must be registered eagerly so they can - # be found by the view registration machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) - -class IRemoteUserAuthenticationPolicyDirective(Interface): - environ_key = TextLine(title=u'environ_key', required=False, - default=u'REMOTE_USER') - callback = GlobalObject(title=u'callback', required=False) - -def remoteuserauthenticationpolicy(_context, environ_key='REMOTE_USER', - callback=None): - policy = RemoteUserAuthenticationPolicy(environ_key=environ_key, - callback=callback) - # authentication policies must be registered eagerly so they can - # be found by the view registration machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) - -class IAuthTktAuthenticationPolicyDirective(Interface): - secret = TextLine(title=u'secret', required=True) - callback = GlobalObject(title=u'callback', required=False) - cookie_name = ASCIILine(title=u'cookie_name', required=False, - default='repoze.bfg.auth_tkt') - secure = Bool(title=u"secure", required=False, default=False) - include_ip = Bool(title=u"include_ip", required=False, default=False) - timeout = Int(title=u"timeout", required=False, default=None) - reissue_time = Int(title=u"reissue_time", required=False, default=None) - max_age = Int(title=u"max_age", required=False, default=None) - path = ASCIILine(title=u"path", required=False, default='/') - http_only = Bool(title=u"http_only", required=False, default=False) - -def authtktauthenticationpolicy(_context, - secret, - callback=None, - cookie_name='repoze.bfg.auth_tkt', - secure=False, - include_ip=False, - timeout=None, - reissue_time=None, - max_age=None, - http_only=False, - path='/'): - try: - policy = AuthTktAuthenticationPolicy(secret, - callback=callback, - cookie_name=cookie_name, - secure=secure, - include_ip = include_ip, - timeout = timeout, - reissue_time = reissue_time, - max_age=max_age, - http_only=http_only, - path=path) - except ValueError, why: - raise ConfigurationError(str(why)) - # authentication policies must be registered eagerly so they can - # be found by the view registration machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) - -class IACLAuthorizationPolicyDirective(Interface): - pass - -def aclauthorizationpolicy(_context): - policy = ACLAuthorizationPolicy() - # authorization policies must be registered eagerly so they can be - # found by the view registration machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authorization_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthorizationPolicy) - -class IRendererDirective(Interface): - factory = GlobalObject( - title=u'IRendererFactory implementation', - required=True) - - name = TextLine( - title=u'Token (e.g. ``json``) or filename extension (e.g. ".pt")', - required=False) - -def renderer(_context, factory, name=''): - # renderer factories must be registered eagerly so they can be - # found by the view machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.add_renderer(name, factory, _info=_context.info) - _context.action(discriminator=(IRendererFactory, name)) - -class IStaticDirective(Interface): - name = TextLine( - title=u"The URL prefix of the static view", - description=u""" - The directory will be served up for the route that starts with - this prefix.""", - required=True) - - path = TextLine( - title=u'Path to the directory which contains resources', - description=u'May be package-relative by using a colon to ' - 'seperate package name and path relative to the package directory.', - required=True) - - cache_max_age = Int( - title=u"Cache maximum age in seconds", - required=False, - default=None) - -def static(_context, name, path, cache_max_age=3600): - """ Handle ``static`` ZCML directives - """ - path = path_spec(_context, path) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator=('static', name), - callable=config.add_static_view, - args = (name, path), - kw = {'cache_max_age':cache_max_age, '_info':_context.info}, - ) - - if not '/' in name: - _context.action( - discriminator = ( - 'view', StaticURLInfo, '', None, IView, None, None, None, - name, None, None, None, None, None, - ) - ) - -class IScanDirective(Interface): - package = GlobalObject( - title=u"The package we'd like to scan.", - required=True, - ) - -def scan(_context, package): - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - _context.action( - discriminator=None, - callable=config.scan, - args=(package, None, _context.info) - ) - -class ITranslationDirDirective(Interface): - dir = TextLine( - title=u"Add a translation directory", - description=(u"Add a translation directory"), - required=True, - ) - -def translationdir(_context, dir): - path = path_spec(_context, dir) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = ('tdir', path), - callable=config.add_translation_dirs, - args = (dir,), - ) - -class ILocaleNegotiatorDirective(Interface): - negotiator = GlobalObject( - title=u"Configure a locale negotiator", - description=(u'Configure a locale negotiator'), - required=True, - ) - -def localenegotiator(_context, negotiator): - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = 'lnegotiator', - callable=config.set_locale_negotiator, - args = (negotiator,) - ) - -class IAdapterDirective(Interface): - """ - Register an adapter - """ - - factory = Tokens( - title=u"Adapter factory/factories", - description=(u"A list of factories (usually just one) that create" - " the adapter instance."), - required=True, - value_type=GlobalObject() - ) - - provides = GlobalInterface( - title=u"Interface the component provides", - description=(u"This attribute specifies the interface the adapter" - " instance must provide."), - required=False, - ) - - for_ = Tokens( - title=u"Specifications to be adapted", - description=u"This should be a list of interfaces or classes", - required=False, - value_type=GlobalObject( - missing_value=object(), - ), - ) - - name = TextLine( - title=u"Name", - description=(u"Adapters can have names.\n\n" - "This attribute allows you to specify the name for" - " this adapter."), - required=False, - ) - -def adapter(_context, factory, provides=None, for_=None, name=''): - if for_ is None: - if len(factory) == 1: - for_ = getattr(factory[0], '__component_adapts__', None) - - if for_ is None: - raise TypeError("No for argument was provided and can't " - "determine what the factory adapts.") - - for_ = tuple(for_) - - if provides is None: - if len(factory) == 1: - p = list(implementedBy(factory[0])) - if len(p) == 1: - provides = p[0] - - if provides is None: - raise TypeError("Missing 'provided' argument") - - # Generate a single factory from multiple factories: - factories = factory - if len(factories) == 1: - factory = factories[0] - elif len(factories) < 1: - raise ValueError("No factory specified") - elif len(factories) > 1 and len(for_) != 1: - raise ValueError("Can't use multiple factories and multiple " - "for") - else: - factory = _rolledUpFactory(factories) - - registry = get_current_registry() - _context.action( - discriminator = ('adapter', for_, provides, name), - callable = registry.registerAdapter, - args = (factory, for_, provides, name, _context.info), - ) - -class ISubscriberDirective(Interface): - """ - Register a subscriber - """ - - factory = GlobalObject( - title=u"Subscriber factory", - description=u"A factory used to create the subscriber instance.", - required=False, - ) - - handler = GlobalObject( - title=u"Handler", - description=u"A callable object that handles events.", - required=False, - ) - - provides = GlobalInterface( - title=u"Interface the component provides", - description=(u"This attribute specifies the interface the adapter" - " instance must provide."), - required=False, - ) - - for_ = Tokens( - title=u"Interfaces or classes that this subscriber depends on", - description=u"This should be a list of interfaces or classes", - required=False, - value_type=GlobalObject( - missing_value = object(), - ), - ) - -def subscriber(_context, for_=None, factory=None, handler=None, provides=None): - if factory is None: - if handler is None: - raise TypeError("No factory or handler provided") - if provides is not None: - raise TypeError("Cannot use handler with provides") - factory = handler - else: - if handler is not None: - raise TypeError("Cannot use handler with factory") - if provides is None: - raise TypeError( - "You must specify a provided interface when registering " - "a factory") - - if for_ is None: - for_ = getattr(factory, '__component_adapts__', None) - if for_ is None: - raise TypeError("No for attribute was provided and can't " - "determine what the factory (or handler) adapts.") - - for_ = tuple(for_) - - registry = get_current_registry() - config = Configurator(registry=registry, package=_context.package) - - if handler is not None: - _context.action( - discriminator = None, - callable = config.add_subscriber, - args = (handler, for_, _context.info), - ) - else: - _context.action( - discriminator = None, - callable = registry.registerSubscriptionAdapter, - args = (factory, for_, provides, None, _context.info), - ) - -class IUtilityDirective(Interface): - """Register a utility.""" - - component = GlobalObject( - title=u"Component to use", - description=(u"Python name of the implementation object. This" - " must identify an object in a module using the" - " full dotted name. If specified, the" - " ``factory`` field must be left blank."), - required=False, - ) - - factory = GlobalObject( - title=u"Factory", - description=(u"Python name of a factory which can create the" - " implementation object. This must identify an" - " object in a module using the full dotted name." - " If specified, the ``component`` field must" - " be left blank."), - required=False, - ) - - provides = GlobalInterface( - title=u"Provided interface", - description=u"Interface provided by the utility.", - required=False, - ) - - name = TextLine( - title=u"Name", - description=(u"Name of the registration. This is used by" - " application code when locating a utility."), - required=False, - ) - -def utility(_context, provides=None, component=None, factory=None, name=''): - if factory and component: - raise TypeError("Can't specify factory and component.") - - if provides is None: - if factory: - provides = list(implementedBy(factory)) - else: - provides = list(providedBy(component)) - if len(provides) == 1: - provides = provides[0] - else: - raise TypeError("Missing 'provides' attribute") - - if factory: - kw = dict(factory=factory) - else: - # older component registries don't accept factory as a kwarg, - # so if we don't need it, we don't pass it - kw = {} - - registry = get_current_registry() - _context.action( - discriminator = ('utility', provides, name), - callable = registry.registerUtility, - args = (component, provides, name, _context.info), - kw = kw, - ) - -class IDefaultPermissionDirective(Interface): - name = TextLine(title=u'name', required=True) - -def default_permission(_context, name): - """ Register a default permission name """ - # the default permission must be registered eagerly so it can - # be found by the view registration machinery - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.set_default_permission(name) - _context.action(discriminator=IDefaultPermission) - -def path_spec(context, path): - # we prefer registering resource specifications over absolute - # paths because these can be overridden by the resource directive. - if ':' in path and not os.path.isabs(path): - # it's already a resource specification - return path - abspath = context.path(path) - if hasattr(context, 'package') and context.package: - return resource_spec_from_abspath(abspath, context.package) - return abspath - -def zcml_configure(name, package): - """ Given a ZCML filename as ``name`` and a Python package as - ``package`` which the filename should be relative to, load the - ZCML into the current ZCML registry. - - .. note:: This feature is new as of :mod:`repoze.bfg` 1.1. - """ - context = ConfigurationMachine() - xmlconfig.registerCommonDirectives(context) - context.package = package - xmlconfig.include(context, name, package) - context.execute_actions(clear=False) # the raison d'etre - return context.actions - -file_configure = zcml_configure # backwards compat (>0.8.1) - -def _rolledUpFactory(factories): - def factory(ob): - for f in factories: - ob = f(ob) - return ob - # Store the original factory for documentation - factory.factory = factories[0] - return factory - -- cgit v1.2.3