From 34c59071c28e18aeec87c7a244e38ed22e2f35c6 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 03:27:44 -0500 Subject: ocd break: sort imports --- pyramid/configuration.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 3f959aabf..63b1f70d7 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -24,6 +24,8 @@ from pyramid.interfaces import IChameleonTranslate from pyramid.interfaces import IDebugLogger from pyramid.interfaces import IDefaultPermission from pyramid.interfaces import IDefaultRootFactory +from pyramid.interfaces import IException +from pyramid.interfaces import IExceptionResponse from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import ILocaleNegotiator from pyramid.interfaces import IMultiView @@ -36,34 +38,32 @@ from pyramid.interfaces import IRootFactory from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import ISecuredView +from pyramid.interfaces import ISessionFactory from pyramid.interfaces import IStaticURLInfo from pyramid.interfaces import ITranslationDirectories from pyramid.interfaces import ITraverser from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid.interfaces import IExceptionResponse -from pyramid.interfaces import IException -from pyramid.interfaces import ISessionFactory from pyramid import chameleon_text from pyramid import chameleon_zpt -from pyramid.mako_templating import renderer_factory as mako_renderer_factory from pyramid import renderers -from pyramid.renderers import RendererHelper from pyramid.authorization import ACLAuthorizationPolicy from pyramid.compat import all from pyramid.compat import md5 from pyramid.events import ApplicationCreated +from pyramid.exceptions import ConfigurationError from pyramid.exceptions import Forbidden from pyramid.exceptions import NotFound from pyramid.exceptions import PredicateMismatch -from pyramid.exceptions import ConfigurationError from pyramid.i18n import get_localizer from pyramid.log import make_stream_logger +from pyramid.mako_templating import renderer_factory as mako_renderer_factory from pyramid.path import caller_package -from pyramid.path import package_path from pyramid.path import package_of +from pyramid.path import package_path from pyramid.registry import Registry +from pyramid.renderers import RendererHelper from pyramid.request import route_request_iface from pyramid.resource import PackageOverrides from pyramid.resource import resolve_resource_spec @@ -72,12 +72,12 @@ from pyramid.static import StaticURLInfo from pyramid.threadlocal import get_current_registry from pyramid.threadlocal import get_current_request from pyramid.threadlocal import manager -from pyramid.traversal import traversal_path from pyramid.traversal import DefaultRootFactory from pyramid.traversal import find_interface +from pyramid.traversal import traversal_path from pyramid.urldispatch import RoutesMapper -from pyramid.view import render_view_to_response from pyramid.view import default_exceptionresponse_view +from pyramid.view import render_view_to_response MAX_ORDER = 1 << 30 DEFAULT_PHASH = md5().hexdigest() -- cgit v1.2.3 From 85ee02b24caf0bf6b491b5ecf7ad84c29ca8c75c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 04:21:22 -0500 Subject: - Make test suite pass on Jython (requires PasteScript trunk, presumably to be 1.7.4). --- CHANGES.txt | 3 +++ pyramid/tests/test_configuration.py | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 604f28cf4..14b2c569d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,9 @@ Features - Add a ``pyramid.request.Request.route_path`` API. This is a convenience method of the request which calls ``pyramid.url.route_url``. +- Make test suite pass on Jython (requires PasteScript trunk, presumably to + be 1.7.4). + Bug Fixes --------- diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index ded17cb33..0d8905f7c 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -2940,6 +2940,7 @@ class ConfiguratorTests(unittest.TestCase): pyramid.tests) def test_scan_integration(self): + import os from zope.interface import alsoProvides from pyramid.interfaces import IRequest from pyramid.view import render_view_to_response @@ -3011,8 +3012,12 @@ class ConfiguratorTests(unittest.TestCase): 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') + if not os.name.startswith('java'): + # on Jython, a class without an __init__ apparently accepts + # any number of arguments without raising a TypeError. + + self.assertRaises(TypeError, + render_view_to_response, ctx, req, 'basemethod') result = render_view_to_response(ctx, req, 'method1') self.assertEqual(result, 'method1') -- cgit v1.2.3 From e57f5c2dd29f6bd88158a4b84ef67694fd33ba47 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 04:41:50 -0500 Subject: fix race condition test failure (seen on Jython) --- pyramid/authentication.py | 6 +++++- pyramid/tests/test_authentication.py | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pyramid/authentication.py b/pyramid/authentication.py index 4849d2c41..86d725bcf 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -286,6 +286,7 @@ EXPIRE = object() class AuthTktCookieHelper(object): auth_tkt = auth_tkt # for tests + now = None # for tests userid_type_decoders = { 'int':int, @@ -373,7 +374,10 @@ class AuthTktCookieHelper(object): except self.auth_tkt.BadTicket: return None - now = time.time() + now = self.now # service tests + + if now is None: + now = time.time() if self.timeout and ( (timestamp + self.timeout) < now ): return None diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index 69762fdb0..d9d0c2c97 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -411,8 +411,10 @@ class TestAuthTktCookieHelper(unittest.TestCase): def test_identify_cookie_reissue(self): import time - plugin = self._makeOne('secret', timeout=5000, reissue_time=0) - plugin.auth_tkt.timestamp = time.time() + plugin = self._makeOne('secret', timeout=10, reissue_time=0) + now = time.time() + plugin.auth_tkt.timestamp = now + plugin.now = now + 1 request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) result = plugin.identify(request) self.failUnless(result) -- cgit v1.2.3 From 165020a7ba21d0bbebaaefd142b70c28d278ea83 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 09:42:17 -0500 Subject: gardening --- TODO.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO.txt b/TODO.txt index f12dcee73..fe411f749 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,11 +4,6 @@ Pyramid TODOs Must-Have (before 1.0) ---------------------- -- Test on GAE, Jython, PyPy, IronPython. - -- Add docs for httpexceptions module for each webob.exc class that inherits - from WSGIHTTPException. - - Add a ``handler`` ZCML directive. This implies some slightly dicey refactoring of the configurator to allow it to generate ZCML "discriminators" for views and routes. @@ -60,6 +55,11 @@ Must-Have (before 1.0) Should-Have ----------- +- Try to make test suite pass on PyPy, IronPython. + +- Add docs for httpexceptions module for each webob.exc class that inherits + from WSGIHTTPException. + - Create a ``docs`` directory for each paster template. - Remove "BFG" from Pyramid-specific environ variables. -- cgit v1.2.3 From 3b913e3f8a2cb4afbe57033643fd998cdde0217b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 09:42:41 -0500 Subject: add pypyenv, change ignores for things that are directories to end in a slash --- .gitignore | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 5d46de01f..706f6493d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,10 @@ *.pt.py *.txt.py .coverage -env26 -env24 -env27 -jyenv +env26/ +env24/ +env27/ +jyenv/ +pypyenv/ build/ dist/ -- cgit v1.2.3 From 1e467e1bacc915d1e00bdce189e35f5afb568132 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 09:43:18 -0500 Subject: we dont actually need coverage (and it fails on pypy) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c2d11384f..c1939f73c 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ install_requires=[ if platform.system() == 'Java': tests_require = install_requires + ['WebTest'] else: - tests_require= install_requires + ['Sphinx', 'docutils', 'coverage', + tests_require= install_requires + ['Sphinx', 'docutils', 'WebTest', 'repoze.sphinx.autointerface'] if sys.version_info[:2] < (2, 6): -- cgit v1.2.3 From a66593d25e77f1a0e749f5590b45498bbaa66755 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 16:56:05 -0500 Subject: - Fix apparent failures when calling ``pyramid.traversal.find_model(root, path)`` or ``pyramid.traversal.traverse(path)`` when ``path`` is (erroneously) a Unicode object. The user is meant to pass these APIs a string object, never a Unicode object. In practice, however, users indeed pass Unicode. Because the string that is passed must be ASCII encodeable, now, if they pass a Unicode object, its data is eagerly converted to an ASCII string rather than being passed along to downstream code as a convenience to the user and to prevent puzzling second-order failures from cropping up (all failures will occur within ``pyramid.traversal.traverse`` rather than later down the line as the result of calling ``traversal_path``). --- CHANGES.txt | 12 ++++++++++++ pyramid/tests/test_traversal.py | 26 ++++++++++++++++++++++++++ pyramid/traversal.py | 13 ++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 14b2c569d..0720034b2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -28,6 +28,18 @@ Bug Fixes - The ``pyramid_alchemy`` paster template had a typo, preventing an import from working. +- Fix apparent failures when calling ``pyramid.traversal.find_model(root, + path)`` or ``pyramid.traversal.traverse(path)`` when ``path`` is + (erroneously) a Unicode object. The user is meant to pass these APIs a + string object, never a Unicode object. In practice, however, users indeed + pass Unicode. Because the string that is passed must be ASCII encodeable, + now, if they pass a Unicode object, its data is eagerly converted to an + ASCII string rather than being passed along to downstream code as a + convenience to the user and to prevent puzzling second-order failures from + cropping up (all failures will occur within ``pyramid.traversal.traverse`` + rather than later down the line as the result of calling + ``traversal_path``). + Backwards Incompatibilities --------------------------- diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py index 2deb5982c..3245d6302 100644 --- a/pyramid/tests/test_traversal.py +++ b/pyramid/tests/test_traversal.py @@ -522,6 +522,32 @@ class FindModelTests(unittest.TestCase): self.assertEqual(root.wascontext, True) self.assertEqual(root.request.environ['PATH_INFO'], '/') + def test_absolute_unicode_found(self): + # test for bug wiggy found in wild, traceback stack: + # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF' + # wiggy's code: section=find_model(page, root) + # find_model L76: D = traverse(model, path) + # traverse L291: return traverser(request) + # __call__ line 568: vpath_tuple = traversal_path(vpath) + # lru_cached line 91: f(*arg) + # traversal_path line 443: path.encode('ascii') + # UnicodeEncodeError: 'ascii' codec can't encode characters in + # position 1-12: ordinal not in range(128) + # + # solution: encode string to ascii in pyramid.traversal.traverse + # before passing it along to webob as path_info + from pyramid.traversal import ModelGraphTraverser + unprintable = DummyContext() + root = DummyContext(unprintable) + unprintable.__parent__ = root + unprintable.__name__ = unicode( + '/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8') + root.__parent__ = None + root.__name__ = None + traverser = ModelGraphTraverser + self._registerTraverser(traverser) + result = self._callFUT(root, u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF') + self.assertEqual(result, unprintable) class ModelPathTests(unittest.TestCase): def _callFUT(self, model, *elements): diff --git a/pyramid/traversal.py b/pyramid/traversal.py index e928c33f7..fb73ad906 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -228,7 +228,7 @@ def traverse(model, path): 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 + manner: each Unicode path segment must be encoded as UTF-8 and 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 @@ -272,6 +272,17 @@ def traverse(model, path): else: path = '' + # The user is supposed to pass us a string object, never Unicode. In + # practice, however, users indeed pass Unicode to this API. If they do + # pass a Unicode object, its data *must* be entirely encodeable to ASCII, + # so we encode it here as a convenience to the user and to prevent + # second-order failures from cropping up (all failures will occur at this + # step rather than later down the line as the result of calling + # ``traversal_path``). + + if isinstance(path, unicode): + path = path.encode('ascii') + if path and path[0] == '/': model = find_root(model) -- cgit v1.2.3 From e84116c068f45c68752f89062fa545dd40acd63f Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 14:13:02 -0800 Subject: - URL Dispatch now allows for replacement markers to be located anywhere in the pattern, instead of immediately following a ``/``. - Added ``marker_pattern`` option to ``add_route`` to supply a dict of regular expressions to be used for markers in the pattern instead of the default regular expression that matched everything except a ``/``. --- CHANGES.txt | 5 +++++ docs/narr/urldispatch.rst | 39 +++++++++++++++++++++++++++++---------- pyramid/configuration.py | 12 ++++++++++-- pyramid/tests/test_urldispatch.py | 13 ++++++++++--- pyramid/urldispatch.py | 20 +++++++++++--------- 5 files changed, 65 insertions(+), 24 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 604f28cf4..b418565fa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,11 @@ Next release Features -------- +- URL Dispatch now allows for replacement markers to be located anywhere + in the pattern, instead of immediately following a ``/``. +- Added ``marker_pattern`` option to ``add_route`` to supply a dict of + regular expressions to be used for markers in the pattern instead of the + default regular expression that matched everything except a ``/``. - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative URLs. Calling ``route_path`` is the same as calling ``pyramid.url.route_url`` with the argument ``_app_url`` equal to the empty diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 4442be355..0170b36e2 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -208,16 +208,16 @@ and: /:foo/bar/baz -A patttern segment (an individual item between ``/`` characters in the -pattern) may either be a literal string (e.g. ``foo``) *or* it may be -a segment replacement marker (e.g. ``:foo``) or a certain combination -of both. +A pattern segment (an individual item between ``/`` characters in the pattern) +may either be a literal string (e.g. ``foo``) *or* it may be a replacement +marker (e.g. ``:foo``) or a certain combination of both. A replacement marker +does not need to be preceded by a ``/`` character. -A segment replacement marker is in the format ``:name``, where this -means "accept any characters up to the next nonalphaunumeric character +A replacement marker is in the format ``:name``, where this +means "accept any characters up to the next non-alphanumeric character and use this as the ``name`` matchdict value." For example, the following pattern defines one literal segment ("foo") and two dynamic -segments ("baz", and "bar"): +replacement markers ("baz", and "bar"): .. code-block:: text @@ -252,9 +252,21 @@ literal path ``/foo/biz`` will not match, because it does not contain a literal ``.html`` at the end of the segment represented by ``:name.html`` (it only contains ``biz``, not ``biz.html``). -This does not mean, however, that you can use two segment replacement -markers in the same segment. For instance, ``/:foo:bar`` is a -nonsensical route pattern. It will never match anything. +To capture both segments, two replacement markers can be used: + +.. code-block:: text + + foo/:name.:ext + +The literal path ``/foo/biz.html`` will match the above route pattern, and the +match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs because +the replacement marker ``:name`` has a literal part of ``.`` between the other +replacement marker ``:ext``. + +It is possible to use two replacement markers without any literal characters +between them, for instance ``/:foo:bar``. This would be a nonsensical pattern +without specifying any ``pattern_regexes`` to restrict valid values of each +replacement marker. Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL ``/abc/``: @@ -471,6 +483,13 @@ represent neither predicates nor view configuration information. as ``path``. ``path`` continues to work as an alias for ``pattern``. +``marker_pattern`` + A dict of regular expression replacements for replacement markers in the + pattern to use when generating the complete regular expression used to + match the route. By default, every replacement marker in the pattern is + replaced with the regular expression ``[^/]+``. Values in this dict will + be used instead if present. + ``xhr`` This value should be either ``True`` or ``False``. If this value is specified and is ``True``, the :term:`request` must possess an diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 3f959aabf..41b774e65 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -1188,6 +1188,7 @@ class Configurator(object): def add_route(self, name, pattern=None, + marker_pattern=None, view=None, view_for=None, permission=None, @@ -1306,7 +1307,13 @@ class Configurator(object): 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. - + + marker_pattern + + A dict of regular expression's that will be used in the place + of the default ``[^/]+`` regular expression for all replacement + markers in the route pattern. + xhr This value should be either ``True`` or ``False``. If this @@ -1529,7 +1536,8 @@ class Configurator(object): raise ConfigurationError('"pattern" argument may not be None') return mapper.connect(name, pattern, factory, predicates=predicates, - pregenerator=pregenerator) + pregenerator=pregenerator, + marker_pattern=marker_pattern) def get_routes_mapper(self): """ Return the :term:`routes mapper` object associated with diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index 799f4986f..f77ebbbb4 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -218,9 +218,9 @@ class RoutesMapperTests(unittest.TestCase): self.assertEqual(mapper.generate('abc', {}), 123) class TestCompileRoute(unittest.TestCase): - def _callFUT(self, pattern): + def _callFUT(self, pattern, marker_pattern=None): from pyramid.urldispatch import _compile_route - return _compile_route(pattern) + return _compile_route(pattern, marker_pattern) def test_no_star(self): matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') @@ -251,6 +251,14 @@ class TestCompileRoute(unittest.TestCase): from pyramid.exceptions import URLDecodeError matcher, generator = self._callFUT('/:foo') self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00') + + def test_custom_regex(self): + matcher, generator = self._callFUT('foo/:baz/biz/:buz.:bar', + {'buz': '[^/\.]+'}) + self.assertEqual(matcher('/foo/baz/biz/buz.bar'), + {'baz':'baz', 'buz':'buz', 'bar':'bar'}) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') class TestCompileRouteMatchFunctional(unittest.TestCase): def matches(self, pattern, path, expected): @@ -271,7 +279,6 @@ class TestCompileRouteMatchFunctional(unittest.TestCase): 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'}) diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index aa0bddfe9..06ec647e5 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -17,10 +17,10 @@ _marker = object() class Route(object): implements(IRoute) def __init__(self, name, pattern, factory=None, predicates=(), - pregenerator=None): + pregenerator=None, marker_pattern=None): self.pattern = pattern self.path = pattern # indefinite b/w compat, not in interface - self.match, self.generate = _compile_route(pattern) + self.match, self.generate = _compile_route(pattern, marker_pattern) self.name = name self.factory = factory self.predicates = predicates @@ -42,11 +42,12 @@ class RoutesMapper(object): return self.routes.get(name) def connect(self, name, pattern, factory=None, predicates=(), - pregenerator=None): + pregenerator=None, marker_pattern=None): if name in self.routes: oldroute = self.routes[name] self.routelist.remove(oldroute) - route = Route(name, pattern, factory, predicates, pregenerator) + route = Route(name, pattern, factory, predicates, pregenerator, + marker_pattern) self.routelist.append(route) self.routes[name] = route return route @@ -74,8 +75,9 @@ class RoutesMapper(object): return {'route':None, 'match':None} # stolen from bobo and modified -route_re = re.compile(r'(/:[a-zA-Z]\w*)') -def _compile_route(route): +route_re = re.compile(r'(:[a-zA-Z]\w*)') +def _compile_route(route, marker_pattern=None): + marker_pattern = marker_pattern or {} if not route.startswith('/'): route = '/' + route star = None @@ -91,9 +93,9 @@ def _compile_route(route): gen.append(prefix) while pat: name = pat.pop() - name = name[2:] - gen.append('/%%(%s)s' % name) - name = '/(?P<%s>[^/]+)' % name + name = name[1:] + gen.append('%%(%s)s' % name) + name = '(?P<%s>%s)' % (name, marker_pattern.get(name, '[^/]+')) rpat.append(name) s = pat.pop() if s: -- cgit v1.2.3 From 47c9f884f72e6902c3b789ab9c4c08a6fabcfc6a Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 14:13:33 -0800 Subject: Gardening. --- CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 4d51b89f5..dbd774086 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,9 +6,11 @@ Features - URL Dispatch now allows for replacement markers to be located anywhere in the pattern, instead of immediately following a ``/``. + - Added ``marker_pattern`` option to ``add_route`` to supply a dict of regular expressions to be used for markers in the pattern instead of the default regular expression that matched everything except a ``/``. + - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative URLs. Calling ``route_path`` is the same as calling ``pyramid.url.route_url`` with the argument ``_app_url`` equal to the empty -- cgit v1.2.3 From 29b7aabcb7b50024ead27cb5ff9cb47757ffe462 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 18 Nov 2010 22:26:41 -0500 Subject: - Make test suite pass on PyPy (Chameleon doesn't work). --- CHANGES.txt | 2 ++ pyramid/configuration.py | 19 +++++++++++++++---- pyramid/testing.py | 7 +++++++ pyramid/tests/test_chameleon_text.py | 30 ++++++++++++++++-------------- pyramid/tests/test_chameleon_zpt.py | 30 ++++++++++++++++-------------- pyramid/tests/test_configuration.py | 10 ++++++++-- pyramid/tests/test_renderers.py | 2 +- 7 files changed, 65 insertions(+), 35 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dbd774086..c1e89734d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,6 +22,8 @@ Features - Make test suite pass on Jython (requires PasteScript trunk, presumably to be 1.7.4). +- Make test suite pass on PyPy (Chameleon doesn't work). + Bug Fixes --------- diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 543f8f82d..d861292b6 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -45,8 +45,15 @@ from pyramid.interfaces import ITraverser from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid import chameleon_text -from pyramid import chameleon_zpt +try: + from pyramid import chameleon_text +except TypeError: # pragma: no cover + chameleon_text = None # pypy +try: + from pyramid import chameleon_zpt +except TypeError: # pragma: no cover + chameleon_zpt = None # pypy + from pyramid import renderers from pyramid.authorization import ACLAuthorizationPolicy from pyramid.compat import all @@ -83,14 +90,18 @@ MAX_ORDER = 1 << 30 DEFAULT_PHASH = md5().hexdigest() DEFAULT_RENDERERS = ( - ('.pt', chameleon_zpt.renderer_factory), - ('.txt', chameleon_text.renderer_factory), ('.mak', mako_renderer_factory), ('.mako', mako_renderer_factory), ('json', renderers.json_renderer_factory), ('string', renderers.string_renderer_factory), ) +if chameleon_text: + DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),) +if chameleon_zpt: + DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),) + + class Configurator(object): """ A Configurator is used to configure a :app:`Pyramid` diff --git a/pyramid/testing.py b/pyramid/testing.py index c6c999147..4acede879 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -741,9 +741,16 @@ def skip_on(*platforms): for platform in platforms: if skip_on.os_name.startswith(platform): return + if platform == 'pypy' and skip_on.pypy: # pragma: no cover + return return func(*args, **kw) wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ return wrapper return decorator skip_on.os_name = os.name # for testing +try: # pragma: no cover + import __pypy__ + skip_on.pypy = True +except ImportError: + skip_on.pypy = False diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py index 654cfdf3b..9486bbc88 100644 --- a/pyramid/tests/test_chameleon_text.py +++ b/pyramid/tests/test_chameleon_text.py @@ -51,6 +51,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): klass = self._getTargetClass() return klass(*arg, **kw) + @skip_on('pypy') def test_instance_implements_ITemplate(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITemplateRenderer @@ -58,12 +59,13 @@ class TextTemplateRendererTests(Base, unittest.TestCase): lookup = DummyLookup() verifyObject(ITemplateRenderer, self._makeOne(path, lookup)) + @skip_on('pypy') def test_class_implements_ITemplate(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_reified(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -72,7 +74,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -81,7 +83,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.translate, lookup.translate) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -91,7 +93,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, True) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -101,7 +103,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, True) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -111,7 +113,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, False) - @skip_on('java') + @skip_on('java', 'pypy') def test_call(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -120,14 +122,14 @@ class TextTemplateRendererTests(Base, unittest.TestCase): self.failUnless(isinstance(result, str)) self.assertEqual(result, 'Hello.\n') - @skip_on('java') + @skip_on('java', 'pypy') def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) - @skip_on('java') + @skip_on('java', 'pypy') def test_call_nonminimal(self): nonminimal = self._getTemplatePath('nonminimal.txt') lookup = DummyLookup() @@ -136,7 +138,7 @@ class TextTemplateRendererTests(Base, unittest.TestCase): self.failUnless(isinstance(result, str)) self.assertEqual(result, 'Hello, Chris!\n') - @skip_on('java') + @skip_on('java', 'pypy') def test_implementation(self): minimal = self._getTemplatePath('minimal.txt') lookup = DummyLookup() @@ -150,7 +152,7 @@ class RenderTemplateTests(Base, unittest.TestCase): from pyramid.chameleon_text import render_template return render_template(name, **kw) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): minimal = self._getTemplatePath('minimal.txt') result = self._callFUT(minimal) @@ -162,7 +164,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase): from pyramid.chameleon_text import render_template_to_response return render_template_to_response(name, **kw) - @skip_on('java') + @skip_on('java', 'pypy') def test_minimal(self): minimal = self._getTemplatePath('minimal.txt') result = self._callFUT(minimal) @@ -172,7 +174,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase): self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) - @skip_on('java') + @skip_on('java', 'pypy') def test_iresponsefactory_override(self): from webob import Response class Response2(Response): @@ -188,7 +190,7 @@ class GetRendererTests(Base, unittest.TestCase): from pyramid.chameleon_text import get_renderer return get_renderer(name) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): from pyramid.interfaces import IRendererFactory class Dummy: @@ -206,7 +208,7 @@ class GetTemplateTests(Base, unittest.TestCase): from pyramid.chameleon_text import get_template return get_template(name) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): from pyramid.interfaces import IRendererFactory class Dummy: diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py index 786e96129..802f4460f 100644 --- a/pyramid/tests/test_chameleon_zpt.py +++ b/pyramid/tests/test_chameleon_zpt.py @@ -44,6 +44,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): klass = self._getTargetClass() return klass(*arg, **kw) + @skip_on('pypy') def test_instance_implements_ITemplate(self): from zope.interface.verify import verifyObject from pyramid.interfaces import ITemplateRenderer @@ -51,12 +52,13 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): lookup = DummyLookup() verifyObject(ITemplateRenderer, self._makeOne(path, lookup)) + @skip_on('pypy') def test_class_implements_ITemplate(self): from zope.interface.verify import verifyClass from pyramid.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) - @skip_on('java') + @skip_on('java', 'pypy') def test_call(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -66,7 +68,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): self.assertEqual(result, '
\n
') - @skip_on('java') + @skip_on('java', 'pypy') def test_template_reified(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -75,7 +77,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template, instance.__dict__['template']) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_ichameleon_translate(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -84,7 +86,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.translate, lookup.translate) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -94,7 +96,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, True) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_without_debug_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -104,7 +106,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.debug, False) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_with_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -114,7 +116,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, True) - @skip_on('java') + @skip_on('java', 'pypy') def test_template_without_reload_templates(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -124,14 +126,14 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): template = instance.template self.assertEqual(template.auto_reload, False) - @skip_on('java') + @skip_on('java', 'pypy') def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) - @skip_on('java') + @skip_on('java', 'pypy') def test_implementation(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() @@ -147,7 +149,7 @@ class RenderTemplateTests(Base, unittest.TestCase): from pyramid.chameleon_zpt import render_template return render_template(name, **kw) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): minimal = self._getTemplatePath('minimal.pt') result = self._callFUT(minimal) @@ -160,7 +162,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase): from pyramid.chameleon_zpt import render_template_to_response return render_template_to_response(name, **kw) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): minimal = self._getTemplatePath('minimal.pt') result = self._callFUT(minimal) @@ -171,7 +173,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase): self.assertEqual(result.status, '200 OK') self.assertEqual(len(result.headerlist), 2) - @skip_on('java') + @skip_on('java', 'pypy') def test_iresponsefactory_override(self): from webob import Response class Response2(Response): @@ -187,7 +189,7 @@ class GetRendererTests(Base, unittest.TestCase): from pyramid.chameleon_zpt import get_renderer return get_renderer(name) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): from pyramid.interfaces import IRendererFactory class Dummy: @@ -205,7 +207,7 @@ class GetTemplateTests(Base, unittest.TestCase): from pyramid.chameleon_zpt import get_template return get_template(name) - @skip_on('java') + @skip_on('java', 'pypy') def test_it(self): from pyramid.interfaces import IRendererFactory class Dummy: diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index 0d8905f7c..a8ea63f54 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -2,6 +2,11 @@ import unittest from pyramid import testing +try: + import __pypy__ +except: + __pypy__ = None + class ConfiguratorTests(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.configuration import Configurator @@ -90,8 +95,9 @@ class ConfiguratorTests(unittest.TestCase): 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')) + if not __pypy__: + self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) + self.failUnless(config.registry.getUtility(IRendererFactory,'.txt')) self.failUnless(config.registry.getUtility(IRendererFactory, '.mak')) self.failUnless(config.registry.getUtility(IRendererFactory, '.mako')) diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py index 102a23f92..bd6a0825d 100644 --- a/pyramid/tests/test_renderers.py +++ b/pyramid/tests/test_renderers.py @@ -138,7 +138,7 @@ class TestTemplateRendererFactory(unittest.TestCase): result = self._callFUT(info, factory) self.failUnless(result is renderer) path = os.path.abspath(__file__).split('$')[0] # jython - if path.endswith('.pyc'): + if path.endswith('.pyc'): # pragma: no cover path = path[:-1] self.failUnless(factory.path.startswith(path)) self.assertEqual(factory.kw, {}) -- cgit v1.2.3 From 4018adea20ee73cfeeaebb69e95e4b00dd5cf22e Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 20:38:25 -0800 Subject: - URL Dispatch now uses the form ``{marker}`` to denote a replace marker in the route pattern instead of ``:marker``. The old syntax is still backwards compatible and accepted. The new format allows a regular expression for that marker location to be used instead of the default ``[^/]+``, for example ``{marker:\d+}`` is now valid to require the marker to be digits. --- CHANGES.txt | 8 ++- docs/narr/urldispatch.rst | 140 +++++++++++++++++++++++++------------- pyramid/configuration.py | 10 +-- pyramid/tests/test_urldispatch.py | 7 +- pyramid/urldispatch.py | 30 +++++--- 5 files changed, 121 insertions(+), 74 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dbd774086..8aff366a6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,9 +7,11 @@ Features - URL Dispatch now allows for replacement markers to be located anywhere in the pattern, instead of immediately following a ``/``. -- Added ``marker_pattern`` option to ``add_route`` to supply a dict of - regular expressions to be used for markers in the pattern instead of the - default regular expression that matched everything except a ``/``. +- URL Dispatch now uses the form ``{marker}`` to denote a replace marker in + the route pattern instead of ``:marker``. The old syntax is still backwards + compatible and accepted. The new format allows a regular expression for that + marker location to be used instead of the default ``[^/]+``, for example + ``{marker:\d+}`` is now valid to require the marker to be digits. - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative URLs. Calling ``route_path`` is the same as calling diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 0170b36e2..0d66f28a1 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -92,7 +92,17 @@ registry`. Here's an example: # pyramid.configuration.Configurator class; "myview" is assumed # to be a "view callable" function from views import myview - config.add_route('myroute', '/prefix/:one/:two', view=myview) + config.add_route('myroute', '/prefix/{one}/{two}', view=myview) + +.. versionchanged:: 1.0a4 + + Prior to 1.0a4, routes allow for a marker starting with a ``:``, for + example:: + + config.add_route('myroute', '/prefix/:one/:two', view=myview) + + Starting in 1.0a4, this style is deprecated in favor or ``{}`` usage + which allows for additional functionality. .. index:: single: route configuration; view callable @@ -116,7 +126,7 @@ Here's an example route configuration that references a view callable: # pyramid.configuration.Configurator class; "myview" is assumed # to be a "view callable" function from myproject.views import myview - config.add_route('myroute', '/prefix/:one/:two', view=myview) + config.add_route('myroute', '/prefix/{one}/{two}', view=myview) You can also pass a :term:`dotted Python name` as the ``view`` argument rather than an actual callable: @@ -128,7 +138,7 @@ rather than an actual callable: # pyramid.configuration.Configurator class; "myview" is assumed # to be a "view callable" function from myproject.views import myview - config.add_route('myroute', '/prefix/:one/:two', + config.add_route('myroute', '/prefix/{one}/{two}', view='myproject.views.myview') When a route configuration names a ``view`` attribute, the :term:`view @@ -200,20 +210,20 @@ the following patterns are equivalent: .. code-block:: text - :foo/bar/baz + {foo}/bar/baz and: .. code-block:: text - /:foo/bar/baz + /{foo}/bar/baz A pattern segment (an individual item between ``/`` characters in the pattern) may either be a literal string (e.g. ``foo``) *or* it may be a replacement -marker (e.g. ``:foo``) or a certain combination of both. A replacement marker +marker (e.g. ``{foo}``) or a certain combination of both. A replacement marker does not need to be preceded by a ``/`` character. -A replacement marker is in the format ``:name``, where this +A replacement marker is in the format ``{name}``, where this means "accept any characters up to the next non-alphanumeric character and use this as the ``name`` matchdict value." For example, the following pattern defines one literal segment ("foo") and two dynamic @@ -221,7 +231,7 @@ replacement markers ("baz", and "bar"): .. code-block:: text - foo/:baz/:bar + foo/{baz}/{bar} The above pattern will match these URLs, generating the following matchdicts: @@ -244,27 +254,27 @@ pattern. So, for instance, if this route pattern was used: .. code-block:: text - foo/:name.html + foo/{name}.html The literal path ``/foo/biz.html`` will match the above route pattern, and the match result will be ``{'name':u'biz'}``. However, the literal path ``/foo/biz`` will not match, because it does not contain a literal ``.html`` at the end of the segment represented by -``:name.html`` (it only contains ``biz``, not ``biz.html``). +``{name}.html`` (it only contains ``biz``, not ``biz.html``). To capture both segments, two replacement markers can be used: .. code-block:: text - foo/:name.:ext + foo/{name}.{ext} The literal path ``/foo/biz.html`` will match the above route pattern, and the match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs because -the replacement marker ``:name`` has a literal part of ``.`` between the other +the replacement marker ``{name}`` has a literal part of ``.`` between the other replacement marker ``:ext``. It is possible to use two replacement markers without any literal characters -between them, for instance ``/:foo:bar``. This would be a nonsensical pattern +between them, for instance ``/{foo}{bar}``. This would be a nonsensical pattern without specifying any ``pattern_regexes`` to restrict valid values of each replacement marker. @@ -282,7 +292,7 @@ pattern: .. code-block:: text - foo/:bar + foo/{bar} When matching the following URL: @@ -304,7 +314,7 @@ need to be preceded by a slash. For example: .. code-block:: text - foo/:baz/:bar*fizzle + foo/{baz}/{bar}*fizzle The above pattern will match these URLs, generating the following matchdicts: @@ -336,6 +346,24 @@ Will generate the following matchdict: {'fizzle':(u'La Pe\xf1a', u'a', u'b', u'c')} +By default, the ``*stararg`` will parse the remainder sections into a tuple +split by segment. Changing the regular expression used to match a marker can +also capture the remainder of the URL, for example: + +.. code-block:: text + + foo/{baz}/{bar}{fizzle:.*} + +The above pattern will match these URLs, generating the following matchdicts: + + foo/1/2/ -> {'baz':'1', 'bar':'2', 'fizzle':()} + foo/abc/def/a/b/c -> {'baz':'abc', 'bar':'def', 'fizzle': 'a/b/c')} + +This occurs because the default regular expression for a marker is ``[^/]+`` +which will match everything up to the first ``/``, while ``{filzzle:.*}`` will +result in a regular expression match of ``.*`` capturing the remainder into +a single value. + .. index:: single: route ordering @@ -360,12 +388,12 @@ be added in the following order: .. code-block:: text - members/:def + members/{def} members/abc In such a configuration, the ``members/abc`` pattern would *never* be matched; this is because the match ordering will always match -``members/:def`` first; the route configuration with ``members/abc`` +``members/{def}`` first; the route configuration with ``members/abc`` will never be evaluated. .. index:: @@ -446,8 +474,8 @@ represent neither predicates nor view configuration information. The syntax of the ``traverse`` argument is the same as it is for ``pattern``. For example, if the ``pattern`` provided is - ``articles/:article/edit``, and the ``traverse`` argument provided - is ``/:article``, when a request comes in that causes the route to + ``articles/{article}/edit``, and the ``traverse`` argument provided + 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 @@ -474,7 +502,7 @@ represent neither predicates nor view configuration information. **Predicate Arguments** ``pattern`` - The path of the route e.g. ``ideas/:idea``. This argument is + The path of the route e.g. ``ideas/{idea}``. This argument is required. See :ref:`route_path_pattern_syntax` for information about the syntax of route paths. If the path doesn't match the current URL, route matching continues. @@ -482,13 +510,6 @@ represent neither predicates nor view configuration information. .. note:: In earlier releases of this framework, this argument existed as ``path``. ``path`` continues to work as an alias for ``pattern``. - -``marker_pattern`` - A dict of regular expression replacements for replacement markers in the - pattern to use when generating the complete regular expression used to - match the route. By default, every replacement marker in the pattern is - replaced with the regular expression ``[^/]+``. Values in this dict will - be used instead if present. ``xhr`` This value should be either ``True`` or ``False``. If this value is @@ -663,7 +684,7 @@ match. For example: num_one_two_or_three = any_of('num', 'one', 'two', 'three') - config.add_route('num', '/:num', + config.add_route('num', '/{num}', custom_predicates=(num_one_two_or_three,)) The above ``any_of`` function generates a predicate which ensures that @@ -694,7 +715,7 @@ For instance, a predicate might do some type conversion of values: ymd_to_int = integers('year', 'month', 'day') - config.add_route('num', '/:year/:month/:day', + config.add_route('num', '/{year}/{month}/{day}', custom_predicates=(ymd_to_int,)) Note that a conversion predicate is still a predicate so it must @@ -702,6 +723,29 @@ return ``True`` or ``False``; a predicate that does *only* conversion, such as the one we demonstrate above should unconditionally return ``True``. +To avoid the try/except uncertainty, the route pattern can contain regular +expressions specifying requirements for that marker. For instance: + +.. code-block:: python + :linenos: + + def integers(*segment_names): + def predicate(info, request): + match = info['match'] + for segment_name in segment_names: + match[segment_name] = int(match[segment_name]) + return True + return predicate + + ymd_to_int = integers('year', 'month', 'day') + + config.add_route('num', '/{year:\d+}/{month:\d+}/{day:\d+}', + custom_predicates=(ymd_to_int,)) + +Now the try/except is no longer needed because the route will not match at +all unless these markers match ``\d+`` which requires them to be valid digits +for an ``int`` type conversion. + The ``match`` dictionary passed within ``info`` to each predicate attached to a route will be the same dictionary. Therefore, when registering a custom predicate which modifies the ``match`` dict, the @@ -732,9 +776,9 @@ An example of using the route in a set of route predicates: if info['route'].name in ('ymd', 'ym', 'y'): return info['match']['year'] == '2010' - config.add_route('y', '/:year', custom_predicates=(twenty_ten,)) - config.add_route('ym', '/:year/:month', custom_predicates=(twenty_ten,)) - config.add_route('ymd', '/:year/:month:/day', + config.add_route('y', '/{year}', custom_predicates=(twenty_ten,)) + config.add_route('ym', '/{year}/{month}', custom_predicates=(twenty_ten,)) + config.add_route('ymd', '/{year}/{month}/{day}', custom_predicates=(twenty_ten,)) The above predicate, when added to a number of route configurations @@ -833,7 +877,7 @@ The simplest route declaration which configures a route match to .. code-block:: python :linenos: - config.add_route('idea', 'site/:id', view='mypackage.views.site_view') + config.add_route('idea', 'site/{id}', view='mypackage.views.site_view') When a route configuration with a ``view`` attribute is added to the system, and an incoming request matches the *pattern* of the route @@ -841,12 +885,12 @@ configuration, the :term:`view callable` named as the ``view`` attribute of the route configuration will be invoked. In the case of the above example, when the URL of a request matches -``/site/:id``, the view callable at the Python dotted path name +``/site/{id}``, the view callable at the Python dotted path name ``mypackage.views.site_view`` will be called with the request. In other words, we've associated a view callable directly with a route pattern. -When the ``/site/:id`` route pattern matches during a request, the +When the ``/site/{id}`` route pattern matches during a request, the ``site_view`` view callable is invoked with that request as its sole argument. When this route matches, a ``matchdict`` will be generated and attached to the request as ``request.matchdict``. If the specific @@ -879,30 +923,30 @@ might add to your application: .. code-block:: python :linenos: - config.add_route('idea', 'ideas/:idea', view='mypackage.views.idea_view') - config.add_route('user', 'users/:user', view='mypackage.views.user_view') - config.add_route('tag', 'tags/:tags', view='mypackage.views.tag_view') + config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view') + config.add_route('user', 'users/{user}', view='mypackage.views.user_view') + config.add_route('tag', 'tags/{tags}', view='mypackage.views.tag_view') The above configuration will allow :app:`Pyramid` to service URLs in these forms: .. code-block:: text - /ideas/:idea - /users/:user - /tags/:tag + /ideas/{idea} + /users/{user} + /tags/{tag} -- When a URL matches the pattern ``/ideas/:idea``, the view callable +- When a URL matches the pattern ``/ideas/{idea}``, the view callable available at the dotted Python pathname ``mypackage.views.idea_view`` will be called. For the specific URL ``/ideas/1``, the ``matchdict`` generated and attached to the :term:`request` will consist of ``{'idea':'1'}``. -- When a URL matches the pattern ``/users/:user``, the view callable +- When a URL matches the pattern ``/users/{user}``, the view callable available at the dotted Python pathname ``mypackage.views.user_view`` will be called. For the specific URL ``/users/1``, the ``matchdict`` generated and attached to the :term:`request` will consist of ``{'user':'1'}``. -- When a URL matches the pattern ``/tags/:tag``, the view callable available +- When a URL matches the pattern ``/tags/{tag}``, the view callable available at the dotted Python pathname ``mypackage.views.tag_view`` will be called. For the specific URL ``/tags/1``, the ``matchdict`` generated and attached to the :term:`request` will consist of ``{'tag':'1'}``. @@ -930,7 +974,7 @@ An example of using a route with a factory: .. code-block:: python :linenos: - config.add_route('idea', 'ideas/:idea', + config.add_route('idea', 'ideas/{idea}', view='myproject.views.idea_view', factory='myproject.models.Idea') @@ -958,7 +1002,7 @@ a ``view`` declaration. .. code-block:: python :linenos: - config.add_route('idea', 'site/:id') + config.add_route('idea', 'site/{id}') config.add_view(route_name='idea', view='mypackage.views.site_view') This set of configuration parameters creates a configuration @@ -968,7 +1012,7 @@ completely equivalent to this example provided in .. code-block:: python :linenos: - config.add_route('idea', 'site/:id', view='mypackage.views.site_view') + config.add_route('idea', 'site/{id}', view='mypackage.views.site_view') In fact, the spelling which names a ``view`` attribute is just syntactic sugar for the more verbose spelling which contains separate @@ -1009,7 +1053,7 @@ Generating Route URLs Use the :func:`pyramid.url.route_url` function to generate URLs based on route patterns. For example, if you've configured a route with the ``name`` -"foo" and the ``pattern`` ":a/:b/:c", you might do this. +"foo" and the ``pattern`` "{a}/{b}/{c}", you might do this. .. ignore-next-block .. code-block:: python diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 543f8f82d..c1e4088fd 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -1188,7 +1188,6 @@ class Configurator(object): def add_route(self, name, pattern=None, - marker_pattern=None, view=None, view_for=None, permission=None, @@ -1308,12 +1307,6 @@ class Configurator(object): value if the ``pattern`` argument is ``None``. If both ``path`` and ``pattern`` are passed, ``pattern`` wins. - marker_pattern - - A dict of regular expression's that will be used in the place - of the default ``[^/]+`` regular expression for all replacement - markers in the route pattern. - xhr This value should be either ``True`` or ``False``. If this @@ -1536,8 +1529,7 @@ class Configurator(object): raise ConfigurationError('"pattern" argument may not be None') return mapper.connect(name, pattern, factory, predicates=predicates, - pregenerator=pregenerator, - marker_pattern=marker_pattern) + pregenerator=pregenerator) def get_routes_mapper(self): """ Return the :term:`routes mapper` object associated with diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index f77ebbbb4..12c5cf220 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -218,9 +218,9 @@ class RoutesMapperTests(unittest.TestCase): self.assertEqual(mapper.generate('abc', {}), 123) class TestCompileRoute(unittest.TestCase): - def _callFUT(self, pattern, marker_pattern=None): + def _callFUT(self, pattern): from pyramid.urldispatch import _compile_route - return _compile_route(pattern, marker_pattern) + return _compile_route(pattern) def test_no_star(self): matcher, generator = self._callFUT('/foo/:baz/biz/:buz/bar') @@ -253,8 +253,7 @@ class TestCompileRoute(unittest.TestCase): self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00') def test_custom_regex(self): - matcher, generator = self._callFUT('foo/:baz/biz/:buz.:bar', - {'buz': '[^/\.]+'}) + matcher, generator = self._callFUT('foo/{baz}/biz/{buz:[^/\.]+}.{bar}') self.assertEqual(matcher('/foo/baz/biz/buz.bar'), {'baz':'baz', 'buz':'buz', 'bar':'bar'}) self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index 06ec647e5..0f8691a07 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -17,10 +17,10 @@ _marker = object() class Route(object): implements(IRoute) def __init__(self, name, pattern, factory=None, predicates=(), - pregenerator=None, marker_pattern=None): + pregenerator=None): self.pattern = pattern self.path = pattern # indefinite b/w compat, not in interface - self.match, self.generate = _compile_route(pattern, marker_pattern) + self.match, self.generate = _compile_route(pattern) self.name = name self.factory = factory self.predicates = predicates @@ -42,12 +42,11 @@ class RoutesMapper(object): return self.routes.get(name) def connect(self, name, pattern, factory=None, predicates=(), - pregenerator=None, marker_pattern=None): + pregenerator=None): if name in self.routes: oldroute = self.routes[name] self.routelist.remove(oldroute) - route = Route(name, pattern, factory, predicates, pregenerator, - marker_pattern) + route = Route(name, pattern, factory, predicates, pregenerator) self.routelist.append(route) self.routes[name] = route return route @@ -75,9 +74,16 @@ class RoutesMapper(object): return {'route':None, 'match':None} # stolen from bobo and modified -route_re = re.compile(r'(:[a-zA-Z]\w*)') -def _compile_route(route, marker_pattern=None): - marker_pattern = marker_pattern or {} +old_route_re = re.compile(r'(\:[a-zA-Z]\w*)') +route_re = re.compile(r'(\{[a-zA-Z][^\}]*\})') +def update_pattern(matchobj): + name = matchobj.group(0) + return '{%s}' % name[1:] + +def _compile_route(route): + if old_route_re.search(route) and not route_re.search(route): + route = old_route_re.sub(update_pattern, route) + if not route.startswith('/'): route = '/' + route star = None @@ -93,9 +99,13 @@ def _compile_route(route, marker_pattern=None): gen.append(prefix) while pat: name = pat.pop() - name = name[1:] + name = name[1:-1] + if ':' in name: + name, reg = name.split(':') + else: + reg = '[^/]+' gen.append('%%(%s)s' % name) - name = '(?P<%s>%s)' % (name, marker_pattern.get(name, '[^/]+')) + name = '(?P<%s>%s)' % (name, reg) rpat.append(name) s = pat.pop() if s: -- cgit v1.2.3 From 16c510efd5b05c503350e378beb94a9cd6a5de7d Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 20:48:58 -0800 Subject: Ensure the version changed info is visible. --- docs/narr/urldispatch.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 0d66f28a1..1e5d84060 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -95,7 +95,6 @@ registry`. Here's an example: config.add_route('myroute', '/prefix/{one}/{two}', view=myview) .. versionchanged:: 1.0a4 - Prior to 1.0a4, routes allow for a marker starting with a ``:``, for example:: -- cgit v1.2.3 From 0f89f91a5bf4f26734235d01e81dffc77abba9c4 Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 23:12:57 -0800 Subject: More colon references. --- docs/narr/urldispatch.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 1e5d84060..ae0ecf629 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -96,12 +96,8 @@ registry`. Here's an example: .. versionchanged:: 1.0a4 Prior to 1.0a4, routes allow for a marker starting with a ``:``, for - example:: - - config.add_route('myroute', '/prefix/:one/:two', view=myview) - - Starting in 1.0a4, this style is deprecated in favor or ``{}`` usage - which allows for additional functionality. + example ``/prefix/{one}``. Starting in 1.0a4, this style is deprecated + in favor or ``{}`` usage which allows for additional functionality. .. index:: single: route configuration; view callable @@ -280,9 +276,9 @@ replacement marker. Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL ``/abc/``: -- ``/abc/:foo`` will not match. +- ``/abc/{foo}`` will not match. -- ``/:foo/`` will match. +- ``/{foo}/`` will match. Note that values representing path segments matched with a ``:segment`` match will be url-unquoted and decoded from UTF-8 into @@ -1246,7 +1242,7 @@ Such a ``factory`` might look like so: if article == '1': self.__acl__ = [ (Allow, 'editor', 'view') ] -If the route ``archives/:article`` is matched, and the article number +If the route ``archives/{article}`` is matched, and the article number is ``1``, :app:`Pyramid` will generate an ``Article`` :term:`context` with an ACL on it that allows the ``editor`` principal the ``view`` permission. Obviously you can do more generic things -- cgit v1.2.3 From 15c258849aa54eb99d272fbbef0e9c3d911d2cca Mon Sep 17 00:00:00 2001 From: Ben Bangert Date: Thu, 18 Nov 2010 23:25:14 -0800 Subject: There is no pattern_regexes. --- docs/narr/urldispatch.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index ae0ecf629..a86041e55 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -270,8 +270,8 @@ replacement marker ``:ext``. It is possible to use two replacement markers without any literal characters between them, for instance ``/{foo}{bar}``. This would be a nonsensical pattern -without specifying any ``pattern_regexes`` to restrict valid values of each -replacement marker. +without specifying a custom regular expression to restrict what a marker +captures. Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL ``/abc/``: -- cgit v1.2.3 From 60e316de2a685a1662006047ee89aca4f442e7ef Mon Sep 17 00:00:00 2001 From: William Chambers Date: Fri, 19 Nov 2010 19:15:29 -0500 Subject: Fix mako default filter to use unicode. This is mako's default. --- pyramid/mako_templating.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py index e2330f3ad..6356e0db4 100644 --- a/pyramid/mako_templating.py +++ b/pyramid/mako_templating.py @@ -65,7 +65,7 @@ def renderer_factory(info): module_directory = settings.get('mako.module_directory') input_encoding = settings.get('mako.input_encoding', 'utf-8') error_handler = settings.get('mako.error_handler', None) - default_filters = settings.get('mako.default_filters', []) + default_filters = settings.get('mako.default_filters', None) imports = settings.get('mako.imports', []) if directories is None: raise ConfigurationError( -- cgit v1.2.3 From b5e5018c9b948929548615139533e9a8a504ff11 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 19 Nov 2010 21:13:48 -0500 Subject: gardening --- TODO.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO.txt b/TODO.txt index fe411f749..3d36e3a3d 100644 --- a/TODO.txt +++ b/TODO.txt @@ -52,6 +52,10 @@ Must-Have (before 1.0) - Better ``config.add_handler`` documentation. +- Fix DottedNameResolver to not convert ImportError to ConfigurationError if + the import that failed was unrelated to the import requested via a dotted + name. + Should-Have ----------- -- cgit v1.2.3 From 614f00c88733b5248922e2b610c96f7c8c3ff57c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 19 Nov 2010 22:28:50 -0500 Subject: - Remove calls to config.begin()/config.end() from startup config code in tutorials and paster templates (no longer required). --- CHANGES.txt | 10 +++++ TODO.txt | 5 +-- docs/narr/MyProject/myproject/__init__.py | 3 -- docs/narr/configuration.rst | 18 +++----- docs/narr/firstapp.rst | 49 ---------------------- docs/narr/project.rst | 8 ++-- docs/narr/unittesting.rst | 2 +- docs/tutorials/wiki/basiclayout.rst | 9 ++-- .../wiki/src/authorization/tutorial/__init__.py | 2 - .../wiki/src/basiclayout/tutorial/__init__.py | 2 - .../tutorials/wiki/src/models/tutorial/__init__.py | 2 - .../wiki/src/viewdecorators/tutorial/__init__.py | 2 - docs/tutorials/wiki/src/views/tutorial/__init__.py | 2 - docs/tutorials/wiki2/basiclayout.rst | 12 ++---- .../wiki2/src/authorization/tutorial/__init__.py | 2 - .../wiki2/src/basiclayout/tutorial/__init__.py | 2 - .../wiki2/src/models/tutorial/__init__.py | 2 - .../tutorials/wiki2/src/views/tutorial/__init__.py | 2 - .../alchemy/+package+/__init__.py_tmpl | 2 - .../pylons_basic/+package+/__init__.py_tmpl | 2 - .../pylons_minimal/+package+/__init__.py_tmpl | 2 - .../pylons_sqla/+package+/__init__.py_tmpl | 2 - .../routesalchemy/+package+/__init__.py_tmpl | 2 - .../starter/+package+/__init__.py_tmpl | 2 - .../starter_zcml/+package+/__init__.py_tmpl | 2 - .../zodb/+package+/__init__.py_tmpl | 2 - 26 files changed, 28 insertions(+), 122 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed68770fd..692e3cc35 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,16 @@ Features - Make test suite pass on PyPy (Chameleon doesn't work). +- Surrounding application configuration with ``config.begin()`` and + ``config.end()`` is no longer necessary. All paster templates have been + changed to no longer call these functions. + +Documentation +------------- + +- SQLAlchemy+URLDispatch and ZODB+Traversal tutorials have been updated to + not call ``config.begin()`` or ``config.end()``. + Bug Fixes --------- diff --git a/TODO.txt b/TODO.txt index 3d36e3a3d..ab2b521ae 100644 --- a/TODO.txt +++ b/TODO.txt @@ -16,9 +16,6 @@ Must-Have (before 1.0) - Use ``@register_view`` instead of ``@view_config`` and change view docs to use "view registration" instead of "view configuration". -- Remove calls to config.begin()/config.end() from startup config code in - tutorials and paster templates (no longer required). - - SQLAlchemy idiomatics: mcdonc: those paster templates all look pretty good... the @@ -59,7 +56,7 @@ Must-Have (before 1.0) Should-Have ----------- -- Try to make test suite pass on PyPy, IronPython. +- Try to make test suite pass on IronPython. - Add docs for httpexceptions module for each webob.exc class that inherits from WSGIHTTPException. diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py index bfbbfd4df..580dfe546 100644 --- a/docs/narr/MyProject/myproject/__init__.py +++ b/docs/narr/MyProject/myproject/__init__.py @@ -5,11 +5,8 @@ def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.add_view('myproject.views.my_view', context='myproject.models.MyModel', renderer='myproject:templates/mytemplate.pt') config.add_static_view('static', 'myproject:static') - config.end() return config.make_wsgi_app() - diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index 6a91cbf75..ae02a5a6c 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -47,20 +47,16 @@ imperatively: if __name__ == '__main__': config = Configurator() - config.begin() config.add_view(hello_world) - config.end() app = config.make_wsgi_app() serve(app, host='0.0.0.0') -We won't talk much about what this application does yet. Just note -that the "configuration' statements take place underneath the ``if -__name__ == '__main__':`` stanza in the form of method calls on a -:term:`Configurator` object (e.g. ``config.begin()``, -``config.add_view(...)``, and ``config.end()``. These statements take -place one after the other, and are executed in order, so the full -power of Python, including conditionals, can be employed in this mode -of configuration. +We won't talk much about what this application does yet. Just note that the +"configuration' statements take place underneath the ``if __name__ == +'__main__':`` stanza in the form of method calls on a :term:`Configurator` +object (e.g. ``config.add_view(...)``). These statements take place one +after the other, and are executed in order, so the full power of Python, +including conditionals, can be employed in this mode of configuration. .. index:: single: view_config @@ -123,9 +119,7 @@ and its subpackages. For example: if __name__ == '__main__': from pyramid.configuration import Configurator config = Configurator() - config.begin() config.scan() - config.end() app = config.make_wsgi_app() serve(app, host='0.0.0.0') diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index bc21bf29f..9d3cad13c 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -38,10 +38,8 @@ configured imperatively: if __name__ == '__main__': config = Configurator() - config.begin() config.add_view(hello_world) config.add_view(goodbye_world, name='goodbye') - config.end() app = config.make_wsgi_app() serve(app, host='0.0.0.0') @@ -149,10 +147,8 @@ imports and function definitions is placed within the confines of an if __name__ == '__main__': config = Configurator() - config.begin() config.add_view(hello_world) config.add_view(goodbye_world, name='goodbye') - config.end() app = config.make_wsgi_app() serve(app, host='0.0.0.0') @@ -190,29 +186,6 @@ this particular :app:`Pyramid` application. Methods called on the Configurator will cause registrations to be made in a :term:`application registry` associated with the application. -Beginning Configuration -~~~~~~~~~~~~~~~~~~~~~~~ - -.. ignore-next-block -.. code-block:: python - - config.begin() - -The :meth:`pyramid.configuration.Configurator.begin` method tells -the system that application configuration has begun. In particular, -this causes the :term:`application registry` associated with this -configurator to become the "current" application registry, meaning -that code which attempts to use the application registry :term:`thread -local` will obtain the registry associated with the configurator. -This is an explicit step because it's sometimes convenient to use a -configurator without causing the registry associated with the -configurator to become "current". - -.. note:: - - See :ref:`threadlocals_chapter` for a discussion about what it - means for an application registry to be "current". - .. _adding_configuration: Adding Configuration @@ -281,28 +254,6 @@ important. We can register ``goodbye_world`` first and ``hello_world`` second; :app:`Pyramid` will still give us the most specific callable when a request is dispatched to it. -Ending Configuration -~~~~~~~~~~~~~~~~~~~~ - -.. ignore-next-block -.. code-block:: python - - config.end() - -The :meth:`pyramid.configuration.Configurator.end` method tells the -system that application configuration has ended. It is the inverse of -:meth:`pyramid.configuration.Configurator.begin`. In particular, -this causes the :term:`application registry` associated with this -configurator to no longer be the "current" application registry, -meaning that code which attempts to use the application registry -:term:`thread local` will no longer obtain the registry associated -with the configurator. - -.. note:: - - See :ref:`threadlocals_chapter` for a discussion about what it - means for an application registry to be "current". - .. index:: single: make_wsgi_app single: WSGI application diff --git a/docs/narr/project.rst b/docs/narr/project.rst index f47e9f293..6e466b284 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -740,14 +740,14 @@ also informs Python that the directory which contains it is a *package*. #. Line 2 imports the ``get_root`` function from :mod:`myproject.models` that we use later. -#. Lines 4-14 define a function that returns a :app:`Pyramid` +#. Lines 4-12 define a function that returns a :app:`Pyramid` WSGI application. This function is meant to be called by the :term:`PasteDeploy` framework as a result of running ``paster serve``. Within this function, configuration is performed. - Lines 9-11 register a "default view" (a view that has no ``name`` + Lines 8-10 register a "default view" (a view that has no ``name`` attribute). It is registered so that it will be found when the :term:`context` of the request is an instance of the :class:`myproject.models.MyModel` class. The first argument to @@ -761,11 +761,11 @@ also informs Python that the directory which contains it is a *package*. ``templates`` directory of the ``myproject`` package. The template file it actually points to is a :term:`Chameleon` ZPT template file. - Line 12 registers a static view, which will serve up the files from the + Line 11 registers a static view, which will serve up the files from the ``mypackage:static`` :term:`resource specification` (the ``static`` directory of the ``mypackage`` package). - Line 14 returns a :term:`WSGI` application to the caller of the function + Line 12 returns a :term:`WSGI` application to the caller of the function (Paste). ``views.py`` diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index 5cd8a0683..26035726b 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -1,4 +1,4 @@ -.. index:: +\.. index:: single: unit testing single: integration testing diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index c05a53831..a94a1632d 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -48,14 +48,11 @@ entry point happens to be the ``app`` function within the file named factory` and the settings keywords parsed by PasteDeploy. The root factory is named ``get_root``. -#. *Lines 16-18*. Begin configuration using the ``begin`` method of - the :meth:`pyramid.configuration.Configurator` class, load the +#. *Line 16*. Load the ``configure.zcml`` file from our package using the - :meth:`pyramid.configuration.Configurator.load_zcml` method, and - end configuration using the - :meth:`pyramid.configuration.Configurator.end` method. + :meth:`pyramid.configuration.Configurator.load_zcml` method. -#. *Line 19*. Use the +#. *Line 17*. Use the :meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py index 124647ba9..a8a3c513e 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py @@ -16,7 +16,5 @@ def main(global_config, **settings): def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.load_zcml('configure.zcml') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py index f6cf8b479..45e4d722b 100644 --- a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py @@ -13,8 +13,6 @@ def main(global_config, **settings): def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.load_zcml('configure.zcml') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki/src/models/tutorial/__init__.py b/docs/tutorials/wiki/src/models/tutorial/__init__.py index 7ef07e767..66e2f05ee 100644 --- a/docs/tutorials/wiki/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/models/tutorial/__init__.py @@ -16,8 +16,6 @@ def main(global_config, **settings): def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.load_zcml('configure.zcml') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py index 7ef07e767..66e2f05ee 100644 --- a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py @@ -16,8 +16,6 @@ def main(global_config, **settings): def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.load_zcml('configure.zcml') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki/src/views/tutorial/__init__.py b/docs/tutorials/wiki/src/views/tutorial/__init__.py index 7ef07e767..66e2f05ee 100644 --- a/docs/tutorials/wiki/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/views/tutorial/__init__.py @@ -16,8 +16,6 @@ def main(global_config, **settings): def get_root(request): return finder(request.environ) config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.load_zcml('configure.zcml') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 9aca94fe5..9bcc17e97 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -52,10 +52,7 @@ entry point happens to be the ``app`` function within the file named deployment-related values such as ``reload_templates``, ``db_string``, etc. -#. *Line 15*. We call :meth:`pyramid.configuration.Configurator.begin` which - tells the configuration machinery we are starting configuration. - -#. *Line 16*. We call +#. *Line 15*. We call :meth:`pyramid.configuration.Configurator.add_static_view` with the arguments ``static`` (the name), and ``tutorial:static`` (the path). This registers a static resource view which will match any URL that starts with @@ -67,7 +64,7 @@ entry point happens to be the ``app`` function within the file named ``/static/foo``) will be used to compose a path to a static file resource, such as a CSS file. -#. *Lines 17-18*. Register a :term:`route configuration` via the +#. *Lines 16-17*. Register a :term:`route configuration` via the :meth:`pyramid.configuration.Configurator.add_route` method that will be used when the URL is ``/``. Since this route has an ``pattern`` equalling ``/`` it is the "default" route. The argument named ``view`` with the @@ -81,10 +78,7 @@ entry point happens to be the ``app`` function within the file named ``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` will use this template to create a response. -#. *Line 19*. We call :meth:`pyramid.configuration.Configurator.end` which - tells the configuration machinery we are ending configuration. - -#. *Line 20*. We use the +#. *Line 18*. We use the :meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 4bddea74c..8269617f2 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -22,7 +22,6 @@ def main(global_config, **settings): root_factory='tutorial.models.RootFactory', authentication_policy=authn_policy, authorization_policy=authz_policy) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('view_wiki', '/', view='tutorial.views.view_wiki') config.add_route('login', '/login', @@ -44,6 +43,5 @@ def main(global_config, **settings): config.add_view('tutorial.login.login', renderer='tutorial:templates/login.pt', context='pyramid.exceptions.Forbidden') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 7a701fc02..0ae61fccd 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -12,11 +12,9 @@ def main(global_config, **settings): db_echo = settings.get('db_echo', 'false') initialize_sql(db_string, asbool(db_echo)) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', view_renderer='templates/mytemplate.pt') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 10fcd0cbc..6e291ffdf 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -12,9 +12,7 @@ def main(global_config, **settings): db_echo = settings.get('db_echo', 'false') initialize_sql(db_string, asbool(db_echo)) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', view_renderer='templates/mytemplate.pt') - config.end() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 9ef923e0b..947ce9b93 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -12,7 +12,6 @@ def main(global_config, **settings): db_echo = settings.get('db_echo', 'false') initialize_sql(db_string, asbool(db_echo)) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.view_wiki') config.add_route('view_page', '/:pagename', @@ -24,6 +23,5 @@ def main(global_config, **settings): config.add_route('edit_page', '/:pagename/edit_page', view='tutorial.views.edit_page', view_renderer='tutorial:templates/edit.pt') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl index 748f58692..c20e7dd15 100755 --- a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl @@ -12,7 +12,6 @@ def main(global_config, **settings): 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.add_static_view('static', '{{package}}:static') config.add_view('{{package}}.views.view_root', context='{{package}}.models.MyApp', @@ -20,7 +19,6 @@ def main(global_config, **settings): config.add_view('{{package}}.views.view_model', context='{{package}}.models.MyModel', renderer="templates/model.pt") - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl index 6a8beb1a7..dd6edd3cf 100644 --- a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl @@ -5,7 +5,6 @@ def main(global_config, **settings): """ from pyramid.configuration import Configurator config = Configurator(settings=settings) - config.begin() session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') @@ -15,5 +14,4 @@ def main(global_config, **settings): action='index') config.add_subscriber('{{package}}.lib.subscribers.add_renderer_globals', 'pyramid.events.BeforeRender') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl index a1a353aae..57371eec9 100644 --- a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl @@ -5,7 +5,6 @@ def main(global_config, **settings): """ from pyramid.configuration import Configurator config = Configurator(settings=settings) - config.begin() session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') @@ -14,6 +13,5 @@ def main(global_config, **settings): action='index') config.add_subscriber('{{package}}.subscribers.add_renderer_globals', 'pyramid.events.BeforeRender') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl index a8df81fd6..0a4313976 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl @@ -13,7 +13,6 @@ def main(global_config, **settings): "configuration.") initialize_sql(db_string, asbool(settings.get('db_echo'))) config = Configurator(settings=settings) - config.begin() session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') @@ -22,5 +21,4 @@ def main(global_config, **settings): action='index') config.add_subscriber('{{package}}.subscribers.add_renderer_globals', 'pyramid.events.BeforeRender') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl index 8b13e3590..e68e0ed66 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl @@ -12,11 +12,9 @@ def main(global_config, **settings): db_echo = settings.get('db_echo', 'false') initialize_sql(db_string, asbool(db_echo)) config = Configurator(settings=settings) - config.begin() config.add_static_view('static', '{{package}}:static') config.add_route('home', '/', view='{{package}}.views.my_view', view_renderer='templates/mytemplate.pt') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl index 5f170ad27..85cb7ec6a 100644 --- a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl @@ -5,11 +5,9 @@ def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=get_root, settings=settings) - config.begin() config.add_view('{{package}}.views.my_view', context='{{package}}.models.MyModel', renderer='{{package}}:templates/mytemplate.pt') config.add_static_view('static', '{{package}}:static') - config.end() return config.make_wsgi_app() diff --git a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl index 19f35bee9..3a0c6b7de 100644 --- a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl @@ -6,8 +6,6 @@ def main(global_config, **settings): """ 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/zodb/+package+/__init__.py_tmpl b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl index 4e879d40f..6b05ac7ad 100644 --- a/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl @@ -14,7 +14,5 @@ def main(global_config, **settings): 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() -- cgit v1.2.3