@@ -683,6 +683,43 @@ def is_primitive_container(obj: Any) -> bool:
683
683
return is_primitive_list (obj ) or is_primitive_dict (obj )
684
684
685
685
686
+ def maybe_escape (value : Any ) -> Any :
687
+ """Escape interpolation strings and return other values unchanged.
688
+
689
+ When the input value is an interpolation string, the returned value is such that
690
+ it yields the original input string when resolved.
691
+ """
692
+ if not isinstance (value , str ) or not _is_interpolation_string (
693
+ value , strict_interpolation_validation = False
694
+ ):
695
+ return value
696
+ start = 0
697
+ tokens = []
698
+ while True :
699
+ # Find next ${ that needs escaping.
700
+ first_inter = value .find ("${" , start )
701
+ if first_inter < 0 :
702
+ tokens .append (value [start :]) # ensure we keep the end of the string
703
+ break
704
+ # Any backslash that comes before ${ will need to be escaped as well.
705
+ count_esc = 0
706
+ while (
707
+ first_inter - count_esc - 1 >= 0
708
+ and value [first_inter - count_esc - 1 ] == "\\ "
709
+ ):
710
+ count_esc += 1
711
+ tokens += [
712
+ # Characters that need not be changed.
713
+ value [start : first_inter - count_esc ],
714
+ # Escaped backslashes before the interpolation.
715
+ "\\ " * (count_esc * 2 ),
716
+ # Escaped interpolation.
717
+ "\\ ${" ,
718
+ ]
719
+ start = first_inter + 2
720
+ return "" .join (tokens )
721
+
722
+
686
723
def get_list_element_type (ref_type : Optional [Type [Any ]]) -> Any :
687
724
args = getattr (ref_type , "__args__" , None )
688
725
if ref_type is not List and args is not None and args [0 ]:
0 commit comments