json-repair 0.29.3__py3-none-any.whl → 0.29.5__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,