jsonpointer 3.2.0__tar.gz → 3.3__tar.gz

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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonpointer
3
- Version: 3.2.0
3
+ Version: 3.3
4
4
  Summary: Identify specific nodes in a JSON document (RFC 6901)
5
5
  Home-page: https://github.com/stefankoegl/python-json-pointer
6
6
  Author: Stefan Kögl
@@ -42,7 +42,8 @@ python-json-pointer
42
42
 
43
43
  [![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
44
44
  [![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
45
- [![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)
45
+ [![Tests](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
46
+ [![Coverage Status](https://coveralls.io/repos/github/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/github/stefankoegl/python-json-pointer?branch=master)
46
47
 
47
48
 
48
49
  Resolve JSON Pointers in Python
@@ -56,5 +57,5 @@ See source code for examples
56
57
  * Repository: https://github.com/stefankoegl/python-json-pointer.git
57
58
  * Documentation: https://python-json-pointer.readthedocs.org/
58
59
  * PyPI: https://pypi.python.org/pypi/jsonpointer
59
- * Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer
60
- * Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer
60
+ * GitHub Actions: https://github.com/stefankoegl/python-json-pointer/actions
61
+ * Coveralls: https://coveralls.io/github/stefankoegl/python-json-pointer
@@ -3,7 +3,8 @@ python-json-pointer
3
3
 
4
4
  [![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
5
5
  [![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
6
- [![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)
6
+ [![Tests](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
7
+ [![Coverage Status](https://coveralls.io/repos/github/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/github/stefankoegl/python-json-pointer?branch=master)
7
8
 
8
9
 
9
10
  Resolve JSON Pointers in Python
@@ -17,5 +18,5 @@ See source code for examples
17
18
  * Repository: https://github.com/stefankoegl/python-json-pointer.git
18
19
  * Documentation: https://python-json-pointer.readthedocs.org/
19
20
  * PyPI: https://pypi.python.org/pypi/jsonpointer
20
- * Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer
21
- * Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer
21
+ * GitHub Actions: https://github.com/stefankoegl/python-json-pointer/actions
22
+ * Coveralls: https://coveralls.io/github/stefankoegl/python-json-pointer
@@ -27,6 +27,8 @@ parser.add_argument('FILE', type=str, nargs='+',
27
27
  help='Files for which the pointer should be resolved')
28
28
  parser.add_argument('--indent', type=int, default=None,
29
29
  help='Indent output by n spaces')
30
+ parser.add_argument('--continue-on-error', action='store_true',
31
+ help='Continue with remaining files after JSON or text decoding errors')
30
32
  parser.add_argument('-v', '--version', action='version',
31
33
  version='%(prog)s ' + jsonpointer.__version__)
32
34
 
@@ -55,7 +57,8 @@ def parse_pointer(args):
55
57
  if args.pointer is not None:
56
58
  ptr = args.pointer
57
59
  else:
58
- ptr = args.pointer_file.read().strip()
60
+ # 2026-10-11: Trim file line endings without changing member names.
61
+ ptr = args.pointer_file.read().rstrip("\r\n")
59
62
 
60
63
  elif args.POINTER is not None:
61
64
  ptr = args.POINTER
@@ -76,8 +79,8 @@ def parse_pointer(args):
76
79
  def resolve_files():
77
80
  """ Resolve a JSON pointer on JSON files
78
81
 
79
- Returns False if the pointer could not be resolved on at least one
80
- file. """
82
+ Returns False if the pointer could not be resolved on at least one file,
83
+ or if --continue-on-error skipped a JSON or text decoding error. """
81
84
  args = parser.parse_args()
82
85
 
83
86
  ptr, files = parse_pointer(args)
@@ -89,7 +92,16 @@ def resolve_files():
89
92
  except argparse.ArgumentTypeError as e:
90
93
  parser.error(str(e))
91
94
 
92
- doc = json.load(f)
95
+ try:
96
+ doc = json.load(f)
97
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
98
+ if not args.continue_on_error:
99
+ raise
100
+ print('Could not parse JSON in %r: %s' % (name, e),
101
+ file=sys.stderr)
102
+ ok = False
103
+ continue
104
+
93
105
  try:
94
106
  result = jsonpointer.resolve_pointer(doc, ptr)
95
107
  print(json.dumps(result, indent=args.indent))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonpointer
3
- Version: 3.2.0
3
+ Version: 3.3
4
4
  Summary: Identify specific nodes in a JSON document (RFC 6901)
5
5
  Home-page: https://github.com/stefankoegl/python-json-pointer
6
6
  Author: Stefan Kögl
@@ -42,7 +42,8 @@ python-json-pointer
42
42
 
43
43
  [![PyPI version](https://img.shields.io/pypi/v/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
44
44
  [![Supported Python versions](https://img.shields.io/pypi/pyversions/jsonpointer.svg)](https://pypi.python.org/pypi/jsonpointer/)
45
- [![Coverage Status](https://coveralls.io/repos/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/r/stefankoegl/python-json-pointer?branch=master)
45
+ [![Tests](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml/badge.svg?branch=master)](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
46
+ [![Coverage Status](https://coveralls.io/repos/github/stefankoegl/python-json-pointer/badge.svg?branch=master)](https://coveralls.io/github/stefankoegl/python-json-pointer?branch=master)
46
47
 
47
48
 
48
49
  Resolve JSON Pointers in Python
@@ -56,5 +57,5 @@ See source code for examples
56
57
  * Repository: https://github.com/stefankoegl/python-json-pointer.git
57
58
  * Documentation: https://python-json-pointer.readthedocs.org/
58
59
  * PyPI: https://pypi.python.org/pypi/jsonpointer
59
- * Travis CI: https://travis-ci.org/stefankoegl/python-json-pointer
60
- * Coveralls: https://coveralls.io/r/stefankoegl/python-json-pointer
60
+ * GitHub Actions: https://github.com/stefankoegl/python-json-pointer/actions
61
+ * Coveralls: https://coveralls.io/github/stefankoegl/python-json-pointer
@@ -32,7 +32,7 @@
32
32
 
33
33
  # Will be parsed by setup.py to determine package metadata
34
34
  __author__ = 'Stefan Kögl <stefan@skoegl.net>'
35
- __version__ = '3.2.0'
35
+ __version__ = '3.3'
36
36
  __website__ = 'https://github.com/stefankoegl/python-json-pointer'
37
37
  __license__ = 'Modified BSD License'
38
38
 
@@ -307,7 +307,7 @@ class JsonPointer:
307
307
  suffix_parts = suffix
308
308
  try:
309
309
  return self.__class__.from_parts(chain(self.parts, suffix_parts))
310
- except: # noqa E722
310
+ except Exception:
311
311
  raise JsonPointerException("Invalid suffix")
312
312
 
313
313
  def __truediv__(self, suffix):
@@ -196,6 +196,19 @@ class ComparisonTests(unittest.TestCase):
196
196
  ptr12e = self.ptr1 / ["a", "b"]
197
197
  self.assertEqual(ptr12e.path, "/a/b/c/a/b")
198
198
 
199
+ def test_join_preserves_control_flow_exceptions(self):
200
+ for exception_type in (KeyboardInterrupt, SystemExit, GeneratorExit):
201
+ with self.subTest(exception_type=exception_type):
202
+ error = exception_type()
203
+
204
+ def parts():
205
+ yield 'valid'
206
+ raise error
207
+
208
+ with self.assertRaises(exception_type) as caught:
209
+ self.ptr1.join(parts())
210
+ self.assertIs(caught.exception, error)
211
+
199
212
  def test_join_subclass(self):
200
213
  class Pointer(JsonPointer):
201
214
  pass
@@ -494,7 +507,7 @@ class CommandLineTests(unittest.TestCase):
494
507
  f.write(content)
495
508
 
496
509
  def _run(self, *args):
497
- env = dict(os.environ, PYTHONPATH=self.ROOT)
510
+ env = dict(os.environ, PYTHONPATH=self.ROOT, PYTHONUTF8='1')
498
511
  return subprocess.run([sys.executable, self.SCRIPT] + list(args),
499
512
  cwd=self.dir, env=env, capture_output=True,
500
513
  text=True)
@@ -514,6 +527,17 @@ class CommandLineTests(unittest.TestCase):
514
527
  self.assertEqual(proc.returncode, 0, proc.stderr)
515
528
  self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
516
529
 
530
+ # 2026-10-11: A pointer file's trailing spaces are part of its member name.
531
+ def test_pointer_file_preserves_member_whitespace(self):
532
+ self._write('space.json', '{"a ": "expected", "a": "wrong"}')
533
+ for newline in ('', '\n', '\r\n'):
534
+ with self.subTest(newline=newline):
535
+ with open(os.path.join(self.dir, 'space.txt'), 'w', newline='') as f:
536
+ f.write('/a ' + newline)
537
+ proc = self._run('-f', 'space.txt', 'space.json')
538
+ self.assertEqual(proc.returncode, 0, proc.stderr)
539
+ self.assertEqual(proc.stdout, '"expected"\n')
540
+
517
541
  def test_pointer_file_single_file(self):
518
542
  proc = self._run('-f', 'ptr.txt', 'a.json')
519
543
  self.assertEqual(proc.returncode, 0, proc.stderr)
@@ -560,6 +584,92 @@ class CommandLineTests(unittest.TestCase):
560
584
  self.assertIn('Could not resolve pointer', proc.stderr)
561
585
  self.assertEqual(proc.stdout, '1\n')
562
586
 
587
+ def test_invalid_json(self):
588
+ for content in ('', '{"a": }', '{"a": 1}\n{"a": 2}'):
589
+ with self.subTest(content=content):
590
+ self._write('invalid.json', content)
591
+ proc = self._run('--continue-on-error', '/a', 'invalid.json')
592
+ self.assertEqual(proc.returncode, 1)
593
+ self.assertEqual(proc.stdout, '')
594
+ self.assertIn("Could not parse JSON in 'invalid.json':",
595
+ proc.stderr)
596
+ self.assertIn('line ', proc.stderr)
597
+ self.assertNotIn('Traceback', proc.stderr)
598
+
599
+ def test_invalid_json_in_one_file(self):
600
+ self._write('invalid.json', '{"a": }')
601
+ for index in range(3):
602
+ with self.subTest(index=index):
603
+ files = ['a.json', 'b.json']
604
+ files.insert(index, 'invalid.json')
605
+ proc = self._run('--continue-on-error', '/a', *files)
606
+ self.assertEqual(proc.returncode, 1)
607
+ self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
608
+ self.assertIn("Could not parse JSON in 'invalid.json':",
609
+ proc.stderr)
610
+ self.assertNotIn('Traceback', proc.stderr)
611
+
612
+ def test_invalid_json_and_unresolvable_pointer(self):
613
+ self._write('invalid.json', '{"a": }')
614
+ proc = self._run('--continue-on-error', '/b',
615
+ 'invalid.json', 'a.json', 'b.json')
616
+ self.assertEqual(proc.returncode, 1)
617
+ self.assertEqual(proc.stdout, '1\n')
618
+ self.assertIn("Could not parse JSON in 'invalid.json':", proc.stderr)
619
+ self.assertIn('Could not resolve pointer:', proc.stderr)
620
+ self.assertNotIn('Traceback', proc.stderr)
621
+
622
+ def test_invalid_json_stops_without_continue_on_error(self):
623
+ self._write('invalid.json', '{"a": }')
624
+ for index in range(3):
625
+ with self.subTest(index=index):
626
+ files = ['a.json', 'b.json']
627
+ files.insert(index, 'invalid.json')
628
+ proc = self._run('/a', *files)
629
+ self.assertEqual(proc.returncode, 1)
630
+ self.assertEqual(proc.stdout,
631
+ ''.join([self.A_OUT, self.B_OUT][:index]))
632
+ self.assertIn('JSONDecodeError', proc.stderr)
633
+
634
+ def test_invalid_encoding_in_one_file(self):
635
+ with open(os.path.join(self.dir, 'invalid.json'), 'wb') as f:
636
+ f.write(b'{"a":"\xff"}')
637
+ for index in range(3):
638
+ with self.subTest(index=index):
639
+ files = ['a.json', 'b.json']
640
+ files.insert(index, 'invalid.json')
641
+ proc = self._run('--continue-on-error', '/a', *files)
642
+ self.assertEqual(proc.returncode, 1)
643
+ self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
644
+ self.assertIn("Could not parse JSON in 'invalid.json':",
645
+ proc.stderr)
646
+ self.assertIn('decode', proc.stderr)
647
+ self.assertNotIn('Traceback', proc.stderr)
648
+
649
+ def test_invalid_encoding_stops_without_continue_on_error(self):
650
+ with open(os.path.join(self.dir, 'invalid.json'), 'wb') as f:
651
+ f.write(b'{"a":"\xff"}')
652
+ proc = self._run('/a', 'a.json', 'invalid.json', 'b.json')
653
+ self.assertEqual(proc.returncode, 1)
654
+ self.assertEqual(proc.stdout, self.A_OUT)
655
+ self.assertIn('UnicodeDecodeError', proc.stderr)
656
+
657
+ def test_continue_on_error_with_valid_files(self):
658
+ for pointer in (['/a'], ['-p', '/a'], ['-f', 'ptr.txt']):
659
+ with self.subTest(pointer=pointer):
660
+ proc = self._run('--continue-on-error', *pointer,
661
+ 'a.json', 'b.json')
662
+ self.assertEqual(proc.returncode, 0, proc.stderr)
663
+ self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
664
+ self.assertEqual(proc.stderr, '')
665
+
666
+ def test_continue_on_error_does_not_skip_missing_files(self):
667
+ proc = self._run('--continue-on-error', '/a',
668
+ 'a.json', 'missing.json', 'b.json')
669
+ self.assertEqual(proc.returncode, 2)
670
+ self.assertEqual(proc.stdout, self.A_OUT)
671
+ self.assertIn('missing.json', proc.stderr)
672
+
563
673
  def test_pointer_file_requires_value(self):
564
674
  proc = self._run('-f', '-p', '/a', 'a.json')
565
675
  self.assertEqual(proc.returncode, 2)
File without changes
File without changes
File without changes
File without changes
File without changes