1 # SPDX-License-Identifier: MIT
3 # Copyright (C) 2023 EfficiOS, inc.
5 # pyright: strict, reportTypeCommentUsage=false
26 # Internal type aliases and variables
27 _RawArrayT
= List
["_RawValT"]
28 _RawObjT
= Dict
[str, "_RawValT"]
29 _RawValT
= Union
[None, bool, int, float, str, _RawArrayT
, _RawObjT
]
30 _RawValTV
= TypeVar("_RawValTV", bound
="_RawValT")
31 _ValTV
= TypeVar("_ValTV", bound
="Val")
34 # Type of a single JSON value path element.
35 PathElemT
= Union
[str, int]
40 def __init__(self
, elems
: Optional
[List
[PathElemT
]] = None):
46 # Elements of this path.
51 # Returns a new path containing the current elements plus `elem`.
52 def __truediv__(self
, elem
: PathElemT
):
53 return Path(self
._elems
+ [elem
])
55 # Returns a valid jq filter.
59 for elem
in self
._elems
:
61 if re
.match(r
"[a-zA-Z]\w*$", elem
):
62 s
+= ".{}".format(elem
)
64 s
+= '."{}"'.format(elem
)
66 assert type(elem
) is int
67 s
+= "[{}]".format(elem
)
69 if not s
.startswith("."):
75 # Base of any JSON value.
79 def __init__(self
, path
: Optional
[Path
] = None):
85 # Path to this JSON value.
97 class _ScalarVal(Val
, Generic
[_RawValTV
]):
98 def __init__(self
, raw_val
: _RawValTV
, path
: Optional
[Path
] = None):
99 super().__init
__(path
)
100 self
._raw
_val
= raw_val
108 # JSON boolean value.
109 class BoolVal(_ScalarVal
[bool]):
116 # JSON integer value.
117 class IntVal(_ScalarVal
[int]):
124 # JSON floating point number value.
125 class FloatVal(_ScalarVal
[float]):
126 _name
= "a floating point number"
133 class StrVal(_ScalarVal
[str]):
141 class ArrayVal(Val
, Sequence
[Val
]):
144 def __init__(self
, raw_val
: _RawArrayT
, path
: Optional
[Path
] = None):
145 super().__init
__(path
)
146 self
._raw
_val
= raw_val
148 # Returns the value at index `index`.
150 # Raises `TypeError` if the type of the returned value isn't
151 # `expected_elem_type`.
152 def at(self
, index
: int, expected_elem_type
: Type
[_ValTV
]):
154 elem
= self
._raw
_val
[index
]
157 "`{}`: array index {} out of range".format(self
._path
, index
)
160 return wrap(elem
, self
._path
/ index
, expected_elem_type
)
162 # Returns an iterator yielding the values of this array value.
164 # Raises `TypeError` if the type of any yielded value isn't
165 # `expected_elem_type`.
166 def iter(self
, expected_elem_type
: Type
[_ValTV
]):
167 for i
in range(len(self
._raw
_val
)):
168 yield self
.at(i
, expected_elem_type
)
171 def __getitem__(self
, index
: int) -> Val
:
175 def __getitem__(self
, index
: slice) -> Sequence
[Val
]:
178 def __getitem__(self
, index
: Union
[int, slice]) -> Union
[Val
, Sequence
[Val
]]:
179 if type(index
) is slice:
180 raise NotImplementedError
182 return self
.at(index
, Val
)
185 return len(self
._raw
_val
)
189 class ObjVal(Val
, Mapping
[str, Val
]):
192 def __init__(self
, raw_val
: _RawObjT
, path
: Optional
[Path
] = None):
193 super().__init
__(path
)
194 self
._raw
_val
= raw_val
196 # Returns the value having the key `key`.
198 # Raises `TypeError` if the type of the returned value isn't
200 def at(self
, key
: str, expected_type
: Type
[_ValTV
]):
202 val
= self
._raw
_val
[key
]
204 raise KeyError("`{}`: no value has the key `{}`".format(self
._path
, key
))
206 return wrap(val
, self
._path
/ key
, expected_type
)
208 def __getitem__(self
, key
: str) -> Val
:
209 return self
.at(key
, Val
)
212 return len(self
._raw
_val
)
215 return iter(self
._raw
_val
)
218 # Raises `TypeError` if the type of `val` is not `expected_type`.
219 def _check_type(val
: Val
, expected_type
: Type
[Val
]):
220 if not isinstance(val
, expected_type
):
222 "`{}`: expecting {} value".format(
223 val
.path
, expected_type
._name
# pyright: ignore [reportPrivateUsage]
228 # Wraps the raw value `raw_val` into an equivalent instance of some
229 # `Val` subclass having the path `path` and returns it.
231 # If the resulting JSON value type isn't `expected_type`, then this
232 # function raises `TypeError`.
234 raw_val
: _RawValT
, path
: Optional
[Path
] = None, expected_type
: Type
[_ValTV
] = Val
240 elif isinstance(raw_val
, bool):
241 val
= BoolVal(raw_val
, path
)
242 elif isinstance(raw_val
, int):
243 val
= IntVal(raw_val
, path
)
244 elif isinstance(raw_val
, float):
245 val
= FloatVal(raw_val
, path
)
246 elif isinstance(raw_val
, str):
247 val
= StrVal(raw_val
, path
)
248 elif isinstance(raw_val
, list):
249 val
= ArrayVal(raw_val
, path
)
251 assert isinstance(raw_val
, dict)
252 val
= ObjVal(raw_val
, path
)
254 assert val
is not None
255 _check_type(val
, expected_type
)
256 return typing
.cast(_ValTV
, val
)
259 # Like json.loads(), but returns a `Val` instance, raising `TypeError`
260 # if its type isn't `expected_type`.
261 def loads(s
: str, expected_type
: Type
[_ValTV
] = Val
, **kwargs
: Any
) -> _ValTV
:
262 return wrap(json
.loads(s
, **kwargs
), Path(), expected_type
)
265 # Like json.load(), but returns a `Val` instance, raising `TypeError` if
266 # its type isn't `expected_type`.
267 def load(fp
: TextIO
, expected_type
: Type
[_ValTV
] = Val
, **kwargs
: Any
) -> _ValTV
:
268 return wrap(json
.load(fp
, **kwargs
), Path(), expected_type
)
This page took 0.034614 seconds and 4 git commands to generate.