Example #1
0
def urlencode(query, enc='utf-8', sort=False, doseq=1):
    """Encode a sequence of two-element tuples or dictionary into a URL query string.

    If any values in the query arg are sequences and doseq is true, each
    sequence element is converted to a separate parameter.

    If the query arg is a sequence of two-element tuples, the order of the
    parameters in the output will match the order of parameters in the
    input.
    """

    if hasattr(query, "iteritems"):
        # mapping objects
        if sort:
            query = SortedDict(query)
        query = query.iteritems()
    else:
        # it's a bother at times that strings and string-like objects are
        # sequences...
        try:
            # non-sequence items should not work with len()
            # non-empty strings will fail this
            if len(query) and not isinstance(query[0], tuple):
                raise TypeError
            # zero-length sequences of all types will get here and succeed,
            # but that's a minor nit - since the original implementation
            # allowed empty dicts that type of behavior probably should be
            # preserved for consistency
            if sort:
                query = sorted(query, lambda x, y: cmp(x[0], y[0]))
        except TypeError:
            _, _, tb = sys.exc_info()
            raise TypeError, "not a valid non-string sequence or mapping object", tb

    l = []
    if not doseq:
        # preserve old behavior
        for k, v in query:
            k = encode(k, safe='', enc=enc)
            v = encode(v, safe='', enc=enc)
            l.append(k + '=' + v)
    else:
        for k, v in query:
            k = encode(k, safe='', enc=enc)
            if isinstance(v, (str, unicode)):
                v = encode(v, safe='', enc=enc)
                l.append(k + '=' + v)
            else:
                try:
                    # is this a sufficient test for sequence-ness?
                    _ = len(v)
                except TypeError:
                    # not a sequence
                    v = encode(v, safe='')
                    l.append(k + '=' + v)
                else:
                    # loop over the sequence
                    for elt in v:
                        l.append(k + '=' + encode(elt, safe='', enc=enc))
    return '&'.join(l)
Example #2
0
def compose_qs(params, sort=False, pattern='%s=%s', join='&', wrap=None):
    """
    Compose a single string using RFC3986 specified escaping using
    `urlencoding.escape`_ for keys and values.

    Arguments:

        `params`
            The dict of parameters to encode into a query string.

        `sort`
            Boolean indicating if the key/values should be sorted.

    >>> urlencoding.compose_qs({'a': '1', 'b': ' c d'})
    'a=1&b=%20c%20d'
    >>> urlencoding.compose_qs({'a': ['2', '1']})
    'a=2&a=1'
    >>> urlencoding.compose_qs({'a': ['2', '1', '3']}, sort=True)
    'a=1&a=2&a=3'
    >>> urlencoding.compose_qs({'a': '1', 'b': {'c': 2, 'd': 3}}, sort=True)
    'a=1&b%5Bc%5D=2&b%5Bd%5D=3'

    """

    if sort:
        params = SortedDict(params)

    pieces = []
    for key, value in params.iteritems():
        escaped_key = escape(str(key))
        if wrap:
            escaped_key = wrap + ENCODED_OPEN_BRACKET + escaped_key + ENCODED_CLOSE_BRACKET

        if isinstance(value, collections.Mapping):
            p = compose_qs(value, sort, pattern, join, escaped_key)
        elif is_nonstring_iterable(value):
            p = join.join([pattern % (escaped_key, escape(str(v))) for v in value])
        else:
            p = pattern % (escaped_key, escape(str(value)))
        pieces.append(p)
    return join.join(pieces)