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.
@@ -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 = parse_array
18
- parse_boolean_or_null = parse_boolean_or_null
19
- parse_comment = parse_comment
20
- parse_number = parse_number
21
- parse_object = parse_object
22
- parse_string = parse_string
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,
@@ -17,20 +17,31 @@ class ObjectComparer: # pragma: no cover
17
17
  return False
18
18
 
19
19
  if isinstance(obj1, dict):
20
- # Quick length check before key compares
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
- if len(obj1) != len(obj2):
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 atoms: types already match, so just return True
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
@@ -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
- def parse_boolean_or_null(self) -> bool | str | None:
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()
@@ -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) -> str:
10
+ def parse_comment(self: "JSONParser") -> JSONReturnType:
5
11
  """
6
12
  Parse code-like comments:
7
13
 
@@ -1,10 +1,15 @@
1
- from .constants import JSONReturnType
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
- def parse_number(self) -> float | int | str | JSONReturnType:
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()
@@ -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
@@ -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)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: json_repair
3
- Version: 0.47.6
3
+ Version: 0.47.8
4
4
  Summary: A package to repair broken json strings
5
5
  Author-email: Stefano Baccianella <4247706+mangiucugna@users.noreply.github.com>
6
6
  License: MIT License
@@ -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,,