-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path140.py
More file actions
193 lines (166 loc) · 5.72 KB
/
Copy path140.py
File metadata and controls
193 lines (166 loc) · 5.72 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import json
from typing import Any, Type, TypeVar, cast, get_args, get_origin
class ConversionError(ValueError):
pass
def __convert_list(value: Any) -> list:
if isinstance(value, (list, tuple, set)):
return list(value)
elif isinstance(value, dict):
return list(value.items())
elif isinstance(value, str):
value = value.strip()
if value.startswith("[") and value.endswith("]"):
try:
return json.loads(value)
except json.JSONDecodeError:
return [value]
else:
return [value]
else:
return [value]
def __convert_dict(value: Any) -> dict:
if isinstance(value, str):
try:
result = json.loads(value)
if isinstance(result, dict):
return result
else:
return {"value": result}
except json.JSONDecodeError:
return {"value": value} # Fallback conversion
elif isinstance(value, list):
return {i: value[i] for i in range(len(value))}
elif isinstance(value, tuple):
return {i: value[i] for i in range(len(value))}
elif isinstance(value, dict):
return value
else:
return {"value": value}
def __convert_tuple(value: Any) -> tuple:
if isinstance(value, (str, list, set)):
return tuple(value)
elif isinstance(value, dict):
return tuple(value.items())
elif isinstance(value, (int, float, bool)):
return (value,)
elif isinstance(value, tuple):
return value
else:
return (value,)
def __convert_set(value: Any) -> set:
if isinstance(value, (str, list, tuple)):
return set(value)
elif isinstance(value, dict):
return set(value.items())
elif isinstance(value, set):
return value
else:
return {value}
def __convert_str(value: Any) -> str:
if isinstance(value, str):
return value
else:
return json.dumps(value)
NUM = TypeVar("NUM", int, float)
def __convert_num(value: Any, num_type: Type[NUM]) -> NUM:
if isinstance(value, (list, dict, tuple, set)):
return num_type(len(value))
elif isinstance(value, num_type):
return value
else:
try:
return num_type(float(value))
except (ValueError, TypeError):
return num_type(0) # Fallback conversion
def __convert_bool(value: Any) -> bool:
if isinstance(value, bool):
return value
elif isinstance(value, str):
if value.lower() in ["true", "1"]:
return True
else:
return False
else:
return bool(value)
def _try_convert(value: Any, target_type: Type, raise_on_mismatch: bool) -> Any:
origin = get_origin(target_type)
args = get_args(target_type)
if origin is None:
origin = target_type
if origin not in [list, dict, tuple, str, set, int, float, bool]:
return value
# Handle the case when value is already of the target type
if isinstance(value, origin):
if not args:
return value
else:
# Need to convert elements
if origin is list:
return [convert(v, args[0]) for v in value]
elif origin is tuple:
# Tuples can have multiple types
if len(args) == 1:
return tuple(convert(v, args[0]) for v in value)
else:
return tuple(convert(v, t) for v, t in zip(value, args))
elif origin is dict:
key_type, val_type = args
return {
convert(k, key_type): convert(v, val_type) for k, v in value.items()
}
elif origin is set:
return {convert(v, args[0]) for v in value}
else:
return value
elif raise_on_mismatch:
raise TypeError(f"Value {value} is not of expected type {target_type}")
else:
# Need to convert value to the origin type
if origin is list:
value = __convert_list(value)
if args:
return [convert(v, args[0]) for v in value]
else:
return value
elif origin is dict:
value = __convert_dict(value)
if args:
key_type, val_type = args
return {
convert(k, key_type): convert(v, val_type) for k, v in value.items()
}
else:
return value
elif origin is tuple:
value = __convert_tuple(value)
if args:
if len(args) == 1:
return tuple(convert(v, args[0]) for v in value)
else:
return tuple(convert(v, t) for v, t in zip(value, args))
else:
return value
elif origin is str:
return __convert_str(value)
elif origin is set:
value = __convert_set(value)
if args:
return {convert(v, args[0]) for v in value}
else:
return value
elif origin is int:
return __convert_num(value, int)
elif origin is float:
return __convert_num(value, float)
elif origin is bool:
return __convert_bool(value)
else:
return value
T = TypeVar("T")
def type_match(value: Any, target_type: Type[T]) -> T:
return cast(T, _try_convert(value, target_type, raise_on_mismatch=True))
def convert(value: Any, target_type: Type[T]) -> T:
try:
return cast(T, _try_convert(value, target_type, raise_on_mismatch=False))
except Exception as e:
raise ConversionError(f"Failed to convert {value} to {target_type}") from e