summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaise Laflamme <blaise@laflamme.org>2012-08-02 23:20:20 -0400
committerBlaise Laflamme <blaise@laflamme.org>2012-08-02 23:20:20 -0400
commit059017df8ca73660734072de1b781940b9079dd4 (patch)
tree12141bd7011001ec860911bb4d5d00f55a668ca5
parentd4147eb8dc4962fa86863c77fc190717113994a7 (diff)
parent1bbe668972a6ac3b116f44e50825c66ae5813326 (diff)
downloadpyramid-059017df8ca73660734072de1b781940b9079dd4.tar.gz
pyramid-059017df8ca73660734072de1b781940b9079dd4.tar.bz2
pyramid-059017df8ca73660734072de1b781940b9079dd4.zip
Merge branch 'refs/heads/master' into fix.mako-606
-rw-r--r--CHANGES.txt12
-rw-r--r--HACKING.txt10
-rw-r--r--docs/tutorials/modwsgi/index.rst11
-rw-r--r--docs/tutorials/wiki2/authorization.rst2
-rw-r--r--docs/tutorials/wiki2/design.rst2
-rw-r--r--pyramid/config/util.py7
-rw-r--r--pyramid/config/views.py12
-rw-r--r--pyramid/tests/test_config/test_views.py31
-rw-r--r--pyramid/tests/test_urldispatch.py8
-rw-r--r--pyramid/urldispatch.py4
-rw-r--r--pyramid/view.py6
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