simple_jsonpath 0.1.2__tar.gz → 0.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.
@@ -7,9 +7,6 @@ name: CI
7
7
 
8
8
  on:
9
9
  push:
10
- branches:
11
- - main
12
- - master
13
10
  tags:
14
11
  - '*'
15
12
  pull_request:
@@ -40,7 +37,7 @@ jobs:
40
37
  - uses: actions/checkout@v6
41
38
  - uses: actions/setup-python@v6
42
39
  with:
43
- python-version: 3.x
40
+ python-version: '>=3.10 <3.15'
44
41
  - name: Build wheels
45
42
  uses: PyO3/maturin-action@v1
46
43
  with:
@@ -71,7 +68,7 @@ jobs:
71
68
  - uses: actions/checkout@v6
72
69
  - uses: actions/setup-python@v6
73
70
  with:
74
- python-version: 3.x
71
+ python-version: '>=3.10 <3.15'
75
72
  - name: Build wheels
76
73
  uses: PyO3/maturin-action@v1
77
74
  with:
@@ -103,7 +100,7 @@ jobs:
103
100
  - uses: actions/checkout@v6
104
101
  - uses: actions/setup-python@v6
105
102
  with:
106
- python-version: 3.13
103
+ python-version: '>=3.10 <3.15'
107
104
  architecture: ${{ matrix.platform.python_arch }}
108
105
  - name: Build wheels
109
106
  uses: PyO3/maturin-action@v1
@@ -130,7 +127,7 @@ jobs:
130
127
  - uses: actions/checkout@v6
131
128
  - uses: actions/setup-python@v6
132
129
  with:
133
- python-version: 3.x
130
+ python-version: '>=3.10 <3.15'
134
131
  - name: Build wheels
135
132
  uses: PyO3/maturin-action@v1
136
133
  with:
@@ -70,3 +70,7 @@ docs/_build/
70
70
 
71
71
  # Pyenv
72
72
  .python-version
73
+
74
+ path.py
75
+ device.json
76
+ ben
@@ -231,17 +231,6 @@ dependencies = [
231
231
  "syn",
232
232
  ]
233
233
 
234
- [[package]]
235
- name = "pythonize"
236
- version = "0.28.0"
237
- source = "registry+https://github.com/rust-lang/crates.io-index"
238
- checksum = "0b79f670c9626c8b651c0581011b57b6ba6970bb69faf01a7c4c0cfc81c43f95"
239
- dependencies = [
240
- "pyo3",
241
- "serde",
242
- "serde_json",
243
- ]
244
-
245
234
  [[package]]
246
235
  name = "quote"
247
236
  version = "1.0.45"
@@ -427,13 +416,14 @@ dependencies = [
427
416
 
428
417
  [[package]]
429
418
  name = "simple_jsonpath"
430
- version = "0.1.2"
419
+ version = "0.3.3"
431
420
  dependencies = [
432
421
  "pyo3",
433
- "pythonize",
434
422
  "rstest",
423
+ "serde",
435
424
  "serde_json",
436
425
  "serde_json_path",
426
+ "serde_json_path_core",
437
427
  ]
438
428
 
439
429
  [[package]]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "simple_jsonpath"
3
- version = "0.1.2"
3
+ version = "0.3.3"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -12,9 +12,14 @@ crate-type = ["cdylib"]
12
12
  [dependencies]
13
13
  pyo3 = { version = "0.28.2", features = ["serde"] }
14
14
  serde_json_path = { git = "https://github.com/seojumper/serde_json_path.git", branch = "quotes" }
15
+ serde_json_path_core = { git = "https://github.com/seojumper/serde_json_path.git", branch = "quotes" }
15
16
  serde_json = "1.0.149"
16
- pythonize = { version = "0.28.0", features = ["serde_json"] }
17
+ serde = { version = "1.0.228", features = ["derive"] }
17
18
 
18
19
 
19
20
  [dev-dependencies]
20
21
  rstest = "0.26.1"
22
+
23
+ [profile.release]
24
+ lto = true
25
+ codegen-units = 1
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2026 Jeremy Spencer
2
+
3
+ Permission is hereby granted, free of charge, to any
4
+ person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the
6
+ Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge,
8
+ publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following
11
+ conditions:
12
+
13
+ The above copyright notice and this permission notice
14
+ shall be included in all copies or substantial portions
15
+ of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,324 @@
1
+ Metadata-Version: 2.4
2
+ Name: simple_jsonpath
3
+ Version: 0.3.3
4
+ Classifier: Intended Audience :: Developers
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Dist: orjson>=3.11.7
15
+ License-File: LICENSE
16
+ Summary: A simple - yet quick - JSONPath implementation for querying JSON data.
17
+ Keywords: jsonpath
18
+ Author-email: Jeremy Spencer <seojumper@gmail.com>
19
+ License-Expression: MIT
20
+ Requires-Python: >=3.10, <3.15
21
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
22
+ Project-URL: Homepage, https://github.com/seojumper/simple_jsonpath
23
+
24
+ # simple_jsonpath
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ pip install simple_jsonpath
30
+ ```
31
+
32
+ ## About
33
+
34
+ This module is a JSONPath [RFC9535 - JSONPath: Query Expressions for JSON](https://datatracker.ietf.org/doc/html/rfc9535) utility library.
35
+
36
+ ## Use
37
+
38
+ This module exposes a single simple type - a ***JsonPath*** which has two methods after instantiation.
39
+
40
+ - ***set_data()***: sets the data that will be queried against. If multiple queries will be performed against a single piece of JSON data, this helps with the type conversion cost involved. This function can be called with providing new data whenever the inner data held is wished to be changed while retaining already complied paths (useful for querying multiple similarly structured documents).
41
+ - ***find()***: given a path that is wished to be found in the previously set data, this function will perform the query logic. Mulitple calls to **find()** will query against the previously 'set' data.
42
+ - ***find_located()***: given a path that is wished to be found in the previously set data, this function can return a list of ***LocatedNode*** objects. Each ***LocatedNode*** object will have attributes related to the path where the node was located as well as their corresponding data. This method is slower than ***find()***, so should ideally only be used when path information for the found nodes is needed.
43
+ - ***child()***: spawns a child instance of the ***JsonPath*** object that does not inherit its data, but maintains shared mutable access to the collection of
44
+ compiled paths.
45
+
46
+ ## Child behavior
47
+
48
+ When the ***child()*** method is invoked, a child will be spawned from the current instance of the ***JsonPath*** object.
49
+
50
+ The child will not inherit the data from the parent, so a call to ***set_data()*** needa to be called on it for it to function.
51
+
52
+ It does however retain shared mutable access to the parent's collection of pre-parsed path objects which is shared across all spawned children.
53
+
54
+ This is useful for the pattern of:
55
+
56
+ 1. Searching a document for a path query.
57
+
58
+ 2. Then using those results returned as the basis of a new 'root element' for 'deeper' searches into a document.
59
+
60
+ Instead of assigning the query results to current instance, it can be beneficial to spawn a child for each result, and assign the result
61
+ data to the child or multiple children if more than one query result was returned.
62
+
63
+ With this pattern the 'base' parent object will automatically contain all parsed paths for the document that were searched by any descendant instance spawned from it, and children will have access to updates to the 'base' instance that any of their siblings make.
64
+
65
+ Then the 'base' parent object can be efficiently used on the next similarly structured document as all previously complied queries against the document are retained.
66
+
67
+ ## Examples
68
+
69
+ ### 'Find' Example
70
+
71
+ ```python
72
+ from simple_jsonpath import JsonPath
73
+
74
+
75
+ json_data = {
76
+ "address": {
77
+ "prefix-list": [
78
+ {
79
+ "prefix": "2001:db8::1/64",
80
+ "eui-64": [
81
+ None
82
+ ]
83
+ }
84
+ ],
85
+ "link-local-address": [
86
+ {
87
+ "address": "fe80::1",
88
+ "link-local": [
89
+ None
90
+ ]
91
+ }
92
+ ]
93
+ }
94
+ }
95
+
96
+ # Instantiates the primary class
97
+ finder = JsonPath()
98
+
99
+ # Sets the data that is desired to be queried against
100
+ finder.set_data(json_data)
101
+
102
+ # A path is provided to query against the 'set' data. The path is internally parsed > used to qeury against the 'set' dataset.
103
+ # Notice that this implementaion allows for escaping of specials characters shorthand path syntax with single or double quotes
104
+ results = finder.find("$.address.'prefix-list'[*].prefix")
105
+
106
+ for data in results:
107
+ # Access the found node.
108
+ print(f"{data}")
109
+ # 2001:db8::1/64
110
+
111
+ ```
112
+
113
+ The inner implementation stores previously parsed 'paths'. This allows repeatedly used paths to bypass the parsing step invovled.
114
+
115
+ This is ideal for situations where multiple similar JSON documents will be searched in succession.
116
+
117
+ The same ***JsonPath*** object can then be reused with new data sets by calling ***set_data()*** on it again, and any previously parsed paths by the object will be retained.
118
+
119
+ Only when moving onto data of differing structure would it be potentially advisable to instantiate a new ***JsonPath*** object.
120
+
121
+ ### 'Find Located' Example
122
+
123
+ ```python
124
+ from simple_jsonpath import JsonPath, LocatedNode
125
+
126
+
127
+ json_data = {
128
+ "items": [
129
+ {
130
+ "address": {
131
+ "prefix-list": [
132
+ {
133
+ "prefix": "2001:db8::1/64",
134
+ "eui-64": [
135
+ None
136
+ ]
137
+ }
138
+ ],
139
+ "link-local-address": [
140
+ {
141
+ "address": "fe80::1",
142
+ "link-local": [
143
+ None
144
+ ]
145
+ }
146
+ ]
147
+ }
148
+ },
149
+ {
150
+ "address": {
151
+ "prefix-list": [
152
+ {
153
+ "prefix": "2001:db8::1/64",
154
+ "eui-64": [
155
+ None
156
+ ]
157
+ }
158
+ ],
159
+ "link-local-address": [
160
+ {
161
+ "address": "fe80::1",
162
+ "link-local": [
163
+ None
164
+ ]
165
+ }
166
+ ]
167
+ }
168
+ }
169
+ ]
170
+ }
171
+
172
+ # Instantiates the primary class
173
+ finder = JsonPath()
174
+
175
+ # Sets the data that is desired to be queried against
176
+ finder.set_data(json_data)
177
+
178
+ # Now we are interested in the path information where matches were found as well as the data
179
+ results: list[LocatedNode] = finder.find_located("$.items[*].address.'prefix-list'[*].prefix")
180
+
181
+ # Iterate through each found LocatedNode object
182
+ for data in results:
183
+
184
+ # Print the normalized full path where the node was found
185
+ print(f"{data.full_path}")
186
+ # $['items'][0]['address']['prefix-list'][0]['prefix']
187
+
188
+ # Iterate over the components of the found path
189
+ # Returned elements will either be a 'str' for keys or 'int' for index values
190
+ print(f"{', '.join([str(component) for component in data.path_components])}")
191
+ # $, items, 0, adddress, prefix-list, 0, prefix
192
+
193
+ # Access the found node.
194
+ print(f"{data.node}")
195
+ # 2001:db8::1/64
196
+ ```
197
+
198
+ ### 'Child' Example
199
+
200
+ The child pattern can be useful for speeding up processing of multiple similarly structured documents to avoid overhead
201
+ of parsing the same query strings many times. Children are independent objects from the 'base' instancee, and children
202
+ can also spawn their own children.
203
+
204
+ ```python
205
+ from simple_jsonpath import JsonPath
206
+
207
+
208
+ json_data_1 = {
209
+ "items": [
210
+ {
211
+ "address": {
212
+ "prefix-list": [
213
+ {
214
+ "prefix": "2001:db8::1/64",
215
+ "eui-64": [
216
+ None
217
+ ]
218
+ }
219
+ ],
220
+ "link-local-address": [
221
+ {
222
+ "address": "fe80::1",
223
+ "link-local": [
224
+ None
225
+ ]
226
+ }
227
+ ]
228
+ }
229
+ },
230
+ {
231
+ "address": {
232
+ "prefix-list": [
233
+ {
234
+ "prefix": "2001:db8::1/64",
235
+ "eui-64": [
236
+ None
237
+ ]
238
+ }
239
+ ],
240
+ "link-local-address": [
241
+ {
242
+ "address": "fe80::1",
243
+ "link-local": [
244
+ None
245
+ ]
246
+ }
247
+ ]
248
+ }
249
+ }
250
+ ]
251
+ }
252
+
253
+ json_data_2 = {
254
+ "items": [
255
+ {
256
+ "address": {
257
+ "prefix-list": [
258
+ {
259
+ "prefix": "2001:db8::2/64",
260
+ "eui-64": [
261
+ None
262
+ ]
263
+ }
264
+ ]
265
+ }
266
+ },
267
+ {
268
+ "address": {
269
+ "prefix-list": [
270
+ {
271
+ "prefix": "2001:db8::2/64",
272
+ "eui-64": [
273
+ None
274
+ ]
275
+ }
276
+ ]
277
+ }
278
+ }
279
+ ]
280
+ }
281
+
282
+ def process_document(data, finder: JsonPath)
283
+ # The 'base' instance was instantiated outside of this fn below.
284
+
285
+ # Sets the data that is desired to be queried against
286
+ finder.set_data(json_data)
287
+
288
+ # Search for interested data. This pattern will be cached in the base instance, which will
289
+ # then be availble to all descendents of the base instance.
290
+ results: list[Any] = finder.find("$.items[*]")
291
+
292
+ # Iterate through each found result
293
+ for data in results:
294
+ # Spwn a child for each result
295
+ child = finder.child()
296
+ # Set the result data for the child
297
+ child.set_data(data)
298
+
299
+ # The first child that requests to find a pattern that has not yet been seen by the 'base' instance
300
+ # will parse the pattern and insert it into the 'base' instance's cache of compiled patterns.
301
+ #
302
+ # The 'base' instance now has the pre-compiled pattern should it need to search for it.
303
+ #
304
+ # All descendants of the 'base' instance now have access to the pre-compiled pattern to include
305
+ # the child that will be spawned on the next iteration of this loop which will allow it to
306
+ # process its own searches faster.
307
+ results = child.find("$.address.'prefix-list'[*]")
308
+
309
+ # .... further procesing....
310
+ all_documents = [json_data_1, json_data_2]
311
+
312
+ # create a single base JsonPath
313
+ finder = JsonPath()
314
+
315
+ for document in all_documents:
316
+ # For each document that will be processed known to be similar in structure > pass in the same 'base' instance.
317
+ #
318
+ # By the time it has processed the first document (depending on how deep either iteself, or its child instnaces were able to traverse the document)
319
+ # some/most/all of the possible paths that will need to be compiled have been. Which makes processing the next document in the series
320
+ # quicker.
321
+ process_document(document, finder)
322
+
323
+ ```
324
+