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

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 

7 

8"""Encoding related utilities.""" 

9import re 

10 

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 

18 

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 

23 

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 

31 

32 

33def CEscape(text, as_utf8) -> str: 

34 """Escape a bytes string for use in an text protocol buffer. 

35 

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) 

57 

58 

59_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])') 

60 

61 

62def CUnescape(text: str) -> bytes: 

63 """Unescape a text string with C-style escape sequences to UTF-8 bytes. 

64 

65 Args: 

66 text: The data to parse in a str. 

67 Returns: 

68 A byte string. 

69 """ 

70 

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) 

77 

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) 

81 

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'))