Skip to content

feat: add optional kw-arg 'filtered=True' in Name #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ init:
@echo ">> installing $(if $(EXTRAS),\"$(EXTRAS)\" ,)dependencies by pdm"
$(if $(PYTHON),pdm use -f $(PYTHON),)
pdm info && pdm info --env
pdm sync -v $(EXTRAS_ARGS) $(DEV_EXTRAS_ARGS)
pdm sync --no-editable -v $(EXTRAS_ARGS) $(DEV_EXTRAS_ARGS)
pdm config -l use_venv true

deinit:
Expand Down
23 changes: 19 additions & 4 deletions jsonpath/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,25 +450,40 @@ class Name(Expr):

"""

def __init__(self, name: Optional[str] = None) -> None:
def __init__(self, name: Optional[str] = None, filtered: bool = True) -> None:
super().__init__()
self.name = name
self.filtered = filtered
assert (name, filtered) != (None, False)

def _get_partial_expression(self) -> str:
if self.name is None:
return "*"

return self.name
name = self.name
if name in ("*", "$", "@"):
name = repr(name)

if self.filtered:
return name
else:
return f"?{name}"

def find(self, element: Any) -> List[Any]:
if not isinstance(element, dict):
raise JSONPathFindError
if not self.filtered and self.left is not None:
return [None]
else:
raise JSONPathFindError

if self.name is None:
return list(element.values())

if self.name not in element:
raise JSONPathFindError
if self.filtered:
raise JSONPathFindError
else:
return [None]

return [element[self.name]]

Expand Down
17 changes: 11 additions & 6 deletions jsonpath/grammar.lark
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
start: path
| funccall
| func_call

COLON: ":"
slice: [expr] COLON [expr] COLON [expr] -> three_fields_slice
Expand All @@ -16,10 +16,15 @@ STAR: "*"
DOT: "."
DOUBLE_DOT.2: ".."

identifier: CNAME | STRING
identifier_filtered: CNAME | STRING
QUESTION_MARK: "?"
identifier_unfiltered: QUESTION_MARK identifier_filtered
identifier: identifier_unfiltered | identifier_filtered
self: "@"
root: "$"
first_path: CNAME
cname_unfiltered: QUESTION_MARK CNAME
cname_filterable: CNAME | cname_unfiltered
first_path: cname_filterable
| self
| root

Expand All @@ -44,7 +49,7 @@ OR: "or"
predicate: "[" (expr | slice | STAR) "]"

action_separate_with_dot: DOUBLE_DOT predicate -> search_with_predicate
| DOUBLE_DOT identifier -> search_with_identifier
| DOUBLE_DOT identifier_filtered -> search_with_identifier
| DOT identifier -> chain_with_identifier
| DOT STAR -> chain_with_star

Expand All @@ -56,12 +61,12 @@ path_with_action: atom action_separate_with_dot

args: args "," expr -> multi_args
| expr -> single_arg
funccall: CNAME "(" args ")"
func_call: CNAME "(" args ")"
| CNAME "()"

?atom: value
| path
| funccall
| func_call
| "(" expr ")" -> parenthesized_expr

?expr: test
Expand Down
16 changes: 14 additions & 2 deletions jsonpath/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,16 @@ def NUMBER(self, number: str) -> Union[int, float]:
def STRING(self, quoted_string: str) -> str:
return quoted_string[1:-1]

def identifier(self, string: str) -> Name:
def identifier(self, name: Name) -> Name:
return name

def identifier_filtered(self, string: str) -> Name:
return Name(string)

def identifier_unfiltered(self, question_mark: Literal["?"], name: Name) -> Name:
name.filtered = False
return name

def STAR(self, star_: Literal["*"]) -> None:
return None

Expand Down Expand Up @@ -108,6 +115,11 @@ def comparison_expr(

return left.chain(rv)

cname_filterable = identifier

def cname_unfiltered(self, question_mark: Literal["?"], string: str) -> Name:
return Name(string, filtered=False)

def first_path(self, expr_or_str: Union[Expr, str]) -> Expr:
if isinstance(expr_or_str, str):
return Name(expr_or_str)
Expand Down Expand Up @@ -157,7 +169,7 @@ def three_fields_slice(
) -> Slice:
return Slice(start=first_field, stop=second_field, step=third_field)

def funccall(self, name: str, args: T_ARGS = tuple()) -> Function:
def func_call(self, name: str, args: T_ARGS = tuple()) -> Function:
if name == "key":
return Key(*args)
elif name == "contains":
Expand Down
7 changes: 5 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def coverage_test(session, parser_backend):
session.run(
"pdm",
"sync",
"--no-editable",
"-v",
"-G",
"test",
Expand All @@ -59,7 +60,7 @@ def coverage_test(session, parser_backend):

@nox.session(python=pythons, reuse_venv=True)
def coverage_report(session):
session.run("pdm", "sync", "-v", "-G", "test", external=True)
session.run("pdm", "sync", "--no-editable", "-v", "-G", "test", external=True)
session.run("coverage", "report")
session.run("coverage", "xml")
session.run("coverage", "html")
Expand All @@ -77,7 +78,9 @@ def build(session):

@nox.session(reuse_venv=True)
def build_readme(session):
session.run("pdm", "sync", "-v", "-G", "build_readme", external=True)
session.run(
"pdm", "sync", "--no-editable", "-v", "-G", "build_readme", external=True
)
session.run(
"python", "scripts/build_readme.py", "README.template.rst", "README.rst"
)
Loading