json-repair 0.29.3__py3-none-any.whl → 0.29.4__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_context.py +11 -35
- json_repair/json_parser.py +29 -43
- {json_repair-0.29.3.dist-info → json_repair-0.29.4.dist-info}/METADATA +1 -1
- json_repair-0.29.4.dist-info/RECORD +13 -0
- json_repair-0.29.3.dist-info/RECORD +0 -13
- {json_repair-0.29.3.dist-info → json_repair-0.29.4.dist-info}/LICENSE +0 -0
- {json_repair-0.29.3.dist-info → json_repair-0.29.4.dist-info}/WHEEL +0 -0
- {json_repair-0.29.3.dist-info → json_repair-0.29.4.dist-info}/entry_points.txt +0 -0
- {json_repair-0.29.3.dist-info → json_repair-0.29.4.dist-info}/top_level.txt +0 -0
json_repair/json_context.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from enum import Enum, auto
|
2
|
-
from typing import List
|
2
|
+
from typing import List, Optional
|
3
3
|
|
4
4
|
|
5
5
|
class ContextValues(Enum):
|
@@ -11,6 +11,8 @@ class ContextValues(Enum):
|
|
11
11
|
class JsonContext:
|
12
12
|
def __init__(self) -> None:
|
13
13
|
self.context: List[ContextValues] = []
|
14
|
+
self.current: Optional[ContextValues] = None
|
15
|
+
self.empty: bool = True
|
14
16
|
|
15
17
|
def set(self, value: ContextValues) -> None:
|
16
18
|
"""
|
@@ -25,6 +27,8 @@ class JsonContext:
|
|
25
27
|
# If a value is provided update the context variable and save in stack
|
26
28
|
if value:
|
27
29
|
self.context.append(value)
|
30
|
+
self.current = value
|
31
|
+
self.empty = False
|
28
32
|
|
29
33
|
def reset(self) -> None:
|
30
34
|
"""
|
@@ -33,37 +37,9 @@ class JsonContext:
|
|
33
37
|
Returns:
|
34
38
|
None
|
35
39
|
"""
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
Args:
|
43
|
-
context (ContextValues): The context value to check.
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
bool: True if the given context is the same as the most recent context in the stack, False otherwise.
|
47
|
-
"""
|
48
|
-
return self.context[-1] == context
|
49
|
-
|
50
|
-
def is_any(self, context: ContextValues) -> bool:
|
51
|
-
"""
|
52
|
-
Check if the given context exists anywhere in the context stack.
|
53
|
-
|
54
|
-
Args:
|
55
|
-
context (ContextValues): The context value to check.
|
56
|
-
|
57
|
-
Returns:
|
58
|
-
bool: True if the given context exists in the stack, False otherwise.
|
59
|
-
"""
|
60
|
-
return context in self.context
|
61
|
-
|
62
|
-
def is_empty(self) -> bool:
|
63
|
-
"""
|
64
|
-
Check if the context stack is empty.
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
bool: True if the context stack is empty, False otherwise.
|
68
|
-
"""
|
69
|
-
return len(self.context) == 0
|
40
|
+
try:
|
41
|
+
self.context.pop()
|
42
|
+
self.current = self.context[-1]
|
43
|
+
except IndexError:
|
44
|
+
self.current = None
|
45
|
+
self.empty = True
|
json_repair/json_parser.py
CHANGED
@@ -34,7 +34,8 @@ class JSONParser:
|
|
34
34
|
self.logger: List[Dict[str, str]] = []
|
35
35
|
self.log = self._log
|
36
36
|
else:
|
37
|
-
|
37
|
+
# No-op
|
38
|
+
self.log = lambda *args, **kwargs: None
|
38
39
|
|
39
40
|
def parse(
|
40
41
|
self,
|
@@ -88,12 +89,10 @@ class JSONParser:
|
|
88
89
|
)
|
89
90
|
return ""
|
90
91
|
# <string> starts with a quote
|
91
|
-
elif not self.context.
|
92
|
-
char in ['"', "'", "“"] or char.isalpha()
|
93
|
-
):
|
92
|
+
elif not self.context.empty and (char in ['"', "'", "“"] or char.isalpha()):
|
94
93
|
return self.parse_string()
|
95
94
|
# <number> starts with [0-9] or minus
|
96
|
-
elif not self.context.
|
95
|
+
elif not self.context.empty and (
|
97
96
|
char.isdigit() or char == "-" or char == "."
|
98
97
|
):
|
99
98
|
return self.parse_number()
|
@@ -234,8 +233,9 @@ class JSONParser:
|
|
234
233
|
elif char.isalnum():
|
235
234
|
# This could be a <boolean> and not a string. Because (T)rue or (F)alse or (N)ull are valid
|
236
235
|
# But remember, object keys are only of type string
|
237
|
-
if
|
238
|
-
|
236
|
+
if (
|
237
|
+
char.lower() in ["t", "f", "n"]
|
238
|
+
and self.context.current != ContextValues.OBJECT_KEY
|
239
239
|
):
|
240
240
|
value = self.parse_boolean_or_null()
|
241
241
|
if value != "":
|
@@ -255,15 +255,13 @@ class JSONParser:
|
|
255
255
|
if self.get_char_at() == lstring_delimiter:
|
256
256
|
# If it's an empty key, this was easy
|
257
257
|
if (
|
258
|
-
self.context.
|
258
|
+
self.context.current == ContextValues.OBJECT_KEY
|
259
259
|
and self.get_char_at(1) == ":"
|
260
260
|
):
|
261
261
|
self.index += 1
|
262
262
|
return ""
|
263
263
|
# Find the next delimiter
|
264
|
-
i = self.skip_to_character(
|
265
|
-
character=rstring_delimiter, idx=1, move_main_index=False
|
266
|
-
)
|
264
|
+
i = self.skip_to_character(character=rstring_delimiter, idx=1)
|
267
265
|
next_c = self.get_char_at(i)
|
268
266
|
# Now check that the next character is also a delimiter to ensure that we have "".....""
|
269
267
|
# In that case we ignore this rstring delimiter
|
@@ -296,22 +294,20 @@ class JSONParser:
|
|
296
294
|
while char and char != rstring_delimiter:
|
297
295
|
if (
|
298
296
|
missing_quotes
|
299
|
-
and self.context.
|
297
|
+
and self.context.current == ContextValues.OBJECT_KEY
|
300
298
|
and (char == ":" or char.isspace())
|
301
299
|
):
|
302
300
|
self.log(
|
303
301
|
"While parsing a string missing the left delimiter in object key context, we found a :, stopping here",
|
304
302
|
)
|
305
303
|
break
|
306
|
-
if self.context.
|
304
|
+
if self.context.current == ContextValues.OBJECT_VALUE and char in [
|
307
305
|
",",
|
308
306
|
"}",
|
309
307
|
]:
|
310
308
|
rstring_delimiter_missing = True
|
311
309
|
# check if this is a case in which the closing comma is NOT missing instead
|
312
|
-
i = self.skip_to_character(
|
313
|
-
character=rstring_delimiter, idx=1, move_main_index=False
|
314
|
-
)
|
310
|
+
i = self.skip_to_character(character=rstring_delimiter, idx=1)
|
315
311
|
next_c = self.get_char_at(i)
|
316
312
|
if next_c:
|
317
313
|
i += 1
|
@@ -345,8 +341,9 @@ class JSONParser:
|
|
345
341
|
"While parsing a string, we found a doubled quote, ignoring it"
|
346
342
|
)
|
347
343
|
self.index += 1
|
348
|
-
elif
|
349
|
-
|
344
|
+
elif (
|
345
|
+
missing_quotes
|
346
|
+
and self.context.current == ContextValues.OBJECT_VALUE
|
350
347
|
):
|
351
348
|
# In case of missing starting quote I need to check if the delimeter is the end or the beginning of a key
|
352
349
|
i = 1
|
@@ -387,20 +384,20 @@ class JSONParser:
|
|
387
384
|
# If we are in an object context, let's check for the right delimiters
|
388
385
|
if (
|
389
386
|
(
|
390
|
-
self.context.
|
387
|
+
ContextValues.OBJECT_KEY in self.context.context
|
391
388
|
and next_c in [":", "}"]
|
392
389
|
)
|
393
390
|
or (
|
394
|
-
self.context.
|
391
|
+
ContextValues.OBJECT_VALUE in self.context.context
|
395
392
|
and next_c == "}"
|
396
393
|
)
|
397
394
|
or (
|
398
|
-
self.context.
|
395
|
+
ContextValues.ARRAY in self.context.context
|
399
396
|
and next_c in ["]", ","]
|
400
397
|
)
|
401
398
|
or (
|
402
399
|
check_comma_in_object_value
|
403
|
-
and self.context.
|
400
|
+
and self.context.current == ContextValues.OBJECT_VALUE
|
404
401
|
and next_c == ","
|
405
402
|
)
|
406
403
|
):
|
@@ -408,13 +405,12 @@ class JSONParser:
|
|
408
405
|
i += 1
|
409
406
|
next_c = self.get_char_at(i)
|
410
407
|
# If we stopped for a comma in object_value context, let's check if find a "} at the end of the string
|
411
|
-
if
|
412
|
-
|
408
|
+
if (
|
409
|
+
next_c == ","
|
410
|
+
and self.context.current == ContextValues.OBJECT_VALUE
|
413
411
|
):
|
414
412
|
i += 1
|
415
|
-
i = self.skip_to_character(
|
416
|
-
character=rstring_delimiter, idx=i, move_main_index=False
|
417
|
-
)
|
413
|
+
i = self.skip_to_character(character=rstring_delimiter, idx=i)
|
418
414
|
next_c = self.get_char_at(i)
|
419
415
|
# Ok now I found a delimiter, let's skip whitespaces and see if next we find a }
|
420
416
|
i += 1
|
@@ -429,15 +425,13 @@ class JSONParser:
|
|
429
425
|
self.index += 1
|
430
426
|
char = self.get_char_at()
|
431
427
|
elif next_c == rstring_delimiter:
|
432
|
-
if self.context.
|
428
|
+
if self.context.current == ContextValues.OBJECT_VALUE:
|
433
429
|
# But this might not be it! This could be just a missing comma
|
434
430
|
# We found a delimiter and we need to check if this is a key
|
435
431
|
# so find a rstring_delimiter and a colon after
|
436
432
|
i += 1
|
437
433
|
i = self.skip_to_character(
|
438
|
-
character=rstring_delimiter,
|
439
|
-
idx=i,
|
440
|
-
move_main_index=False,
|
434
|
+
character=rstring_delimiter, idx=i
|
441
435
|
)
|
442
436
|
i += 1
|
443
437
|
next_c = self.get_char_at(i)
|
@@ -462,7 +456,7 @@ class JSONParser:
|
|
462
456
|
if (
|
463
457
|
char
|
464
458
|
and missing_quotes
|
465
|
-
and self.context.
|
459
|
+
and self.context.current == ContextValues.OBJECT_KEY
|
466
460
|
and char.isspace()
|
467
461
|
):
|
468
462
|
self.log(
|
@@ -488,7 +482,7 @@ class JSONParser:
|
|
488
482
|
number_str = ""
|
489
483
|
number_chars = set("0123456789-.eE/,")
|
490
484
|
char = self.get_char_at()
|
491
|
-
is_array = self.context.
|
485
|
+
is_array = self.context.current == ContextValues.ARRAY
|
492
486
|
while char and char in number_chars and (char != "," or not is_array):
|
493
487
|
number_str += char
|
494
488
|
self.index += 1
|
@@ -561,9 +555,7 @@ class JSONParser:
|
|
561
555
|
return idx
|
562
556
|
return idx
|
563
557
|
|
564
|
-
def skip_to_character(
|
565
|
-
self, character: str, idx: int = 0, move_main_index=True
|
566
|
-
) -> int:
|
558
|
+
def skip_to_character(self, character: str, idx: int = 0) -> int:
|
567
559
|
"""
|
568
560
|
This function quickly iterates to find a character, syntactic sugar to make the code more concise
|
569
561
|
"""
|
@@ -572,10 +564,7 @@ class JSONParser:
|
|
572
564
|
except IndexError:
|
573
565
|
return idx
|
574
566
|
while char != character:
|
575
|
-
|
576
|
-
self.index += 1
|
577
|
-
else:
|
578
|
-
idx += 1
|
567
|
+
idx += 1
|
579
568
|
try:
|
580
569
|
char = self.json_str[self.index + idx]
|
581
570
|
except IndexError:
|
@@ -593,6 +582,3 @@ class JSONParser:
|
|
593
582
|
"context": context,
|
594
583
|
}
|
595
584
|
)
|
596
|
-
|
597
|
-
def noop(*args: Any, **kwargs: Any) -> None:
|
598
|
-
pass
|
@@ -0,0 +1,13 @@
|
|
1
|
+
json_repair/__init__.py,sha256=IIzSm1DsCRrr8seF3UeMZXwxcq-tE3j-8d1WBxvEJvE,178
|
2
|
+
json_repair/__main__.py,sha256=EsJb-y89uZEvGQQg1GdIDWzfDwfOMvVekKEtdguQXCM,67
|
3
|
+
json_repair/json_context.py,sha256=DdJu3DJR-ANvr8KrWfJqdtOE3uI6_B0VQidKvE3PjJA,1080
|
4
|
+
json_repair/json_parser.py,sha256=BUPyAsb7wzkjNrBmsZgxgoOM9JhksCN-8cHcbJQpcPU,25525
|
5
|
+
json_repair/json_repair.py,sha256=GTg3OAXRbAJAHWs8oiQDqUHh4h6qKDVvWPXcrqafzLY,6100
|
6
|
+
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
json_repair/string_file_wrapper.py,sha256=EHLhNBWoyUitzT08thytYJiNZh_klEFwfT8zutPSdb4,3905
|
8
|
+
json_repair-0.29.4.dist-info/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
9
|
+
json_repair-0.29.4.dist-info/METADATA,sha256=dBmPfg4wBTxOFXklH4V38aiO4pUks5FS7HcvQlZ4NIg,10686
|
10
|
+
json_repair-0.29.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
11
|
+
json_repair-0.29.4.dist-info/entry_points.txt,sha256=SNfge3zPSP-ASqriYU9r3NAPaXdseYr7ciPMKdV2uSw,57
|
12
|
+
json_repair-0.29.4.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
13
|
+
json_repair-0.29.4.dist-info/RECORD,,
|
@@ -1,13 +0,0 @@
|
|
1
|
-
json_repair/__init__.py,sha256=IIzSm1DsCRrr8seF3UeMZXwxcq-tE3j-8d1WBxvEJvE,178
|
2
|
-
json_repair/__main__.py,sha256=EsJb-y89uZEvGQQg1GdIDWzfDwfOMvVekKEtdguQXCM,67
|
3
|
-
json_repair/json_context.py,sha256=MOzT0z4Pc03SWhggwwEpDNXyeHm04kLfvDBOBd3xkVU,1782
|
4
|
-
json_repair/json_parser.py,sha256=Gimn0LFUTpdGCFo9rOGjH3W39PEj00_Lrj4mPOSnBFU,25949
|
5
|
-
json_repair/json_repair.py,sha256=GTg3OAXRbAJAHWs8oiQDqUHh4h6qKDVvWPXcrqafzLY,6100
|
6
|
-
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
json_repair/string_file_wrapper.py,sha256=EHLhNBWoyUitzT08thytYJiNZh_klEFwfT8zutPSdb4,3905
|
8
|
-
json_repair-0.29.3.dist-info/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
9
|
-
json_repair-0.29.3.dist-info/METADATA,sha256=tQ_crOtYbu3fseCXoc-VDIHIJq0HtGRA9SvmCIldvUE,10686
|
10
|
-
json_repair-0.29.3.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
11
|
-
json_repair-0.29.3.dist-info/entry_points.txt,sha256=SNfge3zPSP-ASqriYU9r3NAPaXdseYr7ciPMKdV2uSw,57
|
12
|
-
json_repair-0.29.3.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
13
|
-
json_repair-0.29.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|