8000 Adjustments and tests for jsonpath.py command line script · cdent/python-jsonpath-rw@60172ee · GitHub
[go: up one dir, main page]

Skip to content

Commit 60172ee

Browse files
committed
Adjustments and tests for jsonpath.py command line script
1 parent b01e2e5 commit 60172ee

File tree

7 files changed

+132
-34
lines changed

7 files changed

+132
-34
lines changed

jsonpath_rw/bin/__init__.py

Whitespace-only changes.

jsonpath_rw/bin/jsonpath.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,67 @@
55
# terms of the Do What The Fuck You Want To Public License, Version 2,
66
# as published by Sam Hocevar. See the COPYING file for more details.
77

8-
from jsonpath_rw import parse
8+
# Use modern Python
9+
from __future__ import unicode_literals, print_function, absolute_import
10+
11+
# Standard Library imports
912
import json
1013
import sys
1114
import glob
12-
if len(sys.argv) < 2:
13-
print("""usage: jsonpath.py expression [files]
15+
import argparse
16+
17+
# JsonPath-RW imports
18+
from jsonpath_rw import parse
1419

15-
The expression is JSONPath and can be:
20+
def find_matches_for_file(expr, f):
21+
return expr.find(json.load(f))
1622

17-
atomics:
18-
$ - root object
19-
`this` - current object
23+
def print_matches(matches):
24+
print('\n'.join(['{0}'.format(match.value) for match in matches]))
2025

21-
operators:
22-
path1.path2 - same as xpath /
23-
path1|path2 - union
24-
path1..path2 - somewhere in between
2526

26-
fiels:
27-
fieldname - field with name
28-
* - any field
29-
[_start_?:_end_?] - array slice
30-
[*] - any array index
31-
""")
32-
sys.exit(1)
27+
def main(*argv):
28+
parser = argparse.ArgumentParser(
29+
description='Search JSON files (or stdin) according to a JSONPath expression.',
30+
formatter_class=argparse.RawTextHelpFormatter,
31+
epilog="""
32+
Quick JSONPath reference (see more at https://github.com/kennknowles/python-jsonpath-rw)
3333
34-
expr = parse(sys.argv[1])
34+
atomics:
35+
$ - root object
36+
`this` - current object
3537
36-
def find_matches_for_file(f):
37-
return [unicode(match.value) for match in expr.find(json.load(f))]
38+
operators:
39+
path1.path2 - same as xpath /
40+
path1|path2 - union
41+
path1..path2 - somewhere in between
3842
39-
def print_matches(matches):
40-
print(u"\n".join(matches).encode("utf-8"))
41-
42-
if len(sys.argv) < 3:
43-
# stdin mode
44-
print_matches(find_matches_for_file(sys.stdin))
45-
else:
46-
# file paths mode
47-
for pattern in sys.argv[2:]:
48-
for filename in glob.glob(pattern):
49-
with open(filename) as f:
50-
print_matches(find_matches_for_file(f))
43+
fields:
44+
fieldname - field with name
45+
* - any field
46+
[_start_?:_end_?] - array slice
47+
[*] - any array index
48+
""")
49+
50+
51+
52+
parser.add_argument('expression', help='A JSONPath expression.')
53+
parser.add_argument('files', metavar='file', nargs='*', help='Files to search (if none, searches stdin)')
54+
55+
args = parser.parse_args(argv[1:])
56+
57+
expr = parse(args.expression)
58+
glob_patterns = args.files
59+
60+
if len(glob_patterns) == 0:
61+
# stdin mode
62+
print_matches(find_matches_for_file(expr, sys.stdin))
63+
else:
64+
# file paths mode
65+
for pattern in glob_patterns:
66+
for filename in glob.glob(pattern):
67+
with open(filename) as f:
68+
print_matches(find_matches_for_file(expr, f))
5169

70+
def entry_point():
71+
main(*sys.argv)

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
license='Apache 2.0',
1515
long_description=io.open('README.rst', encoding='utf-8').read(),
1616
packages = ['jsonpath_rw'],
17-
scripts = ['jsonpath_rw/bin/jsonpath.py'],
17+
entry_points = {
18+
'console_scripts': ['jsonpath.py = jsonpath_rw.bin.jsonpath:entry_point'],
19+
},
1820
test_suite = 'tests',
1921
install_requires = [ 'ply', 'decorator', 'six' ],
2022
classifiers = [

tests/bin/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Use modern python
2+
from __future__ import absolute_import, print_function, unicode_literals

tests/bin/test1.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"foo": {
3+
"baz": 1,
4+
"bizzle": {
5+
"baz": 2
6+
}
7+
}
8+
}

tests/bin/test2.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"foo": {
3+
"foo": {
4+
"baz": 3,
5+
"merp": {
6+
"baz": 4
7+
}
8+
}
9+
}
10+
}

tests/bin/test_jsonpath.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Use modern Python
2+
from __future__ import unicode_literals, print_function, absolute_import, division, generators, nested_scopes
3+
4+
# Standard library imports
5+
import unittest
6+
import logging
7+
import io
8+
import sys
9+
import os
10+
import json
11+
12+
from jsonpath_rw.bin.jsonpath import main
13+
14+
class TestJsonPathScript(unittest.TestCase):
15+
"""
16+
Tests for the jsonpath.py command line interface.
17+
"""
18+
19+
@classmethod
20+
def setup_class(cls):
21+
logging.basicConfig()
22+
23+
def setUp(self):
24+
self.input = io.StringIO()
25+
self.output = io.StringIO()
26+
self.saved_stdout = sys.stdout
27+
self.saved_stdin = sys.stdin
28+
sys.stdout = self.output
29+
sys.stdin = self.input
30+
31+
def tearDown(self):
32+
self.output.close()
33+
self.input.close()
34+
sys.stdout = self.saved_stdout
35+
sys.stdin = self.saved_stdin
36+
37+
def test_stdin_mode(self):
38+
# 'format' is a benign Python 2/3 way of ensuring it is a text type rather than binary
39+
self.input.write('{0}'.format(json.dumps({
40+
'foo': {
41+
'baz': 1,
42+
'bizzle': {
43+
'baz': 2
44+
}
45+
}
46+
})))
47+
self.input.seek(0)
48+
main('jsonpath.py', 'foo..baz')
49+
self.assertEqual(self.output.getvalue(), '1\n2\n')
50+
51+
def test_filename_mode(self):
52+
test1 = os.path.join(os.path.dirname(__file__), 'test1.json')
53+
test2 = os.path.join(os.path.dirname(__file__), 'test2.json')
54+
main('jsonpath.py', 'foo..baz', test1, test2)
55+
self.assertEqual(self.output.getvalue(), '1\n2\n3\n4\n')
56+

0 commit comments

Comments
 (0)
0