3636import unicodedata
3737import urllib .parse
3838from dataclasses import dataclass
39- from typing import Any , AnyStr , Mapping , NoReturn , Optional , Sequence , Callable , Type , Union
39+ from typing import Any , AnyStr , Mapping , NoReturn , Optional , Protocol , Sequence , Callable , Type , Union
4040
4141from jsonata import datetimeutils , jexception , parser , utils
4242
@@ -506,6 +506,18 @@ class RegexpMatch:
506506 index : int
507507 groups : Sequence [AnyStr ]
508508
509+ class CompiledPattern (Protocol ):
510+ """
511+ Structural type for a compiled regex: matches stdlib `re.Pattern`
512+ as well as whatever a pluggable regex_engine's compile() returns
513+ (e.g. a `google-re2` pattern object). See Functions.is_regex.
514+ """
515+
516+ def search (self , string : str ) -> Optional [Any ]: ...
517+ def finditer (self , string : str ) -> Any : ...
518+ def sub (self , repl : Any , string : str , count : int = 0 ) -> str : ...
519+ def split (self , string : str , maxsplit : int = 0 ) -> list [str ]: ...
520+
509521 #
510522 # Evaluate the matcher function against the str arg
511523 #
@@ -514,7 +526,7 @@ class RegexpMatch:
514526 # @returns {object} - structure that represents the match(es)
515527 #
516528 @staticmethod
517- def evaluate_matcher (matcher : re . Pattern , string : Optional [str ]) -> list [RegexpMatch ]:
529+ def evaluate_matcher (matcher : CompiledPattern , string : Optional [str ]) -> list [RegexpMatch ]:
518530 res = []
519531 matches = matcher .finditer (string )
520532 for m in matches :
@@ -537,7 +549,7 @@ def evaluate_matcher(matcher: re.Pattern, string: Optional[str]) -> list[RegexpM
537549 # @returns {Boolean} - true if str contains token
538550 #
539551 @staticmethod
540- def contains (string : Optional [str ], token : Union [None , str , re . Pattern ]) -> Optional [bool ]:
552+ def contains (string : Optional [str ], token : Union [None , str , CompiledPattern ]) -> Optional [bool ]:
541553 # undefined inputs always return undefined
542554 if string is None :
543555 return None
@@ -568,7 +580,7 @@ def contains(string: Optional[str], token: Union[None, str, re.Pattern]) -> Opti
568580 # @returns {Array} The array of match objects
569581 #
570582 @staticmethod
571- def match_ (string : Optional [str ], regex : Optional [re . Pattern ], limit : Optional [int ]) -> Optional [list [dict [str , Any ]]]:
583+ def match_ (string : Optional [str ], regex : Optional [CompiledPattern ], limit : Optional [int ]) -> Optional [list [dict [str , Any ]]]:
572584 # undefined inputs always return undefined
573585 if string is None :
574586 return None
@@ -636,7 +648,7 @@ def safe_replacement(in_: str) -> str:
636648 # @return
637649 #
638650 @staticmethod
639- def safe_replace_all (s : str , pattern : re . Pattern , replacement : Optional [Any ]) -> Optional [str ]:
651+ def safe_replace_all (s : str , pattern : CompiledPattern , replacement : Optional [Any ]) -> Optional [str ]:
640652
641653 if not (isinstance (replacement , str )):
642654 return Functions .safe_replace_all_fn (s , pattern , replacement )
@@ -696,7 +708,7 @@ def to_jsonata_match(mr: re.Match[str]) -> dict[str, list[str]]:
696708 # @return
697709 #
698710 @staticmethod
699- def safe_replace_all_fn (s : str , pattern : re . Pattern , fn : Optional [Any ]) -> str :
711+ def safe_replace_all_fn (s : str , pattern : CompiledPattern , fn : Optional [Any ]) -> str :
700712 def replace_fn (t ):
701713 res = Functions .func_apply (fn , [Functions .to_jsonata_match (t )])
702714 if isinstance (res , str ):
@@ -716,7 +728,7 @@ def replace_fn(t):
716728 # @return
717729 #
718730 @staticmethod
719- def safe_replace_first (s : str , pattern : re . Pattern , replacement : str ) -> Optional [str ]:
731+ def safe_replace_first (s : str , pattern : CompiledPattern , replacement : str ) -> Optional [str ]:
720732 replacement = Functions .safe_replacement (replacement )
721733 r = None
722734 for i in range (0 , 10 ):
@@ -744,7 +756,7 @@ def safe_replace_first(s: str, pattern: re.Pattern, replacement: str) -> Optiona
744756 return r
745757
746758 @staticmethod
747- def replace (string : Optional [str ], pattern : Union [str , re . Pattern ], replacement : Optional [Any ], limit : Optional [int ]) -> Optional [str ]:
759+ def replace (string : Optional [str ], pattern : Union [str , CompiledPattern ], replacement : Optional [Any ], limit : Optional [int ]) -> Optional [str ]:
748760 if string is None :
749761 return None
750762
@@ -938,7 +950,7 @@ def decode_url(string: Optional[str]) -> Optional[str]:
938950 return urllib .parse .unquote (string , errors = "strict" )
939951
940952 @staticmethod
941- def split (string : Optional [str ], pattern : Union [str , Optional [re . Pattern ]], limit : Optional [float ]) -> Optional [list [str ]]:
953+ def split (string : Optional [str ], pattern : Union [str , Optional [CompiledPattern ]], limit : Optional [float ]) -> Optional [list [str ]]:
942954 if string is None :
943955 return None
944956
0 commit comments