mercurial/py3kcompat.py
author Renato Cunha <renatoc@gmail.com>
Tue, 03 Aug 2010 13:52:48 -0300
changeset 11748 37a70a784397
child 11878 8bb1481cf08f
permissions -rw-r--r--
py3kcompat: added a "compatibility layer" for py3k This patch adds some ugly constructs. The first of them is bytesformatter, a function that formats strings like when '%' is called. The main motivation for this function is py3k's strange behavior: >>> 'foo %s' % b'bar' "foo b'bar'" >>> b'foo %s' % b'bar' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for %: 'bytes' and 'bytes' >>> b'foo %s' % 'bar' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for %: 'bytes' and 'str' In other words, if we can't format bytes with bytes, and recall that all mercurial strings will be converted by a fixer, then things will break badly if we don't take a similar approach. The other addition with this patch is that the os.environ dictionary is monkeypatched to have bytes items. Hopefully this won't be needed in the future, as python 3.2 might get a os.environb dictionary that holds bytes items.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11748
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     1
# py3kcompat.py - compatibility definitions for running hg in py3k
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     2
#
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     3
# Copyright 2010 Renato Cunha <renatoc@gmail.com>
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     4
#
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms of the
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     6
# GNU General Public License version 2 or any later version.
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     7
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     8
import os, builtins
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
     9
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    10
from numbers import Number
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    11
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    12
def bytesformatter(format, args):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    13
    '''Custom implementation of a formatter for bytestrings.
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    14
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    15
    This function currently relias on the string formatter to do the
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    16
    formatting and always returns bytes objects.
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    17
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    18
    >>> bytesformatter(20, 10)
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    19
    0
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    20
    >>> bytesformatter('unicode %s, %s!', ('string', 'foo'))
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    21
    b'unicode string, foo!'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    22
    >>> bytesformatter(b'test %s', 'me')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    23
    b'test me'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    24
    >>> bytesformatter('test %s', 'me')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    25
    b'test me'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    26
    >>> bytesformatter(b'test %s', b'me')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    27
    b'test me'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    28
    >>> bytesformatter('test %s', b'me')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    29
    b'test me'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    30
    >>> bytesformatter('test %d: %s', (1, b'result'))
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    31
    b'test 1: result'
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    32
    '''
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    33
    # The current implementation just converts from bytes to unicode, do
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    34
    # what's needed and then convert the results back to bytes.
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    35
    # Another alternative is to use the Python C API implementation.
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    36
    if isinstance(format, Number):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    37
        # If the fixer erroneously passes a number remainder operation to
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    38
        # bytesformatter, we just return the correct operation
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    39
        return format % args
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    40
    if isinstance(format, bytes):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    41
        format = format.decode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    42
    if isinstance(args, bytes):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    43
        args = args.decode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    44
    if isinstance(args, tuple):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    45
        newargs = []
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    46
        for arg in args:
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    47
            if isinstance(arg, bytes):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    48
                arg = arg.decode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    49
            newargs.append(arg)
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    50
        args = tuple(newargs)
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    51
    ret = format % args
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    52
    return ret.encode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    53
builtins.bytesformatter = bytesformatter
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    54
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    55
# Create bytes equivalents for os.environ values
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    56
for key in list(os.environ.keys()):
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    57
    # UTF-8 is fine for us
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    58
    bkey = key.encode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    59
    bvalue = os.environ[key].encode('utf-8', 'surrogateescape')
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    60
    os.environ[bkey] = bvalue
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    61
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    62
if __name__ == '__main__':
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    63
    import doctest
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    64
    doctest.testmod()
37a70a784397 py3kcompat: added a "compatibility layer" for py3k
Renato Cunha <renatoc@gmail.com>
parents:
diff changeset
    65