From c0724af4d8ff40361755c1482778801040335fee Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sat, 16 Sep 2023 13:41:36 +0530 Subject: [PATCH] Update `PLE2510`, `PLE2512-2515` to check in f-strings (#7387) This PR updates `PLE2510`, `PLE2512-2515` to check in f-strings. > ### Reference: > * `PLE2510`: Invalid unescaped character backspace, use "\b" instead > * `PLE2512`: Invalid unescaped character SUB, use "\x1A" instead > * `PLE2513`: Invalid unescaped character ESC, use "\x1B" instead > * `PLE2514`: Invalid unescaped character NUL, use "\0" instead > * `PLE2515`: Invalid unescaped character zero-width-space, use "\u200B" instead Add test cases for f-strings. --- .../fixtures/pylint/invalid_characters.py | Bin 1106 -> 1461 bytes crates/ruff_linter/src/checkers/tokens.rs | 4 +- .../pylint/rules/invalid_string_characters.rs | 8 +- ..._tests__PLE2510_invalid_characters.py.snap | 46 ++++- ..._tests__PLE2512_invalid_characters.py.snap | 63 +++++-- ..._tests__PLE2513_invalid_characters.py.snap | 63 +++++-- ..._tests__PLE2514_invalid_characters.py.snap | Bin 408 -> 799 bytes ..._tests__PLE2515_invalid_characters.py.snap | 178 +++++++++++++----- 8 files changed, 282 insertions(+), 80 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/invalid_characters.py b/crates/ruff_linter/resources/test/fixtures/pylint/invalid_characters.py index 6233ed9353308d93ea948e16755fa23d395c9552..e1864fa38175502c3dcdc92b2b38f0d924a1cb34 100644 GIT binary patch delta 322 zcmcb_v6Xv+EOTU%f~`WDI)^$JS5ka_Hjtwp6O*J43>QtD7%+GIy&No}AIL?cjXg<&F4ZEA5cNG*s0VQF=!NZRB?X4xR15R!VJJGk;n zb3p3V)xj(V7z5;JWHFE|%!vkIwNP^=&taC~s46cm0Gg6Ec_p(lH;4n1Jk0De`7W~{ zW7_0*%qf$`TzRR*C8;U#X~iW)nR)5OAkFF=)oJQdKrF3Z3xr$%3CmlF delta 111 zcmdnWeTid(EVBz&Qha{4f~|sjOiYqG7gur-oLdCs7MCW0, + tok: &Tok, range: TextRange, locator: &Locator, ) { - let text = locator.slice(range); + let text = match tok { + Tok::String { .. } => locator.slice(range), + Tok::FStringMiddle { value, .. } => value.as_str(), + _ => return, + }; for (column, match_) in text.match_indices(&['\x08', '\x1A', '\x1B', '\0', '\u{200b}']) { let c = match_.chars().next().unwrap(); diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2510_invalid_characters.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2510_invalid_characters.py.snap index 48a360b3c363e..25e9c12e479f0 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2510_invalid_characters.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2510_invalid_characters.py.snap @@ -7,8 +7,7 @@ invalid_characters.py:15:6: PLE2510 [*] Invalid unescaped character backspace, u 14 | #foo = 'hi' 15 | b = '' | PLE2510 -16 | -17 | b_ok = '\\b' +16 | b = f'' | = help: Replace with escape sequence @@ -18,8 +17,45 @@ invalid_characters.py:15:6: PLE2510 [*] Invalid unescaped character backspace, u 14 14 | #foo = 'hi' 15 |-b = '' 15 |+b = '\b' -16 16 | -17 17 | b_ok = '\\b' -18 18 | +16 16 | b = f'' +17 17 | +18 18 | b_ok = '\\b' + +invalid_characters.py:16:7: PLE2510 [*] Invalid unescaped character backspace, use "\b" instead + | +14 | #foo = 'hi' +15 | b = '' +16 | b = f'' + | PLE2510 +17 | +18 | b_ok = '\\b' + | + = help: Replace with escape sequence + +ℹ Fix +13 13 | # (Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall, +14 14 | #foo = 'hi' +15 15 | b = '' +16 |-b = f'' + 16 |+b = f'\b' +17 17 | +18 18 | b_ok = '\\b' +19 19 | b_ok = f'\\b' + +invalid_characters.py:55:21: PLE2510 [*] Invalid unescaped character backspace, use "\b" instead + | +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 | +55 | nested_fstrings = f'{f'{f''}'}' + | PLE2510 + | + = help: Replace with escape sequence + +ℹ Fix +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | +55 |-nested_fstrings = f'{f'{f''}'}' + 55 |+nested_fstrings = f'\b{f'{f''}'}' diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2512_invalid_characters.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2512_invalid_characters.py.snap index eaab51876b789..efda03729de47 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2512_invalid_characters.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2512_invalid_characters.py.snap @@ -1,25 +1,60 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -invalid_characters.py:21:12: PLE2512 [*] Invalid unescaped character SUB, use "\x1A" instead +invalid_characters.py:24:12: PLE2512 [*] Invalid unescaped character SUB, use "\x1A" instead | -19 | cr_ok = '\\r' -20 | -21 | sub = 'sub ' +22 | cr_ok = f'\\r' +23 | +24 | sub = 'sub ' | PLE2512 -22 | -23 | sub_ok = '\x1a' +25 | sub = f'sub ' | = help: Replace with escape sequence ℹ Fix -18 18 | -19 19 | cr_ok = '\\r' -20 20 | -21 |-sub = 'sub ' - 21 |+sub = 'sub \x1A' -22 22 | -23 23 | sub_ok = '\x1a' -24 24 | +21 21 | cr_ok = '\\r' +22 22 | cr_ok = f'\\r' +23 23 | +24 |-sub = 'sub ' + 24 |+sub = 'sub \x1A' +25 25 | sub = f'sub ' +26 26 | +27 27 | sub_ok = '\x1a' + +invalid_characters.py:25:13: PLE2512 [*] Invalid unescaped character SUB, use "\x1A" instead + | +24 | sub = 'sub ' +25 | sub = f'sub ' + | PLE2512 +26 | +27 | sub_ok = '\x1a' + | + = help: Replace with escape sequence + +ℹ Fix +22 22 | cr_ok = f'\\r' +23 23 | +24 24 | sub = 'sub ' +25 |-sub = f'sub ' + 25 |+sub = f'sub \x1A' +26 26 | +27 27 | sub_ok = '\x1a' +28 28 | sub_ok = f'\x1a' + +invalid_characters.py:55:25: PLE2512 [*] Invalid unescaped character SUB, use "\x1A" instead + | +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 | +55 | nested_fstrings = f'{f'{f''}'}' + | PLE2512 + | + = help: Replace with escape sequence + +ℹ Fix +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | +55 |-nested_fstrings = f'{f'{f''}'}' + 55 |+nested_fstrings = f'{f'\x1A{f''}'}' diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2513_invalid_characters.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2513_invalid_characters.py.snap index ae13f2baba496..fbfd76cfa23a8 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2513_invalid_characters.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2513_invalid_characters.py.snap @@ -1,25 +1,60 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -invalid_characters.py:25:16: PLE2513 [*] Invalid unescaped character ESC, use "\x1B" instead +invalid_characters.py:30:16: PLE2513 [*] Invalid unescaped character ESC, use "\x1B" instead | -23 | sub_ok = '\x1a' -24 | -25 | esc = 'esc esc ' +28 | sub_ok = f'\x1a' +29 | +30 | esc = 'esc esc ' | PLE2513 -26 | -27 | esc_ok = '\x1b' +31 | esc = f'esc esc ' | = help: Replace with escape sequence ℹ Fix -22 22 | -23 23 | sub_ok = '\x1a' -24 24 | -25 |-esc = 'esc esc ' - 25 |+esc = 'esc esc \x1B' -26 26 | -27 27 | esc_ok = '\x1b' -28 28 | +27 27 | sub_ok = '\x1a' +28 28 | sub_ok = f'\x1a' +29 29 | +30 |-esc = 'esc esc ' + 30 |+esc = 'esc esc \x1B' +31 31 | esc = f'esc esc ' +32 32 | +33 33 | esc_ok = '\x1b' + +invalid_characters.py:31:17: PLE2513 [*] Invalid unescaped character ESC, use "\x1B" instead + | +30 | esc = 'esc esc ' +31 | esc = f'esc esc ' + | PLE2513 +32 | +33 | esc_ok = '\x1b' + | + = help: Replace with escape sequence + +ℹ Fix +28 28 | sub_ok = f'\x1a' +29 29 | +30 30 | esc = 'esc esc ' +31 |-esc = f'esc esc ' + 31 |+esc = f'esc esc \x1B' +32 32 | +33 33 | esc_ok = '\x1b' +34 34 | esc_ok = f'\x1b' + +invalid_characters.py:55:29: PLE2513 [*] Invalid unescaped character ESC, use "\x1B" instead + | +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 | +55 | nested_fstrings = f'{f'{f''}'}' + | PLE2513 + | + = help: Replace with escape sequence + +ℹ Fix +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | +55 |-nested_fstrings = f'{f'{f''}'}' + 55 |+nested_fstrings = f'{f'{f'\x1B'}'}' diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2514_invalid_characters.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2514_invalid_characters.py.snap index 49a6a4f5186ddf8fba75a4641300b045aa1ad785..0227171c311f933297bc3fe5571b41ba8fb98962 100644 GIT binary patch literal 799 zcmb`E!AiqG5QaVPQ~dN2P}8(cYTH1M3PKSPJXtC0W-Z$jcH3av zcn}Pe!!G}@-+be`F4K9XgbX1RFQxWW6~~J-$x5j_tpuG@x-Hh`%6rM9uF}k^IFzg3-IDN#m&7sPq7QB!VnhxCD&(2Y4iO#&&L6YfG z@`wSbnSX&AS(Rc6$8nfH+8$5L$t38fZ~GGS$4xxmgrs^29p(;VRfo7^46LHM$^!89?R&dBz3`HQFG77z40P ch6*rS6pW1^j({0&0Cb440uTWW=VIgn0LYRka{vGU diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2515_invalid_characters.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2515_invalid_characters.py.snap index 25210fb48e358..3a9ba051eea5a 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2515_invalid_characters.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE2515_invalid_characters.py.snap @@ -1,73 +1,165 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -invalid_characters.py:34:13: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead +invalid_characters.py:44:13: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead | -32 | nul_ok = '\0' -33 | -34 | zwsp = 'zero​width' +42 | nul_ok = f'\0' +43 | +44 | zwsp = 'zero​width' | PLE2515 -35 | -36 | zwsp_ok = '\u200b' +45 | zwsp = f'zero​width' | = help: Replace with escape sequence ℹ Fix -31 31 | -32 32 | nul_ok = '\0' -33 33 | -34 |-zwsp = 'zero​width' - 34 |+zwsp = 'zero\u200bwidth' -35 35 | -36 36 | zwsp_ok = '\u200b' -37 37 | - -invalid_characters.py:38:36: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead - | -36 | zwsp_ok = '\u200b' -37 | -38 | zwsp_after_multibyte_character = "ಫ​" +41 41 | nul_ok = '\0' +42 42 | nul_ok = f'\0' +43 43 | +44 |-zwsp = 'zero​width' + 44 |+zwsp = 'zero\u200bwidth' +45 45 | zwsp = f'zero​width' +46 46 | +47 47 | zwsp_ok = '\u200b' + +invalid_characters.py:45:14: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead + | +44 | zwsp = 'zero​width' +45 | zwsp = f'zero​width' + | PLE2515 +46 | +47 | zwsp_ok = '\u200b' + | + = help: Replace with escape sequence + +ℹ Fix +42 42 | nul_ok = f'\0' +43 43 | +44 44 | zwsp = 'zero​width' +45 |-zwsp = f'zero​width' + 45 |+zwsp = f'zero\u200bwidth' +46 46 | +47 47 | zwsp_ok = '\u200b' +48 48 | zwsp_ok = f'\u200b' + +invalid_characters.py:50:36: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead + | +48 | zwsp_ok = f'\u200b' +49 | +50 | zwsp_after_multibyte_character = "ಫ​" | PLE2515 -39 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +51 | zwsp_after_multibyte_character = f"ಫ​" +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + | + = help: Replace with escape sequence + +ℹ Fix +47 47 | zwsp_ok = '\u200b' +48 48 | zwsp_ok = f'\u200b' +49 49 | +50 |-zwsp_after_multibyte_character = "ಫ​" + 50 |+zwsp_after_multibyte_character = "ಫ\u200b" +51 51 | zwsp_after_multibyte_character = f"ಫ​" +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + +invalid_characters.py:51:37: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead + | +50 | zwsp_after_multibyte_character = "ಫ​" +51 | zwsp_after_multibyte_character = f"ಫ​" + | PLE2515 +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" | = help: Replace with escape sequence ℹ Fix -35 35 | -36 36 | zwsp_ok = '\u200b' -37 37 | -38 |-zwsp_after_multibyte_character = "ಫ​" - 38 |+zwsp_after_multibyte_character = "ಫ\u200b" -39 39 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +48 48 | zwsp_ok = f'\u200b' +49 49 | +50 50 | zwsp_after_multibyte_character = "ಫ​" +51 |-zwsp_after_multibyte_character = f"ಫ​" + 51 |+zwsp_after_multibyte_character = f"ಫ\u200b" +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | -invalid_characters.py:39:60: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead +invalid_characters.py:52:60: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead | -38 | zwsp_after_multibyte_character = "ಫ​" -39 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +50 | zwsp_after_multibyte_character = "ಫ​" +51 | zwsp_after_multibyte_character = f"ಫ​" +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" | PLE2515 +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" | = help: Replace with escape sequence ℹ Fix -36 36 | zwsp_ok = '\u200b' -37 37 | -38 38 | zwsp_after_multibyte_character = "ಫ​" -39 |-zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" - 39 |+zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ \u200b​" +49 49 | +50 50 | zwsp_after_multibyte_character = "ಫ​" +51 51 | zwsp_after_multibyte_character = f"ಫ​" +52 |-zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + 52 |+zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ \u200b​" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | +55 55 | nested_fstrings = f'{f'{f''}'}' -invalid_characters.py:39:61: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead +invalid_characters.py:52:61: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead | -38 | zwsp_after_multibyte_character = "ಫ​" -39 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +50 | zwsp_after_multibyte_character = "ಫ​" +51 | zwsp_after_multibyte_character = f"ಫ​" +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" | PLE2515 +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + | + = help: Replace with escape sequence + +ℹ Fix +49 49 | +50 50 | zwsp_after_multibyte_character = "ಫ​" +51 51 | zwsp_after_multibyte_character = f"ಫ​" +52 |-zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + 52 |+zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​\u200b" +53 53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +54 54 | +55 55 | nested_fstrings = f'{f'{f''}'}' + +invalid_characters.py:53:61: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead + | +51 | zwsp_after_multibyte_character = f"ಫ​" +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + | PLE2515 +54 | +55 | nested_fstrings = f'{f'{f''}'}' + | + = help: Replace with escape sequence + +ℹ Fix +50 50 | zwsp_after_multibyte_character = "ಫ​" +51 51 | zwsp_after_multibyte_character = f"ಫ​" +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 |-zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + 53 |+zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ \u200b​" +54 54 | +55 55 | nested_fstrings = f'{f'{f''}'}' + +invalid_characters.py:53:62: PLE2515 [*] Invalid unescaped character zero-width-space, use "\u200B" instead + | +51 | zwsp_after_multibyte_character = f"ಫ​" +52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 | zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + | PLE2515 +54 | +55 | nested_fstrings = f'{f'{f''}'}' | = help: Replace with escape sequence ℹ Fix -36 36 | zwsp_ok = '\u200b' -37 37 | -38 38 | zwsp_after_multibyte_character = "ಫ​" -39 |-zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" - 39 |+zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​\u200b" +50 50 | zwsp_after_multibyte_character = "ಫ​" +51 51 | zwsp_after_multibyte_character = f"ಫ​" +52 52 | zwsp_after_multicharacter_grapheme_cluster = "ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" +53 |-zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​​" + 53 |+zwsp_after_multicharacter_grapheme_cluster = f"ಫ್ರಾನ್ಸಿಸ್ಕೊ ​\u200b" +54 54 | +55 55 | nested_fstrings = f'{f'{f''}'}'