jsonpointer 3.1.1__tar.gz → 3.2.1__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.1/jsonpointer.egg-info → jsonpointer-3.2.1}/PKG-INFO +5 -4
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/README.md +4 -3
- jsonpointer-3.2.1/bin/jsonpointer +105 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1/jsonpointer.egg-info}/PKG-INFO +5 -4
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/jsonpointer.py +24 -6
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/tests.py +217 -0
- jsonpointer-3.1.1/bin/jsonpointer +0 -66
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/AUTHORS +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/LICENSE.txt +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/MANIFEST.in +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/jsonpointer.egg-info/SOURCES.txt +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/jsonpointer.egg-info/dependency_links.txt +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/jsonpointer.egg-info/top_level.txt +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/setup.cfg +0 -0
- {jsonpointer-3.1.1 → jsonpointer-3.2.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonpointer
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.1
|
|
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
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
44
44
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
45
|
-
[](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
|
|
46
|
+
[](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
|
-
*
|
|
60
|
-
* Coveralls: https://coveralls.io/
|
|
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
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
5
5
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
6
|
-
[](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
|
|
7
|
+
[](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
|
-
*
|
|
21
|
-
* Coveralls: https://coveralls.io/
|
|
21
|
+
* GitHub Actions: https://github.com/stefankoegl/python-json-pointer/actions
|
|
22
|
+
* Coveralls: https://coveralls.io/github/stefankoegl/python-json-pointer
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
# 2026-10-11: Trim file line endings without changing member names.
|
|
59
|
+
ptr = args.pointer_file.read().rstrip("\r\n")
|
|
60
|
+
|
|
61
|
+
elif args.POINTER is not None:
|
|
62
|
+
ptr = args.POINTER
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
parser.error('a JSON pointer is required (POINTER, -p or -f)')
|
|
66
|
+
|
|
67
|
+
# Catches e.g. `jsonpointer a.json b.json`, where the pointer was omitted
|
|
68
|
+
# and the first file would otherwise be used as the pointer
|
|
69
|
+
try:
|
|
70
|
+
jsonpointer.JsonPointer(ptr)
|
|
71
|
+
except jsonpointer.JsonPointerException as e:
|
|
72
|
+
parser.error('invalid JSON pointer %r: %s' % (ptr, e))
|
|
73
|
+
|
|
74
|
+
return ptr, files
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def resolve_files():
|
|
78
|
+
""" Resolve a JSON pointer on JSON files
|
|
79
|
+
|
|
80
|
+
Returns False if the pointer could not be resolved on at least one
|
|
81
|
+
file. """
|
|
82
|
+
args = parser.parse_args()
|
|
83
|
+
|
|
84
|
+
ptr, files = parse_pointer(args)
|
|
85
|
+
ok = True
|
|
86
|
+
|
|
87
|
+
for name in files:
|
|
88
|
+
try:
|
|
89
|
+
f = argparse.FileType('r')(name)
|
|
90
|
+
except argparse.ArgumentTypeError as e:
|
|
91
|
+
parser.error(str(e))
|
|
92
|
+
|
|
93
|
+
doc = json.load(f)
|
|
94
|
+
try:
|
|
95
|
+
result = jsonpointer.resolve_pointer(doc, ptr)
|
|
96
|
+
print(json.dumps(result, indent=args.indent))
|
|
97
|
+
except jsonpointer.JsonPointerException as e:
|
|
98
|
+
print('Could not resolve pointer: %s' % str(e), file=sys.stderr)
|
|
99
|
+
ok = False
|
|
100
|
+
|
|
101
|
+
return ok
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonpointer
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.1
|
|
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
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
44
44
|
[](https://pypi.python.org/pypi/jsonpointer/)
|
|
45
|
-
[](https://github.com/stefankoegl/python-json-pointer/actions/workflows/test.yaml)
|
|
46
|
+
[](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
|
-
*
|
|
60
|
-
* Coveralls: https://coveralls.io/
|
|
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.
|
|
35
|
+
__version__ = '3.2.1'
|
|
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,6 +232,9 @@ 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 == '-':
|
|
@@ -231,7 +243,10 @@ class JsonPointer:
|
|
|
231
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
|
|
|
@@ -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
|
|
|
@@ -219,6 +248,54 @@ class WrongInputTests(unittest.TestCase):
|
|
|
219
248
|
doc = [0, 1, 2]
|
|
220
249
|
self.assertRaises(JsonPointerException, resolve_pointer, doc, '/01')
|
|
221
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
|
+
|
|
222
299
|
|
|
223
300
|
class ToLastTests(unittest.TestCase):
|
|
224
301
|
|
|
@@ -394,6 +471,146 @@ class AltTypesTests(unittest.TestCase):
|
|
|
394
471
|
self.assertRaises(JsonPointerException, resolve_pointer, doc, '/root/1/2/3/4')
|
|
395
472
|
|
|
396
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
|
+
# 2026-10-11: A pointer file's trailing spaces are part of its member name.
|
|
518
|
+
def test_pointer_file_preserves_member_whitespace(self):
|
|
519
|
+
self._write('space.json', '{"a ": "expected", "a": "wrong"}')
|
|
520
|
+
for newline in ('', '\n', '\r\n'):
|
|
521
|
+
with self.subTest(newline=newline):
|
|
522
|
+
with open(os.path.join(self.dir, 'space.txt'), 'w', newline='') as f:
|
|
523
|
+
f.write('/a ' + newline)
|
|
524
|
+
proc = self._run('-f', 'space.txt', 'space.json')
|
|
525
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
526
|
+
self.assertEqual(proc.stdout, '"expected"\n')
|
|
527
|
+
|
|
528
|
+
def test_pointer_file_single_file(self):
|
|
529
|
+
proc = self._run('-f', 'ptr.txt', 'a.json')
|
|
530
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
531
|
+
self.assertEqual(proc.stdout, self.A_OUT)
|
|
532
|
+
|
|
533
|
+
def test_pointer_option(self):
|
|
534
|
+
proc = self._run('-p', '/a', 'a.json', 'b.json')
|
|
535
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
536
|
+
self.assertEqual(proc.stdout, self.A_OUT + self.B_OUT)
|
|
537
|
+
|
|
538
|
+
def test_no_pointer(self):
|
|
539
|
+
proc = self._run('a.json')
|
|
540
|
+
self.assertEqual(proc.returncode, 2)
|
|
541
|
+
self.assertIn('a JSON pointer is required', proc.stderr)
|
|
542
|
+
|
|
543
|
+
def test_missing_pointer_multiple_files(self):
|
|
544
|
+
proc = self._run('a.json', 'b.json')
|
|
545
|
+
self.assertEqual(proc.returncode, 2)
|
|
546
|
+
self.assertIn("invalid JSON pointer 'a.json'", proc.stderr)
|
|
547
|
+
self.assertEqual(proc.stdout, '')
|
|
548
|
+
|
|
549
|
+
def test_invalid_pointer_file(self):
|
|
550
|
+
self._write('bad.txt', 'a\n')
|
|
551
|
+
proc = self._run('-f', 'bad.txt', 'a.json')
|
|
552
|
+
self.assertEqual(proc.returncode, 2)
|
|
553
|
+
self.assertIn("invalid JSON pointer 'a'", proc.stderr)
|
|
554
|
+
|
|
555
|
+
def test_empty_pointer(self):
|
|
556
|
+
proc = self._run('', 'a.json')
|
|
557
|
+
self.assertEqual(proc.returncode, 0, proc.stderr)
|
|
558
|
+
self.assertEqual(proc.stdout, '{"a": [1, 2, 3]}\n')
|
|
559
|
+
|
|
560
|
+
def test_unresolvable_pointer(self):
|
|
561
|
+
proc = self._run('/x', 'a.json')
|
|
562
|
+
self.assertEqual(proc.returncode, 1)
|
|
563
|
+
self.assertIn('Could not resolve pointer', proc.stderr)
|
|
564
|
+
self.assertEqual(proc.stdout, '')
|
|
565
|
+
|
|
566
|
+
def test_unresolvable_pointer_in_one_file(self):
|
|
567
|
+
# /b resolves in b.json only; a.json is still reported but b.json
|
|
568
|
+
# is processed
|
|
569
|
+
proc = self._run('/b', 'a.json', 'b.json')
|
|
570
|
+
self.assertEqual(proc.returncode, 1)
|
|
571
|
+
self.assertIn('Could not resolve pointer', proc.stderr)
|
|
572
|
+
self.assertEqual(proc.stdout, '1\n')
|
|
573
|
+
|
|
574
|
+
def test_pointer_file_requires_value(self):
|
|
575
|
+
proc = self._run('-f', '-p', '/a', 'a.json')
|
|
576
|
+
self.assertEqual(proc.returncode, 2)
|
|
577
|
+
self.assertIn('expected one argument', proc.stderr)
|
|
578
|
+
|
|
579
|
+
def test_pointer_and_pointer_file_exclusive(self):
|
|
580
|
+
proc = self._run('-p', '/a', '-f', 'ptr.txt', 'a.json')
|
|
581
|
+
self.assertEqual(proc.returncode, 2)
|
|
582
|
+
self.assertIn('not allowed with', proc.stderr)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
class VerboseExceptionsTests(unittest.TestCase):
|
|
586
|
+
|
|
587
|
+
def setUp(self):
|
|
588
|
+
# Save original value and ensure verbose mode is on for each test
|
|
589
|
+
self._original = jsonpointer.VERBOSE_EXCEPTIONS
|
|
590
|
+
jsonpointer.VERBOSE_EXCEPTIONS = True
|
|
591
|
+
|
|
592
|
+
def tearDown(self):
|
|
593
|
+
jsonpointer.VERBOSE_EXCEPTIONS = self._original
|
|
594
|
+
|
|
595
|
+
def test_verbose_exception_includes_doc(self):
|
|
596
|
+
doc = {'foo': 1}
|
|
597
|
+
try:
|
|
598
|
+
resolve_pointer(doc, '/bar')
|
|
599
|
+
self.fail('Expected JsonPointerException')
|
|
600
|
+
except JsonPointerException as e:
|
|
601
|
+
self.assertIn(repr(doc), str(e))
|
|
602
|
+
|
|
603
|
+
def test_non_verbose_exception_excludes_doc(self):
|
|
604
|
+
doc = {'foo': 1}
|
|
605
|
+
jsonpointer.VERBOSE_EXCEPTIONS = False
|
|
606
|
+
try:
|
|
607
|
+
resolve_pointer(doc, '/bar')
|
|
608
|
+
self.fail('Expected JsonPointerException')
|
|
609
|
+
except JsonPointerException as e:
|
|
610
|
+
self.assertNotIn(repr(doc), str(e))
|
|
611
|
+
self.assertIn('bar', str(e))
|
|
612
|
+
|
|
613
|
+
|
|
397
614
|
def load_tests(loader, tests, ignore):
|
|
398
615
|
tests.addTests(doctest.DocTestSuite(jsonpointer))
|
|
399
616
|
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
|