Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/protobuf/text_encoding.py: 59%
32 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:40 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:40 +0000
1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
8"""Encoding related utilities."""
9import re
11_cescape_chr_to_symbol_map = {}
12_cescape_chr_to_symbol_map[9] = r'\t' # optional escape
13_cescape_chr_to_symbol_map[10] = r'\n' # optional escape
14_cescape_chr_to_symbol_map[13] = r'\r' # optional escape
15_cescape_chr_to_symbol_map[34] = r'\"' # necessary escape
16_cescape_chr_to_symbol_map[39] = r"\'" # optional escape
17_cescape_chr_to_symbol_map[92] = r'\\' # necessary escape
19# Lookup table for unicode
20_cescape_unicode_to_str = [chr(i) for i in range(0, 256)]
21for byte, string in _cescape_chr_to_symbol_map.items():
22 _cescape_unicode_to_str[byte] = string
24# Lookup table for non-utf8, with necessary escapes at (o >= 127 or o < 32)
25_cescape_byte_to_str = ([r'\%03o' % i for i in range(0, 32)] +
26 [chr(i) for i in range(32, 127)] +
27 [r'\%03o' % i for i in range(127, 256)])
28for byte, string in _cescape_chr_to_symbol_map.items():
29 _cescape_byte_to_str[byte] = string
30del byte, string
33def CEscape(text, as_utf8) -> str:
34 """Escape a bytes string for use in an text protocol buffer.
36 Args:
37 text: A byte string to be escaped.
38 as_utf8: Specifies if result may contain non-ASCII characters.
39 In Python 3 this allows unescaped non-ASCII Unicode characters.
40 In Python 2 the return value will be valid UTF-8 rather than only ASCII.
41 Returns:
42 Escaped string (str).
43 """
44 # Python's text.encode() 'string_escape' or 'unicode_escape' codecs do not
45 # satisfy our needs; they encodes unprintable characters using two-digit hex
46 # escapes whereas our C++ unescaping function allows hex escapes to be any
47 # length. So, "\0011".encode('string_escape') ends up being "\\x011", which
48 # will be decoded in C++ as a single-character string with char code 0x11.
49 text_is_unicode = isinstance(text, str)
50 if as_utf8 and text_is_unicode:
51 # We're already unicode, no processing beyond control char escapes.
52 return text.translate(_cescape_chr_to_symbol_map)
53 ord_ = ord if text_is_unicode else lambda x: x # bytes iterate as ints.
54 if as_utf8:
55 return ''.join(_cescape_unicode_to_str[ord_(c)] for c in text)
56 return ''.join(_cescape_byte_to_str[ord_(c)] for c in text)
59_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])')
62def CUnescape(text: str) -> bytes:
63 """Unescape a text string with C-style escape sequences to UTF-8 bytes.
65 Args:
66 text: The data to parse in a str.
67 Returns:
68 A byte string.
69 """
71 def ReplaceHex(m):
72 # Only replace the match if the number of leading back slashes is odd. i.e.
73 # the slash itself is not escaped.
74 if len(m.group(1)) & 1:
75 return m.group(1) + 'x0' + m.group(2)
76 return m.group(0)
78 # This is required because the 'string_escape' encoding doesn't
79 # allow single-digit hex escapes (like '\xf').
80 result = _CUNESCAPE_HEX.sub(ReplaceHex, text)
82 return (result.encode('utf-8') # Make it bytes to allow decode.
83 .decode('unicode_escape')
84 # Make it bytes again to return the proper type.
85 .encode('raw_unicode_escape'))