-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enforce JSONPath singular query usage and function well-typedness
- Loading branch information
Showing
6 changed files
with
149 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"""Classes modeling the JSONPath spec type system for function extensions.""" | ||
from abc import ABC | ||
from abc import abstractmethod | ||
from enum import Enum | ||
from typing import Any | ||
from typing import List | ||
|
||
|
||
class ExpressionType(Enum): | ||
"""The type of a filter function argument or return value.""" | ||
|
||
VALUE = 1 | ||
LOGICAL = 2 | ||
NODES = 3 | ||
|
||
|
||
class FilterFunction(ABC): | ||
"""Base class for typed function extensions.""" | ||
|
||
@property | ||
@abstractmethod | ||
def arg_types(self) -> List[ExpressionType]: | ||
"""Argument types expected by the filter function.""" | ||
|
||
@property | ||
@abstractmethod | ||
def return_type(self) -> ExpressionType: | ||
"""The type of the value returned by the filter function.""" | ||
|
||
@abstractmethod | ||
def __call__(self, *args: Any, **kwds: Any) -> Any: | ||
"""Called the filter function.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,21 @@ | ||
"""The standard `match` function extension.""" | ||
|
||
import re | ||
from typing import TYPE_CHECKING | ||
from typing import List | ||
from typing import Pattern | ||
from typing import Union | ||
|
||
from jsonpath.exceptions import JSONPathTypeError | ||
from jsonpath.filter import RegexArgument | ||
from jsonpath.filter import StringLiteral | ||
from jsonpath.function_extensions import ExpressionType | ||
from jsonpath.function_extensions import FilterFunction | ||
|
||
if TYPE_CHECKING: | ||
from jsonpath.env import JSONPathEnvironment | ||
from jsonpath.token import Token | ||
|
||
class Match(FilterFunction): | ||
"""A type-aware implementation of the standard `match` function.""" | ||
|
||
class Match: | ||
"""The built-in `match` function. | ||
This implementation uses the standard _re_ module, without attempting to map | ||
I-Regexps to Python regex. | ||
""" | ||
|
||
def __call__(self, string: str, pattern: Union[str, Pattern[str], None]) -> bool: | ||
"""Return `True` if _pattern_ matches the given string, `False` otherwise.""" | ||
# The IETF JSONPath draft requires us to return `False` if the pattern was | ||
# invalid. We use `None` to indicate the pattern could not be compiled. | ||
if string is None or pattern is None: | ||
return False | ||
arg_types = [ExpressionType.VALUE, ExpressionType.VALUE] | ||
return_type = ExpressionType.LOGICAL | ||
|
||
def __call__(self, string: str, pattern: str) -> bool: | ||
"""Return `True` if _s_ matches _pattern_, or `False` otherwise.""" | ||
try: | ||
# re.fullmatch caches compiled patterns internally | ||
return bool(re.fullmatch(pattern, string)) | ||
except (TypeError, re.error): | ||
return False | ||
|
||
def validate( | ||
self, | ||
_: "JSONPathEnvironment", | ||
args: List[object], | ||
token: "Token", | ||
) -> List[object]: | ||
"""Function argument validation.""" | ||
if len(args) != 2: # noqa: PLR2004 | ||
raise JSONPathTypeError( | ||
f"{token.value!r} requires 2 arguments, found {len(args)}", | ||
token=token, | ||
) | ||
|
||
if isinstance(args[1], StringLiteral): | ||
try: | ||
return [args[0], RegexArgument(re.compile(args[1].value))] | ||
except re.error: | ||
return [None, None] | ||
|
||
return args |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters