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
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:01 +0000
1import re
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
12from tomlkit._compat import decode
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)
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)
35RFC_3339_DATE = re.compile("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$")
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)
41_utc = timezone(timedelta(), "UTC")
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
55 if m.group(7):
56 microsecond = int((f"{m.group(8):<06s}")[:6])
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
70 tzinfo = timezone(offset, f"{sign}{m.group(12)}:{m.group(13)}")
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)
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))
84 return date(year, month, day)
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
93 if m.group(4):
94 microsecond = int((f"{m.group(5):<06s}")[:6])
96 return time(hour, minute, second, microsecond)
98 raise ValueError("Invalid RFC 339 string")
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 | {'"', "\\"}
119def _unicode_escape(seq: str) -> str:
120 return "".join(f"\\u{ord(c):04x}" for c in seq)
123def escape_string(s: str, escape_sequences: Collection[str] = _basic_escapes) -> str:
124 s = decode(s)
126 res = []
127 start = 0
129 def flush(inc=1):
130 if start != i:
131 res.append(s[start:i])
133 return i + inc
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
145 flush()
147 return "".join(res)
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]