diff options
| -rw-r--r-- | CHANGES.txt | 32 | ||||
| -rw-r--r-- | docs/api/interfaces.rst | 3 | ||||
| -rw-r--r-- | docs/conf.py | 2 | ||||
| -rw-r--r-- | docs/narr/project.rst | 2 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 4 | ||||
| -rw-r--r-- | docs/whatsnew-1.3.rst | 15 | ||||
| -rw-r--r-- | pyramid/config/__init__.py | 25 | ||||
| -rw-r--r-- | pyramid/config/util.py | 14 | ||||
| -rw-r--r-- | pyramid/config/views.py | 8 | ||||
| -rw-r--r-- | pyramid/path.py | 2 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_init.py | 22 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_util.py | 16 | ||||
| -rw-r--r-- | setup.py | 4 | ||||
| -rw-r--r-- | tox.ini | 3 |
14 files changed, 129 insertions, 23 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 9edcbdbe8..fcd54217f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,9 +4,34 @@ Next release Features -------- +- The ``scan`` method of a ``Configurator`` can be passed an ``ignore`` + argument, which can be a string, a callable, or a list consisting of + strings and/or callables. This feature allows submodules, subpackages, and + global objects from being scanned. See + http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for + more information about how to use the ``ignore`` argument to ``scan``. + +Dependencies +------------ + +- Depend on ``venusian`` >= 1.0a3 to provide scan ``ignore`` support. + +1.3a7 (2012-02-07) +================== + +Features +-------- + - More informative error message when a ``config.include`` cannot find an ``includeme``. See https://github.com/Pylons/pyramid/pull/392. +- Internal: catch unhashable discriminators early (raise an error instead of + allowing them to find their way into resolveConflicts). + +- The `match_param` view predicate now accepts a string or a tuple. + This replaces the broken behavior of accepting a dict. See + https://github.com/Pylons/pyramid/issues/425 for more information. + Bug Fixes --------- @@ -18,6 +43,13 @@ Bug Fixes - The ``prequest`` script would fail when used against URLs which did not return HTML or text. See https://github.com/Pylons/pyramid/issues/381 +Backwards Incompatibilities +--------------------------- + +- The `match_param` view predicate no longer accepts a dict. This will + have no negative affect because the implementation was broken for + dict-based arguments. + Documentation ------------- diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 5b190b53b..11cd8cf7e 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -76,3 +76,6 @@ Other Interfaces .. autointerface:: IActionInfo :members: + + .. autointerface:: IAssetDescriptor + :members: diff --git a/docs/conf.py b/docs/conf.py index 3496bd38c..0c56f56e7 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.3a6' +version = '1.3a7' # The full version, including alpha/beta/rc tags. release = version diff --git a/docs/narr/project.rst b/docs/narr/project.rst index ea0045ca7..d69f0cf13 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -98,7 +98,7 @@ Or on Windows: .. code-block:: text - $ Scripts\pcreate alchemy MyProject + $ Scripts\pcreate -s alchemy MyProject Here's sample output from a run of ``pcreate`` on UNIX for a project we name ``MyProject``: diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 6d9dfdd92..a7bf74786 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -340,8 +340,8 @@ The above pattern will match these URLs, generating the following matchdicts: .. code-block:: text - foo/1/2/ -> {'baz':u'1', 'bar':u'2', 'fizzle':()} - foo/abc/def/a/b/c -> {'baz':u'abc', 'bar':u'def', 'fizzle': u'a/b/c')} + foo/1/2/ -> {'baz':u'1', 'bar':u'2', 'fizzle':u''} + foo/abc/def/a/b/c -> {'baz':u'abc', 'bar':u'def', 'fizzle': u'a/b/c'} This occurs because the default regular expression for a marker is ``[^/]+`` which will match everything up to the first ``/``, while ``{fizzle:.*}`` will diff --git a/docs/whatsnew-1.3.rst b/docs/whatsnew-1.3.rst index ed7024f62..b6cfde039 100644 --- a/docs/whatsnew-1.3.rst +++ b/docs/whatsnew-1.3.rst @@ -15,7 +15,9 @@ The major feature additions in Pyramid 1.3 follow. Python 3 Compatibility ~~~~~~~~~~~~~~~~~~~~~~ -Pyramid is now Python 3 compatible. Python 3.2 or better is required. +In addition to running on Python 2 (version 2.6 or 2.7 required), Pyramid is +now Python 3 compatible. For Python 3 compatibility, Python 3.2 or better +is required. .. warning:: @@ -250,6 +252,13 @@ Minor Feature Additions http://www.python.org/dev/peps/pep-0333/#optional-platform-specific-file-handling) when one is provided by the web server. +- The :meth:`pyramid.config.Configurator.scan` method can be passed an + ``ignore`` argument, which can be a string, a callable, or a list + consisting of strings and/or callables. This feature allows submodules, + subpackages, and global objects from being scanned. See + http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for + more information about how to use the ``ignore`` argument to ``scan``. + Backwards Incompatibilities --------------------------- @@ -315,6 +324,10 @@ Backwards Incompatibilities (indeterminate value based on Python 3 vs. Python 2). This has to be done to normalize matching on Python 2 and Python 3. +- The ``match_param`` view predicate no longer accepts a dict. This will have + no negative affect because the implementation was broken for dict-based + arguments. + Documentation Enhancements -------------------------- diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index 57ab7e13a..1656b5410 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -533,6 +533,10 @@ class Configurator( ``extra`` provides a facility for inserting extra keys and values into an action dictionary. """ + # catch nonhashable discriminators here; most unit tests use + # autocommit=False, which won't catch unhashable discriminators + assert hash(discriminator) + if kw is None: kw = {} @@ -847,7 +851,8 @@ class Configurator( return self.manager.pop() # this is *not* an action method (uses caller_package) - def scan(self, package=None, categories=None, onerror=None, **kw): + def scan(self, package=None, categories=None, onerror=None, ignore=None, + **kw): """Scan a Python package and any of its subpackages for objects marked with :term:`configuration decoration` such as :class:`pyramid.view.view_config`. Any decorated object found will @@ -875,6 +880,20 @@ class Configurator( :term:`Venusian` documentation for more information about ``onerror`` callbacks. + The ``ignore`` argument, if provided, should be a Venusian ``ignore`` + value. Providing an ``ignore`` argument allows the scan to ignore + particular modules, packages, or global objects during a scan. + ``ignore`` can be a string or a callable, or a list containing + strings or callables. The simplest usage of ``ignore`` is to provide + a module or package by providing a full path to its dotted name. For + example: ``config.scan(ignore='my.module.subpackage')`` would ignore + the ``my.module.subpackage`` package during a scan, which would + prevent the subpackage and any of its submodules from being imported + and scanned. See the :term:`Venusian` documentation for more + information about the ``ignore`` argument. + + .. note:: the ``ignore`` argument is new in Pyramid 1.3. + To perform a ``scan``, Pyramid creates a Venusian ``Scanner`` object. The ``kw`` argument represents a set of keyword arguments to pass to the Venusian ``Scanner`` object's constructor. See the @@ -896,7 +915,9 @@ class Configurator( ctorkw.update(kw) scanner = self.venusian.Scanner(**ctorkw) - scanner.scan(package, categories=categories, onerror=onerror) + + scanner.scan(package, categories=categories, onerror=onerror, + ignore=ignore) def make_wsgi_app(self): """ Commits any pending configuration statements, sends a diff --git a/pyramid/config/util.py b/pyramid/config/util.py index 5f0dd98ac..6b7aa2fa1 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -221,19 +221,21 @@ def make_predicates(xhr=None, request_method=None, path_info=None, h.update(bytes_('request_type:%r' % hash(request_type))) if match_param is not None: - if isinstance(match_param, string_types): - match_param, match_param_val = match_param.split('=', 1) - match_param = {match_param: match_param_val} - text = "match_param %s" % match_param + if not is_nonstr_iter(match_param): + match_param = (match_param,) + match_param = sorted(match_param) + text = "match_param %s" % repr(match_param) + reqs = [p.split('=', 1) for p in match_param] def match_param_predicate(context, request): - for k, v in match_param.items(): + for k, v in reqs: if request.matchdict.get(k) != v: return False return True match_param_predicate.__text__ = text weights.append(1 << 9) predicates.append(match_param_predicate) - h.update(bytes_('match_param:%r' % match_param)) + for p in match_param: + h.update(bytes_('match_param:%r' % p)) if custom: for num, predicate in enumerate(custom): diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 831b8b20d..86b139e3e 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -843,18 +843,18 @@ class ViewsConfiguratorMixin(object): .. note:: This feature is new as of :app:`Pyramid` 1.2. - This param may be either a single string of the format "key=value" - or a dict of key/value pairs. + This value can be a string of the format "key=value" or a tuple + containing one or more of these strings. A view declaration with this argument ensures that the view will only be called when the :term:`request` has key/value pairs in its :term:`matchdict` that equal those supplied in the predicate. e.g. ``match_param="action=edit" would require the ``action`` - parameter in the :term:`matchdict` match the right hande side of + parameter in the :term:`matchdict` match the right hand side of the expression (``edit``) for the view to "match" the current request. - If the ``match_param`` is a dict, every key/value pair must match + If the ``match_param`` is a tuple, every key/value pair must match for the predicate to pass. containment diff --git a/pyramid/path.py b/pyramid/path.py index 8a8898174..d1c3b6d31 100644 --- a/pyramid/path.py +++ b/pyramid/path.py @@ -165,7 +165,7 @@ class AssetResolver(Resolver): """ Resolve the asset spec named as ``spec`` to an object that has the attributes and methods described in - `pyramid.interfaces.IAssetDescriptor`. + :class:`pyramid.interfaces.IAssetDescriptor`. If ``spec`` is an absolute filename (e.g. ``/path/to/myproject/templates/foo.pt``) or an absolute asset diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py index a866bed55..d237b3fe8 100644 --- a/pyramid/tests/test_config/test_init.py +++ b/pyramid/tests/test_config/test_init.py @@ -929,6 +929,28 @@ pyramid.tests.test_config.dummy_include2""", result = render_view_to_response(ctx, req, 'pod_notinit') self.assertEqual(result, None) + def test_scan_integration_with_ignore(self): + from zope.interface import alsoProvides + from pyramid.interfaces import IRequest + from pyramid.view import render_view_to_response + import pyramid.tests.test_config.pkgs.scannable as package + config = self._makeOne(autocommit=True) + config.scan(package, + ignore='pyramid.tests.test_config.pkgs.scannable.another') + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + # ignored + v = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(v, None) + def test_scan_integration_dottedname_package(self): from zope.interface import alsoProvides from pyramid.interfaces import IRequest diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index ebf308929..1ad1fb3c1 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -281,7 +281,7 @@ class Test__make_predicates(unittest.TestCase): self.assertEqual(predicates[5].__text__, 'accept = accept') self.assertEqual(predicates[6].__text__, 'containment = containment') self.assertEqual(predicates[7].__text__, 'request_type = request_type') - self.assertEqual(predicates[8].__text__, "match_param {'foo': 'bar'}") + self.assertEqual(predicates[8].__text__, "match_param ['foo=bar']") self.assertEqual(predicates[9].__text__, 'custom predicate') self.assertEqual(predicates[10].__text__, 'classmethod predicate') self.assertEqual(predicates[11].__text__, '<unknown custom predicate>') @@ -299,13 +299,13 @@ class Test__make_predicates(unittest.TestCase): self.assertFalse(predicates[0](Dummy(), request)) def test_match_param_from_dict(self): - _, predicates, _ = self._callFUT(match_param={'foo':'bar','baz':'bum'}) + _, predicates, _ = self._callFUT(match_param=('foo=bar','baz=bum')) request = DummyRequest() request.matchdict = {'foo':'bar', 'baz':'bum'} self.assertTrue(predicates[0](Dummy(), request)) def test_match_param_from_dict_fails(self): - _, predicates, _ = self._callFUT(match_param={'foo':'bar','baz':'bum'}) + _, predicates, _ = self._callFUT(match_param=('foo=bar','baz=bum')) request = DummyRequest() request.matchdict = {'foo':'bar', 'baz':'foo'} self.assertFalse(predicates[0](Dummy(), request)) @@ -328,6 +328,16 @@ class Test__make_predicates(unittest.TestCase): hash2, _, __= self._callFUT(request_method='GET') self.assertEqual(hash1, hash2) + def test_match_param_hashable(self): + # https://github.com/Pylons/pyramid/issues/425 + import pyramid.testing + def view(request): pass + config = pyramid.testing.setUp(autocommit=False) + config.add_route('foo', '/foo/{a}/{b}') + config.add_view(view, route_name='foo', match_param='a=bar') + config.add_view(view, route_name='foo', match_param=('a=bar', 'b=baz')) + config.commit() + class TestActionInfo(unittest.TestCase): def _getTargetClass(self): from pyramid.config.util import ActionInfo @@ -37,7 +37,7 @@ install_requires=[ 'repoze.lru >= 0.4', # py3 compat 'zope.interface >= 3.8.0', # has zope.interface.registry 'zope.deprecation >= 3.5.0', # py3 compat - 'venusian >= 1.0a1', # ``onerror`` + 'venusian >= 1.0a3', # ``ignore`` 'translationstring >= 0.4', # py3 compat 'PasteDeploy >= 1.5.0', # py3 compat ] @@ -56,7 +56,7 @@ if not PY3: ]) setup(name='pyramid', - version='1.3a6', + version='1.3a7', description=('The Pyramid web application development framework, a ' 'Pylons project'), long_description=README + '\n\n' + CHANGES, @@ -11,6 +11,7 @@ deps = repoze.sphinx.autointerface WebTest virtualenv + venusian>=1.0a3 [testenv:py32] commands = @@ -18,6 +19,7 @@ commands = deps = WebTest virtualenv + venusian>=1.0a3 [testenv:cover] basepython = @@ -30,6 +32,7 @@ deps = WebTest repoze.sphinx.autointerface virtualenv + venusian>=1.0a3 nose coverage==3.4 nosexcover |
