jsonpointer 3.1.0__tar.gz → 3.2.0__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.
- {jsonpointer-3.1.0/jsonpointer.egg-info → jsonpointer-3.2.0}/PKG-INFO +2 -1
- jsonpointer-3.2.0/bin/jsonpointer +104 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0/jsonpointer.egg-info}/PKG-INFO +2 -1
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/jsonpointer.py +25 -7
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/setup.py +1 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/tests.py +210 -0
- jsonpointer-3.1.0/bin/jsonpointer +0 -66
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/AUTHORS +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/LICENSE.txt +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/MANIFEST.in +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/README.md +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/jsonpointer.egg-info/SOURCES.txt +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/jsonpointer.egg-info/dependency_links.txt +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/jsonpointer.egg-info/top_level.txt +0 -0
- {jsonpointer-3.1.0 → jsonpointer-3.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonpointer
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
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
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import jsonpointer
|
|
9
|
+
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description='Resolve a JSON pointer on JSON files')
|
|
12
|
+
|
|
13
|
+
# Accept pointer as option, as file or as first positional argument
|
|
14
|
+
ptr_group = parser.add_mutually_exclusive_group()
|
|
15
|
+
|
|
16
|
+
ptr_group.add_argument('-f', '--pointer-file', type=argparse.FileType('r'),
|
|
17
|
+
help='File containing a JSON pointer expression')
|
|
18
|
+
|
|
19
|
+
ptr_group.add_argument('-p', '--pointer', type=str,
|
|
20
|
+
help='A JSON pointer expression')
|
|
21
|
+
|
|
22
|
+
parser.add_argument('POINTER', type=str, nargs='?',
|
|
23
|
+
help='A JSON pointer expression (if neither -f nor -p '
|
|
24
|
+
'is given)')
|
|
25
|
+
|
|
26
|
+
parser.add_argument('FILE', type=str, nargs='+',
|
|
27
|
+
help='Files for which the pointer should be resolved')
|
|
28
|
+
parser.add_argument('--indent', type=int, default=None,
|
|
29
|
+
help='Indent output by n spaces')
|
|
30
|
+
parser.add_argument('-v', '--version', action='version',
|
|
31
|
+
version='%(prog)s ' + jsonpointer.__version__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
try:
|
|
36
|
+
ok = resolve_files()
|
|
37
|
+
except KeyboardInterrupt:
|
|
38
|
+
sys.exit(1)
|
|
39
|
+
|
|
40
|
+
if not ok:
|
|
41
|
+
sys.exit(1)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def parse_pointer(args):
|
|
45
|
+
""" Return the pointer and the list of files to resolve it on
|
|
46
|
+
|
|
47
|
+
If the pointer is given via -f or -p, a POINTER positional argument is
|
|
48
|
+
actually the first file. """
|
|
49
|
+
files = args.FILE
|
|
50
|
+
|
|
51
|
+
if args.pointer is not None or args.pointer_file is not None:
|
|
52
|
+
if args.POINTER is not None:
|
|
53
|
+
files = [args.POINTER] + files
|
|
54
|
+
|
|
55
|
+
if args.pointer is not None:
|
|
56
|
+
ptr = args.pointer
|
|
57
|
+
else:
|
|
58
|
+
ptr = args.pointer_file.read().strip()
|
|
59
|
+
|
|
60
|
+
elif args.POINTER is not None:
|
|
61
|
+
ptr = args.POINTER
|
|
62
|
+
|
|
63
|
+
else:
|
|
64
|
+
parser.error('a JSON pointer is required (POINTER, -p or -f)')
|
|
65
|
+
|
|
66
|
+
# Catches e.g. `jsonpointer a.json b.json`, where the pointer was omitted
|
|
67
|
+
# and the first file would otherwise be used as the pointer
|
|
68
|
+
try:
|
|
69
|
+
jsonpointer.JsonPointer(ptr)
|
|
70
|
+
except jsonpointer.JsonPointerException as e:
|
|
71
|
+
parser.error('invalid JSON pointer %r: %s' % (ptr, e))
|
|
72
|
+
|
|
73
|
+
return ptr, files
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def resolve_files():
|
|
77
|
+
""" Resolve a JSON pointer on JSON files
|
|
78
|
+
|
|
79
|
+
Returns False if the pointer could not be resolved on at least one
|
|
80
|
+
file. """
|
|
81
|
+
args = parser.parse_args()
|
|
82
|
+
|
|
83
|
+
ptr, files = parse_pointer(args)
|
|
84
|
+
ok = True
|
|
85
|
+
|
|
86
|
+
for name in files:
|
|
87
|
+
try:
|
|
88
|
+
f = argparse.FileType('r')(name)
|
|
89
|
+
except argparse.ArgumentTypeError as e:
|
|
90
|
+
parser.error(str(e))
|
|
91
|
+
|
|
92
|
+
doc = json.load(f)
|
|
93
|
+
try:
|
|
94
|
+
result = jsonpointer.resolve_pointer(doc, ptr)
|
|
95
|
+
print(json.dumps(result, indent=args.indent))
|
|
96
|
+
except jsonpointer.JsonPointerException as e:
|
|
97
|
+
print('Could not resolve pointer: %s' % str(e), file=sys.stderr)
|
|
98
|
+
ok = False
|
|
99
|
+
|
|
100
|
+
return ok
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonpointer
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
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
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries
|
|
@@ -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.
|
|
35
|
+
__version__ = '3.2.0'
|
|
36
36
|
__website__ = 'https://github.com/stefankoegl/python-json-pointer'
|
|
37
37
|
__license__ = 'Modified BSD License'
|
|
38
38
|
|
|
@@ -43,6 +43,10 @@ from itertools import tee, chain
|
|
|
43
43
|
|
|
44
44
|
_nothing = object()
|
|
45
45
|
|
|
46
|
+
# If True, exceptions include the doc content in the error message.
|
|
47
|
+
# Set to False to suppress potentially large document content from exceptions.
|
|
48
|
+
VERBOSE_EXCEPTIONS = True
|
|
49
|
+
|
|
46
50
|
|
|
47
51
|
def set_pointer(doc, pointer, value, inplace=True):
|
|
48
52
|
"""Resolves a pointer against doc and sets the value of the target within doc.
|
|
@@ -209,10 +213,15 @@ class JsonPointer:
|
|
|
209
213
|
|
|
210
214
|
(parent, part) = self.to_last(doc)
|
|
211
215
|
|
|
212
|
-
if isinstance(parent,
|
|
216
|
+
if isinstance(parent, str):
|
|
217
|
+
raise JsonPointerException("Cannot set value in a string")
|
|
218
|
+
elif isinstance(parent, Sequence) and part == '-':
|
|
213
219
|
parent.append(value)
|
|
214
220
|
else:
|
|
215
|
-
|
|
221
|
+
try:
|
|
222
|
+
parent[part] = value
|
|
223
|
+
except (TypeError, IndexError) as e:
|
|
224
|
+
raise JsonPointerException("Invalid assignment target: %s" % (e,))
|
|
216
225
|
|
|
217
226
|
return doc
|
|
218
227
|
|
|
@@ -223,15 +232,21 @@ class JsonPointer:
|
|
|
223
232
|
if isinstance(doc, Mapping):
|
|
224
233
|
return part
|
|
225
234
|
|
|
235
|
+
elif isinstance(doc, str):
|
|
236
|
+
raise JsonPointerException("Cannot apply token '%s' to non-container type %s" % (part, type(doc)))
|
|
237
|
+
|
|
226
238
|
elif isinstance(doc, Sequence):
|
|
227
239
|
|
|
228
240
|
if part == '-':
|
|
229
241
|
return part
|
|
230
242
|
|
|
231
|
-
if not JsonPointer._RE_ARRAY_INDEX.
|
|
243
|
+
if not JsonPointer._RE_ARRAY_INDEX.fullmatch(str(part)):
|
|
232
244
|
raise JsonPointerException("'%s' is not a valid sequence index" % part)
|
|
233
245
|
|
|
234
|
-
|
|
246
|
+
try:
|
|
247
|
+
return int(part)
|
|
248
|
+
except ValueError as e:
|
|
249
|
+
raise JsonPointerException("Invalid sequence index: %s" % (e,))
|
|
235
250
|
|
|
236
251
|
elif hasattr(doc, '__getitem__'):
|
|
237
252
|
# Allow indexing via ducktyping
|
|
@@ -269,7 +284,10 @@ class JsonPointer:
|
|
|
269
284
|
return doc[part]
|
|
270
285
|
|
|
271
286
|
except KeyError:
|
|
272
|
-
|
|
287
|
+
if VERBOSE_EXCEPTIONS:
|
|
288
|
+
raise JsonPointerException("member '%s' not found in %s" % (part, doc))
|
|
289
|
+
else:
|
|
290
|
+
raise JsonPointerException("member '%s' not found" % (part,))
|
|
273
291
|
|
|
274
292
|
def contains(self, ptr):
|
|
275
293
|
""" Returns True if self contains the given ptr """
|
|
@@ -288,7 +306,7 @@ class JsonPointer:
|
|
|
288
306
|
else:
|
|
289
307
|
suffix_parts = suffix
|
|
290
308
|
try:
|
|
291
|
-
return
|
|
309
|
+
return self.__class__.from_parts(chain(self.parts, suffix_parts))
|
|
292
310
|
except: # noqa E722
|
|
293
311
|
raise JsonPointerException("Invalid suffix")
|
|
294
312
|
|
|
@@ -42,6 +42,7 @@ CLASSIFIERS = [
|
|
|
42
42
|
'Programming Language :: Python :: 3.11',
|
|
43
43
|
'Programming Language :: Python :: 3.12',
|
|
44
44
|
'Programming Language :: Python :: 3.13',
|
|
45
|
+
'Programming Language :: Python :: 3.14',
|
|
45
46
|
'Programming Language :: Python :: Implementation :: CPython',
|
|
46
47
|
'Programming Language :: Python :: Implementation :: PyPy',
|
|
47
48
|
'Topic :: Software Development :: Libraries',
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import doctest
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
5
9
|
import unittest
|
|
6
10
|
|
|
7
11
|
import jsonpointer
|
|
@@ -192,6 +196,31 @@ class ComparisonTests(unittest.TestCase):
|
|
|
192
196
|
ptr12e = self.ptr1 / ["a", "b"]
|
|
193
197
|
self.assertEqual(ptr12e.path, "/a/b/c/a/b")
|
|
194
198
|
|
|
199
|
+
def test_join_subclass(self):
|
|
200
|
+
class Pointer(JsonPointer):
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
class OtherPointer(JsonPointer):
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
ptr = Pointer("/a~1b")
|
|
207
|
+
suffixes = [JsonPointer("/m~0n"), Pointer("/m~0n"),
|
|
208
|
+
OtherPointer("/m~0n"), "/m~0n", ["m~n"]]
|
|
209
|
+
for suffix in suffixes:
|
|
210
|
+
with self.subTest(suffix=suffix):
|
|
211
|
+
joined = ptr.join(suffix)
|
|
212
|
+
self.assertIs(type(joined), Pointer)
|
|
213
|
+
self.assertEqual(joined.path, "/a~1b/m~0n")
|
|
214
|
+
self.assertEqual(ptr.path, "/a~1b")
|
|
215
|
+
|
|
216
|
+
def test_join_magic_subclass(self):
|
|
217
|
+
class Pointer(JsonPointer):
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
ptr = Pointer("/a") / JsonPointer("/b") / ["c"]
|
|
221
|
+
self.assertIs(type(ptr), Pointer)
|
|
222
|
+
self.assertEqual(ptr.path, "/a/b/c")
|
|
223
|
+
|
|
195
224
|
|
|
196
225
|
class WrongInputTests(unittest.TestCase):
|
|
197
226
|
|
|
@@ -215,6 +244,58 @@ class WrongInputTests(unittest.TestCase):
|
|
|
215
244
|
def test_invalid_escape(self):
|
|
216
245
|
self.assertRaises(JsonPointerException, JsonPointer, '/foo/bar~2')
|
|
217
246
|
|
|
247
|
+
def test_leading_zero(self):
|
|
248
|
+
doc = [0, 1, 2]
|
|
249
|
+
self.assertRaises(JsonPointerException, resolve_pointer, doc, '/01')
|
|
250
|
+
|
|
251
|
+
def test_string_not_indexable(self):
|
|
252
|
+
doc = {"foo": "should-not-be-indexable"}
|
|
253
|
+
self.assertRaises(JsonPointerException, resolve_pointer, doc, "/foo/0")
|
|
254
|
+
|
|
255
|
+
ptr = JsonPointer("/foo/0")
|
|
256
|
+
self.assertRaises(JsonPointerException, ptr.resolve, doc)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@unittest.skipUnless(hasattr(sys, 'get_int_max_str_digits'),
|
|
260
|
+
'Integer string conversion limits are not available')
|
|
261
|
+
class LargeIndexTests(unittest.TestCase):
|
|
262
|
+
|
|
263
|
+
def setUp(self):
|
|
264
|
+
limit = sys.get_int_max_str_digits()
|
|
265
|
+
if not limit:
|
|
266
|
+
self.skipTest('Integer string conversion limit is disabled')
|
|
267
|
+
self.token = '1' * (limit + 1)
|
|
268
|
+
self.pointer = '/' + self.token
|
|
269
|
+
|
|
270
|
+
def test_resolve_large_index(self):
|
|
271
|
+
self.assertRaises(JsonPointerException, resolve_pointer,
|
|
272
|
+
[0], self.pointer)
|
|
273
|
+
|
|
274
|
+
def test_resolve_large_index_default(self):
|
|
275
|
+
default = object()
|
|
276
|
+
self.assertIs(resolve_pointer([0], self.pointer, default), default)
|
|
277
|
+
self.assertIs(resolve_pointer({'items': [0]},
|
|
278
|
+
'/items' + self.pointer, default),
|
|
279
|
+
default)
|
|
280
|
+
|
|
281
|
+
def test_to_last_large_index(self):
|
|
282
|
+
self.assertRaises(JsonPointerException,
|
|
283
|
+
JsonPointer(self.pointer).to_last, [0])
|
|
284
|
+
|
|
285
|
+
def test_set_large_index(self):
|
|
286
|
+
for inplace in (True, False):
|
|
287
|
+
with self.subTest(inplace=inplace):
|
|
288
|
+
doc = [0]
|
|
289
|
+
self.assertRaises(JsonPointerException, set_pointer,
|
|
290
|
+
doc, self.pointer, 1, inplace=inplace)
|
|
291
|
+
self.assertEqual(doc, [0])
|
|
292
|
+
|
|
293
|
+
def test_large_numeric_object_member(self):
|
|
294
|
+
doc = {self.token: 'old'}
|
|
295
|
+
self.assertEqual(resolve_pointer(doc, self.pointer), 'old')
|
|
296
|
+
set_pointer(doc, self.pointer, 'new')
|
|
297
|
+
self.assertEqual(doc, {self.token: 'new'})
|
|
298
|
+
|
|
218
299
|
|
|
219
300
|
class ToLastTests(unittest.TestCase):
|
|
220
301
|
|
|
@@ -390,6 +471,135 @@ class AltTypesTests(unittest.TestCase):
|
|
|
390
471
|
self.assertRaises(JsonPointerException, resolve_pointer, doc, '/root/1/2/3/4')
|
|
391
472
|
|
|
392
473
|
|
|
474
|
+
class CommandLineTests(unittest.TestCase):
|
|
475
|
+
""" Tests the jsonpointer command line utility """
|
|
476
|
+
|
|
477
|
+
ROOT = os.path.dirname(os.path.abspath(__file__))
|
|
478
|
+
SCRIPT = os.path.join(ROOT, 'bin', 'jsonpointer')
|
|
479
|
+
|
|
480
|
+
A_OUT = '[1, 2, 3]\n'
|
|
481
|
+
B_OUT = '{"b": [1, 3, 4]}\n'
|
|
482
|
+
|
|
483
|
+
def setUp(self):
|
|
484
|
+
tmpdir = tempfile.TemporaryDirectory()
|
|
485
|
+
self.addCleanup(tmpdir.cleanup)
|
|
486
|
+
self.dir = tmpdir.name
|
|
487
|
+
|
|
488
|
+
self._write('a.json', '{ "a": [1, 2, 3] }')
|
|
489
|
+
self._write('b.json', '{ "a": {"b": [1, 3, 4]}, "b": 1 }')
|
|
490
|
+
self._write('ptr.txt', '/a\n')
|
|
491
|
+
|
|
492
|
+
def _write(self, name, content):
|
|
493
|
+
with open(os.path.join(self.dir, name), 'w') as f:
|
|
494
|
+
f.write(content)
|
|
495
|
+
|
|
496
|
+
def _run(self, *args):
|
|
497
|
+
env = dict(os.environ, PYTHONPATH=self.ROOT)
|
|
498
|
+
return subprocess.run([sys.executable, self.SCRIPT] + list(args),
|
|
499
|
+
cwd=self.dir, env=env, capture_output=True,
|
|
500
|
+
text=True)
|
|
501
|
+
|
|
502
|
+
def test_positional_pointer(self):
|
|
503
|
+
proc = self._run('/a', 'a.json', 'b.json')
|
|
504
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
505
|
+
self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
|
|
506
|
+
|
|
507
|
+
def test_positional_pointer_single_file(self):
|
|
508
|
+
proc = self._run('/a', 'a.json')
|
|
509
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
510
|
+
self.assertEqual(proc.stdout, self.A_OUT)
|
|
511
|
+
|
|
512
|
+
def test_pointer_file(self):
|
|
513
|
+
proc = self._run('-f', 'ptr.txt', 'a.json', 'b.json')
|
|
514
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
515
|
+
self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
|
|
516
|
+
|
|
517
|
+
def test_pointer_file_single_file(self):
|
|
518
|
+
proc = self._run('-f', 'ptr.txt', 'a.json')
|
|
519
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
520
|
+
self.assertEqual(proc.stdout, self.A_OUT)
|
|
521
|
+
|
|
522
|
+
def test_pointer_option(self):
|
|
523
|
+
proc = self._run('-p', '/a', 'a.json', 'b.json')
|
|
524
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
525
|
+
self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
|
|
526
|
+
|
|
527
|
+
def test_no_pointer(self):
|
|
528
|
+
proc = self._run('a.json')
|
|
529
|
+
self.assertEqual(proc.returncode, 2)
|
|
530
|
+
self.assertIn('a JSON pointer is required', proc.stderr)
|
|
531
|
+
|
|
532
|
+
def test_missing_pointer_multiple_files(self):
|
|
533
|
+
proc = self._run('a.json', 'b.json')
|
|
534
|
+
self.assertEqual(proc.returncode, 2)
|
|
535
|
+
self.assertIn("invalid JSON pointer 'a.json'", proc.stderr)
|
|
536
|
+
self.assertEqual(proc.stdout, '')
|
|
537
|
+
|
|
538
|
+
def test_invalid_pointer_file(self):
|
|
539
|
+
self._write('bad.txt', 'a\n')
|
|
540
|
+
proc = self._run('-f', 'bad.txt', 'a.json')
|
|
541
|
+
self.assertEqual(proc.returncode, 2)
|
|
542
|
+
self.assertIn("invalid JSON pointer 'a'", proc.stderr)
|
|
543
|
+
|
|
544
|
+
def test_empty_pointer(self):
|
|
545
|
+
proc = self._run('', 'a.json')
|
|
546
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
547
|
+
self.assertEqual(proc.stdout, '{"a": [1, 2, 3]}\n')
|
|
548
|
+
|
|
549
|
+
def test_unresolvable_pointer(self):
|
|
550
|
+
proc = self._run('/x', 'a.json')
|
|
551
|
+
self.assertEqual(proc.returncode, 1)
|
|
552
|
+
self.assertIn('Could not resolve pointer', proc.stderr)
|
|
553
|
+
self.assertEqual(proc.stdout, '')
|
|
554
|
+
|
|
555
|
+
def test_unresolvable_pointer_in_one_file(self):
|
|
556
|
+
# /b resolves in b.json only; a.json is still reported but b.json
|
|
557
|
+
# is processed
|
|
558
|
+
proc = self._run('/b', 'a.json', 'b.json')
|
|
559
|
+
self.assertEqual(proc.returncode, 1)
|
|
560
|
+
self.assertIn('Could not resolve pointer', proc.stderr)
|
|
561
|
+
self.assertEqual(proc.stdout, '1\n')
|
|
562
|
+
|
|
563
|
+
def test_pointer_file_requires_value(self):
|
|
564
|
+
proc = self._run('-f', '-p', '/a', 'a.json')
|
|
565
|
+
self.assertEqual(proc.returncode, 2)
|
|
566
|
+
self.assertIn('expected one argument', proc.stderr)
|
|
567
|
+
|
|
568
|
+
def test_pointer_and_pointer_file_exclusive(self):
|
|
569
|
+
proc = self._run('-p', '/a', '-f', 'ptr.txt', 'a.json')
|
|
570
|
+
self.assertEqual(proc.returncode, 2)
|
|
571
|
+
self.assertIn('not allowed with', proc.stderr)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class VerboseExceptionsTests(unittest.TestCase):
|
|
575
|
+
|
|
576
|
+
def setUp(self):
|
|
577
|
+
# Save original value and ensure verbose mode is on for each test
|
|
578
|
+
self._original = jsonpointer.VERBOSE_EXCEPTIONS
|
|
579
|
+
jsonpointer.VERBOSE_EXCEPTIONS = True
|
|
580
|
+
|
|
581
|
+
def tearDown(self):
|
|
582
|
+
jsonpointer.VERBOSE_EXCEPTIONS = self._original
|
|
583
|
+
|
|
584
|
+
def test_verbose_exception_includes_doc(self):
|
|
585
|
+
doc = {'foo': 1}
|
|
586
|
+
try:
|
|
587
|
+
resolve_pointer(doc, '/bar')
|
|
588
|
+
self.fail('Expected JsonPointerException')
|
|
589
|
+
except JsonPointerException as e:
|
|
590
|
+
self.assertIn(repr(doc), str(e))
|
|
591
|
+
|
|
592
|
+
def test_non_verbose_exception_excludes_doc(self):
|
|
593
|
+
doc = {'foo': 1}
|
|
594
|
+
jsonpointer.VERBOSE_EXCEPTIONS = False
|
|
595
|
+
try:
|
|
596
|
+
resolve_pointer(doc, '/bar')
|
|
597
|
+
self.fail('Expected JsonPointerException')
|
|
598
|
+
except JsonPointerException as e:
|
|
599
|
+
self.assertNotIn(repr(doc), str(e))
|
|
600
|
+
self.assertIn('bar', str(e))
|
|
601
|
+
|
|
602
|
+
|
|
393
603
|
def load_tests(loader, tests, ignore):
|
|
394
604
|
tests.addTests(doctest.DocTestSuite(jsonpointer))
|
|
395
605
|
return tests
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import argparse
|
|
5
|
-
import json
|
|
6
|
-
import sys
|
|
7
|
-
|
|
8
|
-
import jsonpointer
|
|
9
|
-
|
|
10
|
-
parser = argparse.ArgumentParser(
|
|
11
|
-
description='Resolve a JSON pointer on JSON files')
|
|
12
|
-
|
|
13
|
-
# Accept pointer as argument or as file
|
|
14
|
-
ptr_group = parser.add_mutually_exclusive_group(required=True)
|
|
15
|
-
|
|
16
|
-
ptr_group.add_argument('-f', '--pointer-file', type=argparse.FileType('r'),
|
|
17
|
-
nargs='?',
|
|
18
|
-
help='File containing a JSON pointer expression')
|
|
19
|
-
|
|
20
|
-
ptr_group.add_argument('POINTER', type=str, nargs='?',
|
|
21
|
-
help='A JSON pointer expression')
|
|
22
|
-
|
|
23
|
-
parser.add_argument('FILE', type=argparse.FileType('r'), nargs='+',
|
|
24
|
-
help='Files for which the pointer should be resolved')
|
|
25
|
-
parser.add_argument('--indent', type=int, default=None,
|
|
26
|
-
help='Indent output by n spaces')
|
|
27
|
-
parser.add_argument('-v', '--version', action='version',
|
|
28
|
-
version='%(prog)s ' + jsonpointer.__version__)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def main():
|
|
32
|
-
try:
|
|
33
|
-
resolve_files()
|
|
34
|
-
except KeyboardInterrupt:
|
|
35
|
-
sys.exit(1)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def parse_pointer(args):
|
|
39
|
-
if args.POINTER:
|
|
40
|
-
ptr = args.POINTER
|
|
41
|
-
elif args.pointer_file:
|
|
42
|
-
ptr = args.pointer_file.read().strip()
|
|
43
|
-
else:
|
|
44
|
-
parser.print_usage()
|
|
45
|
-
sys.exit(1)
|
|
46
|
-
|
|
47
|
-
return ptr
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def resolve_files():
|
|
51
|
-
""" Resolve a JSON pointer on JSON files """
|
|
52
|
-
args = parser.parse_args()
|
|
53
|
-
|
|
54
|
-
ptr = parse_pointer(args)
|
|
55
|
-
|
|
56
|
-
for f in args.FILE:
|
|
57
|
-
doc = json.load(f)
|
|
58
|
-
try:
|
|
59
|
-
result = jsonpointer.resolve_pointer(doc, ptr)
|
|
60
|
-
print(json.dumps(result, indent=args.indent))
|
|
61
|
-
except jsonpointer.JsonPointerException as e:
|
|
62
|
-
print('Could not resolve pointer: %s' % str(e), file=sys.stderr)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if __name__ == "__main__":
|
|
66
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|