Michael Merickel
2011-07-17 3c83bd4eb0c92c2ba0b753f4b4d180f7b3ed2b53
Moved p.url._join_elements to p.util.join_elements and
p.traversal.quote_path_segment to p.util.quote_path_segment.
5 files modified
179 ■■■■ changed files
pyramid/tests/test_traversal.py 35 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_util.py 35 ●●●●● patch | view | raw | blame | history
pyramid/traversal.py 46 ●●●●● patch | view | raw | blame | history
pyramid/url.py 12 ●●●● patch | view | raw | blame | history
pyramid/util.py 51 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_traversal.py
@@ -735,41 +735,6 @@
        result = self._callFUT(other2)
        self.assertEqual(result, ('', '', 'other2'))
class QuotePathSegmentTests(unittest.TestCase):
    def _callFUT(self, s):
        from pyramid.traversal import quote_path_segment
        return quote_path_segment(s)
    def test_unicode(self):
        la = unicode('/La Pe\xc3\xb1a', 'utf-8')
        result = self._callFUT(la)
        self.assertEqual(result, '%2FLa%20Pe%C3%B1a')
    def test_string(self):
        s = '/ hello!'
        result = self._callFUT(s)
        self.assertEqual(result, '%2F%20hello%21')
    def test_int(self):
        s = 12345
        result = self._callFUT(s)
        self.assertEqual(result, '12345')
    def test_long(self):
        import sys
        s = long(sys.maxint + 1)
        result = self._callFUT(s)
        expected = str(s)
        self.assertEqual(result, expected)
    def test_other(self):
        class Foo(object):
            def __str__(self):
                return 'abc'
        s = Foo()
        result = self._callFUT(s)
        self.assertEqual(result, 'abc')
class TraversalContextURLTests(unittest.TestCase):
    def _makeOne(self, context, url):
        return self._getTargetClass()(context, url)
pyramid/tests/test_util.py
@@ -246,5 +246,40 @@
        self.assertEqual(list(wos), [])
        self.assertEqual(wos.last, None)
class QuotePathSegmentTests(unittest.TestCase):
    def _callFUT(self, s):
        from pyramid.traversal import quote_path_segment
        return quote_path_segment(s)
    def test_unicode(self):
        la = unicode('/La Pe\xc3\xb1a', 'utf-8')
        result = self._callFUT(la)
        self.assertEqual(result, '%2FLa%20Pe%C3%B1a')
    def test_string(self):
        s = '/ hello!'
        result = self._callFUT(s)
        self.assertEqual(result, '%2F%20hello%21')
    def test_int(self):
        s = 12345
        result = self._callFUT(s)
        self.assertEqual(result, '12345')
    def test_long(self):
        import sys
        s = long(sys.maxint + 1)
        result = self._callFUT(s)
        expected = str(s)
        self.assertEqual(result, expected)
    def test_other(self):
        class Foo(object):
            def __str__(self):
                return 'abc'
        s = Foo()
        result = self._callFUT(s)
        self.assertEqual(result, 'abc')
class Dummy(object):
    pass
pyramid/traversal.py
@@ -12,10 +12,10 @@
from pyramid.interfaces import ITraverser
from pyramid.interfaces import VH_ROOT_KEY
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
from pyramid.util import quote_path_segment # bw-compat
def find_root(resource):
    """ Find the root node in the resource tree to which ``resource``
@@ -508,50 +508,6 @@
                    )
            clean.append(segment)
    return tuple(clean)
_segment_cache = {}
def quote_path_segment(segment, safe=''):
    """ Return a quoted representation of a 'path segment' (such as
    the string ``__name__`` attribute of a resource) as a string.  If the
    ``segment`` passed in is a unicode object, it is converted to a
    UTF-8 string, then it is URL-quoted using Python's
    ``urllib.quote``.  If the ``segment`` passed in is a string, it is
    URL-quoted using Python's :mod:`urllib.quote`.  If the segment
    passed in is not a string or unicode object, an error will be
    raised.  The return value of ``quote_path_segment`` is always a
    string, never Unicode.
    You may pass a string of characters that need not be encoded as
    the ``safe`` argument to this function.  This corresponds to the
    ``safe`` argument to :mod:`urllib.quote`.
    .. note:: The return value for each segment passed to this
              function is cached in a module-scope dictionary for
              speed: the cached version is returned when possible
              rather than recomputing the quoted version.  No cache
              emptying is ever done for the lifetime of an
              application, however.  If you pass arbitrary
              user-supplied strings to this function (as opposed to
              some bounded set of values from a 'working set' known to
              your application), it may become a memory leak.
    """
    # The bit of this code that deals with ``_segment_cache`` is an
    # optimization: we cache all the computation of URL path segments
    # in this module-scope dictionary with the original string (or
    # unicode value) as the key, so we can look it up later without
    # needing to reencode or re-url-quote it
    try:
        return _segment_cache[(segment, safe)]
    except KeyError:
        if segment.__class__ is unicode: # isinstance slighly slower (~15%)
            result = url_quote(segment.encode('utf-8'), safe)
        else:
            result = url_quote(str(segment), safe)
        # we don't need a lock to mutate _segment_cache, as the below
        # will generate exactly one Python bytecode (STORE_SUBSCR)
        _segment_cache[(segment, safe)] = result
        return result
class ResourceTreeTraverser(object):
    """ A resource tree traverser that should be used (for speed) when
pyramid/url.py
@@ -4,8 +4,6 @@
from zope.deprecation import deprecated
from repoze.lru import lru_cache
from pyramid.interfaces import IContextURL
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IStaticURLInfo
@@ -14,7 +12,7 @@
from pyramid.path import caller_package
from pyramid.threadlocal import get_current_registry
from pyramid.traversal import TraversalContextURL
from pyramid.traversal import quote_path_segment
from pyramid.util import join_elements
def route_url(route_name, request, *elements, **kw):
    """Generates a fully qualified URL for a named :app:`Pyramid`
@@ -141,7 +139,7 @@
    path = route.generate(kw) # raises KeyError if generate fails
    if elements:
        suffix = _join_elements(elements)
        suffix = join_elements(elements)
        if not path.endswith('/'):
            suffix = '/' + suffix
    else:
@@ -304,7 +302,7 @@
        anchor = '#' + anchor
    if elements:
        suffix = _join_elements(elements)
        suffix = join_elements(elements)
    else:
        suffix = ''
@@ -429,7 +427,3 @@
    newkw.update(request.matchdict)
    newkw.update(kw)
    return route_url(route_name, request, *elements, **newkw)
@lru_cache(1000)
def _join_elements(elements):
    return '/'.join([quote_path_segment(s, safe=':@&+$,') for s in elements])
pyramid/util.py
@@ -2,6 +2,9 @@
import sys
import weakref
from repoze.lru import lru_cache
from pyramid.encode import url_quote
from pyramid.exceptions import ConfigurationError
from pyramid.path import package_of
@@ -206,3 +209,51 @@
        if self._order:
            oid = self._order[-1]
            return self._items[oid]()
_segment_cache = {}
def quote_path_segment(segment, safe=''):
    """ Return a quoted representation of a 'path segment' (such as
    the string ``__name__`` attribute of a resource) as a string.  If the
    ``segment`` passed in is a unicode object, it is converted to a
    UTF-8 string, then it is URL-quoted using Python's
    ``urllib.quote``.  If the ``segment`` passed in is a string, it is
    URL-quoted using Python's :mod:`urllib.quote`.  If the segment
    passed in is not a string or unicode object, an error will be
    raised.  The return value of ``quote_path_segment`` is always a
    string, never Unicode.
    You may pass a string of characters that need not be encoded as
    the ``safe`` argument to this function.  This corresponds to the
    ``safe`` argument to :mod:`urllib.quote`.
    .. note:: The return value for each segment passed to this
              function is cached in a module-scope dictionary for
              speed: the cached version is returned when possible
              rather than recomputing the quoted version.  No cache
              emptying is ever done for the lifetime of an
              application, however.  If you pass arbitrary
              user-supplied strings to this function (as opposed to
              some bounded set of values from a 'working set' known to
              your application), it may become a memory leak.
    """
    # The bit of this code that deals with ``_segment_cache`` is an
    # optimization: we cache all the computation of URL path segments
    # in this module-scope dictionary with the original string (or
    # unicode value) as the key, so we can look it up later without
    # needing to reencode or re-url-quote it
    try:
        return _segment_cache[(segment, safe)]
    except KeyError:
        if segment.__class__ is unicode: # isinstance slighly slower (~15%)
            result = url_quote(segment.encode('utf-8'), safe)
        else:
            result = url_quote(str(segment), safe)
        # we don't need a lock to mutate _segment_cache, as the below
        # will generate exactly one Python bytecode (STORE_SUBSCR)
        _segment_cache[(segment, safe)] = result
        return result
@lru_cache(1000)
def join_elements(elements):
    return '/'.join([quote_path_segment(s, safe=':@&+$,') for s in elements])