json-repair 0.47.6__py3-none-any.whl → 0.47.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- json_repair/json_parser.py +23 -12
- json_repair/object_comparer.py +15 -4
- json_repair/parse_array.py +8 -2
- json_repair/parse_boolean_or_null.py +7 -1
- json_repair/parse_comment.py +7 -1
- json_repair/parse_number.py +7 -2
- json_repair/parse_object.py +6 -1
- json_repair/parse_string.py +15 -2
- {json_repair-0.47.6.dist-info → json_repair-0.47.8.dist-info}/METADATA +1 -1
- json_repair-0.47.8.dist-info/RECORD +21 -0
- json_repair-0.47.6.dist-info/RECORD +0 -21
- {json_repair-0.47.6.dist-info → json_repair-0.47.8.dist-info}/WHEEL +0 -0
- {json_repair-0.47.6.dist-info → json_repair-0.47.8.dist-info}/entry_points.txt +0 -0
- {json_repair-0.47.6.dist-info → json_repair-0.47.8.dist-info}/licenses/LICENSE +0 -0
- {json_repair-0.47.6.dist-info → json_repair-0.47.8.dist-info}/top_level.txt +0 -0
json_repair/json_parser.py
CHANGED
@@ -3,23 +3,34 @@ from typing import Literal, TextIO
|
|
3
3
|
from .constants import STRING_DELIMITERS, JSONReturnType
|
4
4
|
from .json_context import JsonContext
|
5
5
|
from .object_comparer import ObjectComparer
|
6
|
-
from .parse_array import parse_array
|
7
|
-
from .parse_boolean_or_null import parse_boolean_or_null
|
8
|
-
from .parse_comment import parse_comment
|
9
|
-
from .parse_number import parse_number
|
10
|
-
from .parse_object import parse_object
|
11
|
-
from .parse_string import parse_string
|
6
|
+
from .parse_array import parse_array as _parse_array
|
7
|
+
from .parse_boolean_or_null import parse_boolean_or_null as _parse_boolean_or_null
|
8
|
+
from .parse_comment import parse_comment as _parse_comment
|
9
|
+
from .parse_number import parse_number as _parse_number
|
10
|
+
from .parse_object import parse_object as _parse_object
|
11
|
+
from .parse_string import parse_string as _parse_string
|
12
12
|
from .string_file_wrapper import StringFileWrapper
|
13
13
|
|
14
14
|
|
15
15
|
class JSONParser:
|
16
16
|
# Split the parse methods into separate files because this one was like 3000 lines
|
17
|
-
parse_array
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
def parse_array(self, *args, **kwargs):
|
18
|
+
return _parse_array(self, *args, **kwargs)
|
19
|
+
|
20
|
+
def parse_boolean_or_null(self, *args, **kwargs):
|
21
|
+
return _parse_boolean_or_null(self, *args, **kwargs)
|
22
|
+
|
23
|
+
def parse_comment(self, *args, **kwargs):
|
24
|
+
return _parse_comment(self, *args, **kwargs)
|
25
|
+
|
26
|
+
def parse_number(self, *args, **kwargs):
|
27
|
+
return _parse_number(self, *args, **kwargs)
|
28
|
+
|
29
|
+
def parse_object(self, *args, **kwargs):
|
30
|
+
return _parse_object(self, *args, **kwargs)
|
31
|
+
|
32
|
+
def parse_string(self, *args, **kwargs):
|
33
|
+
return _parse_string(self, *args, **kwargs)
|
23
34
|
|
24
35
|
def __init__(
|
25
36
|
self,
|
json_repair/object_comparer.py
CHANGED
@@ -17,20 +17,31 @@ class ObjectComparer: # pragma: no cover
|
|
17
17
|
return False
|
18
18
|
|
19
19
|
if isinstance(obj1, dict):
|
20
|
-
#
|
21
|
-
if len(obj1) != len(obj2):
|
20
|
+
# Check that both are dicts and same length
|
21
|
+
if not isinstance(obj2, dict) or len(obj1) != len(obj2):
|
22
22
|
return False
|
23
23
|
for key in obj1:
|
24
24
|
if key not in obj2:
|
25
25
|
return False
|
26
|
+
# Recursively compare each value
|
26
27
|
if not ObjectComparer.is_same_object(obj1[key], obj2[key]):
|
27
28
|
return False
|
28
29
|
return True
|
29
30
|
|
30
31
|
elif isinstance(obj1, list):
|
31
|
-
|
32
|
+
# Check that both are lists and same length
|
33
|
+
if not isinstance(obj2, list) or len(obj1) != len(obj2):
|
32
34
|
return False
|
35
|
+
# Recursively compare each item
|
33
36
|
return all(ObjectComparer.is_same_object(obj1[i], obj2[i]) for i in range(len(obj1)))
|
34
37
|
|
35
|
-
# For
|
38
|
+
# For atomic values: types already match, so return True
|
36
39
|
return True
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
def is_strictly_empty(value: Any) -> bool:
|
43
|
+
"""
|
44
|
+
Returns True if value is an empty container (str, list, dict, set, tuple).
|
45
|
+
Returns False for non-containers like None, 0, False, etc.
|
46
|
+
"""
|
47
|
+
return isinstance(value, str | list | dict | set | tuple) and len(value) == 0
|
json_repair/parse_array.py
CHANGED
@@ -1,8 +1,14 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
1
3
|
from .constants import STRING_DELIMITERS, JSONReturnType
|
2
4
|
from .json_context import ContextValues
|
5
|
+
from .object_comparer import ObjectComparer
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from .json_parser import JSONParser
|
3
9
|
|
4
10
|
|
5
|
-
def parse_array(self) -> list[JSONReturnType]:
|
11
|
+
def parse_array(self: "JSONParser") -> list[JSONReturnType]:
|
6
12
|
# <array> ::= '[' [ <json> *(', ' <json>) ] ']' ; A sequence of JSON values separated by commas
|
7
13
|
arr = []
|
8
14
|
self.context.set(ContextValues.ARRAY)
|
@@ -23,7 +29,7 @@ def parse_array(self) -> list[JSONReturnType]:
|
|
23
29
|
value = self.parse_json()
|
24
30
|
|
25
31
|
# It is possible that parse_json() returns nothing valid, so we increase by 1
|
26
|
-
if value
|
32
|
+
if ObjectComparer.is_strictly_empty(value):
|
27
33
|
self.index += 1
|
28
34
|
elif value == "..." and self.get_char_at(-1) == ".":
|
29
35
|
self.log(
|
@@ -1,4 +1,10 @@
|
|
1
|
-
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
if TYPE_CHECKING:
|
4
|
+
from .json_parser import JSONParser
|
5
|
+
|
6
|
+
|
7
|
+
def parse_boolean_or_null(self: "JSONParser") -> bool | str | None:
|
2
8
|
# <boolean> is one of the literal strings 'true', 'false', or 'null' (unquoted)
|
3
9
|
starting_index = self.index
|
4
10
|
char = (self.get_char_at() or "").lower()
|
json_repair/parse_comment.py
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from .constants import JSONReturnType
|
1
4
|
from .json_context import ContextValues
|
2
5
|
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from .json_parser import JSONParser
|
8
|
+
|
3
9
|
|
4
|
-
def parse_comment(self) ->
|
10
|
+
def parse_comment(self: "JSONParser") -> JSONReturnType:
|
5
11
|
"""
|
6
12
|
Parse code-like comments:
|
7
13
|
|
json_repair/parse_number.py
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
from
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
2
3
|
from .json_context import ContextValues
|
3
4
|
|
4
5
|
NUMBER_CHARS: set[str] = set("0123456789-.eE/,")
|
5
6
|
|
6
7
|
|
7
|
-
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from .json_parser import JSONParser
|
10
|
+
|
11
|
+
|
12
|
+
def parse_number(self: "JSONParser") -> float | int | str | bool | None:
|
8
13
|
# <number> is a valid real number expressed in one of a number of given formats
|
9
14
|
number_str = ""
|
10
15
|
char = self.get_char_at()
|
json_repair/parse_object.py
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
1
3
|
from .constants import JSONReturnType
|
2
4
|
from .json_context import ContextValues
|
3
5
|
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from .json_parser import JSONParser
|
8
|
+
|
4
9
|
|
5
|
-
def parse_object(self) -> dict[str, JSONReturnType]:
|
10
|
+
def parse_object(self: "JSONParser") -> dict[str, JSONReturnType]:
|
6
11
|
# <object> ::= '{' [ <member> *(', ' <member>) ] '}' ; A sequence of 'members'
|
7
12
|
obj: dict[str, JSONReturnType] = {}
|
8
13
|
# Stop when you either find the closing parentheses or you have iterated over the entire string
|
json_repair/parse_string.py
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
1
3
|
from .constants import STRING_DELIMITERS
|
2
4
|
from .json_context import ContextValues
|
3
5
|
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from .json_parser import JSONParser
|
8
|
+
|
4
9
|
|
5
|
-
def parse_string(self) -> str | bool | None:
|
10
|
+
def parse_string(self: "JSONParser") -> str | bool | None:
|
6
11
|
# <string> is a string of valid characters enclosed in quotes
|
7
12
|
# i.e. { name: "John" }
|
8
13
|
# Somehow all weird cases in an invalid JSON happen to be resolved in this function, so be careful here
|
@@ -319,10 +324,18 @@ def parse_string(self) -> str | bool | None:
|
|
319
324
|
i += 1
|
320
325
|
i = self.skip_to_character(character=rstring_delimiter, idx=i)
|
321
326
|
next_c = self.get_char_at(i)
|
322
|
-
# Ok now I found a delimiter, let's skip whitespaces and see if next we find a }
|
327
|
+
# Ok now I found a delimiter, let's skip whitespaces and see if next we find a } or a ,
|
323
328
|
i += 1
|
324
329
|
i = self.skip_whitespaces_at(idx=i, move_main_index=False)
|
325
330
|
next_c = self.get_char_at(i)
|
331
|
+
if next_c in ["}", ","]:
|
332
|
+
self.log(
|
333
|
+
"While parsing a string, we a misplaced quote that would have closed the string but has a different meaning here, ignoring it",
|
334
|
+
)
|
335
|
+
string_acc += str(char)
|
336
|
+
self.index += 1
|
337
|
+
char = self.get_char_at()
|
338
|
+
continue
|
326
339
|
elif next_c == rstring_delimiter and self.get_char_at(i - 1) != "\\":
|
327
340
|
# Check if self.index:self.index+i is only whitespaces, break if that's the case
|
328
341
|
if all(str(self.get_char_at(j)).isspace() for j in range(1, i) if self.get_char_at(j)):
|
@@ -0,0 +1,21 @@
|
|
1
|
+
json_repair/__init__.py,sha256=JdJIZNCKV3MfIviryqK8NH8yGssCta2-192CekcwH-o,174
|
2
|
+
json_repair/__main__.py,sha256=EsJb-y89uZEvGQQg1GdIDWzfDwfOMvVekKEtdguQXCM,67
|
3
|
+
json_repair/constants.py,sha256=cv2gvyosuq0me0600WyTysM9avrtfXPuXYR26tawcuo,158
|
4
|
+
json_repair/json_context.py,sha256=WsMOjqpGSr6aaDONcrk8UFtTurzWon2Qq9AoBBYseoI,934
|
5
|
+
json_repair/json_parser.py,sha256=rTuL8ESslQ4XK9fkLmBIpS4e8xr6QwlZRVyJwzJFqBE,7356
|
6
|
+
json_repair/json_repair.py,sha256=txblCJtcTpXcQaT15tavulkJPtyRYe2cfYpPHZcvPv0,11233
|
7
|
+
json_repair/object_comparer.py,sha256=XKV3MRab8H7_v4sm-wpEa5le0XX9OeycWo5S-MFm-GI,1716
|
8
|
+
json_repair/parse_array.py,sha256=-rh65JcfT-FtXiR6s8RYlMfI-6LzVr08ytlDh6Z2CFE,2181
|
9
|
+
json_repair/parse_boolean_or_null.py,sha256=WMSkvvxsp4wvauBcDqtt9WnLMD5SMoxeRfZFXp3FEBc,890
|
10
|
+
json_repair/parse_comment.py,sha256=JHtQ_QlxOvPNnMh7lhUaoTjFGelqjhTNq7qn9xUE7SU,2648
|
11
|
+
json_repair/parse_number.py,sha256=33zAtkbuVzi9Lqjxu7cXn9WlVzd3WjRx9Ln_LFzVL4o,1259
|
12
|
+
json_repair/parse_object.py,sha256=UzkY0C5NSE2CtVnZwugMyhhtUJPgs0MwBb4kF4l2ftU,4563
|
13
|
+
json_repair/parse_string.py,sha256=QTMHdc9_tLwzFwYMVrSJbkWoeV6-mQK7e2RDbJR_JJM,22677
|
14
|
+
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
+
json_repair/string_file_wrapper.py,sha256=tGkWBEUPE-CZPf4uSM5NE9oSDTpskX0myJiXsl-gbds,4333
|
16
|
+
json_repair-0.47.8.dist-info/licenses/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
17
|
+
json_repair-0.47.8.dist-info/METADATA,sha256=S63nu7L4zgNPB3HzlA4uaJtb3204R_b1pCAUrShwyjU,12411
|
18
|
+
json_repair-0.47.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
19
|
+
json_repair-0.47.8.dist-info/entry_points.txt,sha256=SNfge3zPSP-ASqriYU9r3NAPaXdseYr7ciPMKdV2uSw,57
|
20
|
+
json_repair-0.47.8.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
21
|
+
json_repair-0.47.8.dist-info/RECORD,,
|
@@ -1,21 +0,0 @@
|
|
1
|
-
json_repair/__init__.py,sha256=JdJIZNCKV3MfIviryqK8NH8yGssCta2-192CekcwH-o,174
|
2
|
-
json_repair/__main__.py,sha256=EsJb-y89uZEvGQQg1GdIDWzfDwfOMvVekKEtdguQXCM,67
|
3
|
-
json_repair/constants.py,sha256=cv2gvyosuq0me0600WyTysM9avrtfXPuXYR26tawcuo,158
|
4
|
-
json_repair/json_context.py,sha256=WsMOjqpGSr6aaDONcrk8UFtTurzWon2Qq9AoBBYseoI,934
|
5
|
-
json_repair/json_parser.py,sha256=WpU8K3E51gJ9BKbuW7LcMQGXWArte8noYMzvA9qu6Wc,6850
|
6
|
-
json_repair/json_repair.py,sha256=txblCJtcTpXcQaT15tavulkJPtyRYe2cfYpPHZcvPv0,11233
|
7
|
-
json_repair/object_comparer.py,sha256=LlIF0MisRglzC-CiG5AxAEDCBWBHeJd-6uXYx0uRmCk,1175
|
8
|
-
json_repair/parse_array.py,sha256=YnESGXnRaX57zXv7NP6EcHOlqgeaLEzOy1s_l9ghTeY,2002
|
9
|
-
json_repair/parse_boolean_or_null.py,sha256=2KoUkjiZ68fkge_n_Q4bbFVG6WskRroKD55jW2Ep2OU,782
|
10
|
-
json_repair/parse_comment.py,sha256=kNTinpdHZftrtV190-Julq5eKFxMSmGYNbwlj4vGtsg,2492
|
11
|
-
json_repair/parse_number.py,sha256=o7wEER7_H6xG0WsmvKS8VucoMJ7AsaJdxkDzulJ9o-Q,1192
|
12
|
-
json_repair/parse_object.py,sha256=yQ9SilLdBBW5cYJOcQGB4ZR8MtOp4cH6WilfXF_kdgE,4456
|
13
|
-
json_repair/parse_string.py,sha256=8f-zr9I2n5tAHkot0pBzqcn8avZbhEb-CX0NLwQUMUU,22131
|
14
|
-
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
json_repair/string_file_wrapper.py,sha256=tGkWBEUPE-CZPf4uSM5NE9oSDTpskX0myJiXsl-gbds,4333
|
16
|
-
json_repair-0.47.6.dist-info/licenses/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
17
|
-
json_repair-0.47.6.dist-info/METADATA,sha256=S7fZgSot13SzgMXXk4Qh4PJEmDyUJMUy_Vb6FuLW6v8,12411
|
18
|
-
json_repair-0.47.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
19
|
-
json_repair-0.47.6.dist-info/entry_points.txt,sha256=SNfge3zPSP-ASqriYU9r3NAPaXdseYr7ciPMKdV2uSw,57
|
20
|
-
json_repair-0.47.6.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
21
|
-
json_repair-0.47.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|