1010
1111from black ._width_table import WIDTH_TABLE
1212from black .mode import Mode , Preview
13- from blib2to3 .pytree import Leaf
13+ from blib2to3 .pgen2 import token
14+ from blib2to3 .pytree import Leaf , Node
1415
1516STRING_PREFIX_CHARS : Final = "fturbFTURB" # All possible string prefix characters.
1617STRING_PREFIX_RE : Final = re .compile (
@@ -330,13 +331,7 @@ def normalize_fstring_quotes(
330331 return middles , new_quote
331332
332333
333- def normalize_unicode_escape_sequences (leaf : Leaf ) -> None :
334- """Replace hex codes in Unicode escape sequences with lowercase representation."""
335- text = leaf .value
336- prefix = get_string_prefix (text )
337- if "r" in prefix .lower ():
338- return
339-
334+ def _normalize_unicode_escape_sequences_in_text (text : str ) -> str :
340335 def replace (m : Match [str ]) -> str :
341336 groups = m .groupdict ()
342337 back_slashes = groups ["backslashes" ]
@@ -358,7 +353,67 @@ def replace(m: Match[str]) -> str:
358353 # \N{}
359354 return back_slashes + "N{" + groups ["N" ].upper () + "}"
360355
361- leaf .value = re .sub (UNICODE_ESCAPE_RE , replace , text )
356+ return re .sub (UNICODE_ESCAPE_RE , replace , text )
357+
358+
359+ def normalize_unicode_escape_sequences (leaf : Leaf ) -> None :
360+ """Replace hex codes in Unicode escape sequences with lowercase representation."""
361+ text = leaf .value
362+ prefix = get_string_prefix (text )
363+ if "r" in prefix .lower ():
364+ return
365+
366+ leaf .value = _normalize_unicode_escape_sequences_in_text (text )
367+
368+
369+ def _normalize_fstring_field_node (node : Leaf | Node ) -> None :
370+ from black .nodes import syms
371+
372+ if isinstance (node , Leaf ):
373+ if node .type == token .STRING :
374+ normalize_unicode_escape_sequences (node )
375+ return
376+
377+ if node .type in (syms .fstring , syms .tstring ):
378+ normalize_fstring_unicode_escape_sequences (node )
379+ return
380+
381+ for child in node .children :
382+ _normalize_fstring_field_node (child )
383+
384+
385+ def normalize_fstring_unicode_escape_sequences (node : Node ) -> None :
386+ """Replace hex codes in Unicode escape sequences with lowercase representation
387+ within an f-string or t-string node."""
388+ from black .nodes import syms
389+
390+ if not node .children :
391+ return
392+
393+ first_child = node .children [0 ]
394+ is_raw_fstring = (
395+ isinstance (first_child , Leaf ) and "r" in first_child .value .rstrip ("'\" " ).lower ()
396+ )
397+
398+ for child in node .children :
399+ if child .type in (token .FSTRING_MIDDLE , token .TSTRING_MIDDLE ):
400+ if not is_raw_fstring :
401+ assert isinstance (child , Leaf )
402+ child .value = _normalize_unicode_escape_sequences_in_text (child .value )
403+ elif child .type in (
404+ syms .fstring_replacement_field ,
405+ syms .tstring_replacement_field ,
406+ ):
407+ # If this is a debug f-string (e.g. f"{foo=}"), do not touch the
408+ # expression before '=', as Python preserves the exact source expression.
409+ if any (
410+ isinstance (c , Leaf ) and c .type == token .EQUAL and c .value == "="
411+ for c in child .children
412+ ):
413+ continue
414+
415+ for subchild in child .children :
416+ _normalize_fstring_field_node (subchild )
362417
363418
364419@lru_cache (maxsize = 4096 )
0 commit comments