11import random
2+ import re
23import string
34import sys
45import typing as t
@@ -53,6 +54,7 @@ def format_interpolation(interpolation: Interpolation) -> object:
5354
5455_PLACEHOLDER_PREFIX = f"t🐍-{ '' .join (random .choices (string .ascii_lowercase , k = 4 ))} -"
5556_PP_LEN = len (_PLACEHOLDER_PREFIX )
57+ _PLACEHOLDER_PATTERN = re .compile (re .escape (_PLACEHOLDER_PREFIX ) + r"(\d+)" )
5658
5759
5860def _placeholder (i : int ) -> str :
@@ -65,6 +67,47 @@ def _placholder_index(s: str) -> int:
6567 return int (s [_PP_LEN :])
6668
6769
70+ def _replace_placeholders_in_string (
71+ value : str , interpolations : tuple [Interpolation , ...]
72+ ) -> object :
73+ """Replace any placeholders embedded within a string attribute value."""
74+ segments : list [tuple [str , object ]] = []
75+ has_static_content = False
76+ last_index = 0
77+
78+ for match in _PLACEHOLDER_PATTERN .finditer (value ):
79+ if match .start () > last_index :
80+ static_segment = value [last_index : match .start ()]
81+ segments .append (("static" , static_segment ))
82+ if static_segment :
83+ has_static_content = True
84+
85+ index = int (match .group (1 ))
86+ interpolation = interpolations [index ]
87+ formatted = format_interpolation (interpolation )
88+ segments .append (("dynamic" , formatted ))
89+ last_index = match .end ()
90+
91+ if last_index < len (value ):
92+ static_segment = value [last_index :]
93+ segments .append (("static" , static_segment ))
94+ if static_segment :
95+ has_static_content = True
96+
97+ if not segments :
98+ return value
99+
100+ dynamic_segments = [segment for segment in segments if segment [0 ] == "dynamic" ]
101+
102+ if not has_static_content and len (dynamic_segments ) == 1 and len (segments ) == 1 :
103+ return dynamic_segments [0 ][1 ]
104+
105+ return "" .join (
106+ segment [1 ] if segment [0 ] == "static" else str (segment [1 ])
107+ for segment in segments
108+ )
109+
110+
68111def _instrument (
69112 strings : tuple [str , ...], callable_infos : tuple [CallableInfo | None , ...]
70113) -> t .Iterable [str ]:
@@ -256,13 +299,22 @@ def _substitute_interpolated_attrs(
256299 """
257300 new_attrs : dict [str , object | None ] = {}
258301 for key , value in attrs .items ():
259- if value and value .startswith (_PLACEHOLDER_PREFIX ):
260- # Interpolated attribute value
261- index = _placholder_index (value )
262- interpolation = interpolations [index ]
263- interpolated_value = format_interpolation (interpolation )
264- new_attrs [key ] = interpolated_value
265- elif key .startswith (_PLACEHOLDER_PREFIX ):
302+ if isinstance (value , str ):
303+ matches = tuple (_PLACEHOLDER_PATTERN .finditer (value ))
304+ if matches :
305+ if len (matches ) == 1 :
306+ match = matches [0 ]
307+ if match .start () == 0 and match .end () == len (value ):
308+ index = int (match .group (1 ))
309+ interpolation = interpolations [index ]
310+ interpolated_value = format_interpolation (interpolation )
311+ new_attrs [key ] = interpolated_value
312+ continue
313+
314+ new_attrs [key ] = _replace_placeholders_in_string (value , interpolations )
315+ continue
316+
317+ if key .startswith (_PLACEHOLDER_PREFIX ):
266318 # Spread attributes
267319 index = _placholder_index (key )
268320 interpolation = interpolations [index ]
0 commit comments