1111from typing import Iterator
1212from typing import List
1313from typing import Optional
14+ from typing import Union
1415
1516from jsonpath .function_extensions .filter_function import ExpressionType
1617from jsonpath .function_extensions .filter_function import FilterFunction
4546from .selectors import Filter
4647from .selectors import IndexSelector
4748from .selectors import JSONPathSelector
49+ from .selectors import KeySelector
50+ from .selectors import KeysFilter
4851from .selectors import KeysSelector
4952from .selectors import PropertySelector
5053from .selectors import SliceSelector
6972from .token import TOKEN_INT
7073from .token import TOKEN_INTERSECTION
7174from .token import TOKEN_KEY
75+ from .token import TOKEN_KEY_NAME
7276from .token import TOKEN_KEYS
77+ from .token import TOKEN_KEYS_FILTER
7378from .token import TOKEN_LBRACKET
7479from .token import TOKEN_LE
7580from .token import TOKEN_LG
@@ -314,15 +319,15 @@ def parse_path(self, stream: TokenStream) -> Iterable[JSONPathSegment]:
314319 if _token .kind == TOKEN_DOT :
315320 stream .eat (TOKEN_DOT )
316321 # Assert that dot is followed by shorthand selector without whitespace.
317- stream .expect (TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS )
322+ stream .expect (TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS , TOKEN_KEY_NAME )
318323 token = stream .current ()
319- selectors = self .parse_selectors (stream )
324+ selectors = self .parse_selector (stream )
320325 yield JSONPathChildSegment (
321326 env = self .env , token = token , selectors = selectors
322327 )
323328 elif _token .kind == TOKEN_DDOT :
324329 token = stream .eat (TOKEN_DDOT )
325- selectors = self .parse_selectors (stream )
330+ selectors = self .parse_selector (stream )
326331 if not selectors :
327332 raise JSONPathSyntaxError (
328333 "missing selector for recursive descent segment" ,
@@ -332,22 +337,22 @@ def parse_path(self, stream: TokenStream) -> Iterable[JSONPathSegment]:
332337 env = self .env , token = token , selectors = selectors
333338 )
334339 elif _token .kind == TOKEN_LBRACKET :
335- selectors = self .parse_selectors (stream )
340+ selectors = self .parse_selector (stream )
336341 yield JSONPathChildSegment (
337342 env = self .env , token = _token , selectors = selectors
338343 )
339- elif _token .kind in {TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS }:
344+ elif _token .kind in {TOKEN_NAME , TOKEN_WILD , TOKEN_KEYS , TOKEN_KEY_NAME }:
340345 # A non-standard "bare" path. One without a leading identifier (`$`,
341346 # `@`, `^` or `_`).
342347 token = stream .current ()
343- selectors = self .parse_selectors (stream )
348+ selectors = self .parse_selector (stream )
344349 yield JSONPathChildSegment (
345350 env = self .env , token = token , selectors = selectors
346351 )
347352 else :
348353 break
349354
350- def parse_selectors (self , stream : TokenStream ) -> tuple [JSONPathSelector , ...]:
355+ def parse_selector (self , stream : TokenStream ) -> tuple [JSONPathSelector , ...]:
351356 token = stream .next ()
352357
353358 if token .kind == TOKEN_NAME :
@@ -359,6 +364,15 @@ def parse_selectors(self, stream: TokenStream) -> tuple[JSONPathSelector, ...]:
359364 ),
360365 )
361366
367+ if token .kind == TOKEN_KEY_NAME :
368+ return (
369+ KeySelector (
370+ env = self .env ,
371+ token = token ,
372+ key = token .value ,
373+ ),
374+ )
375+
362376 if token .kind == TOKEN_WILD :
363377 return (
364378 WildSelector (
@@ -432,6 +446,8 @@ def parse_bracketed_selection(self, stream: TokenStream) -> List[JSONPathSelecto
432446 stream .next ()
433447 elif token .kind == TOKEN_FILTER :
434448 selectors .append (self .parse_filter_selector (stream ))
449+ elif token .kind == TOKEN_KEYS_FILTER :
450+ selectors .append (self .parse_filter_selector (stream , keys = True ))
435451 elif token .kind == TOKEN_EOF :
436452 raise JSONPathSyntaxError ("unexpected end of query" , token = token )
437453 else :
@@ -514,8 +530,10 @@ def _maybe_index(token: Token) -> bool:
514530 step = step ,
515531 )
516532
517- def parse_filter_selector (self , stream : TokenStream ) -> Filter :
518- token = stream .eat (TOKEN_FILTER )
533+ def parse_filter_selector (
534+ self , stream : TokenStream , * , keys : bool = False
535+ ) -> Union [Filter , KeysFilter ]:
536+ token = stream .next ()
519537 expr = self .parse_filter_expression (stream )
520538
521539 if self .env .well_typed and isinstance (expr , FunctionExtension ):
@@ -536,6 +554,11 @@ def parse_filter_selector(self, stream: TokenStream) -> Filter:
536554 token = token ,
537555 )
538556
557+ if keys :
558+ return KeysFilter (
559+ env = self .env , token = token , expression = BooleanExpression (expr )
560+ )
561+
539562 return Filter (env = self .env , token = token , expression = BooleanExpression (expr ))
540563
541564 def parse_boolean (self , stream : TokenStream ) -> FilterExpression :
0 commit comments