view mercurial/pure/charencode.py @ 49396:ece490b02a9b

setup: use the full executable manifest from `python.exe` The manifest embedded by the build process (before the string here is added) already accounts for the `<requestedExecutionLevel level="asInvoker" ...>` setting. (Note that the PyOxidizer build is missing this, so it will likely trigger the UAC escalation prompt on each run.) However, using `mt.exe` to merge the fragment with what is already in the manifest seems to strip all whitespace, making it unreadable. Since Mercurial can be run via `python.exe`, it makes sense that we would have the same manifest settings (like the supported OS list), though I'm unaware of any functionality this enables. It also has the nice effect of making the content readable from a resource editor. The manifest comes from python 3.9.12. Note that this seems to strip the `<?xml ... ?>` declaration when viewed with ResourceHacker 5.1.7, but this was also the state of things with the previous commit, and `mt.exe "-inputresource:hg.exe;#1" -out:extracted` does contain the declaration and the BOM in both cases. No idea why this differs from other executables.
author Matt Harbison <matt_harbison@yahoo.com>
date Mon, 18 Jul 2022 19:18:00 -0400
parents b677bccf74b9
children 43adbe03079b
line wrap: on
line source

# charencode.py - miscellaneous character encoding
#
#  Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.


import array

from .. import pycompat


def isasciistr(s):
    try:
        s.decode('ascii')
        return True
    except UnicodeDecodeError:
        return False


def asciilower(s):
    """convert a string to lowercase if ASCII

    Raises UnicodeDecodeError if non-ASCII characters are found."""
    s.decode('ascii')
    return s.lower()


def asciiupper(s):
    """convert a string to uppercase if ASCII

    Raises UnicodeDecodeError if non-ASCII characters are found."""
    s.decode('ascii')
    return s.upper()


_jsonmap = []
_jsonmap.extend(b"\\u%04x" % x for x in range(32))
_jsonmap.extend(pycompat.bytechr(x) for x in range(32, 127))
_jsonmap.append(b'\\u007f')
_jsonmap[0x09] = b'\\t'
_jsonmap[0x0A] = b'\\n'
_jsonmap[0x22] = b'\\"'
_jsonmap[0x5C] = b'\\\\'
_jsonmap[0x08] = b'\\b'
_jsonmap[0x0C] = b'\\f'
_jsonmap[0x0D] = b'\\r'
_paranoidjsonmap = _jsonmap[:]
_paranoidjsonmap[0x3C] = b'\\u003c'  # '<' (e.g. escape "</script>")
_paranoidjsonmap[0x3E] = b'\\u003e'  # '>'
_jsonmap.extend(pycompat.bytechr(x) for x in range(128, 256))


def jsonescapeu8fast(u8chars, paranoid):
    """Convert a UTF-8 byte string to JSON-escaped form (fast path)

    Raises ValueError if non-ASCII characters have to be escaped.
    """
    if paranoid:
        jm = _paranoidjsonmap
    else:
        jm = _jsonmap
    try:
        return b''.join(jm[x] for x in bytearray(u8chars))
    except IndexError:
        raise ValueError


_utf8strict = r'surrogatepass'


def jsonescapeu8fallback(u8chars, paranoid):
    """Convert a UTF-8 byte string to JSON-escaped form (slow path)

    Escapes all non-ASCII characters no matter if paranoid is False.
    """
    if paranoid:
        jm = _paranoidjsonmap
    else:
        jm = _jsonmap
    # non-BMP char is represented as UTF-16 surrogate pair
    u16b = u8chars.decode('utf-8', _utf8strict).encode('utf-16', _utf8strict)
    u16codes = array.array('H', u16b)
    u16codes.pop(0)  # drop BOM
    return b''.join(jm[x] if x < 128 else b'\\u%04x' % x for x in u16codes)