1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
from __future__ import annotations
"""
In round-trip mode the original tag needs to be preserved, but the tag
transformed based on the directives needs to be available as well.
A Tag that is created during loading has a handle and a suffix.
Not all objects loaded currently have a Tag, that .tag attribute can be None
A Tag that is created for dumping only (on an object loaded without a tag) has a suffix
only.
"""
if False: # MYPY
from typing import Any, Dict, Optional, List, Union, Optional, Iterator # NOQA
tag_attrib = '_yaml_tag'
class Tag:
"""store original tag information for roundtripping"""
attrib = tag_attrib
def __init__(self, handle: Any = None, suffix: Any = None, handles: Any = None) -> None:
self.handle = handle
self.suffix = suffix
self.handles = handles
self._transform_type: Optional[bool] = None
def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.trval!r})'
def __str__(self) -> str:
return f'{self.trval}'
def __hash__(self) -> int:
try:
return self._hash_id # type: ignore
except AttributeError:
self._hash_id = res = hash((self.handle, self.suffix))
return res
def __eq__(self, other: Any) -> bool:
# other should not be a string, but the serializer sometimes provides these
if isinstance(other, str):
return self.trval == other
return bool(self.trval == other.trval)
def startswith(self, x: str) -> bool:
if self.trval is not None:
return self.trval.startswith(x)
return False
@property
def trval(self) -> Optional[str]:
try:
return self._trval
except AttributeError:
pass
if self.handle is None:
self._trval: Optional[str] = self.uri_decoded_suffix
return self._trval
assert self._transform_type is not None
if not self._transform_type:
# the non-round-trip case
self._trval = self.handles[self.handle] + self.uri_decoded_suffix
return self._trval
# round-trip case
if self.handle == '!!' and self.suffix in (
'null',
'bool',
'int',
'float',
'binary',
'timestamp',
'omap',
'pairs',
'set',
'str',
'seq',
'map',
):
self._trval = self.handles[self.handle] + self.uri_decoded_suffix
else:
# self._trval = self.handle + self.suffix
self._trval = self.handles[self.handle] + self.uri_decoded_suffix
return self._trval
value = trval
@property
def uri_decoded_suffix(self) -> Optional[str]:
try:
return self._uri_decoded_suffix
except AttributeError:
pass
if self.suffix is None:
self._uri_decoded_suffix: Optional[str] = None
return None
res = ''
# don't have to check for scanner errors here
idx = 0
while idx < len(self.suffix):
ch = self.suffix[idx]
idx += 1
if ch != '%':
res += ch
else:
res += chr(int(self.suffix[idx : idx + 2], 16))
idx += 2
self._uri_decoded_suffix = res
return res
def select_transform(self, val: bool) -> None:
"""
val: False -> non-round-trip
True -> round-trip
"""
assert self._transform_type is None
self._transform_type = val
def check_handle(self) -> bool:
if self.handle is None:
return False
return self.handle not in self.handles
|