Skip to content

Commit 1a7d9c2

Browse files
Preserve visible quote types for f-string debug expressions (#4005)
Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent f4c7be5 commit 1a7d9c2

File tree

3 files changed

+98
-5
lines changed

3 files changed

+98
-5
lines changed

CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
indented less (#3964)
2727
- Multiline list and dict unpacking as the sole argument to a function is now also
2828
indented less (#3992)
29+
- In f-string debug expressions preserve quote types that are visible in the final
30+
string (#4005)
2931
- Fix a bug where long `case` blocks were not split into multiple lines. Also enable
3032
general trailing comma rules on `case` blocks (#4024)
3133
- Keep requiring two empty lines between module-level docstring and first function or

src/black/trans.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,22 @@ def make_naked(string: str, string_prefix: str) -> str:
590590
"""
591591
assert_is_leaf_string(string)
592592
if "f" in string_prefix:
593-
string = _toggle_fexpr_quotes(string, QUOTE)
594-
# After quotes toggling, quotes in expressions won't be escaped
595-
# because quotes can't be reused in f-strings. So we can simply
596-
# let the escaping logic below run without knowing f-string
597-
# expressions.
593+
f_expressions = (
594+
string[span[0] + 1 : span[1] - 1] # +-1 to get rid of curly braces
595+
for span in iter_fexpr_spans(string)
596+
)
597+
debug_expressions_contain_visible_quotes = any(
598+
re.search(r".*[\'\"].*(?<![!:=])={1}(?!=)(?![^\s:])", expression)
599+
for expression in f_expressions
600+
)
601+
if not debug_expressions_contain_visible_quotes:
602+
# We don't want to toggle visible quotes in debug f-strings, as
603+
# that would modify the AST
604+
string = _toggle_fexpr_quotes(string, QUOTE)
605+
# After quotes toggling, quotes in expressions won't be escaped
606+
# because quotes can't be reused in f-strings. So we can simply
607+
# let the escaping logic below run without knowing f-string
608+
# expressions.
598609

599610
RE_EVEN_BACKSLASHES = r"(?:(?<!\\)(?:\\\\)*)"
600611
naked_string = string[len(string_prefix) + 1 : -1]

tests/data/cases/preview_long_strings.py

+80
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,29 @@ def foo():
305305
# Test case of an outer string' parens enclose an inner string's parens.
306306
call(body=("%s %s" % ((",".join(items)), suffix)))
307307

308+
log.info(f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}')
309+
310+
log.info(f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}")
311+
312+
log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
313+
314+
log.info(f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
315+
316+
log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
317+
318+
log.info(f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
319+
320+
log.info(f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
321+
322+
log.info(f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}')
323+
324+
log.info(f'Skipping: { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }')
325+
326+
log.info(f'''Skipping: {"a" == 'b'} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}''')
327+
328+
log.info(f'''Skipping: {'a' == "b"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}''')
329+
330+
log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""")
308331

309332
# output
310333

@@ -846,3 +869,60 @@ def foo():
846869

847870
# Test case of an outer string' parens enclose an inner string's parens.
848871
call(body="%s %s" % (",".join(items), suffix))
872+
873+
log.info(
874+
"Skipping:"
875+
f' {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}'
876+
)
877+
878+
log.info(
879+
"Skipping:"
880+
f" {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}"
881+
)
882+
883+
log.info(
884+
"Skipping:"
885+
f" {desc['db_id']} {foo('bar',x=123)} {'foo' != 'bar'} {(x := 'abc=')} {pos_share=} {desc['status']} {desc['exposure_max']}"
886+
)
887+
888+
log.info(
889+
"Skipping:"
890+
f' {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}'
891+
)
892+
893+
log.info(
894+
"Skipping:"
895+
f' {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}'
896+
)
897+
898+
log.info(
899+
"Skipping:"
900+
f' {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}'
901+
)
902+
903+
log.info(
904+
"Skipping:"
905+
f" {'a' == 'b' == 'c' == 'd'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}"
906+
)
907+
908+
log.info(
909+
"Skipping:"
910+
f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}'
911+
)
912+
913+
log.info(
914+
"Skipping:"
915+
f' { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }'
916+
)
917+
918+
log.info(
919+
f"""Skipping: {"a" == 'b'} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}"""
920+
)
921+
922+
log.info(
923+
f"""Skipping: {'a' == "b"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}"""
924+
)
925+
926+
log.info(
927+
f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}"""
928+
)

0 commit comments

Comments
 (0)