json-repair 0.29.3__py3-none-any.whl → 0.29.5__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.
@@ -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
- self.context.pop()
37
-
38
- def is_current(self, context: ContextValues) -> bool:
39
- """
40
- Check if the given context is the current (most recent) context.
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
@@ -34,7 +34,8 @@ class JSONParser:
34
34
  self.logger: List[Dict[str, str]] = []
35
35
  self.log = self._log
36
36
  else:
37
- self.log = self.noop
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.is_empty() and (
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.is_empty() and (
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 char.lower() in ["t", "f", "n"] and not self.context.is_current(
238
- ContextValues.OBJECT_KEY
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.is_current(ContextValues.OBJECT_KEY)
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,23 @@ class JSONParser:
296
294
  while char and char != rstring_delimiter:
297
295
  if (
298
296
  missing_quotes
299
- and self.context.is_current(ContextValues.OBJECT_KEY)
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.is_current(ContextValues.OBJECT_VALUE) and char in [
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)
311
+ # If the rstring_delimeter is escaped then it's not what we are looking for
312
+ while self.get_char_at(i - 1) == "\\":
313
+ i = self.skip_to_character(character=rstring_delimiter, idx=i + 1)
315
314
  next_c = self.get_char_at(i)
316
315
  if next_c:
317
316
  i += 1
@@ -345,8 +344,9 @@ class JSONParser:
345
344
  "While parsing a string, we found a doubled quote, ignoring it"
346
345
  )
347
346
  self.index += 1
348
- elif missing_quotes and self.context.is_current(
349
- ContextValues.OBJECT_VALUE
347
+ elif (
348
+ missing_quotes
349
+ and self.context.current == ContextValues.OBJECT_VALUE
350
350
  ):
351
351
  # In case of missing starting quote I need to check if the delimeter is the end or the beginning of a key
352
352
  i = 1
@@ -387,20 +387,20 @@ class JSONParser:
387
387
  # If we are in an object context, let's check for the right delimiters
388
388
  if (
389
389
  (
390
- self.context.is_any(ContextValues.OBJECT_KEY)
390
+ ContextValues.OBJECT_KEY in self.context.context
391
391
  and next_c in [":", "}"]
392
392
  )
393
393
  or (
394
- self.context.is_any(ContextValues.OBJECT_VALUE)
394
+ ContextValues.OBJECT_VALUE in self.context.context
395
395
  and next_c == "}"
396
396
  )
397
397
  or (
398
- self.context.is_any(ContextValues.ARRAY)
398
+ ContextValues.ARRAY in self.context.context
399
399
  and next_c in ["]", ","]
400
400
  )
401
401
  or (
402
402
  check_comma_in_object_value
403
- and self.context.is_current(ContextValues.OBJECT_VALUE)
403
+ and self.context.current == ContextValues.OBJECT_VALUE
404
404
  and next_c == ","
405
405
  )
406
406
  ):
@@ -408,13 +408,17 @@ class JSONParser:
408
408
  i += 1
409
409
  next_c = self.get_char_at(i)
410
410
  # If we stopped for a comma in object_value context, let's check if find a "} at the end of the string
411
- if next_c == "," and self.context.is_current(
412
- ContextValues.OBJECT_VALUE
411
+ if (
412
+ next_c == ","
413
+ and self.context.current == ContextValues.OBJECT_VALUE
413
414
  ):
414
415
  i += 1
415
- i = self.skip_to_character(
416
- character=rstring_delimiter, idx=i, move_main_index=False
417
- )
416
+ i = self.skip_to_character(character=rstring_delimiter, idx=i)
417
+ # If the rstring_delimeter is escaped then it's not what we are looking for
418
+ while self.get_char_at(i - 1) == "\\":
419
+ i = self.skip_to_character(
420
+ character=rstring_delimiter, idx=i + 1
421
+ )
418
422
  next_c = self.get_char_at(i)
419
423
  # Ok now I found a delimiter, let's skip whitespaces and see if next we find a }
420
424
  i += 1
@@ -429,16 +433,19 @@ class JSONParser:
429
433
  self.index += 1
430
434
  char = self.get_char_at()
431
435
  elif next_c == rstring_delimiter:
432
- if self.context.is_current(ContextValues.OBJECT_VALUE):
436
+ if self.context.current == ContextValues.OBJECT_VALUE:
433
437
  # But this might not be it! This could be just a missing comma
434
438
  # We found a delimiter and we need to check if this is a key
435
439
  # so find a rstring_delimiter and a colon after
436
440
  i += 1
437
441
  i = self.skip_to_character(
438
- character=rstring_delimiter,
439
- idx=i,
440
- move_main_index=False,
442
+ character=rstring_delimiter, idx=i
441
443
  )
444
+ # If the rstring_delimeter is escaped then it's not what we are looking for
445
+ while self.get_char_at(i - 1) == "\\":
446
+ i = self.skip_to_character(
447
+ character=rstring_delimiter, idx=i + 1
448
+ )
442
449
  i += 1
443
450
  next_c = self.get_char_at(i)
444
451
  while next_c and next_c != ":":
@@ -462,7 +469,7 @@ class JSONParser:
462
469
  if (
463
470
  char
464
471
  and missing_quotes
465
- and self.context.is_current(ContextValues.OBJECT_KEY)
472
+ and self.context.current == ContextValues.OBJECT_KEY
466
473
  and char.isspace()
467
474
  ):
468
475
  self.log(
@@ -488,7 +495,7 @@ class JSONParser:
488
495
  number_str = ""
489
496
  number_chars = set("0123456789-.eE/,")
490
497
  char = self.get_char_at()
491
- is_array = self.context.is_current(ContextValues.ARRAY)
498
+ is_array = self.context.current == ContextValues.ARRAY
492
499
  while char and char in number_chars and (char != "," or not is_array):
493
500
  number_str += char
494
501
  self.index += 1
@@ -561,9 +568,7 @@ class JSONParser:
561
568
  return idx
562
569
  return idx
563
570
 
564
- def skip_to_character(
565
- self, character: str, idx: int = 0, move_main_index=True
566
- ) -> int:
571
+ def skip_to_character(self, character: str, idx: int = 0) -> int:
567
572
  """
568
573
  This function quickly iterates to find a character, syntactic sugar to make the code more concise
569
574
  """
@@ -572,10 +577,7 @@ class JSONParser:
572
577
  except IndexError:
573
578
  return idx
574
579
  while char != character:
575
- if move_main_index: # pragma: no cover
576
- self.index += 1
577
- else:
578
- idx += 1
580
+ idx += 1
579
581
  try:
580
582
  char = self.json_str[self.index + idx]
581
583
  except IndexError:
@@ -593,6 +595,3 @@ class JSONParser:
593
595
  "context": context,
594
596
  }
595
597
  )
596
-
597
- def noop(*args: Any, **kwargs: Any) -> None:
598
- pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: json_repair
3
- Version: 0.29.3
3
+ Version: 0.29.5
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,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=jwpaUQlWXIr1LpgJa0K-J6Mb5YDNypzdmdzRr8t9HyQ,26419
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.5.dist-info/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
9
+ json_repair-0.29.5.dist-info/METADATA,sha256=hN659VLMQsy-hceD0ZBJnBhCKnQXi3NC_4JS0vttGYc,10686
10
+ json_repair-0.29.5.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
11
+ json_repair-0.29.5.dist-info/entry_points.txt,sha256=SNfge3zPSP-ASqriYU9r3NAPaXdseYr7ciPMKdV2uSw,57
12
+ json_repair-0.29.5.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
13
+ json_repair-0.29.5.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,,