Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tomlkit/_utils.py: 87%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import re
5from collections.abc import Mapping
6from datetime import date
7from datetime import datetime
8from datetime import time
9from datetime import timedelta
10from datetime import timezone
11from typing import Collection
13from tomlkit._compat import decode
16RFC_3339_LOOSE = re.compile(
17 "^"
18 r"(([0-9]+)-(\d{2})-(\d{2}))?" # Date
19 "("
20 "([Tt ])?" # Separator
21 r"(\d{2}):(\d{2}):(\d{2})(\.([0-9]+))?" # Time
22 r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
23 ")?"
24 "$"
25)
27RFC_3339_DATETIME = re.compile(
28 "^"
29 "([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])" # Date
30 "[Tt ]" # Separator
31 r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.([0-9]+))?" # Time
32 r"(([Zz])|([\+|\-]([01][0-9]|2[0-3]):([0-5][0-9])))?" # Timezone
33 "$"
34)
36RFC_3339_DATE = re.compile("^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$")
38RFC_3339_TIME = re.compile(
39 r"^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.([0-9]+))?$"
40)
42_utc = timezone(timedelta(), "UTC")
45def parse_rfc3339(string: str) -> datetime | date | time:
46 m = RFC_3339_DATETIME.match(string)
47 if m:
48 year = int(m.group(1))
49 month = int(m.group(2))
50 day = int(m.group(3))
51 hour = int(m.group(4))
52 minute = int(m.group(5))
53 second = int(m.group(6))
54 microsecond = 0
56 if m.group(7):
57 microsecond = int((f"{m.group(8):<06s}")[:6])
59 if m.group(9):
60 # Timezone
61 tz = m.group(9)
62 if tz.upper() == "Z":
63 tzinfo = _utc
64 else:
65 sign = m.group(11)[0]
66 hour_offset, minute_offset = int(m.group(12)), int(m.group(13))
67 offset = timedelta(seconds=hour_offset * 3600 + minute_offset * 60)
68 if sign == "-":
69 offset = -offset
71 tzinfo = timezone(offset, f"{sign}{m.group(12)}:{m.group(13)}")
73 return datetime(
74 year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo
75 )
76 else:
77 return datetime(year, month, day, hour, minute, second, microsecond)
79 m = RFC_3339_DATE.match(string)
80 if m:
81 year = int(m.group(1))
82 month = int(m.group(2))
83 day = int(m.group(3))
85 return date(year, month, day)
87 m = RFC_3339_TIME.match(string)
88 if m:
89 hour = int(m.group(1))
90 minute = int(m.group(2))
91 second = int(m.group(3))
92 microsecond = 0
94 if m.group(4):
95 microsecond = int((f"{m.group(5):<06s}")[:6])
97 return time(hour, minute, second, microsecond)
99 raise ValueError("Invalid RFC 339 string")
102# https://toml.io/en/v1.0.0#string
103CONTROL_CHARS = frozenset(chr(c) for c in range(0x20)) | {chr(0x7F)}
104_escaped = {
105 "b": "\b",
106 "t": "\t",
107 "n": "\n",
108 "f": "\f",
109 "r": "\r",
110 '"': '"',
111 "\\": "\\",
112}
113_compact_escapes = {
114 **{v: f"\\{k}" for k, v in _escaped.items()},
115 '"""': '""\\"',
116}
117_basic_escapes = CONTROL_CHARS | {'"', "\\"}
120def _unicode_escape(seq: str) -> str:
121 return "".join(f"\\u{ord(c):04x}" for c in seq)
124def escape_string(s: str, escape_sequences: Collection[str] = _basic_escapes) -> str:
125 s = decode(s)
127 res = []
128 start = 0
130 def flush(inc=1):
131 if start != i:
132 res.append(s[start:i])
134 return i + inc
136 found_sequences = {seq for seq in escape_sequences if seq in s}
138 i = 0
139 while i < len(s):
140 for seq in found_sequences:
141 seq_len = len(seq)
142 if s[i:].startswith(seq):
143 start = flush(seq_len)
144 res.append(_compact_escapes.get(seq) or _unicode_escape(seq))
145 i += seq_len - 1 # fast-forward escape sequence
146 i += 1
148 flush()
150 return "".join(res)
153def merge_dicts(d1: dict, d2: dict) -> dict:
154 for k, v in d2.items():
155 if k in d1 and isinstance(d1[k], dict) and isinstance(v, Mapping):
156 merge_dicts(d1[k], v)
157 else:
158 d1[k] = d2[k]