You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Non-singular queries and function well-typedness (#35)
* Enforce JSONPath singular query usage and function well-typedness
* Make standard fitler function type aware
* Enforce non-singular query and function comparison rules
* Add well-typedness test cases.
* Handle comparison expressions as JSONPath function arguments
* Handle nested function calls as arguments to JSONPath functions.
* Remove unnecessary flag on type-aware function implementations.
* Refactor filter expression comparisons.
* Add CLI option for disabling JSONPath expression type checks.
* Update docs with info about well-typed function extensions.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,13 +4,18 @@
4
4
5
5
**Breaking Changes**
6
6
7
+
- We now enforce JSONPath filter expression "well-typedness" by default. That is, filter expressions are checked at compile time according to the IETF JSONPath Draft function extension type system and rules regarding non-singular query usage. If an expression is deemed to not be well-typed, a `JSONPathTypeError` is raised. This can be disabled in Python JSONPath by setting the `well_typed` argument to `JSONPathEnvironment` to `False`, or using `--no-type-checks` on the command line.
7
8
- The JSONPath lexer now yields distinct tokens for single and double quoted string literals. This is so the parser can do a better job of detecting invalid escape sequences.
8
9
- Changed the canonical representation of a JSONPath string literal to use double quotes instead of single quotes.
10
+
- The built-in implementation of the standard `length()` filter function is now a class and is renamed to `jsonpath.function_extensions.Length`.
11
+
- The built-in implementation of the standard `value()` filter function is now a class and is renamed to `jsonpath.function_extensions.Value`.
9
12
10
13
**Fixes**
11
14
12
15
- We no longer silently ignore invalid escape sequences in JSONPath string literals. For example, `$['\"']` used to be OK, it now raises a `JSONPathSyntaxError`.
13
-
- Fixed parsing of JSONPath integer literals that use scientific notation.
16
+
- Fixed parsing of JSONPath integer literals that use scientific notation. Previously we raised a `JSONPathSyntaxError` for literals such as `1e2`.
17
+
- Fixed parsing of JSONPath comparison and logical expressions as filter function arguments. Previously we raised a `JSONPathSyntaxError` if a comparison or logical expression appeared as a filter function argument. Note that none of the built-in, standard filter functions accept arguments of `LogicalType`.
18
+
- Fixed parsing of nested JSONPath filter functions, where a function is used as an argument to another.
Add, remove or replace [filter functions](functions.md) by updating the [`function_extensions`](api.md#jsonpath.env.JSONPathEnvironment.function_extensions) attribute of a [`JSONPathEnvironment`](api.md#jsonpath.env.JSONPathEnvironment). It is a regular Python dictionary mapping filter function names to any [callable](https://docs.python.org/3/library/typing.html#typing.Callable), like a function or class with a `__call__` method.
43
43
44
+
### Type System for Function Expressions
45
+
46
+
[Section 2.4.1](https://datatracker.ietf.org/doc/html/draft-ietf-jsonpath-base-21#section-2.4.1) of the IETF JSONPath Draft specification defines a type system for function expressions and requires that we check that filter expressions are well-typed. With that in mind, you are encouraged to implement custom filter functions by extending [`jsonpath.function_extensions.FilterFunction`](api.md#jsonpath.function_extensions.FilterFunction), which forces you to be explicit about the [types](api.md#jsonpath.function_extensions.ExpressionType) of arguments the function extension accepts and the type of its return value.
47
+
48
+
!!! info
49
+
50
+
[`FilterFunction`](api.md#jsonpath.function_extensions.FilterFunction) was new in Python JSONPath version 0.10.0. Prior to that we did not enforce function expression well-typedness. To use any arbitrary [callable](https://docs.python.org/3/library/typing.html#typing.Callable) as a function extension - or if you don't want built-in filter functions to raise a `JSONPathTypeError` for function expressions that are not well-typed - set [`well_typed`](api.md#jsonpath.env.JSONPathEnvironment.well_typed) to `False` when constructing a [`JSONPathEnvironment`](api.md#jsonpath.env.JSONPathEnvironment).
51
+
44
52
### Example
45
53
46
54
As an example, we'll add a `min()` filter function, which will return the minimum of a sequence of values. If any of the values are not comparable, we'll return the special `undefined` value instead.
47
55
48
56
```python
49
57
from typing import Iterable
58
+
50
59
import jsonpath
60
+
from jsonpath.function_extensions import ExpressionType
61
+
from jsonpath.function_extensions import FilterFunction
51
62
52
63
53
-
defmin_filter(obj: object) -> object:
54
-
ifnotisinstance(obj, Iterable):
55
-
return jsonpath.UNDEFINED
64
+
classMinFilterFunction(FilterFunction):
65
+
"""A JSONPath function extension returning the minimum of a sequence."""
Now, when we use `env.finall()`, `env.finditer()` or `env.compile()`, our `min` function will be available for use in filter expressions.
@@ -117,7 +137,7 @@ env = MyEnv()
117
137
118
138
### Compile Time Validation
119
139
120
-
A functionextension's arguments can be validated at compile time by implementing the function as a class with a `__call__` method, and a `validate` method. `validate` will be called after parsing the function, giving you the opportunity to inspect its arguments and raise a `JSONPathTypeError` should any arguments be unacceptable. If defined, `validate` must take a reference to the current environment, an argument list and the token pointing to the start of the function call.
140
+
Calls to [type-aware](#type-system-for-function-expressions) function extension are validated at JSONPath compile-time automatically. If [`well_typed`](api.md#jsonpath.env.JSONPathEnvironment.well_typed) is set to `False` or a custom function extension does not inherit from [`FilterFunction`](api.md#jsonpath.function_extensions.FilterFunction), its arguments can be validated by implementing the function as a class with a `__call__` method, and a `validate` method. `validate` will be called after parsing the function, giving you the opportunity to inspect its arguments and raise a `JSONPathTypeError` should any arguments be unacceptable. If defined, `validate` must take a reference to the current environment, an argument list and the token pointing to the start of the function call.
Copy file name to clipboardExpand all lines: docs/cli.md
+15-7Lines changed: 15 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,6 @@
4
4
5
5
Python JSONPath includes a script called `json`, exposing [JSONPath](quickstart.md#findallpath-data), [JSON Pointer](quickstart.md#pointerresolvepointer-data) and [JSON Patch](quickstart.md#patchapplypatch-data) features on the command line. Use the `--version` argument to check the current version of Python JSONPath, and the `--help` argument to display command information.
6
6
7
-
8
7
```console
9
8
$ json --version
10
9
python-jsonpath, version 0.9.0
@@ -62,6 +61,7 @@ optional arguments:
62
61
-o OUTPUT, --output OUTPUT
63
62
File to write resulting objects to, as a JSON array. Defaults to the standard
@@ -73,14 +73,14 @@ These arguments apply to any subcommand and must be listed before the command.
73
73
Enable debugging. Display full stack traces, if available, when errors occur. Without the `--debug` option, the following example shows a short "json path syntax error" message.
Enable pretty formatting when outputting JSON. Adds newlines and indentation to output specified with the `-o` or `--output` option. Without the `--pretty` option, the following example output is on one line.
Disable decoding of UTF-16 escape sequences, including surrogate paris. This can improve performance if you know your paths and pointers don't contain UTF-16 escape sequences.
Disables JSONPath filter expression well-typedness checks. The well-typedness of a filter expression is defined by the IETF JSONPath Draft specification.
193
+
188
194
### `pointer`
189
195
190
196
Resolve a JSON Pointer against a JSON document. One of `-p`/`--pointer` or `-r`/`--pointer-file` must be given. `-p` being a JSON Pointer given on the command line as a string, `-r` being the path to a file containing a JSON Pointer.
@@ -236,6 +242,7 @@ The path to a file to write the resulting object to. If omitted or a hyphen (`-`
Copy file name to clipboardExpand all lines: docs/pointers.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,7 @@ JSON Pointer ([RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)) is a st
7
7
JSON Pointers are a fundamental part of JSON Patch ([RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)). Each patch operation must have at least one pointer, identifying the target value.
8
8
9
9
!!! note
10
+
10
11
We have extended RFC 6901 to handle our non-standard JSONPath [keys selector](syntax.md#keys-or) and index/property pointers from [Relative JSON Pointer](#torel).
Copy file name to clipboardExpand all lines: docs/syntax.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -187,15 +187,16 @@ This is a list of things that you might find in other JSONPath implementation th
187
187
188
188
And this is a list of areas where we deviate from the [IETF JSONPath draft](https://datatracker.ietf.org/doc/html/draft-ietf-jsonpath-base-13).
189
189
190
-
- We don't follow all "singular query" rules when evaluating a filter comparison. Note that we support membership operators `in` and `contains`, plus list literals, so testing non-singular queries for membership is OK.
191
-
- We don't yet force the result of some filter functions to be compared.
190
+
- The root token (default `$`) is optional and paths starting with a dot (`.`) are OK. `.thing` is the same as `$.thing`, as is `thing`, `$[thing]` and `$["thing"]`.
192
191
- Whitespace is mostly insignificant unless inside quotes.
193
-
- The root token (default `$`) is optional.
194
-
- Paths starting with a dot (`.`) are OK. `.thing` is the same as `$.thing`, as is `thing`, `$[thing]` and `$["thing"]`.
195
192
- The built-in `match()` and `search()` filter functions use Python's standard library `re` module, which, at least, doesn't support Unicode properties. We might add an implementation of `match()` and `search()` using the third party [regex](https://pypi.org/project/regex/) package in the future.
193
+
- We don't require property names to be quoted inside a bracketed selection, unless the name contains reserved characters.
194
+
- We don't require the recursive descent segment to have a selector. `$..` is equivalent to `$..*`.
195
+
- We support explicit comparisons to `undefined` as well as implicit existence tests.
196
196
197
197
And this is a list of features that are uncommon or unique to Python JSONPath.
198
198
199
+
- We support membership operators `in` and `contains`, plus list/array literals.
199
200
-`|` is a union operator, where matches from two or more JSONPaths are combined. This is not part of the Python API, but built-in to the JSONPath syntax.
200
201
-`&` is an intersection operator, where we exclude matches that don't exist in both left and right paths. This is not part of the Python API, but built-in to the JSONPath syntax.
201
202
-`#` is the current key/property or index identifier when filtering a mapping or sequence.
0 commit comments