From 51b3b2624d4b1e4608eb9addd7d292cc5174a047 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 4 Apr 2018 21:20:46 +0100 Subject: [PATCH] Handle backslashes in raw strings while normalizing (#105) In raw strings, a single backslash means a literal backslash. It is also used to escape quotes if it precedes them. This means it is impossible to change the quote type for strings that contain an unescaped version of the other quote type. Fixes #100 --- black.py | 17 +++++++++++++---- tests/string_quotes.py | 6 ++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/black.py b/black.py index 01390b3..4995b53 100644 --- a/black.py +++ b/black.py @@ -1917,10 +1917,20 @@ def normalize_string_quotes(leaf: Leaf) -> None: if first_quote_pos == -1: return # There's an internal error + prefix = leaf.value[:first_quote_pos] body = leaf.value[first_quote_pos + len(orig_quote):-len(orig_quote)] - new_body = body.replace(f"\\{orig_quote}", orig_quote).replace( - new_quote, f"\\{new_quote}" - ) + if "r" in prefix.casefold(): + if body.count(new_quote) != body.count(f"\\{new_quote}"): + # There's at least one unescaped new_quote in this raw string + # so converting is impossible + return + + # Do not introduce or remove backslashes in raw strings + new_body = body + else: + new_body = body.replace(f"\\{orig_quote}", orig_quote).replace( + new_quote, f"\\{new_quote}" + ) if new_quote == '"""' and new_body[-1] == '"': # edge case: new_body = new_body[:-1] + '\\"' @@ -1932,7 +1942,6 @@ def normalize_string_quotes(leaf: Leaf) -> None: if new_escape_count == orig_escape_count and orig_quote == '"': return # Prefer double quotes - prefix = leaf.value[:first_quote_pos] leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}" diff --git a/tests/string_quotes.py b/tests/string_quotes.py index 1080aaf..f2d720b 100644 --- a/tests/string_quotes.py +++ b/tests/string_quotes.py @@ -15,6 +15,9 @@ f'''This is a triple-quoted {f}-string''' f'MOAR {" ".join([])}' f"MOAR {' '.join([])}" r"raw string ftw" +r'Date d\'expiration:(.*)' +r'Tricky "quote' +r'Not-so-tricky \"quote' # output @@ -35,3 +38,6 @@ f"""This is a triple-quoted {f}-string""" f'MOAR {" ".join([])}' f"MOAR {' '.join([])}" r"raw string ftw" +r"Date d\'expiration:(.*)" +r'Tricky "quote' +r"Not-so-tricky \"quote" -- 2.39.2