diff options
| author | Blaise Laflamme <blaise@laflamme.org> | 2012-08-02 23:20:20 -0400 |
|---|---|---|
| committer | Blaise Laflamme <blaise@laflamme.org> | 2012-08-02 23:20:20 -0400 |
| commit | 059017df8ca73660734072de1b781940b9079dd4 (patch) | |
| tree | 12141bd7011001ec860911bb4d5d00f55a668ca5 | |
| parent | d4147eb8dc4962fa86863c77fc190717113994a7 (diff) | |
| parent | 1bbe668972a6ac3b116f44e50825c66ae5813326 (diff) | |
| download | pyramid-059017df8ca73660734072de1b781940b9079dd4.tar.gz pyramid-059017df8ca73660734072de1b781940b9079dd4.tar.bz2 pyramid-059017df8ca73660734072de1b781940b9079dd4.zip | |
Merge branch 'refs/heads/master' into fix.mako-606
| -rw-r--r-- | CHANGES.txt | 12 | ||||
| -rw-r--r-- | HACKING.txt | 10 | ||||
| -rw-r--r-- | docs/tutorials/modwsgi/index.rst | 11 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/design.rst | 2 | ||||
| -rw-r--r-- | pyramid/config/util.py | 7 | ||||
| -rw-r--r-- | pyramid/config/views.py | 12 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 31 | ||||
| -rw-r--r-- | pyramid/tests/test_urldispatch.py | 8 | ||||
| -rw-r--r-- | pyramid/urldispatch.py | 4 | ||||
| -rw-r--r-- | pyramid/view.py | 6 |
11 files changed, 87 insertions, 18 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 3cb2f2848..ecb2bf659 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,14 @@ Bug Fixes return the empty list. This was incorrect, it should have unconditionally returned ``[Everyone]``, and now does. +- Explicit url dispatch regexes can now contain colons. + https://github.com/Pylons/pyramid/issues/629 + +- On at least one 64-bit Ubuntu system under Python 3.2, using the + ``view_config`` decorator caused a ``RuntimeError: dictionary changed size + during iteration`` exception. It no longer does. See + https://github.com/Pylons/pyramid/issues/635 for more information. + Features -------- @@ -46,3 +54,7 @@ Features result for the view being called. The uri format using an asset spec is package:path/to/template#defname.mako. The old way of returning a tuple from the view is supported for backward compatibility, ('defname', {}). + +- When there is a predicate mismatch exception (seen when no view matches for + a given request due to predicates not working), the exception now contains + a textual description of the predicate which didn't match. diff --git a/HACKING.txt b/HACKING.txt index dd735bf22..38c263ed7 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -127,13 +127,19 @@ using to develop Pyramid): 1. Run ``$yourvenv/bin/python setup.py dev docs``. This will cause Sphinx and all development requirements to be installed in your virtualenv. -2. cd to the ``docs`` directory within your Pyramid checkout and execute +2. Update all git submodules from the top-level of your Pyramid checkout, like + so: + git submodule update --init --recursive + This will checkout theme subrepositories and prevent error conditions when + HTML docs are generated. + +3. cd to the ``docs`` directory within your Pyramid checkout and execute ``make clean html SPHINXBUILD=$yourvenv/bin/sphinx-build``. The ``SPHINXBUILD=...`` hair is there in order to tell it to use the virtualenv Python, which will have both Sphinx and Pyramid (for API documentation generation) installed. -3. Open the ``docs/_build/html/index.html`` file to see the resulting HTML +4. Open the ``docs/_build/html/index.html`` file to see the resulting HTML rendering. Change Log diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst index e070f8eda..d11167344 100644 --- a/docs/tutorials/modwsgi/index.rst +++ b/docs/tutorials/modwsgi/index.rst @@ -73,9 +73,10 @@ commands and files. .. code-block:: python - from pyramid.paster import get_app - application = get_app( - '/Users/chrism/modwsgi/env/myapp/production.ini', 'main') + from pyramid.paster import get_app, setup_logging + ini_path = '/Users/chrism/modwsgi/env/myapp/production.ini' + setup_logging(ini_path) + application = get_app(ini_path, 'main') The first argument to ``get_app`` is the project configuration file name. It's best to use the ``production.ini`` file provided by your @@ -85,6 +86,10 @@ commands and files. ``application`` is important: mod_wsgi requires finding such an assignment when it opens the file. + The call to ``setup_logging`` initializes the standard library's + `logging` module to allow logging within your application. + See :ref:`logging_config`. + #. Make the ``pyramid.wsgi`` script executable. .. code-block:: text diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index 2ef55d15b..d7bd24a53 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -353,7 +353,7 @@ when we're done: .. literalinclude:: src/authorization/tutorial/views.py :linenos: - :emphasize-lines: 11,14-18,31,37,58,61,73,76,88,91-117,119-123 + :emphasize-lines: 11,14-18,25,31,37,58,61,73,76,88,91-117,119-123 :language: python (Only the highlighted lines need to be added.) diff --git a/docs/tutorials/wiki2/design.rst b/docs/tutorials/wiki2/design.rst index 2e6fc0e77..deaf32ef6 100644 --- a/docs/tutorials/wiki2/design.rst +++ b/docs/tutorials/wiki2/design.rst @@ -20,7 +20,7 @@ Models We'll be using a SQLite database to hold our wiki data, and we'll be using :term:`SQLAlchemy` to access the data in this database. -Within the database, we define a single table named `tables`, whose elements +Within the database, we define a single table named `pages`, whose elements will store the wiki pages. There are two columns: `name` and `data`. URLs like ``/PageName`` will try to find an element in diff --git a/pyramid/config/util.py b/pyramid/config/util.py index b8d0f2319..4e4c93be3 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -113,6 +113,12 @@ def make_predicates(xhr=None, request_method=None, path_info=None, # any predicates get an order of MAX_ORDER, meaning that they will # be tried very last. + # NB: each predicate callable constructed by this function (or examined + # by this function, in the case of custom predicates) must leave this + # function with a ``__text__`` attribute. The subsystem which reports + # errors when no predicates match depends upon the existence of this + # attribute on each predicate callable. + predicates = [] weights = [] h = md5() @@ -273,6 +279,7 @@ def make_predicates(xhr=None, request_method=None, path_info=None, tvalue = tgenerate(m) # tvalue will be urlquoted string m['traverse'] = traversal_path(tvalue) # will be seq of unicode return True + traverse_predicate.__text__ = 'traverse matchdict pseudo-predicate' # This isn't actually a predicate, it's just a infodict # modifier that injects ``traverse`` into the matchdict. As a # result, the ``traverse_predicate`` function above always diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 9e9b5321b..4354b4691 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -276,11 +276,13 @@ class ViewDeriver(object): if not predicates: return view def predicate_wrapper(context, request): - if all((predicate(context, request) for predicate in predicates)): - return view(context, request) - view_name = getattr(view, '__name__', view) - raise PredicateMismatch( - 'predicate mismatch for view %s' % view_name) + for predicate in predicates: + if not predicate(context, request): + view_name = getattr(view, '__name__', view) + raise PredicateMismatch( + 'predicate mismatch for view %s (%s)' % ( + view_name, predicate.__text__)) + return view(context, request) def checker(context, request): return all((predicate(context, request) for predicate in predicates)) diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index 9b46f83c9..ebf1dfb39 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -2905,6 +2905,7 @@ class TestViewDeriver(unittest.TestCase): view = lambda *arg: response def predicate1(context, request): return False + predicate1.__text__ = 'text' deriver = self._makeOne(predicates=[predicate1]) result = deriver(view) request = self._makeRequest() @@ -2912,7 +2913,8 @@ class TestViewDeriver(unittest.TestCase): try: result(None, None) except PredicateMismatch as e: - self.assertEqual(e.detail, 'predicate mismatch for view <lambda>') + self.assertEqual(e.detail, + 'predicate mismatch for view <lambda> (text)') else: # pragma: no cover raise AssertionError @@ -2921,6 +2923,7 @@ class TestViewDeriver(unittest.TestCase): def myview(request): pass def predicate1(context, request): return False + predicate1.__text__ = 'text' deriver = self._makeOne(predicates=[predicate1]) result = deriver(myview) request = self._makeRequest() @@ -2928,7 +2931,29 @@ class TestViewDeriver(unittest.TestCase): try: result(None, None) except PredicateMismatch as e: - self.assertEqual(e.detail, 'predicate mismatch for view myview') + self.assertEqual(e.detail, + 'predicate mismatch for view myview (text)') + else: # pragma: no cover + raise AssertionError + + def test_predicate_mismatch_exception_has_text_in_detail(self): + from pyramid.exceptions import PredicateMismatch + def myview(request): pass + def predicate1(context, request): + return True + predicate1.__text__ = 'pred1' + def predicate2(context, request): + return False + predicate2.__text__ = 'pred2' + deriver = self._makeOne(predicates=[predicate1, predicate2]) + result = deriver(myview) + request = self._makeRequest() + request.method = 'POST' + try: + result(None, None) + except PredicateMismatch as e: + self.assertEqual(e.detail, + 'predicate mismatch for view myview (pred2)') else: # pragma: no cover raise AssertionError @@ -2974,9 +2999,11 @@ class TestViewDeriver(unittest.TestCase): def predicate1(context, request): predicates.append(True) return True + predicate1.__text__ = 'text' def predicate2(context, request): predicates.append(True) return False + predicate2.__text__ = 'text' deriver = self._makeOne(predicates=[predicate1, predicate2]) result = deriver(view) request = self._makeRequest() diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index e15242f75..b2164717e 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -311,6 +311,14 @@ class TestCompileRoute(unittest.TestCase): self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html') + + def test_custom_regex_with_colons(self): + matcher, generator = self._callFUT('foo/{baz}/biz/{buz:(?:[^/\.]+)}.{bar}') + self.assertEqual(matcher('/foo/baz/biz/buz.bar'), + {'baz':'baz', 'buz':'buz', 'bar':'bar'}) + self.assertEqual(matcher('foo/baz/biz/buz/bar'), None) + self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), + '/foo/1/biz/2.html') def test_mixed_newstyle_oldstyle_pattern_defaults_to_newstyle(self): # pattern: '\\/foo\\/(?P<baz>abc)\\/biz\\/(?P<buz>[^/]+)\\/bar$' diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index cccff14ba..4182ea665 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -148,7 +148,9 @@ def _compile_route(route): name = pat.pop() # unicode name = name[1:-1] if ':' in name: - name, reg = name.split(':') + # reg may contain colons as well, + # so we must strictly split name into two parts + name, reg = name.split(':', 1) else: reg = '[^/]+' gen.append('%%(%s)s' % native_(name)) # native diff --git a/pyramid/view.py b/pyramid/view.py index bb531c326..1df0849c0 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -199,7 +199,7 @@ class view_config(object): custom_predicates=default, context=default, decorator=default, mapper=default, http_cache=default, match_param=default): - L = locals() + L = dict(locals()) # See issue #635 for dict() rationale if (context is not default) or (for_ is not default): L['context'] = context or for_ for k, v in L.items(): @@ -367,7 +367,7 @@ class notfound_view_config(object): path_info=default, custom_predicates=default, decorator=default, mapper=default, match_param=default, append_slash=False): - L = locals() + L = dict(locals()) # See issue #635 for dict() rationale for k, v in L.items(): if k not in ('self', 'L') and v is not default: self.__dict__[k] = v @@ -432,7 +432,7 @@ class forbidden_view_config(object): xhr=default, accept=default, header=default, path_info=default, custom_predicates=default, decorator=default, mapper=default, match_param=default): - L = locals() + L = dict(locals()) # See issue #635 for dict() rationale for k, v in L.items(): if k not in ('self', 'L') and v is not default: self.__dict__[k] = v |
