Skip to content

Commit 2832305

Browse files
authored
Merge pull request #56 from jg-rp/doc-comments
Improve docs and doc comments.
2 parents 880c123 + 7867d6a commit 2832305

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

docs/syntax.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ This is a list of things that you might find in other JSONPath implementation th
199199
- We do not support arithmetic in filter expression.
200200
- We don't allow dotted array indices. An array index must be surrounded by square brackets.
201201
- Python JSONPath is strictly read only. There are no update "selectors", but we do provide methods for converting `JSONPathMatch` instances to `JSONPointer`s, and a `JSONPatch` builder API for modifying JSON-like data structures using said pointers.
202-
- We don't attempt to handle JSON documents without a top-level array or object (or equivalent Python objects).
203202

204203
And this is a list of areas where we deviate from [RFC 9535](https://datatracker.ietf.org/doc/html/rfc9535).
205204

@@ -209,14 +208,18 @@ And this is a list of areas where we deviate from [RFC 9535](https://datatracker
209208
- We don't require the recursive descent segment to have a selector. `$..` is equivalent to `$..*`.
210209
- We support explicit comparisons to `undefined` as well as implicit existence tests.
211210
- Float literals without a fractional digit are OK. `1.` is equivalent to `1.0`.
212-
- We treat literals (such as `true` and `false`) as valid "basic" expressions. So `$[?true || false]` does not raise a syntax error, which is and invalid query according to RFC 9535.
211+
- We treat literals (such as `true` and `false`) as valid "basic" expressions. For example, `$[?true || false]`, without an existence test or comparison either side of logical _or_, does not raise a syntax error.
212+
- By default, `and` is equivalent to `&&` and `or` is equivalent to `||`.
213+
- `none` and `nil` are aliases for `null`.
214+
- `null` (and its aliases), `true` and `false` can start with an upper or lower case letter.
213215

214216
And this is a list of features that are uncommon or unique to Python JSONPath.
215217

216218
- We support membership operators `in` and `contains`, plus list/array literals.
217219
- `|` 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.
218220
- `&` 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.
219221
- `#` is the current key/property or index identifier when filtering a mapping or sequence.
220-
- `_` is a filter context selector. With usage similar to `$` and `@`, `_` exposes arbitrary data from the `filter_context` argument to `findall()` and `finditer()`.
222+
- `_` is a filter context identifier. With usage similar to `$` and `@`, `_` exposes arbitrary data from the `filter_context` argument to `findall()` and `finditer()`.
221223
- `~` is a "keys" or "properties" selector.
222224
- `^` is a "fake root" identifier. It is equivalent to `$`, but wraps the target JSON document in a single-element array, so the root value can be conditionally selected with a filter selector.
225+
- `=~` is the the regex match operator, matching a value to a JavaScript-style regex literal.

jsonpath/selectors.py

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""JSONPath selector objects, as returned from `Parser.parse`."""
1+
"""JSONPath segments and selectors, as returned from `Parser.parse`."""
22
from __future__ import annotations
33

44
from abc import ABC
@@ -28,7 +28,7 @@
2828

2929

3030
class JSONPathSelector(ABC):
31-
"""Base class for all JSONPath selectors."""
31+
"""Base class for all JSONPath segments and selectors."""
3232

3333
__slots__ = ("env", "token")
3434

@@ -38,7 +38,17 @@ def __init__(self, *, env: JSONPathEnvironment, token: Token) -> None:
3838

3939
@abstractmethod
4040
def resolve(self, matches: Iterable[JSONPathMatch]) -> Iterable[JSONPathMatch]:
41-
"""Expand matches from previous JSONPath selectors in to new matches."""
41+
"""Apply the segment/selector to each node in _matches_.
42+
43+
Arguments:
44+
matches: Nodes matched by preceding segments/selectors. This is like
45+
a lazy _NodeList_, as described in RFC 9535, but each match carries
46+
more than the node's value and location.
47+
48+
Returns:
49+
The `JSONPathMatch` instances created by applying this selector to each
50+
preceding node.
51+
"""
4252

4353
@abstractmethod
4454
def resolve_async(
@@ -48,7 +58,7 @@ def resolve_async(
4858

4959

5060
class PropertySelector(JSONPathSelector):
51-
"""A JSONPath property."""
61+
"""A shorthand or bracketed property selector."""
5262

5363
__slots__ = ("name", "shorthand")
5464

@@ -115,7 +125,12 @@ async def resolve_async(
115125

116126

117127
class IndexSelector(JSONPathSelector):
118-
"""Dotted and bracketed sequence access by index."""
128+
"""Select an element from an array by index.
129+
130+
Considering we don't require mapping (JSON object) keys/properties to
131+
be quoted, and that we support mappings with numeric keys, we also check
132+
to see if the "index" is a mapping key, which is non-standard.
133+
"""
119134

120135
__slots__ = ("index", "_as_key")
121136

@@ -213,7 +228,10 @@ async def resolve_async(
213228

214229

215230
class KeysSelector(JSONPathSelector):
216-
"""Select an mapping's keys/properties."""
231+
"""Select mapping/object keys/properties.
232+
233+
NOTE: This is a non-standard selector.
234+
"""
217235

218236
__slots__ = ("shorthand",)
219237

@@ -354,7 +372,7 @@ async def resolve_async(
354372

355373

356374
class WildSelector(JSONPathSelector):
357-
"""Wildcard expansion selector."""
375+
"""Select all items from a sequence/array or values from a mapping/object."""
358376

359377
__slots__ = ("shorthand",)
360378

@@ -433,7 +451,10 @@ async def resolve_async(
433451

434452

435453
class RecursiveDescentSelector(JSONPathSelector):
436-
"""A JSONPath selector that visits all objects recursively."""
454+
"""A JSONPath selector that visits all nodes recursively.
455+
456+
NOTE: Strictly this is a "segment", not a "selector".
457+
"""
437458

438459
def __str__(self) -> str:
439460
return ".."
@@ -504,7 +525,10 @@ async def _alist(it: List[T]) -> AsyncIterable[T]:
504525

505526

506527
class ListSelector(JSONPathSelector):
507-
"""A JSONPath selector representing a list of properties, slices or indices."""
528+
"""A bracketed list of selectors, the results of which are concatenated together.
529+
530+
NOTE: Strictly this is a "segment", not a "selector".
531+
"""
508532

509533
__slots__ = ("items",)
510534

@@ -555,7 +579,7 @@ async def resolve_async(
555579

556580

557581
class Filter(JSONPathSelector):
558-
"""A filter selector."""
582+
"""Filter sequence/array items or mapping/object values with a filter expression."""
559583

560584
__slots__ = ("expression", "cacheable_nodes")
561585

@@ -713,7 +737,7 @@ async def resolve_async( # noqa: PLR0912
713737

714738

715739
class FilterContext:
716-
"""A filter expression context."""
740+
"""Contextual information and data for evaluating a filter expression."""
717741

718742
__slots__ = (
719743
"current_key",

0 commit comments

Comments
 (0)