From b95fc6d1621a3fc868b4fe486b02a4d30e0e4a02 Mon Sep 17 00:00:00 2001 From: Luc Khai Hai Date: Mon, 31 Jul 2023 17:46:40 +0900 Subject: [PATCH] Format bytes string (#6166) ## Summary Format bytes string Closes #6064 ## Test Plan Added a fixture based on string's one --- .../ruff/expression/bytes.options.json | 8 + .../test/fixtures/ruff/expression/bytes.py | 120 +++++ .../src/expression/expr_constant.rs | 11 +- .../src/expression/string.rs | 4 +- ...patibility@simple_cases__comments2.py.snap | 11 +- ...atibility@simple_cases__expression.py.snap | 46 +- ...lity@simple_cases__string_prefixes.py.snap | 31 +- ...tibility@simple_cases__tupleassign.py.snap | 9 +- .../format@expression__bytes.py.snap | 426 ++++++++++++++++++ 9 files changed, 577 insertions(+), 89 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.py create mode 100644 crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.options.json b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.options.json new file mode 100644 index 0000000000000..7d6d0512c20a0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.options.json @@ -0,0 +1,8 @@ +[ + { + "quote_style": "double" + }, + { + "quote_style": "single" + } +] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.py new file mode 100644 index 0000000000000..f505c5945a805 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.py @@ -0,0 +1,120 @@ +b"' test" +b'" test' + +b"\" test" +b'\' test' + +# Prefer single quotes for string with more double quotes +b"' \" \" '' \" \" '" + +# Prefer double quotes for string with more single quotes +b'\' " " \'\' " " \'' + +# Prefer double quotes for string with equal amount of single and double quotes +b'" \' " " \'\'' +b"' \" '' \" \"" + +b"\\' \"\"" +b'\\\' ""' + + +b"Test" +B"Test" + +rb"Test" +Rb"Test" + +b'This string will not include \ +backslashes or newline characters.' + +if True: + b'This string will not include \ + backslashes or newline characters.' + +b"""Multiline +String \" +""" + +b'''Multiline +String \' +''' + +b'''Multiline +String "" +''' + +b'''Multiline +String """ +''' + +b'''Multiline +String "''' + +b"""Multiline +String ''' +""" + +b"""Multiline +String '""" + +b'''Multiline +String \"\"\" +''' + +# String continuation + +b"Let's" b"start" b"with" b"a" b"simple" b"example" + +b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" + +( + b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" +) + +if ( + a + b"Let's" + b"start" + b"with" + b"a" + b"simple" + b"example" + b"now repeat after me:" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" +): + pass + +if b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident": + pass + +( + # leading + b"a" # trailing part comment + + # leading part comment + + b"b" # trailing second part comment + # trailing +) + +test_particular = [ + # squares + b'1.00000000100000000025', + b'1.0000000000000000000000000100000000000000000000000' #... + b'00025', + b'1.0000000000000000000000000000000000000000000010000' #... + b'0000000000000000000000000000000000000000025', +] + +# Parenthesized string continuation with messed up indentation +{ + "key": ( + [], + b'a' + b'b' + b'c' + ) +} diff --git a/crates/ruff_python_formatter/src/expression/expr_constant.rs b/crates/ruff_python_formatter/src/expression/expr_constant.rs index c4ef6928348d3..7c8dd90b4c34c 100644 --- a/crates/ruff_python_formatter/src/expression/expr_constant.rs +++ b/crates/ruff_python_formatter/src/expression/expr_constant.rs @@ -9,7 +9,7 @@ use crate::expression::number::{FormatComplex, FormatFloat, FormatInt}; use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses}; use crate::expression::string::{FormatString, StringLayout, StringPrefix, StringQuotes}; use crate::prelude::*; -use crate::{not_yet_implemented_custom_text, FormatNodeRule}; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatExprConstant { @@ -51,16 +51,13 @@ impl FormatNodeRule for FormatExprConstant { Constant::Int(_) => FormatInt::new(item).fmt(f), Constant::Float(_) => FormatFloat::new(item).fmt(f), Constant::Complex { .. } => FormatComplex::new(item).fmt(f), - Constant::Str(_) => { + Constant::Str(_) | Constant::Bytes(_) => { let string_layout = match self.layout { ExprConstantLayout::Default => StringLayout::Default, ExprConstantLayout::String(layout) => layout, }; FormatString::new(item).with_layout(string_layout).fmt(f) } - Constant::Bytes(_) => { - not_yet_implemented_custom_text(r#"b"NOT_YET_IMPLEMENTED_BYTE_STRING""#).fmt(f) - } } } @@ -79,7 +76,7 @@ impl NeedsParentheses for ExprConstant { _parent: AnyNodeRef, context: &PyFormatContext, ) -> OptionalParentheses { - if self.value.is_str() { + if self.value.is_str() || self.value.is_bytes() { let contents = context.locator().slice(self.range()); // Don't wrap triple quoted strings if is_multiline_string(self, context.source()) || !is_implicit_concatenation(contents) { @@ -94,7 +91,7 @@ impl NeedsParentheses for ExprConstant { } pub(super) fn is_multiline_string(constant: &ExprConstant, source: &str) -> bool { - if constant.value.is_str() { + if constant.value.is_str() || constant.value.is_bytes() { let contents = &source[constant.range()]; let prefix = StringPrefix::parse(contents); let quotes = diff --git a/crates/ruff_python_formatter/src/expression/string.rs b/crates/ruff_python_formatter/src/expression/string.rs index 8395122be2235..f9b8679b34cc7 100644 --- a/crates/ruff_python_formatter/src/expression/string.rs +++ b/crates/ruff_python_formatter/src/expression/string.rs @@ -31,7 +31,7 @@ pub enum StringLayout { impl<'a> FormatString<'a> { pub(super) fn new(constant: &'a ExprConstant) -> Self { - debug_assert!(constant.value.is_str()); + debug_assert!(constant.value.is_str() || constant.value.is_bytes()); Self { constant, layout: StringLayout::Default, @@ -70,7 +70,7 @@ struct FormatStringContinuation<'a> { impl<'a> FormatStringContinuation<'a> { fn new(constant: &'a ExprConstant) -> Self { - debug_assert!(constant.value.is_str()); + debug_assert!(constant.value.is_str() || constant.value.is_bytes()); Self { constant } } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap index e7b23836102c9..9628978a2c681 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap @@ -191,15 +191,6 @@ instruction()#comment with bad spacing ) # Please keep __all__ alphabetized within each category. -@@ -45,7 +45,7 @@ - # user-defined types and objects - Cheese, - Cheese("Wensleydale"), -- SubBytes(b"spam"), -+ SubBytes(b"NOT_YET_IMPLEMENTED_BYTE_STRING"), - ] - - if "PYTHON" in os.environ: @@ -60,8 +60,12 @@ # Comment before function. def inline_comments_in_brackets_ruin_everything(): @@ -314,7 +305,7 @@ not_shareables = [ # user-defined types and objects Cheese, Cheese("Wensleydale"), - SubBytes(b"NOT_YET_IMPLEMENTED_BYTE_STRING"), + SubBytes(b"spam"), ] if "PYTHON" in os.environ: diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap index af4a17bd1522e..ad9938cb83183 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap @@ -266,14 +266,6 @@ last_call() ```diff --- Black +++ Ruff -@@ -1,6 +1,6 @@ - ... - "some_string" --b"\\xa3" -+b"NOT_YET_IMPLEMENTED_BYTE_STRING" - Name - None - True @@ -31,7 +31,7 @@ -1 ~int and not v1 ^ 123 + v2 | True @@ -299,22 +291,7 @@ last_call() 1 if True else 2 str or None if True else str or bytes or None (str or None) if True else (str or bytes or None) -@@ -57,7 +58,13 @@ - {"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} - {**a, **b, **c} - {"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} --({"a": "b"}, (True or False), (+value), "string", b"bytes") or None -+( -+ {"a": "b"}, -+ (True or False), -+ (+value), -+ "string", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+) or None - () - (1,) - (1, 2) -@@ -115,7 +122,7 @@ +@@ -115,7 +116,7 @@ arg, another, kwarg="hey", @@ -323,11 +300,10 @@ last_call() ) # note: no trailing comma pre-3.6 call(*gidgets[:2]) call(a, *gidgets[:2]) -@@ -207,25 +214,15 @@ - ) +@@ -208,24 +209,14 @@ what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( vars_to_remove --) + ) -result = ( - session.query(models.Customer.id) - .filter( @@ -335,7 +311,7 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() - ) +-) -result = ( - session.query(models.Customer.id) - .filter( @@ -357,7 +333,7 @@ last_call() Ø = set() authors.łukasz.say_thanks() mapping = { -@@ -328,13 +325,18 @@ +@@ -328,13 +319,18 @@ ): return True if ( @@ -379,7 +355,7 @@ last_call() ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n ): return True -@@ -342,7 +344,8 @@ +@@ -342,7 +338,8 @@ ~aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e @@ -396,7 +372,7 @@ last_call() ```py ... "some_string" -b"NOT_YET_IMPLEMENTED_BYTE_STRING" +b"\\xa3" Name None True @@ -454,13 +430,7 @@ str or None if (1 if True else 2) else str or bytes or None {"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}} {**a, **b, **c} {"2.7", "3.6", "3.7", "3.8", "3.9", ("4.0" if gilectomy else "3.10")} -( - {"a": "b"}, - (True or False), - (+value), - "string", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", -) or None +({"a": "b"}, (True or False), (+value), "string", b"bytes") or None () (1,) (1, 2) diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap index 5d5f5028ad681..a312eb7488a2d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__string_prefixes.py.snap @@ -32,19 +32,17 @@ def docstring_multiline(): ```diff --- Black +++ Ruff -@@ -1,13 +1,31 @@ +@@ -1,12 +1,21 @@ #!/usr/bin/env python3 name = "Łukasz" -(f"hello {name}", f"hello {name}") --(b"", b"") +(f"NOT_YET_IMPLEMENTED_ExprJoinedStr", f"NOT_YET_IMPLEMENTED_ExprJoinedStr") -+(b"NOT_YET_IMPLEMENTED_BYTE_STRING", b"NOT_YET_IMPLEMENTED_BYTE_STRING") + (b"", b"") ("", "") (r"", R"") -(rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") --(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") +( + f"NOT_YET_IMPLEMENTED_ExprJoinedStr", + f"NOT_YET_IMPLEMENTED_ExprJoinedStr", @@ -55,19 +53,9 @@ def docstring_multiline(): + f"NOT_YET_IMPLEMENTED_ExprJoinedStr", + f"NOT_YET_IMPLEMENTED_ExprJoinedStr", +) -+( -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+ b"NOT_YET_IMPLEMENTED_BYTE_STRING", -+) + (rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") - def docstring_singleline(): ``` ## Ruff Output @@ -77,7 +65,7 @@ def docstring_multiline(): name = "Łukasz" (f"NOT_YET_IMPLEMENTED_ExprJoinedStr", f"NOT_YET_IMPLEMENTED_ExprJoinedStr") -(b"NOT_YET_IMPLEMENTED_BYTE_STRING", b"NOT_YET_IMPLEMENTED_BYTE_STRING") +(b"", b"") ("", "") (r"", R"") @@ -91,16 +79,7 @@ name = "Łukasz" f"NOT_YET_IMPLEMENTED_ExprJoinedStr", f"NOT_YET_IMPLEMENTED_ExprJoinedStr", ) -( - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", - b"NOT_YET_IMPLEMENTED_BYTE_STRING", -) +(rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") def docstring_singleline(): diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap index 6e9dbd02a26ac..d2bc4735aeb8e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap @@ -19,7 +19,7 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") ```diff --- Black +++ Ruff -@@ -4,9 +4,9 @@ +@@ -4,7 +4,7 @@ sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf, @@ -27,10 +27,7 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") +) = 1, 2, 3 # This is as well. --(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") -+(this_will_be_wrapped_in_parens,) = struct.unpack(b"NOT_YET_IMPLEMENTED_BYTE_STRING") - - (a,) = call() + (this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") ``` ## Ruff Output @@ -45,7 +42,7 @@ this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") ) = 1, 2, 3 # This is as well. -(this_will_be_wrapped_in_parens,) = struct.unpack(b"NOT_YET_IMPLEMENTED_BYTE_STRING") +(this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") (a,) = call() ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap new file mode 100644 index 0000000000000..51f3792371b3b --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap @@ -0,0 +1,426 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/bytes.py +--- +## Input +```py +b"' test" +b'" test' + +b"\" test" +b'\' test' + +# Prefer single quotes for string with more double quotes +b"' \" \" '' \" \" '" + +# Prefer double quotes for string with more single quotes +b'\' " " \'\' " " \'' + +# Prefer double quotes for string with equal amount of single and double quotes +b'" \' " " \'\'' +b"' \" '' \" \"" + +b"\\' \"\"" +b'\\\' ""' + + +b"Test" +B"Test" + +rb"Test" +Rb"Test" + +b'This string will not include \ +backslashes or newline characters.' + +if True: + b'This string will not include \ + backslashes or newline characters.' + +b"""Multiline +String \" +""" + +b'''Multiline +String \' +''' + +b'''Multiline +String "" +''' + +b'''Multiline +String """ +''' + +b'''Multiline +String "''' + +b"""Multiline +String ''' +""" + +b"""Multiline +String '""" + +b'''Multiline +String \"\"\" +''' + +# String continuation + +b"Let's" b"start" b"with" b"a" b"simple" b"example" + +b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" + +( + b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" +) + +if ( + a + b"Let's" + b"start" + b"with" + b"a" + b"simple" + b"example" + b"now repeat after me:" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" +): + pass + +if b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident": + pass + +( + # leading + b"a" # trailing part comment + + # leading part comment + + b"b" # trailing second part comment + # trailing +) + +test_particular = [ + # squares + b'1.00000000100000000025', + b'1.0000000000000000000000000100000000000000000000000' #... + b'00025', + b'1.0000000000000000000000000000000000000000000010000' #... + b'0000000000000000000000000000000000000000025', +] + +# Parenthesized string continuation with messed up indentation +{ + "key": ( + [], + b'a' + b'b' + b'c' + ) +} +``` + +## Outputs +### Output 1 +``` +indent-style = Spaces, size: 4 +line-width = 88 +quote-style = Double +magic-trailing-comma = Respect +``` + +```py +b"' test" +b'" test' + +b'" test' +b"' test" + +# Prefer single quotes for string with more double quotes +b"' \" \" '' \" \" '" + +# Prefer double quotes for string with more single quotes +b"' \" \" '' \" \" '" + +# Prefer double quotes for string with equal amount of single and double quotes +b"\" ' \" \" ''" +b"' \" '' \" \"" + +b'\\\' ""' +b'\\\' ""' + + +b"Test" +b"Test" + +rb"Test" +Rb"Test" + +b"This string will not include \ +backslashes or newline characters." + +if True: + b"This string will not include \ + backslashes or newline characters." + +b"""Multiline +String \" +""" + +b"""Multiline +String \' +""" + +b"""Multiline +String "" +""" + +b'''Multiline +String """ +''' + +b'''Multiline +String "''' + +b"""Multiline +String ''' +""" + +b"""Multiline +String '""" + +b"""Multiline +String \"\"\" +""" + +# String continuation + +b"Let's" b"start" b"with" b"a" b"simple" b"example" + +b"Let's" b"start" b"with" b"a" b"simple" b"example" b"now repeat after me:" b"I am confident" b"I am confident" b"I am confident" b"I am confident" b"I am confident" + +( + b"Let's" + b"start" + b"with" + b"a" + b"simple" + b"example" + b"now repeat after me:" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" +) + +if ( + a + + b"Let's" + b"start" + b"with" + b"a" + b"simple" + b"example" + b"now repeat after me:" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" +): + pass + +if ( + b"Let's" + b"start" + b"with" + b"a" + b"simple" + b"example" + b"now repeat after me:" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" + b"I am confident" +): + pass + +( + # leading + b"a" # trailing part comment + # leading part comment + b"b" # trailing second part comment + # trailing +) + +test_particular = [ + # squares + b"1.00000000100000000025", + b"1.0000000000000000000000000100000000000000000000000" # ... + b"00025", + b"1.0000000000000000000000000000000000000000000010000" # ... + b"0000000000000000000000000000000000000000025", +] + +# Parenthesized string continuation with messed up indentation +{"key": ([], b"a" b"b" b"c")} +``` + + +### Output 2 +``` +indent-style = Spaces, size: 4 +line-width = 88 +quote-style = Single +magic-trailing-comma = Respect +``` + +```py +b"' test" +b'" test' + +b'" test' +b"' test" + +# Prefer single quotes for string with more double quotes +b'\' " " \'\' " " \'' + +# Prefer double quotes for string with more single quotes +b'\' " " \'\' " " \'' + +# Prefer double quotes for string with equal amount of single and double quotes +b'" \' " " \'\'' +b'\' " \'\' " "' + +b'\\\' ""' +b'\\\' ""' + + +b'Test' +b'Test' + +rb'Test' +Rb'Test' + +b'This string will not include \ +backslashes or newline characters.' + +if True: + b'This string will not include \ + backslashes or newline characters.' + +b'''Multiline +String \" +''' + +b'''Multiline +String \' +''' + +b'''Multiline +String "" +''' + +b'''Multiline +String """ +''' + +b'''Multiline +String "''' + +b"""Multiline +String ''' +""" + +b"""Multiline +String '""" + +b'''Multiline +String \"\"\" +''' + +# String continuation + +b"Let's" b'start' b'with' b'a' b'simple' b'example' + +b"Let's" b'start' b'with' b'a' b'simple' b'example' b'now repeat after me:' b'I am confident' b'I am confident' b'I am confident' b'I am confident' b'I am confident' + +( + b"Let's" + b'start' + b'with' + b'a' + b'simple' + b'example' + b'now repeat after me:' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' +) + +if ( + a + + b"Let's" + b'start' + b'with' + b'a' + b'simple' + b'example' + b'now repeat after me:' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' +): + pass + +if ( + b"Let's" + b'start' + b'with' + b'a' + b'simple' + b'example' + b'now repeat after me:' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' + b'I am confident' +): + pass + +( + # leading + b'a' # trailing part comment + # leading part comment + b'b' # trailing second part comment + # trailing +) + +test_particular = [ + # squares + b'1.00000000100000000025', + b'1.0000000000000000000000000100000000000000000000000' # ... + b'00025', + b'1.0000000000000000000000000000000000000000000010000' # ... + b'0000000000000000000000000000000000000000025', +] + +# Parenthesized string continuation with messed up indentation +{'key': ([], b'a' b'b' b'c')} +``` + + +