Chris McDonough
2013-08-28 230157fcc3a3d3bd4658189aa5588da41b2da840
Merge branch 'master' of github.com:Pylons/pyramid
10 files modified
96 ■■■■ changed files
CHANGES.txt 15 ●●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt 2 ●●●●● patch | view | raw | blame | history
pyramid/path.py 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/copydir.py 4 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/template.py 13 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scaffolds/test_template.py 25 ●●●● patch | view | raw | blame | history
pyramid/tests/test_traversal.py 17 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_urldispatch.py 10 ●●●● patch | view | raw | blame | history
pyramid/traversal.py 2 ●●● patch | view | raw | blame | history
pyramid/urldispatch.py 6 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -4,6 +4,11 @@
Features
--------
- It is now possible to escape double braces in Pyramid scaffolds (unescaped,
  these represent replacement values).  You can use ``\{\{a\}\}`` to
  represent a "bare" ``{{a}}``.  See
  https://github.com/Pylons/pyramid/pull/862
- Add ``localizer`` property (reified) to the request.
  See https://github.com/Pylons/pyramid/issues/508.
@@ -149,6 +154,11 @@
Bug Fixes
---------
- Fix an obscure problem when combining a virtual root with a route with a
  ``*traverse`` in its pattern.  Now the traversal path generated in
  such a configuration will be correct, instead of an element missing
  a leading slash.
- Fixed a Mako renderer bug returning a tuple with a previous defname value
  in some circumstances. See https://github.com/Pylons/pyramid/issues/1037
  for more information.
@@ -202,6 +212,11 @@
  previously returned the URL without the query string by default, it now does
  attach the query string unless it is overriden.
- The ``route_url`` and ``route_path`` APIs no longer quote ``/``
  to ``%2F`` when a replacement value contains a ``/``.  This was pointless,
  as WSGI servers always unquote the slash anyway, and Pyramid never sees the
  quoted value.
1.4 (2012-12-18)
================
CONTRIBUTORS.txt
@@ -218,3 +218,5 @@
- Andreas Zeidler, 2013/08/15
- Matthew Wilkes, 2013/08/23
- Takahiro Fujiwara, 2013/08/28
pyramid/path.py
@@ -324,7 +324,7 @@
    def _pkg_resources_style(self, value, package):
        """ package.module:attr style """
        if value.startswith('.') or value.startswith(':'):
        if value.startswith(('.', ':')):
            if not package:
                raise ValueError(
                    'relative name %r irresolveable without package' % (value,)
pyramid/scaffolds/copydir.py
@@ -156,9 +156,9 @@
    """
    if name.startswith('.'):
        return 'Skipping hidden file %(filename)s'
    if name.endswith('~') or name.endswith('.bak'):
    if name.endswith(('~', '.bak')):
        return 'Skipping backup file %(filename)s'
    if name.endswith('.pyc') or name.endswith('.pyo'):
    if name.endswith(('.pyc', '.pyo')):
        return 'Skipping %s file ' % os.path.splitext(name)[1] + '%(filename)s'
    if name.endswith('$py.class'):
        return 'Skipping $py.class file %(filename)s'
pyramid/scaffolds/template.py
@@ -35,7 +35,8 @@
        content = native_(content, fsenc)
        try:
            return bytes_(
                substitute_double_braces(content, TypeMapper(vars)), fsenc)
                substitute_escaped_double_braces(
                    substitute_double_braces(content, TypeMapper(vars))), fsenc)
        except Exception as e:
            _add_except(e, ' in file %s' % filename)
            raise
@@ -148,7 +149,15 @@
        value = match.group('braced').strip()
        return values[value]
    return double_brace_pattern.sub(double_bracerepl, content)
escaped_double_brace_pattern = re.compile(r'\\{\\{(?P<escape_braced>[^\\]*?)\\}\\}')
def substitute_escaped_double_braces(content):
    def escaped_double_bracerepl(match):
        value = match.group('escape_braced').strip()
        return "{{%(value)s}}" % locals()
    return escaped_double_brace_pattern.sub(escaped_double_bracerepl, content)
def _add_except(exc, info): # pragma: no cover
    if not hasattr(exc, 'args') or exc.args is None:
        return
pyramid/tests/test_scaffolds/test_template.py
@@ -11,7 +11,7 @@
        inst = self._makeOne()
        result = inst.render_template('{{a}} {{b}}', {'a':'1', 'b':'2'})
        self.assertEqual(result, bytes_('1 2'))
    def test_render_template_expr_failure(self):
        inst = self._makeOne()
        self.assertRaises(AttributeError, inst.render_template,
@@ -36,6 +36,21 @@
        inst = self._makeOne()
        result = inst.render_template('{{a}}', {'a':None})
        self.assertEqual(result, b'')
    def test_render_template_with_escaped_double_braces(self):
        inst = self._makeOne()
        result = inst.render_template('{{a}} {{b}} \{\{a\}\} \{\{c\}\}', {'a':'1', 'b':'2'})
        self.assertEqual(result, bytes_('1 2 {{a}} {{c}}'))
    def test_render_template_with_breaking_escaped_braces(self):
        inst = self._makeOne()
        result = inst.render_template('{{a}} {{b}} \{\{a\} \{b\}\}', {'a':'1', 'b':'2'})
        self.assertEqual(result, bytes_('1 2 \{\{a\} \{b\}\}'))
    def test_render_template_with_escaped_single_braces(self):
        inst = self._makeOne()
        result = inst.render_template('{{a}} {{b}} \{a\} \{b', {'a':'1', 'b':'2'})
        self.assertEqual(result, bytes_('1 2 \{a\} \{b'))
    def test_module_dir(self):
        import sys
@@ -90,7 +105,7 @@
                          'overwrite':False,
                          'interactive':False,
                          })
    def test_write_files_path_missing(self):
        L = []
        inst = self._makeOne()
@@ -132,9 +147,9 @@
    simulate = False
    overwrite = False
    interactive = False
class DummyCommand(object):
    options = DummyOptions()
    verbosity = 1
pyramid/tests/test_traversal.py
@@ -456,6 +456,23 @@
        self.assertEqual(result['virtual_root'], resource)
        self.assertEqual(result['virtual_root_path'], ())
    def test_withroute_and_traverse_and_vroot(self):
        abc = DummyContext()
        resource = DummyContext(next=abc)
        environ = self._getEnviron(HTTP_X_VHM_ROOT='/abc')
        request = DummyRequest(environ)
        traverser = self._makeOne(resource)
        matchdict =  {'traverse':text_('/foo/bar')}
        request.matchdict = matchdict
        result = traverser(request)
        self.assertEqual(result['context'], abc)
        self.assertEqual(result['view_name'], 'foo')
        self.assertEqual(result['subpath'], ('bar',))
        self.assertEqual(result['traversed'], ('abc', 'foo'))
        self.assertEqual(result['root'], resource)
        self.assertEqual(result['virtual_root'], abc)
        self.assertEqual(result['virtual_root_path'], ('abc',))
class FindInterfaceTests(unittest.TestCase):
    def _callFUT(self, context, iface):
        from pyramid.traversal import find_interface
pyramid/tests/test_urldispatch.py
@@ -295,7 +295,7 @@
                          'remainder':'/everything/else/here'})
        self.assertEqual(matcher('foo/baz/biz/buz/bar'), None)
        self.assertEqual(generator(
            {'baz':1, 'buz':2, 'remainder':'/a/b'}), '/foo/1/biz/2/bar%2Fa%2Fb')
            {'baz':1, 'buz':2, 'remainder':'/a/b'}), '/foo/1/biz/2/bar/a/b')
    def test_no_beginning_slash(self):
        matcher, generator = self._callFUT('foo/:baz/biz/:buz/bar')
@@ -491,10 +491,10 @@
        self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':'/def/g'},
                       '/zzz/abc/def/g')
        self.generates('/{x}', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')},
                       '/%2FLa%20Pe%C3%B1a')
                       '//La%20Pe%C3%B1a')
        self.generates('/{x}*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'),
                                 'y':'/rest/of/path'},
                       '/%2FLa%20Pe%C3%B1a/rest/of/path')
                       '//La%20Pe%C3%B1a/rest/of/path')
        self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))},
                       '/a/La%20Pe%C3%B1a')
        self.generates('/foo/{id}.html', {'id':'bar'}, '/foo/bar.html')
@@ -511,10 +511,10 @@
        self.generates('zzz/:x*traverse', {'x':'abc', 'traverse':'/def/g'},
                       '/zzz/abc/def/g')
        self.generates('/:x', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')},
                       '/%2FLa%20Pe%C3%B1a')
                       '//La%20Pe%C3%B1a')
        self.generates('/:x*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'),
                                 'y':'/rest/of/path'},
                       '/%2FLa%20Pe%C3%B1a/rest/of/path')
                       '//La%20Pe%C3%B1a/rest/of/path')
        self.generates('*traverse', {'traverse':('a', text_(b'La Pe\xf1a'))},
                       '/a/La%20Pe%C3%B1a')
        self.generates('/foo/:id.html', {'id':'bar'}, '/foo/bar.html')
pyramid/traversal.py
@@ -640,7 +640,7 @@
                # this is a *traverse stararg (not a {traverse})
                # routing has already decoded these elements, so we just
                # need to join them
                path = slash.join(path) or slash
                path = '/' + slash.join(path) or slash
            subpath = matchdict.get('subpath', ())
            if not is_nonstr_iter(subpath):
pyramid/urldispatch.py
@@ -213,7 +213,9 @@
            if k == remainder:
                # a stararg argument
                if is_nonstr_iter(v):
                    v = '/'.join([quote_path_segment(x) for x in v]) # native
                    v = '/'.join(
                        [quote_path_segment(x, safe='/') for x in v]
                        ) # native
                else:
                    if v.__class__ not in string_types:
                        v = str(v)
@@ -222,7 +224,7 @@
                if v.__class__ not in string_types:
                    v = str(v)
                # v may be bytes (py2) or native string (py3)
                v = quote_path_segment(v)
                v = quote_path_segment(v, safe='/')
            # at this point, the value will be a native string
            newdict[k] = v