diff options
| author | Chris McDonough <chrism@plope.com> | 2012-01-29 13:36:07 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-01-29 13:36:07 -0500 |
| commit | bfe8046689b9f9b4273df773a43be19080958193 (patch) | |
| tree | 1fa043841e64fb861919febbcdfb94ea05fe1f4c | |
| parent | 76363823a22a709a15b6895430b4eece67da3b39 (diff) | |
| parent | 71c94c0ae7a36dab1b39585907d8a0c24d377c90 (diff) | |
| download | pyramid-bfe8046689b9f9b4273df773a43be19080958193.tar.gz pyramid-bfe8046689b9f9b4273df773a43be19080958193.tar.bz2 pyramid-bfe8046689b9f9b4273df773a43be19080958193.zip | |
Merge branch '1.3-branch'
| -rw-r--r-- | CHANGES.txt | 32 | ||||
| -rw-r--r-- | docs/api/config.rst | 2 | ||||
| -rw-r--r-- | docs/conf.py | 2 | ||||
| -rw-r--r-- | docs/narr/project.rst | 6 | ||||
| -rw-r--r-- | docs/narr/security.rst | 69 | ||||
| -rw-r--r-- | docs/tutorials/wiki/authorization.rst | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/authorization/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/tests/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingviews.rst | 8 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/authorization/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/tests/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/whatsnew-1.3.rst | 15 | ||||
| -rw-r--r-- | pyramid/config/__init__.py | 8 | ||||
| -rw-r--r-- | pyramid/config/views.py | 12 | ||||
| -rw-r--r-- | pyramid/scripts/prequest.py | 5 | ||||
| -rw-r--r-- | pyramid/scripts/pserve.py | 7 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_init.py | 5 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 59 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_prequest.py | 14 | ||||
| -rw-r--r-- | setup.py | 2 |
21 files changed, 200 insertions, 78 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index ba90c3bad..2a8515d01 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,25 @@ -Unreleased -========== +Next release +============ + +Features +-------- + +- More informative error message when a ``config.include`` cannot find an + ``includeme``. See https://github.com/Pylons/pyramid/pull/392. + +Bug Fixes +--------- + +- The process will now restart when ``pserve`` is used with the ``--reload`` + flag when the ``development.ini`` file (or any other .ini file in use) is + changed. See https://github.com/Pylons/pyramid/issues/377 and + https://github.com/Pylons/pyramid/pull/411 + +- The ``prequest`` script would fail when used against URLs which did not + return HTML or text. See https://github.com/Pylons/pyramid/issues/381 + +1.3a6 (2012-01-20) +================== Features -------- @@ -21,6 +41,14 @@ Bug Fixes a different view that had the same predicate arguments. See https://github.com/Pylons/pyramid/pull/404 for more information. +- When using a dotted name for a ``view`` argument to + ``Configurator.add_view`` that pointed to a class with a ``view_defaults`` + decorator, the view defaults would not be applied. See + https://github.com/Pylons/pyramid/issues/396 . + +- Static URL paths were URL-quoted twice. See + https://github.com/Pylons/pyramid/issues/407 . + 1.3a5 (2012-01-09) ================== diff --git a/docs/api/config.rst b/docs/api/config.rst index dbfbb1761..d16930cc0 100644 --- a/docs/api/config.rst +++ b/docs/api/config.rst @@ -72,6 +72,8 @@ .. automethod:: set_request_factory + .. automethod:: set_request_property + .. automethod:: set_root_factory .. automethod:: set_view_mapper diff --git a/docs/conf.py b/docs/conf.py index 2ab56cadf..3496bd38c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,7 +80,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year # other places throughout the built documents. # # The short X.Y version. -version = '1.3a5' +version = '1.3a6' # The full version, including alpha/beta/rc tags. release = version diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 5696b0b73..ea0045ca7 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -118,11 +118,11 @@ your application, or install your application for deployment or development. A ``.ini`` file named ``development.ini`` will be created in the project directory. You will use this ``.ini`` file to configure a server, to run -your application, and to debug your application. It sports configuration +your application, and to debug your application. It contains configuration that enables an interactive debugger and settings optimized for development. Another ``.ini`` file named ``production.ini`` will also be created in the -project directory. It sports configuration that disables any interactive +project directory. It contains configuration that disables any interactive debugger (to prevent inappropriate access and disclosure), and turns off a number of debugging settings. You can use this file to put your application into production. @@ -709,7 +709,7 @@ also informs Python that the directory which contains it is a *package*. #. Line 1 imports the :term:`Configurator` class from :mod:`pyramid.config` that we use later. -#. Lines 3-16 define a function named ``main`` that returns a :app:`Pyramid` +#. Lines 3-10 define a function named ``main`` that returns a :app:`Pyramid` WSGI application. This function is meant to be called by the :term:`PasteDeploy` framework as a result of running ``pserve``. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 1ad35b961..07ec0f21e 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -73,16 +73,15 @@ to enable an authorization policy. Enabling an Authorization Policy Imperatively ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passing an ``authorization_policy`` argument to the constructor of the -:class:`~pyramid.config.Configurator` class enables an -authorization policy. +Use the :meth:`~pyramid.config.Configurator.set_authorization_policy` method +of the :class:`~pyramid.config.Configurator` to enable an authorization +policy. -You must also enable an :term:`authentication policy` in order to -enable the authorization policy. This is because authorization, in -general, depends upon authentication. Use the -``authentication_policy`` argument to the -:class:`~pyramid.config.Configurator` class during -application setup to specify an authentication policy. +You must also enable an :term:`authentication policy` in order to enable the +authorization policy. This is because authorization, in general, depends +upon authentication. Use the +:meth:`~pyramid.config.Configurator.set_authentication_policy` and method +during application setup to specify the authentication policy. For example: @@ -95,13 +94,14 @@ For example: from pyramid.authorization import ACLAuthorizationPolicy authentication_policy = AuthTktAuthenticationPolicy('seekrit') authorization_policy = ACLAuthorizationPolicy() - config = Configurator(authentication_policy=authentication_policy, - authorization_policy=authorization_policy) + config = Configurator() + config.set_authentication_policy(authentication_policy) + config.set_authorization_policy(authorization_policy) .. note:: the ``authentication_policy`` and ``authorization_policy`` - arguments may also be passed to the Configurator as :term:`dotted - Python name` values, each representing the dotted name path to a - suitable implementation global defined at Python module scope. + arguments may also be passed to their respective methods mentioned above + as :term:`dotted Python name` values, each representing the dotted name + path to a suitable implementation global defined at Python module scope. The above configuration enables a policy which compares the value of an "auth ticket" cookie passed in the request's environment which contains a reference @@ -110,9 +110,9 @@ to a single :term:`principal` against the principals present in any :term:`view`. While it is possible to mix and match different authentication and -authorization policies, it is an error to pass an authentication -policy without the authorization policy or vice versa to a -:term:`Configurator` constructor. +authorization policies, it is an error to configure a Pyramid application +with an authentication policy but without the authorization policy or vice +versa. If you do this, you'll receive an error at application startup time. See also the :mod:`pyramid.authorization` and :mod:`pyramid.authentication` modules for alternate implementations @@ -188,13 +188,8 @@ In support of making it easier to configure applications which are the permission string to all view registrations which don't otherwise name a ``permission`` argument. -These APIs are in support of configuring a default permission for an -application: - -- The ``default_permission`` constructor argument to the - :mod:`~pyramid.config.Configurator` constructor. - -- The :meth:`pyramid.config.Configurator.set_default_permission` method. +The :meth:`pyramid.config.Configurator.set_default_permission` method +supports configuring a default permission for an application. When a default permission is registered: @@ -605,8 +600,8 @@ that implements the following interface: current user on subsequent requests. """ After you do so, you can pass an instance of such a class into the -:class:`~pyramid.config.Configurator` class at configuration -time as ``authentication_policy`` to use it. +:class:`~pyramid.config.Configurator.set_authentication_policy` method +configuration time to use it. .. index:: single: authorization policy (creating) @@ -616,18 +611,16 @@ time as ``authentication_policy`` to use it. Creating Your Own Authorization Policy -------------------------------------- -An authorization policy is a policy that allows or denies access after -a user has been authenticated. By default, :app:`Pyramid` will use -the :class:`pyramid.authorization.ACLAuthorizationPolicy` if an -authentication policy is activated and an authorization policy isn't -otherwise specified. +An authorization policy is a policy that allows or denies access after a user +has been authenticated. Most :app:`Pyramid` applications will use the +default :class:`pyramid.authorization.ACLAuthorizationPolicy`. -In some cases, it's useful to be able to use a different +However, in some cases, it's useful to be able to use a different authorization policy than the default -:class:`~pyramid.authorization.ACLAuthorizationPolicy`. For -example, it might be desirable to construct an alternate authorization -policy which allows the application to use an authorization mechanism -that does not involve :term:`ACL` objects. +:class:`~pyramid.authorization.ACLAuthorizationPolicy`. For example, it +might be desirable to construct an alternate authorization policy which +allows the application to use an authorization mechanism that does not +involve :term:`ACL` objects. :app:`Pyramid` ships with only a single default authorization policy, so you'll need to create your own if you'd like to use a @@ -655,5 +648,5 @@ following interface: used.""" After you do so, you can pass an instance of such a class into the -:class:`~pyramid.config.Configurator` class at configuration -time as ``authorization_policy`` to use it. +:class:`~pyramid.config.Configurator.set_authorization_policy` method at +configuration time to use it. diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index fa18d4a41..8f583ece7 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -132,14 +132,14 @@ We'll add these views to the existing ``views.py`` file we have in our project. Here's what the ``login`` view callable will look like: .. literalinclude:: src/authorization/tutorial/views.py - :pyobject: login + :lines: 83-111 :linenos: :language: python Here's what the ``logout`` view callable will look like: .. literalinclude:: src/authorization/tutorial/views.py - :pyobject: logout + :lines: 113-117 :linenos: :language: python diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py index 20ee685ee..6989145d8 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py @@ -17,9 +17,9 @@ def main(global_config, **settings): authn_policy = AuthTktAuthenticationPolicy(secret='sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() - config = Configurator(root_factory=root_factory, settings=settings, - authentication_policy=authn_policy, - authorization_policy=authz_policy) + config = Configurator(root_factory=root_factory, settings=settings) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_static_view('static', 'static', cache_max_age=3600) config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki/src/tests/tutorial/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/__init__.py index 20ee685ee..6989145d8 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/tests/tutorial/__init__.py @@ -17,9 +17,9 @@ def main(global_config, **settings): authn_policy = AuthTktAuthenticationPolicy(secret='sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() - config = Configurator(root_factory=root_factory, settings=settings, - authentication_policy=authn_policy, - authorization_policy=authz_policy) + config = Configurator(root_factory=root_factory, settings=settings) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_static_view('static', 'static', cache_max_age=3600) config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index ab04ea405..56237a1b9 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -159,14 +159,14 @@ logged in user and redirect back to the front page. The ``login`` view callable will look something like this: .. literalinclude:: src/authorization/tutorial/views.py - :pyobject: login + :lines: 90-116 :linenos: :language: python The ``logout`` view callable will look something like this: .. literalinclude:: src/authorization/tutorial/views.py - :pyobject: logout + :lines: 118-122 :linenos: :language: python diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 7f533b635..bda0a2eb7 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -104,7 +104,7 @@ when a request is made to the root URL of our wiki. It always redirects to a URL which represents the path to our "FrontPage". .. literalinclude:: src/views/tutorial/views.py - :pyobject: view_wiki + :lines: 18-21 :linenos: :language: python @@ -126,7 +126,7 @@ HTML anchor for each *WikiWord* reference in the rendered HTML using a compiled regular expression. .. literalinclude:: src/views/tutorial/views.py - :pyobject: view_page + :lines: 23-44 :linenos: :language: python @@ -161,7 +161,7 @@ The ``matchdict`` attribute of the request passed to the ``add_page`` view will have the values we need to construct URLs and find model objects. .. literalinclude:: src/views/tutorial/views.py - :pyobject: add_page + :lines: 46-58 :linenos: :language: python @@ -197,7 +197,7 @@ request passed to the ``edit_page`` view will have a ``'pagename'`` key matching the name of the page the user wants to edit. .. literalinclude:: src/views/tutorial/views.py - :pyobject: edit_page + :lines: 60-73 :linenos: :language: python diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 04dd5fe82..7e290a1e1 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -17,9 +17,9 @@ def main(global_config, **settings): 'sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, - root_factory='tutorial.models.RootFactory', - authentication_policy=authn_policy, - authorization_policy=authz_policy) + root_factory='tutorial.models.RootFactory') + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py index 04dd5fe82..7e290a1e1 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py @@ -17,9 +17,9 @@ def main(global_config, **settings): 'sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, - root_factory='tutorial.models.RootFactory', - authentication_policy=authn_policy, - authorization_policy=authz_policy) + root_factory='tutorial.models.RootFactory') + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/whatsnew-1.3.rst b/docs/whatsnew-1.3.rst index ee4e2ccb5..ed7024f62 100644 --- a/docs/whatsnew-1.3.rst +++ b/docs/whatsnew-1.3.rst @@ -240,6 +240,16 @@ Minor Feature Additions - We allow extra keyword arguments to be passed to the :meth:`pyramid.config.Configurator.action` method. +- New API: :meth:`pyramid.config.Configurator.set_request_property`. Add lazy + property descriptors to a request without changing the request factory. + This method provides conflict detection and is the suggested way to add + properties to a request. + +- Responses generated by Pyramid's :class:`pyramid.views.static_view` now use + a ``wsgi.file_wrapper`` (see + http://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling) + when one is provided by the web server. + Backwards Incompatibilities --------------------------- @@ -300,6 +310,11 @@ Backwards Incompatibilities ``add_route`` as a pattern, it will now fail at startup time. Use Unicode instead. +- The ``path_info`` route and view predicates now match against + ``request.upath_info`` (Unicode) rather than ``request.path_info`` + (indeterminate value based on Python 3 vs. Python 2). This has to be done + to normalize matching on Python 2 and Python 3. + Documentation Enhancements -------------------------- diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index 04f9b6fb5..57ab7e13a 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -704,7 +704,13 @@ class Configurator( c = self.maybe_dotted(callable) module = inspect.getmodule(c) if module is c: - c = getattr(module, 'includeme') + try: + c = getattr(module, 'includeme') + except AttributeError: + raise ConfigurationError( + "module %r has no attribute 'includeme'" % (module.__name__) + ) + spec = module.__name__ + ':' + c.__name__ sourcefile = inspect.getsourcefile(c) diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 89a0d77c5..0359c46f7 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -567,17 +567,18 @@ class MultiView(object): raise PredicateMismatch(self.name) def viewdefaults(wrapped): - def wrapper(*arg, **kw): + def wrapper(self, *arg, **kw): defaults = {} - if len(arg) > 1: - view = arg[1] + if arg: + view = arg[0] else: view = kw.get('view') + view = self.maybe_dotted(view) if inspect.isclass(view): defaults = getattr(view, '__view_defaults__', {}).copy() defaults.update(kw) defaults['_backframes'] = 3 # for action_method - return wrapped(*arg, **defaults) + return wrapped(self, *arg, **defaults) return wraps(wrapped)(wrapper) class ViewsConfiguratorMixin(object): @@ -1544,11 +1545,12 @@ class StaticURLInfo(object): registry = get_current_registry() for (url, spec, route_name) in self._get_registrations(registry): if path.startswith(spec): - subpath = url_quote(path[len(spec):]) + subpath = path[len(spec):] if url is None: kw['subpath'] = subpath return request.route_url(route_name, **kw) else: + subpath = url_quote(subpath) return urljoin(url, subpath) raise ValueError('No static URL definition matching %s' % path) diff --git a/pyramid/scripts/prequest.py b/pyramid/scripts/prequest.py index a102dda7e..5423a6316 100644 --- a/pyramid/scripts/prequest.py +++ b/pyramid/scripts/prequest.py @@ -140,5 +140,8 @@ class PRequestCommand(object): self.out(response.status) for name, value in response.headerlist: self.out('%s: %s' % (name, value)) - self.out(response.ubody) + if response.charset: + self.out(response.ubody) + else: + self.out(response.body) return 0 diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index a73cbde3a..087549cd2 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -173,7 +173,7 @@ class PServeCommand(object): if os.environ.get(self._reloader_environ_key): if self.verbose > 1: self.out('Running reloading file monitor') - install_reloader(int(self.options.reload_interval)) + install_reloader(int(self.options.reload_interval), [app_spec]) # if self.requires_config_file: # watch_file(self.args[0]) else: @@ -667,7 +667,7 @@ def _turn_sigterm_into_systemexit(): # pragma: no cover raise SystemExit signal.signal(signal.SIGTERM, handle_term) -def install_reloader(poll_interval=1): # pragma: no cover +def install_reloader(poll_interval=1, extra_files=None): # pragma: no cover """ Install the reloading monitor. @@ -677,6 +677,9 @@ def install_reloader(poll_interval=1): # pragma: no cover which causes the whole application to shut-down (rudely). """ mon = Monitor(poll_interval=poll_interval) + if extra_files is None: + extra_files = [] + mon.extra_files.extend(extra_files) t = threading.Thread(target=mon.periodic_reload) t.setDaemon(True) t.start() diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py index 3aa2c7810..a866bed55 100644 --- a/pyramid/tests/test_config/test_init.py +++ b/pyramid/tests/test_config/test_init.py @@ -712,6 +712,11 @@ pyramid.tests.test_config.dummy_include2""", self.assertEqual(action['callable'], None) self.assertEqual(action['args'], test_config) + def test_include_with_module_defaults_to_includeme_missing(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.include, 'pyramid.tests') + def test_include_with_route_prefix(self): root_config = self._makeOne(autocommit=True) def dummy_subapp(config): diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index dd96579b7..4af29325a 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -1474,6 +1474,40 @@ class TestViewsConfigurationMixin(unittest.TestCase): context = DummyContext() request = self._makeRequest(config) self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_view_with_view_defaults_viewname_is_dottedname_kwarg(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + config = self._makeOne(autocommit=True) + config.add_view( + view='pyramid.tests.test_config.test_views.DummyViewDefaultsClass', + renderer=null_renderer) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) + + def test_add_view_with_view_defaults_viewname_is_dottedname_nonkwarg(self): + from pyramid.renderers import null_renderer + from pyramid.exceptions import PredicateMismatch + from zope.interface import directlyProvides + config = self._makeOne(autocommit=True) + config.add_view( + 'pyramid.tests.test_config.test_views.DummyViewDefaultsClass', + renderer=null_renderer) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + request = self._makeRequest(config) + self.assertEqual(wrapper(context, request), 'OK') + context = DummyContext() + request = self._makeRequest(config) + self.assertRaises(PredicateMismatch, wrapper, context, request) def test_add_view_with_view_config_and_view_defaults_doesnt_conflict(self): from pyramid.renderers import null_renderer @@ -3508,6 +3542,18 @@ class TestStaticURLInfo(unittest.TestCase): result = inst.generate('package:path/', request) self.assertEqual(result, 'http://example.com/foo/') + def test_generate_quoting(self): + config = testing.setUp() + try: + config.add_static_view('images', path='mypkg:templates') + inst = self._makeOne() + request = testing.DummyRequest() + request.registry = config.registry + result = inst.generate('mypkg:templates/foo%2Fbar', request) + self.assertEqual(result, 'http://example.com/images/foo%252Fbar') + finally: + testing.tearDown() + def test_generate_route_url(self): inst = self._makeOne() registrations = [(None, 'package:path/', '__viewname/')] @@ -3521,13 +3567,13 @@ class TestStaticURLInfo(unittest.TestCase): result = inst.generate('package:path/abc', request, a=1) self.assertEqual(result, 'url') - def test_generate_url_quoted_local(self): + def test_generate_url_unquoted_local(self): inst = self._makeOne() registrations = [(None, 'package:path/', '__viewname/')] inst._get_registrations = lambda *x: registrations def route_url(n, **kw): self.assertEqual(n, '__viewname/') - self.assertEqual(kw, {'subpath':'abc%20def', 'a':1}) + self.assertEqual(kw, {'subpath':'abc def', 'a':1}) return 'url' request = self._makeRequest() request.route_url = route_url @@ -3745,3 +3791,12 @@ class DummyStaticURLInfo: def add(self, config, name, spec, **kw): self.added.append((config, name, spec, kw)) + +class DummyViewDefaultsClass(object): + __view_defaults__ = { + 'containment':'pyramid.tests.test_config.IDummy' + } + def __init__(self, request): + pass + def __call__(self): + return 'OK' diff --git a/pyramid/tests/test_scripts/test_prequest.py b/pyramid/tests/test_scripts/test_prequest.py index 7f24174ef..cf7af4218 100644 --- a/pyramid/tests/test_scripts/test_prequest.py +++ b/pyramid/tests/test_scripts/test_prequest.py @@ -5,9 +5,10 @@ class TestPRequestCommand(unittest.TestCase): from pyramid.scripts.prequest import PRequestCommand return PRequestCommand - def _makeOne(self, argv): + def _makeOne(self, argv, headers=None): cmd = self._getTargetClass()(argv) cmd.get_app = self.get_app + self._headers = headers or [] self._out = [] cmd.out = self.out return cmd @@ -18,7 +19,7 @@ class TestPRequestCommand(unittest.TestCase): def helloworld(environ, start_request): self._environ = environ self._path_info = environ['PATH_INFO'] - start_request('200 OK', []) + start_request('200 OK', self._headers) return [b'abc'] return helloworld @@ -131,6 +132,15 @@ class TestPRequestCommand(unittest.TestCase): self._out, ['200 OK', 'Content-Type: text/html; charset=UTF-8', 'abc']) + def test_command_response_has_no_charset(self): + command = self._makeOne(['', '--method=GET', 'development.ini', '/'], + headers=[('Content-Type', 'image/jpeg')]) + command.run() + self.assertEqual(self._path_info, '/') + self.assertEqual(self._spec, 'development.ini') + self.assertEqual(self._app_name, None) + self.assertEqual(self._out, [b'abc']) + class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.prequest import main @@ -56,7 +56,7 @@ if not PY3: ]) setup(name='pyramid', - version='1.3a5', + version='1.3a6', description=('The Pyramid web application development framework, a ' 'Pylons project'), long_description=README + '\n\n' + CHANGES, |
