Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tomlkit/_utils.py: 95%

85 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 07:01 +0000

1import re 

2 

3from collections.abc import Mapping 

4from datetime import date 

5from datetime import datetime 

6from datetime import time 

7from datetime import timedelta 

8from datetime import timezone 

9from typing import Collection 

10from typing import Union 

11 

12from tomlkit._compat import decode 

13 

14 

15RFC_3339_LOOSE = re.compile( 

16 "^" 

17 r"(([0-9]+)-(\d{2})-(\d{2}))?" # Date 

18 "(" 

19 "([Tt ])?" # Separator 

20 r"(\d{2}):(\d{2}):(\d{2})(\.([0-9]+))?" # Time 

21 r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone 

22 ")?" 

23 "$" 

24) 

25 

26RFC_3339_DATETIME = re.compile( 

27 "^" 

28 "([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])" # Date 

29 "[Tt ]" # Separator 

30 r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.([0-9]+))?" # Time 

31 r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone 

32 "$" 

33) 

34 

35RFC_3339_DATE = re.compile("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$") 

36 

37RFC_3339_TIME = re.compile( 

38 r"^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.([0-9]+))?$" 

39) 

40 

41_utc = timezone(timedelta(), "UTC") 

42 

43 

44def parse_rfc3339(string: str) -> Union[datetime, date, time]: 

45 m = RFC_3339_DATETIME.match(string) 

46 if m: 

47 year = int(m.group(1)) 

48 month = int(m.group(2)) 

49 day = int(m.group(3)) 

50 hour = int(m.group(4)) 

51 minute = int(m.group(5)) 

52 second = int(m.group(6)) 

53 microsecond = 0 

54 

55 if m.group(7): 

56 microsecond = int((f"{m.group(8):<06s}")[:6]) 

57 

58 if m.group(9): 

59 # Timezone 

60 tz = m.group(9) 

61 if tz.upper() == "Z": 

62 tzinfo = _utc 

63 else: 

64 sign = m.group(11)[0] 

65 hour_offset, minute_offset = int(m.group(12)), int(m.group(13)) 

66 offset = timedelta(seconds=hour_offset * 3600 + minute_offset * 60) 

67 if sign == "-": 

68 offset = -offset 

69 

70 tzinfo = timezone(offset, f"{sign}{m.group(12)}:{m.group(13)}") 

71 

72 return datetime( 

73 year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo 

74 ) 

75 else: 

76 return datetime(year, month, day, hour, minute, second, microsecond) 

77 

78 m = RFC_3339_DATE.match(string) 

79 if m: 

80 year = int(m.group(1)) 

81 month = int(m.group(2)) 

82 day = int(m.group(3)) 

83 

84 return date(year, month, day) 

85 

86 m = RFC_3339_TIME.match(string) 

87 if m: 

88 hour = int(m.group(1)) 

89 minute = int(m.group(2)) 

90 second = int(m.group(3)) 

91 microsecond = 0 

92 

93 if m.group(4): 

94 microsecond = int((f"{m.group(5):<06s}")[:6]) 

95 

96 return time(hour, minute, second, microsecond) 

97 

98 raise ValueError("Invalid RFC 339 string") 

99 

100 

101# https://toml.io/en/v1.0.0#string 

102CONTROL_CHARS = frozenset(chr(c) for c in range(0x20)) | {chr(0x7F)} 

103_escaped = { 

104 "b": "\b", 

105 "t": "\t", 

106 "n": "\n", 

107 "f": "\f", 

108 "r": "\r", 

109 '"': '"', 

110 "\\": "\\", 

111} 

112_compact_escapes = { 

113 **{v: f"\\{k}" for k, v in _escaped.items()}, 

114 '"""': '""\\"', 

115} 

116_basic_escapes = CONTROL_CHARS | {'"', "\\"} 

117 

118 

119def _unicode_escape(seq: str) -> str: 

120 return "".join(f"\\u{ord(c):04x}" for c in seq) 

121 

122 

123def escape_string(s: str, escape_sequences: Collection[str] = _basic_escapes) -> str: 

124 s = decode(s) 

125 

126 res = [] 

127 start = 0 

128 

129 def flush(inc=1): 

130 if start != i: 

131 res.append(s[start:i]) 

132 

133 return i + inc 

134 

135 i = 0 

136 while i < len(s): 

137 for seq in escape_sequences: 

138 seq_len = len(seq) 

139 if s[i:].startswith(seq): 

140 start = flush(seq_len) 

141 res.append(_compact_escapes.get(seq) or _unicode_escape(seq)) 

142 i += seq_len - 1 # fast-forward escape sequence 

143 i += 1 

144 

145 flush() 

146 

147 return "".join(res) 

148 

149 

150def merge_dicts(d1: dict, d2: dict) -> dict: 

151 for k, v in d2.items(): 

152 if k in d1 and isinstance(d1[k], dict) and isinstance(v, Mapping): 

153 merge_dicts(d1[k], v) 

154 else: 

155 d1[k] = d2[k]