mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-30 23:27:38 +00:00
Join implicit concatenated strings when they fit on a line (#13663)
This commit is contained in:
parent
e402e27a09
commit
73ee72b665
50 changed files with 3907 additions and 363 deletions
|
@ -6,7 +6,11 @@ use {
|
|||
|
||||
use ruff_python_ast::visitor::transformer;
|
||||
use ruff_python_ast::visitor::transformer::Transformer;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
use ruff_python_ast::{
|
||||
self as ast, BytesLiteralFlags, Expr, FStringElement, FStringFlags, FStringLiteralElement,
|
||||
FStringPart, Stmt, StringFlags, StringLiteralFlags,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
/// A struct to normalize AST nodes for the purpose of comparing formatted representations for
|
||||
/// semantic equivalence.
|
||||
|
@ -59,6 +63,135 @@ impl Transformer for Normalizer {
|
|||
transformer::walk_stmt(self, stmt);
|
||||
}
|
||||
|
||||
fn visit_expr(&self, expr: &mut Expr) {
|
||||
// Ruff supports joining implicitly concatenated strings. The code below implements this
|
||||
// at an AST level by joining the string literals in the AST if they can be joined (it doesn't mean that
|
||||
// they'll be joined in the formatted output but they could).
|
||||
// Comparable expression handles some of this by comparing the concatenated string
|
||||
// but not joining here doesn't play nicely with other string normalizations done in the
|
||||
// Normalizer.
|
||||
match expr {
|
||||
Expr::StringLiteral(string) => {
|
||||
if string.value.is_implicit_concatenated() {
|
||||
let can_join = string.value.iter().all(|literal| {
|
||||
!literal.flags.is_triple_quoted() && !literal.flags.prefix().is_raw()
|
||||
});
|
||||
|
||||
if can_join {
|
||||
string.value = ast::StringLiteralValue::single(ast::StringLiteral {
|
||||
value: string.value.to_str().to_string().into_boxed_str(),
|
||||
range: string.range,
|
||||
flags: StringLiteralFlags::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::BytesLiteral(bytes) => {
|
||||
if bytes.value.is_implicit_concatenated() {
|
||||
let can_join = bytes.value.iter().all(|literal| {
|
||||
!literal.flags.is_triple_quoted() && !literal.flags.prefix().is_raw()
|
||||
});
|
||||
|
||||
if can_join {
|
||||
bytes.value = ast::BytesLiteralValue::single(ast::BytesLiteral {
|
||||
value: bytes.value.bytes().collect(),
|
||||
range: bytes.range,
|
||||
flags: BytesLiteralFlags::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::FString(fstring) => {
|
||||
if fstring.value.is_implicit_concatenated() {
|
||||
let can_join = fstring.value.iter().all(|part| match part {
|
||||
FStringPart::Literal(literal) => {
|
||||
!literal.flags.is_triple_quoted() && !literal.flags.prefix().is_raw()
|
||||
}
|
||||
FStringPart::FString(string) => {
|
||||
!string.flags.is_triple_quoted() && !string.flags.prefix().is_raw()
|
||||
}
|
||||
});
|
||||
|
||||
if can_join {
|
||||
#[derive(Default)]
|
||||
struct Collector {
|
||||
elements: Vec<FStringElement>,
|
||||
}
|
||||
|
||||
impl Collector {
|
||||
// The logic for concatenating adjacent string literals
|
||||
// occurs here, implicitly: when we encounter a sequence
|
||||
// of string literals, the first gets pushed to the
|
||||
// `elements` vector, while subsequent strings
|
||||
// are concatenated onto this top string.
|
||||
fn push_literal(&mut self, literal: &str, range: TextRange) {
|
||||
if let Some(FStringElement::Literal(existing_literal)) =
|
||||
self.elements.last_mut()
|
||||
{
|
||||
let value = std::mem::take(&mut existing_literal.value);
|
||||
let mut value = value.into_string();
|
||||
value.push_str(literal);
|
||||
existing_literal.value = value.into_boxed_str();
|
||||
existing_literal.range =
|
||||
TextRange::new(existing_literal.start(), range.end());
|
||||
} else {
|
||||
self.elements.push(FStringElement::Literal(
|
||||
FStringLiteralElement {
|
||||
range,
|
||||
value: literal.into(),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn push_expression(
|
||||
&mut self,
|
||||
expression: ast::FStringExpressionElement,
|
||||
) {
|
||||
self.elements.push(FStringElement::Expression(expression));
|
||||
}
|
||||
}
|
||||
|
||||
let mut collector = Collector::default();
|
||||
|
||||
for part in &fstring.value {
|
||||
match part {
|
||||
ast::FStringPart::Literal(string_literal) => {
|
||||
collector
|
||||
.push_literal(&string_literal.value, string_literal.range);
|
||||
}
|
||||
ast::FStringPart::FString(fstring) => {
|
||||
for element in &fstring.elements {
|
||||
match element {
|
||||
ast::FStringElement::Literal(literal) => {
|
||||
collector
|
||||
.push_literal(&literal.value, literal.range);
|
||||
}
|
||||
ast::FStringElement::Expression(expression) => {
|
||||
collector.push_expression(expression.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fstring.value = ast::FStringValue::single(ast::FString {
|
||||
elements: collector.elements.into(),
|
||||
range: fstring.range,
|
||||
flags: FStringFlags::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
transformer::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn visit_string_literal(&self, string_literal: &mut ast::StringLiteral) {
|
||||
static STRIP_DOC_TESTS: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(
|
||||
|
|
|
@ -610,5 +610,3 @@ class C:
|
|||
}
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -667,21 +667,21 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
"Arg #2",
|
||||
"Arg #3",
|
||||
"Arg #4",
|
||||
@@ -315,80 +232,72 @@
|
||||
|
||||
@@ -316,79 +233,75 @@
|
||||
triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched."""
|
||||
|
||||
-assert some_type_of_boolean_expression, (
|
||||
assert some_type_of_boolean_expression, (
|
||||
- "Followed by a really really really long string that is used to provide context to"
|
||||
- " the AssertionError exception."
|
||||
-)
|
||||
+assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception."
|
||||
+ "Followed by a really really really long string that is used to provide context to the AssertionError exception."
|
||||
)
|
||||
|
||||
-assert some_type_of_boolean_expression, (
|
||||
assert some_type_of_boolean_expression, (
|
||||
- "Followed by a really really really long string that is used to provide context to"
|
||||
- " the AssertionError exception, which uses dynamic string {}.".format("formatting")
|
||||
+assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format(
|
||||
+ "formatting"
|
||||
+ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format(
|
||||
+ "formatting"
|
||||
+ )
|
||||
)
|
||||
|
||||
assert some_type_of_boolean_expression, (
|
||||
|
@ -772,7 +772,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
x,
|
||||
y,
|
||||
z,
|
||||
@@ -397,7 +306,7 @@
|
||||
@@ -397,7 +310,7 @@
|
||||
func_with_bad_parens(
|
||||
x,
|
||||
y,
|
||||
|
@ -781,7 +781,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
z,
|
||||
)
|
||||
|
||||
@@ -408,50 +317,27 @@
|
||||
@@ -408,50 +321,27 @@
|
||||
+ CONCATENATED
|
||||
+ "using the '+' operator."
|
||||
)
|
||||
|
@ -813,11 +813,10 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
+backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\"
|
||||
+backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\"
|
||||
|
||||
-short_string = "Hi there."
|
||||
+short_string = "Hi" " there."
|
||||
short_string = "Hi there."
|
||||
|
||||
-func_call(short_string="Hi there.")
|
||||
+func_call(short_string=("Hi" " there."))
|
||||
+func_call(short_string=("Hi there."))
|
||||
|
||||
raw_strings = r"Don't" " get" r" merged" " unless they are all raw."
|
||||
|
||||
|
@ -841,7 +840,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
|
||||
long_unmergable_string_with_pragma = (
|
||||
"This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore
|
||||
@@ -468,51 +354,24 @@
|
||||
@@ -468,51 +358,24 @@
|
||||
" of it."
|
||||
)
|
||||
|
||||
|
@ -902,7 +901,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
)
|
||||
|
||||
dict_with_lambda_values = {
|
||||
@@ -524,65 +383,58 @@
|
||||
@@ -524,65 +387,58 @@
|
||||
|
||||
# Complex string concatenations with a method call in the middle.
|
||||
code = (
|
||||
|
@ -986,7 +985,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share
|
|||
)
|
||||
|
||||
log.info(
|
||||
@@ -590,5 +442,5 @@
|
||||
@@ -590,5 +446,5 @@
|
||||
)
|
||||
|
||||
log.info(
|
||||
|
@ -1232,10 +1231,14 @@ pragma_comment_string2 = "Lines which end with an inline pragma comment of the f
|
|||
|
||||
triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched."""
|
||||
|
||||
assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception."
|
||||
assert some_type_of_boolean_expression, (
|
||||
"Followed by a really really really long string that is used to provide context to the AssertionError exception."
|
||||
)
|
||||
|
||||
assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format(
|
||||
"formatting"
|
||||
assert some_type_of_boolean_expression, (
|
||||
"Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format(
|
||||
"formatting"
|
||||
)
|
||||
)
|
||||
|
||||
assert some_type_of_boolean_expression, (
|
||||
|
@ -1326,9 +1329,9 @@ backslashes = "This is a really long string with \"embedded\" double quotes and
|
|||
backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\"
|
||||
backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\"
|
||||
|
||||
short_string = "Hi" " there."
|
||||
short_string = "Hi there."
|
||||
|
||||
func_call(short_string=("Hi" " there."))
|
||||
func_call(short_string=("Hi there."))
|
||||
|
||||
raw_strings = r"Don't" " get" r" merged" " unless they are all raw."
|
||||
|
||||
|
|
|
@ -614,26 +614,20 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
),
|
||||
varX,
|
||||
varY,
|
||||
@@ -70,9 +69,10 @@
|
||||
def foo(xxxx):
|
||||
@@ -71,8 +70,9 @@
|
||||
for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx:
|
||||
for xxx in xxx_xxxx:
|
||||
- assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), (
|
||||
assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), (
|
||||
- "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}"
|
||||
- .format(xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx))
|
||||
+ assert (
|
||||
+ ("x" in xxx) or (xxx in xxx_xxx_xxxxx)
|
||||
+ ), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format(
|
||||
+ xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)
|
||||
+ "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format(
|
||||
+ xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)
|
||||
+ )
|
||||
)
|
||||
|
||||
|
||||
@@ -80,10 +80,11 @@
|
||||
def disappearing_comment():
|
||||
return (
|
||||
( # xx -x xxxxxxx xx xxx xxxxxxx.
|
||||
- "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format(
|
||||
+ "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx" " {} {{xxxx}} >&2".format(
|
||||
@@ -83,7 +83,8 @@
|
||||
"{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format(
|
||||
"{xxxx} {xxxxxx}"
|
||||
if xxxxx.xx_xxxxxxxxxx
|
||||
- else ( # Disappearing Comment
|
||||
|
@ -689,7 +683,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
+ (
|
||||
+ "xxxxxxxxxx xxxx xx xxxxxx(%x) xx %x xxxx xx xxx %x.xx"
|
||||
+ % (len(self) + 1, xxxx.xxxxxxxxxx, xxxx.xxxxxxxxxx)
|
||||
+ )
|
||||
)
|
||||
+ + (
|
||||
+ " %.3f (%s) to %.3f (%s).\n"
|
||||
+ % (
|
||||
|
@ -698,7 +692,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
+ x,
|
||||
+ xxxx.xxxxxxxxxxxxxx(xx),
|
||||
+ )
|
||||
)
|
||||
+ )
|
||||
)
|
||||
|
||||
|
||||
|
@ -783,7 +777,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
)
|
||||
|
||||
|
||||
@@ -232,39 +248,24 @@
|
||||
@@ -232,36 +248,21 @@
|
||||
|
||||
some_dictionary = {
|
||||
"xxxxx006": [
|
||||
|
@ -827,12 +821,8 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
+ ) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx.
|
||||
|
||||
|
||||
-some_tuple = ("some string", "some string which should be joined")
|
||||
+some_tuple = ("some string", "some string" " which should be joined")
|
||||
|
||||
some_commented_string = ( # This comment stays at the top.
|
||||
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
|
||||
@@ -279,37 +280,26 @@
|
||||
some_tuple = ("some string", "some string which should be joined")
|
||||
@@ -279,34 +280,21 @@
|
||||
)
|
||||
|
||||
lpar_and_rpar_have_comments = func_call( # LPAR Comment
|
||||
|
@ -852,33 +842,28 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
- f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||
-)
|
||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||
+
|
||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||
|
||||
-cmd_fstring = (
|
||||
- "sudo -E deluge-console info --detailed --sort-reverse=time_added"
|
||||
- f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||
-)
|
||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'"
|
||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'"
|
||||
|
||||
-cmd_fstring = (
|
||||
- "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is"
|
||||
- f" None else ID}} | perl -nE 'print if /^{field}:/'"
|
||||
-)
|
||||
+fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}."
|
||||
+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'"
|
||||
|
||||
fstring = (
|
||||
-fstring = (
|
||||
- "This string really doesn't need to be an {{fstring}}, but this one most"
|
||||
- f" certainly, absolutely {does}."
|
||||
+ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
||||
)
|
||||
-)
|
||||
+fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}."
|
||||
|
||||
-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||
-
|
||||
fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||
|
||||
class A:
|
||||
class B:
|
||||
@@ -364,10 +354,7 @@
|
||||
@@ -364,10 +352,7 @@
|
||||
def foo():
|
||||
if not hasattr(module, name):
|
||||
raise ValueError(
|
||||
|
@ -890,7 +875,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
% (name, module_name, get_docs_version())
|
||||
)
|
||||
|
||||
@@ -382,23 +369,19 @@
|
||||
@@ -382,23 +367,19 @@
|
||||
|
||||
class Step(StepBase):
|
||||
def who(self):
|
||||
|
@ -921,7 +906,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
# xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx.
|
||||
"(x.bbbbbbbbbbbb.xxx != "
|
||||
'"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && '
|
||||
@@ -409,8 +392,8 @@
|
||||
@@ -409,8 +390,8 @@
|
||||
if __name__ == "__main__":
|
||||
for i in range(4, 8):
|
||||
cmd = (
|
||||
|
@ -932,7 +917,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
)
|
||||
|
||||
|
||||
@@ -432,9 +415,7 @@
|
||||
@@ -432,9 +413,7 @@
|
||||
assert xxxxxxx_xxxx in [
|
||||
x.xxxxx.xxxxxx.xxxxx.xxxxxx,
|
||||
x.xxxxx.xxxxxx.xxxxx.xxxx,
|
||||
|
@ -943,7 +928,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
|
||||
|
||||
value.__dict__[key] = (
|
||||
@@ -449,8 +430,7 @@
|
||||
@@ -449,8 +428,7 @@
|
||||
|
||||
RE_TWO_BACKSLASHES = {
|
||||
"asdf_hjkl_jkl": re.compile(
|
||||
|
@ -953,23 +938,23 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
),
|
||||
}
|
||||
|
||||
@@ -462,13 +442,9 @@
|
||||
@@ -462,13 +440,9 @@
|
||||
|
||||
# We do NOT split on f-string expressions.
|
||||
print(
|
||||
- "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam."
|
||||
- f" {[f'{i}' for i in range(10)]}"
|
||||
+ f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}"
|
||||
)
|
||||
-)
|
||||
-x = (
|
||||
- "This is a long string which contains an f-expr that should not split"
|
||||
- f" {{{[i for i in range(5)]}}}."
|
||||
-)
|
||||
+ f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}"
|
||||
)
|
||||
+x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}."
|
||||
|
||||
# The parens should NOT be removed in this case.
|
||||
(
|
||||
@@ -478,8 +454,8 @@
|
||||
@@ -478,8 +452,8 @@
|
||||
|
||||
# The parens should NOT be removed in this case.
|
||||
(
|
||||
|
@ -980,7 +965,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
)
|
||||
|
||||
# The parens should NOT be removed in this case.
|
||||
@@ -513,93 +489,83 @@
|
||||
@@ -513,93 +487,83 @@
|
||||
|
||||
|
||||
temp_msg = (
|
||||
|
@ -1110,7 +1095,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
"6. Click on Create Credential at the top."
|
||||
'7. At the top click the link for "API key".'
|
||||
"8. No application restrictions are needed. Click Create at the bottom."
|
||||
@@ -608,60 +574,45 @@
|
||||
@@ -608,7 +572,7 @@
|
||||
|
||||
# It shouldn't matter if the string prefixes are capitalized.
|
||||
temp_msg = (
|
||||
|
@ -1119,11 +1104,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
f"{balance: <{bal_len + 5}} "
|
||||
f"<<{author.display_name}>>\n"
|
||||
)
|
||||
|
||||
-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||
+fstring = (
|
||||
+ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
||||
+)
|
||||
@@ -617,51 +581,34 @@
|
||||
|
||||
welcome_to_programming = R"hello," R" world!"
|
||||
|
||||
|
@ -1189,21 +1170,14 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:
|
|||
)
|
||||
|
||||
# Regression test for https://github.com/psf/black/issues/3455.
|
||||
@@ -672,9 +623,11 @@
|
||||
}
|
||||
|
||||
@@ -674,7 +621,4 @@
|
||||
# Regression test for https://github.com/psf/black/issues/3506.
|
||||
-s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}"
|
||||
-
|
||||
s = (
|
||||
s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}"
|
||||
|
||||
-s = (
|
||||
- "Lorem Ipsum is simply dummy text of the printing and typesetting"
|
||||
- f" industry:'{my_dict['foo']}'"
|
||||
+ "With single quote: ' "
|
||||
+ f" {my_dict['foo']}"
|
||||
+ ' With double quote: " '
|
||||
+ f" {my_dict['bar']}"
|
||||
)
|
||||
+
|
||||
-)
|
||||
+s = f"Lorem Ipsum is simply dummy text of the printing and typesetting industry:'{my_dict['foo']}'"
|
||||
```
|
||||
|
||||
|
@ -1281,10 +1255,10 @@ class A:
|
|||
def foo(xxxx):
|
||||
for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx:
|
||||
for xxx in xxx_xxxx:
|
||||
assert (
|
||||
("x" in xxx) or (xxx in xxx_xxx_xxxxx)
|
||||
), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format(
|
||||
xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)
|
||||
assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), (
|
||||
"{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format(
|
||||
xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -1292,7 +1266,7 @@ class A:
|
|||
def disappearing_comment():
|
||||
return (
|
||||
( # xx -x xxxxxxx xx xxx xxxxxxx.
|
||||
"{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx" " {} {{xxxx}} >&2".format(
|
||||
"{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format(
|
||||
"{xxxx} {xxxxxx}"
|
||||
if xxxxx.xx_xxxxxxxxxx
|
||||
# Disappearing Comment
|
||||
|
@ -1477,7 +1451,7 @@ def foo():
|
|||
) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx.
|
||||
|
||||
|
||||
some_tuple = ("some string", "some string" " which should be joined")
|
||||
some_tuple = ("some string", "some string which should be joined")
|
||||
|
||||
some_commented_string = ( # This comment stays at the top.
|
||||
"This string is long but not so long that it needs hahahah toooooo be so greatttt"
|
||||
|
@ -1508,9 +1482,7 @@ cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added
|
|||
|
||||
fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}."
|
||||
|
||||
fstring = (
|
||||
f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
||||
)
|
||||
fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||
|
||||
|
||||
class A:
|
||||
|
@ -1791,9 +1763,7 @@ temp_msg = (
|
|||
f"<<{author.display_name}>>\n"
|
||||
)
|
||||
|
||||
fstring = (
|
||||
f"We have to remember to escape {braces}." " Like {these}." f" But not {this}."
|
||||
)
|
||||
fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}."
|
||||
|
||||
welcome_to_programming = R"hello," R" world!"
|
||||
|
||||
|
@ -1835,12 +1805,7 @@ a_dict = {
|
|||
}
|
||||
|
||||
# Regression test for https://github.com/psf/black/issues/3506.
|
||||
s = (
|
||||
"With single quote: ' "
|
||||
f" {my_dict['foo']}"
|
||||
' With double quote: " '
|
||||
f" {my_dict['bar']}"
|
||||
)
|
||||
s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}"
|
||||
|
||||
s = f"Lorem Ipsum is simply dummy text of the printing and typesetting industry:'{my_dict['foo']}'"
|
||||
```
|
||||
|
|
|
@ -45,8 +45,9 @@ def func(
|
|||
|
||||
def func(
|
||||
- argument: "int |" "str",
|
||||
+ argument: ("int |" "str"),
|
||||
) -> Set["int |" " str"]:
|
||||
-) -> Set["int |" " str"]:
|
||||
+ argument: ("int |str"),
|
||||
+) -> Set["int | str"]:
|
||||
pass
|
||||
```
|
||||
|
||||
|
@ -76,8 +77,8 @@ def func(
|
|||
|
||||
|
||||
def func(
|
||||
argument: ("int |" "str"),
|
||||
) -> Set["int |" " str"]:
|
||||
argument: ("int |str"),
|
||||
) -> Set["int | str"]:
|
||||
pass
|
||||
```
|
||||
|
||||
|
@ -111,5 +112,3 @@ def func(
|
|||
) -> Set["int |" " str"]:
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -366,22 +366,13 @@ actual: {some_var}"""
|
|||
[
|
||||
"""cow
|
||||
moos""",
|
||||
@@ -198,7 +239,7 @@
|
||||
`--global-option` is reserved to flags like `--verbose` or `--quiet`.
|
||||
"""
|
||||
|
||||
-this_will_become_one_line = "abc"
|
||||
+this_will_become_one_line = "a" "b" "c"
|
||||
|
||||
this_will_stay_on_three_lines = (
|
||||
"a" # comment
|
||||
@@ -206,7 +247,9 @@
|
||||
"c"
|
||||
)
|
||||
|
||||
-this_will_also_become_one_line = "abc" # comment
|
||||
+this_will_also_become_one_line = ( # comment
|
||||
+ "a" "b" "c"
|
||||
+ "abc"
|
||||
+)
|
||||
|
||||
assert some_var == expected_result, """
|
||||
|
@ -632,7 +623,7 @@ Please use `--build-option` instead,
|
|||
`--global-option` is reserved to flags like `--verbose` or `--quiet`.
|
||||
"""
|
||||
|
||||
this_will_become_one_line = "a" "b" "c"
|
||||
this_will_become_one_line = "abc"
|
||||
|
||||
this_will_stay_on_three_lines = (
|
||||
"a" # comment
|
||||
|
@ -641,7 +632,7 @@ this_will_stay_on_three_lines = (
|
|||
)
|
||||
|
||||
this_will_also_become_one_line = ( # comment
|
||||
"a" "b" "c"
|
||||
"abc"
|
||||
)
|
||||
|
||||
assert some_var == expected_result, """
|
||||
|
|
|
@ -175,5 +175,3 @@ assert xxxxxxxxx.xxxxxxxxx.xxxxxxxxx(
|
|||
"xxx {xxxxxxxxx} xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
|
|
50
crates/ruff_python_formatter/tests/snapshots/format.snap
Normal file
50
crates/ruff_python_formatter/tests/snapshots/format.snap
Normal file
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.py
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
"diffent '" 'quote "are fine"' # join
|
||||
|
||||
# More single quotes
|
||||
"one single'" "two 'single'" ' two "double"'
|
||||
|
||||
# More double quotes
|
||||
'one double"' 'two "double"' " two 'single'"
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double"' " two 'single'"
|
||||
|
||||
f"{'Hy \"User\"'}"
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Preserve
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py38
|
||||
source_type = Python
|
||||
```
|
||||
|
||||
```python
|
||||
"diffent 'quote \"are fine\"" # join
|
||||
|
||||
# More single quotes
|
||||
"one single'two 'single' two \"double\""
|
||||
|
||||
# More double quotes
|
||||
'one double"two "double" two \'single\''
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double" two \'single\''
|
||||
|
||||
f"{'Hy "User"'}"
|
||||
```
|
|
@ -406,4 +406,19 @@ class EC2REPATH:
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -197,8 +197,8 @@
|
||||
"dddddddddddddddddddddddddd" % aaaaaaaaaaaa + x
|
||||
)
|
||||
|
||||
-"a" "b" "c" + "d" "e" + "f" "g" + "h" "i" "j"
|
||||
+"abc" + "de" + "fg" + "hij"
|
||||
|
||||
|
||||
class EC2REPATH:
|
||||
- f.write("Pathway name" + "\t" "Database Identifier" + "\t" "Source database" + "\n")
|
||||
+ f.write("Pathway name" + "\tDatabase Identifier" + "\tSource database" + "\n")
|
||||
```
|
||||
|
|
|
@ -285,6 +285,33 @@ b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}"
|
|||
```
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -63,9 +63,9 @@
|
||||
|
||||
# String continuation
|
||||
|
||||
-b"Let's" b"start" b"with" b"a" b"simple" b"example"
|
||||
+b"Let'sstartwithasimpleexample"
|
||||
|
||||
-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'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident"
|
||||
|
||||
(
|
||||
b"Let's"
|
||||
@@ -132,6 +132,6 @@
|
||||
]
|
||||
|
||||
# Parenthesized string continuation with messed up indentation
|
||||
-{"key": ([], b"a" b"b" b"c")}
|
||||
+{"key": ([], b"abc")}
|
||||
|
||||
b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}"
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = space
|
||||
|
@ -441,4 +468,28 @@ b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}"
|
|||
```
|
||||
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -63,9 +63,9 @@
|
||||
|
||||
# String continuation
|
||||
|
||||
-b"Let's" b'start' b'with' b'a' b'simple' b'example'
|
||||
+b"Let'sstartwithasimpleexample"
|
||||
|
||||
-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'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident"
|
||||
|
||||
(
|
||||
b"Let's"
|
||||
@@ -132,6 +132,6 @@
|
||||
]
|
||||
|
||||
# Parenthesized string continuation with messed up indentation
|
||||
-{'key': ([], b'a' b'b' b'c')}
|
||||
+{'key': ([], b'abc')}
|
||||
|
||||
b"Unicode Escape sequence don't apply to bytes: \N{0x} \u{ABCD} \U{ABCDEFGH}"
|
||||
```
|
||||
|
|
|
@ -371,7 +371,7 @@ source_type = Python
|
|||
```
|
||||
|
||||
```python
|
||||
(f"{one}" f"{two}")
|
||||
(f"{one}{two}")
|
||||
|
||||
|
||||
rf"Not-so-tricky \"quote"
|
||||
|
@ -411,7 +411,7 @@ result_f = (
|
|||
)
|
||||
|
||||
(
|
||||
f"{1}" f"{2}" # comment 3
|
||||
f"{1}{2}" # comment 3
|
||||
)
|
||||
|
||||
(
|
||||
|
@ -1097,6 +1097,12 @@ _ = (
|
|||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -1,4 +1,4 @@
|
||||
-(f"{one}" f"{two}")
|
||||
+(f"{one}{two}")
|
||||
|
||||
|
||||
rf"Not-so-tricky \"quote"
|
||||
@@ -6,13 +6,13 @@
|
||||
# Regression test for fstrings dropping comments
|
||||
result_f = (
|
||||
|
@ -1115,6 +1121,15 @@ _ = (
|
|||
" f()\n"
|
||||
# XXX: The following line changes depending on whether the tests
|
||||
# are run through the interactive interpreter or with -m
|
||||
@@ -38,7 +38,7 @@
|
||||
)
|
||||
|
||||
(
|
||||
- f"{1}" f"{2}" # comment 3
|
||||
+ f"{1}{2}" # comment 3
|
||||
)
|
||||
|
||||
(
|
||||
@@ -67,29 +67,31 @@
|
||||
x = f"{a}"
|
||||
x = f"{
|
||||
|
|
|
@ -0,0 +1,735 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string.py
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
"aaaaaaaaa" "bbbbbbbbbbbbbbbbbbbb" # Join
|
||||
|
||||
(
|
||||
"aaaaaaaaaaa" "bbbbbbbbbbbbbbbb"
|
||||
) # join
|
||||
|
||||
|
||||
(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
) # too long to join
|
||||
|
||||
|
||||
"different '" 'quote "are fine"' # join
|
||||
|
||||
# More single quotes
|
||||
"one single'" "two 'single'" ' two "double"'
|
||||
|
||||
# More double quotes
|
||||
'one double"' 'two "double"' " two 'single'"
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double"' " two 'single'"
|
||||
|
||||
f"{'Hy \"User\"'}" 'more'
|
||||
|
||||
b"aaaaaaaaa" b"bbbbbbbbbbbbbbbbbbbb" # Join
|
||||
|
||||
(
|
||||
b"aaaaaaaaaaa" b"bbbbbbbbbbbbbbbb"
|
||||
) # join
|
||||
|
||||
|
||||
(
|
||||
b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
) # too long to join
|
||||
|
||||
|
||||
# Skip joining if there is a trailing comment
|
||||
(
|
||||
"fffffffffffff"
|
||||
"bbbbbbbbbbbbb" # comment
|
||||
"cccccccccccccc"
|
||||
)
|
||||
|
||||
# Skip joining if there is a leading comment
|
||||
(
|
||||
"fffffffffffff"
|
||||
# comment
|
||||
"bbbbbbbbbbbbb"
|
||||
"cccccccccccccc"
|
||||
)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# F-strings
|
||||
##############################################################################
|
||||
|
||||
# Escape `{` and `}` when marging an f-string with a string
|
||||
"a {not_a_variable}" f"b {10}" "c"
|
||||
|
||||
# Join, and break expressions
|
||||
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{
|
||||
expression
|
||||
}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" f"cccccccccccccccccccc {20999}" "more"
|
||||
|
||||
# Join, but don't break the expressions
|
||||
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" f"cccccccccccccccccccc {20999}" "more"
|
||||
|
||||
f"test{
|
||||
expression
|
||||
}flat" f"can be {
|
||||
joined
|
||||
} together"
|
||||
|
||||
aaaaaaaaaaa = f"test{
|
||||
expression
|
||||
}flat" f"cean beeeeeeee {
|
||||
joined
|
||||
} eeeeeeeeeeeeeeeeeeeeeeeeeeeee" # inline
|
||||
|
||||
|
||||
f"single quoted '{x}'" f'double quoted "{x}"' # Same number of quotes => use preferred quote style
|
||||
f"single quote ' {x}" f'double quoted "{x}"' # More double quotes => use single quotes
|
||||
f"single quoted '{x}'" f'double quote " {x}"' # More single quotes => use double quotes
|
||||
|
||||
# Different triple quoted strings
|
||||
f"{'''test'''}" f'{"""other"""}'
|
||||
|
||||
# Now with inner quotes
|
||||
f"{'''test ' '''}" f'{"""other " """}'
|
||||
f"{some_where_nested('''test ' ''')}" f'{"""other " """ + "more"}'
|
||||
f"{b'''test ' '''}" f'{b"""other " """}'
|
||||
f"{f'''test ' '''}" f'{f"""other " """}'
|
||||
|
||||
# debug expressions containing quotes
|
||||
f"{10 + len('bar')=}" f"{10 + len('bar')=}"
|
||||
f"{10 + len('bar')=}" f'no debug{10}' f"{10 + len('bar')=}"
|
||||
|
||||
# We can't savely merge this pre Python 3.12 without altering the debug expression.
|
||||
f"{10 + len('bar')=}" f'{10 + len("bar")=}'
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Don't join raw strings
|
||||
##############################################################################
|
||||
|
||||
r"a" "normal"
|
||||
R"a" "normal"
|
||||
|
||||
f"test" fr"test"
|
||||
f"test" fR"test"
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Don't join triple quoted strings
|
||||
##############################################################################
|
||||
|
||||
"single" """triple"""
|
||||
|
||||
"single" f""""single"""
|
||||
|
||||
b"single" b"""triple"""
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Join strings in with statements
|
||||
##############################################################################
|
||||
|
||||
# Fits
|
||||
with "aa" "bbb" "cccccccccccccccccccccccccccccccccccccccccccccc":
|
||||
pass
|
||||
|
||||
# Parenthesize single-line
|
||||
with "aa" "bbb" "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc":
|
||||
pass
|
||||
|
||||
# Multiline
|
||||
with "aa" "bbb" "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc":
|
||||
pass
|
||||
|
||||
with f"aaaaaaa{expression}bbbb" f"ccc {20999}" "more":
|
||||
pass
|
||||
|
||||
|
||||
##############################################################################
|
||||
# For loops
|
||||
##############################################################################
|
||||
|
||||
# Flat
|
||||
for a in "aaaaaaaaa" "bbbbbbbbb" "ccccccccc" "dddddddddd":
|
||||
pass
|
||||
|
||||
# Parenthesize single-line
|
||||
for a in "aaaaaaaaa" "bbbbbbbbb" "ccccccccc" "dddddddddd" "eeeeeeeeeeeeeee" "fffffffffffff" "ggggggggggggggg" "hh":
|
||||
pass
|
||||
|
||||
# Multiline
|
||||
for a in "aaaaaaaaa" "bbbbbbbbb" "ccccccccc" "dddddddddd" "eeeeeeeeeeeeeee" "fffffffffffff" "ggggggggggggggg" "hhhh":
|
||||
pass
|
||||
|
||||
##############################################################################
|
||||
# Assert statement
|
||||
##############################################################################
|
||||
|
||||
# Fits
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb", "cccccccccccccccc" "dddddddddddddddd"
|
||||
|
||||
# Wrap right
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb", "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffff"
|
||||
|
||||
# Right multiline
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb", "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffffffff" "ggggggggggggg" "hhhhhhhhhhh"
|
||||
|
||||
# Wrap left
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb" "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffffffff", "ggggggggggggg" "hhhhhhhhhhh"
|
||||
|
||||
# Left multiline
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb" "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffffffff" "ggggggggggggg", "hhhhhhhhhhh"
|
||||
|
||||
# wrap both
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb" "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffffffff", "ggggggggggggg" "hhhhhhhhhhh" "iiiiiiiiiiiiiiiiii" "jjjjjjjjjjjjj" "kkkkkkkkkkkkkkkkk" "llllllllllll"
|
||||
|
||||
# both multiline
|
||||
assert "aaaaaaaaa" "bbbbbbbbbbbb" "cccccccccccccccc" "dddddddddddddddd" "eeeeeeeeeeeee" "fffffffffffffff" "ggggggggggggg", "hhhhhhhhhhh" "iiiiiiiiiiiiiiiiii" "jjjjjjjjjjjjj" "kkkkkkkkkkkkkkkkk" "llllllllllll" "mmmmmmmmmmmmmm"
|
||||
|
||||
|
||||
##############################################################################
|
||||
# In clause headers (can_omit_optional_parentheses)
|
||||
##############################################################################
|
||||
# Use can_omit_optional_parentheses layout to avoid an instability where the formatter
|
||||
# picks the can_omit_optional_parentheses layout when the strings are joined.
|
||||
if (
|
||||
f"implicit"
|
||||
"concatenated"
|
||||
"string" + f"implicit"
|
||||
"concaddddddddddded"
|
||||
"ring"
|
||||
* len([aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd])
|
||||
):
|
||||
pass
|
||||
|
||||
# Keep parenthesizing multiline - implicit concatenated strings
|
||||
if (
|
||||
f"implicit"
|
||||
"""concatenate
|
||||
d"""
|
||||
"string" + f"implicit"
|
||||
"concaddddddddddded"
|
||||
"ring"
|
||||
* len([aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd])
|
||||
):
|
||||
pass
|
||||
|
||||
if (
|
||||
[
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]
|
||||
+ "implicitconcat"
|
||||
"enatedstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# In match statements
|
||||
match x:
|
||||
case "implicitconcat" "enatedstring" | [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]:
|
||||
pass
|
||||
|
||||
case [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
] | "implicitconcat" "enatedstring" :
|
||||
pass
|
||||
|
||||
case "implicitconcat" "enatedstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing" | [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]:
|
||||
pass
|
||||
|
||||
|
||||
##############################################################################
|
||||
# In docstring positions
|
||||
##############################################################################
|
||||
|
||||
def short_docstring():
|
||||
"Implicit" "concatenated" "docstring"
|
||||
|
||||
def long_docstring():
|
||||
"Loooooooooooooooooooooong" "doooooooooooooooooooocstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing" "exceding the line width" "but it should be concatenated anyways because it is single line"
|
||||
|
||||
def docstring_with_leading_whitespace():
|
||||
" This is a " "implicit" "concatenated" "docstring"
|
||||
|
||||
def docstring_with_trailing_whitespace():
|
||||
"This is a " "implicit" "concatenated" "docstring "
|
||||
|
||||
def docstring_with_leading_empty_parts():
|
||||
" " " " "" "This is a " "implicit" "concatenated" "docstring"
|
||||
|
||||
def docstring_with_trailing_empty_parts():
|
||||
"This is a " "implicit" "concatenated" "docstring" "" " " " "
|
||||
|
||||
def all_empty():
|
||||
" " " " " "
|
||||
|
||||
def byte_string_in_docstring_position():
|
||||
b" don't trim the" b"bytes literal "
|
||||
|
||||
def f_string_in_docstring_position():
|
||||
f" don't trim the" "f-string literal "
|
||||
|
||||
def single_quoted():
|
||||
' content\ ' ' '
|
||||
return
|
||||
|
||||
def implicit_with_comment():
|
||||
(
|
||||
"a"
|
||||
# leading
|
||||
"the comment above"
|
||||
)
|
||||
|
||||
##############################################################################
|
||||
# Regressions
|
||||
##############################################################################
|
||||
|
||||
LEEEEEEEEEEEEEEEEEEEEEEFT = RRRRRRRRIIIIIIIIIIIIGGGGGHHHT | {
|
||||
"entityNameeeeeeeeeeeeeeeeee", # comment must be long enough to
|
||||
"some long implicit concatenated string" "that should join"
|
||||
}
|
||||
|
||||
# Ensure that flipping between Multiline and BestFit layout results in stable formatting
|
||||
# when using IfBreaksParenthesized layout.
|
||||
assert False, "Implicit concatenated string" "uses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
|
||||
assert False, await "Implicit concatenated string" "uses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
|
||||
assert False, "Implicit concatenated stringuses {} layout on {} format"[
|
||||
aaaaaaaaa, bbbbbb
|
||||
]
|
||||
|
||||
assert False, +"Implicit concatenated string" "uses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Double
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py38
|
||||
source_type = Python
|
||||
```
|
||||
|
||||
```python
|
||||
"aaaaaaaaabbbbbbbbbbbbbbbbbbbb" # Join
|
||||
|
||||
("aaaaaaaaaaabbbbbbbbbbbbbbbb") # join
|
||||
|
||||
|
||||
(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
) # too long to join
|
||||
|
||||
|
||||
'different \'quote "are fine"' # join
|
||||
|
||||
# More single quotes
|
||||
"one single'two 'single' two \"double\""
|
||||
|
||||
# More double quotes
|
||||
'one double"two "double" two \'single\''
|
||||
|
||||
# Equal number of single and double quotes
|
||||
"two \"double\" two 'single'"
|
||||
|
||||
f"{'Hy "User"'}more"
|
||||
|
||||
b"aaaaaaaaabbbbbbbbbbbbbbbbbbbb" # Join
|
||||
|
||||
(b"aaaaaaaaaaabbbbbbbbbbbbbbbb") # join
|
||||
|
||||
|
||||
(
|
||||
b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
) # too long to join
|
||||
|
||||
|
||||
# Skip joining if there is a trailing comment
|
||||
(
|
||||
"fffffffffffff"
|
||||
"bbbbbbbbbbbbb" # comment
|
||||
"cccccccccccccc"
|
||||
)
|
||||
|
||||
# Skip joining if there is a leading comment
|
||||
(
|
||||
"fffffffffffff"
|
||||
# comment
|
||||
"bbbbbbbbbbbbb"
|
||||
"cccccccccccccc"
|
||||
)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# F-strings
|
||||
##############################################################################
|
||||
|
||||
# Escape `{` and `}` when marging an f-string with a string
|
||||
f"a {{not_a_variable}}b {10}c"
|
||||
|
||||
# Join, and break expressions
|
||||
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{
|
||||
expression
|
||||
}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc {20999}more"
|
||||
|
||||
# Join, but don't break the expressions
|
||||
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{expression}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc {20999}more"
|
||||
|
||||
f"test{expression}flatcan be {joined} together"
|
||||
|
||||
aaaaaaaaaaa = (
|
||||
f"test{expression}flat"
|
||||
f"cean beeeeeeee {joined} eeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
) # inline
|
||||
|
||||
|
||||
f"single quoted '{x}'double quoted \"{x}\"" # Same number of quotes => use preferred quote style
|
||||
f'single quote \' {x}double quoted "{x}"' # More double quotes => use single quotes
|
||||
f"single quoted '{x}'double quote \" {x}\"" # More single quotes => use double quotes
|
||||
|
||||
# Different triple quoted strings
|
||||
f"{'''test'''}{'''other'''}"
|
||||
|
||||
# Now with inner quotes
|
||||
f"{'''test ' '''}" f'{"""other " """}'
|
||||
f"{some_where_nested('''test ' ''')}" f'{"""other " """ + "more"}'
|
||||
f"{b'''test ' '''}" f'{b"""other " """}'
|
||||
f"{f'''test ' '''}" f'{f"""other " """}'
|
||||
|
||||
# debug expressions containing quotes
|
||||
f"{10 + len('bar')=}{10 + len('bar')=}"
|
||||
f"{10 + len('bar')=}no debug{10}{10 + len('bar')=}"
|
||||
|
||||
# We can't savely merge this pre Python 3.12 without altering the debug expression.
|
||||
f"{10 + len('bar')=}" f'{10 + len("bar")=}'
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Don't join raw strings
|
||||
##############################################################################
|
||||
|
||||
r"a" "normal"
|
||||
R"a" "normal"
|
||||
|
||||
f"test" rf"test"
|
||||
f"test" Rf"test"
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Don't join triple quoted strings
|
||||
##############################################################################
|
||||
|
||||
"single" """triple"""
|
||||
|
||||
"single" f""""single"""
|
||||
|
||||
b"single" b"""triple"""
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Join strings in with statements
|
||||
##############################################################################
|
||||
|
||||
# Fits
|
||||
with "aabbbcccccccccccccccccccccccccccccccccccccccccccccc":
|
||||
pass
|
||||
|
||||
# Parenthesize single-line
|
||||
with (
|
||||
"aabbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
):
|
||||
pass
|
||||
|
||||
# Multiline
|
||||
with (
|
||||
"aa"
|
||||
"bbb"
|
||||
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
):
|
||||
pass
|
||||
|
||||
with f"aaaaaaa{expression}bbbbccc {20999}more":
|
||||
pass
|
||||
|
||||
|
||||
##############################################################################
|
||||
# For loops
|
||||
##############################################################################
|
||||
|
||||
# Flat
|
||||
for a in "aaaaaaaaabbbbbbbbbcccccccccdddddddddd":
|
||||
pass
|
||||
|
||||
# Parenthesize single-line
|
||||
for a in (
|
||||
"aaaaaaaaabbbbbbbbbcccccccccddddddddddeeeeeeeeeeeeeeefffffffffffffggggggggggggggghh"
|
||||
):
|
||||
pass
|
||||
|
||||
# Multiline
|
||||
for a in (
|
||||
"aaaaaaaaa"
|
||||
"bbbbbbbbb"
|
||||
"ccccccccc"
|
||||
"dddddddddd"
|
||||
"eeeeeeeeeeeeeee"
|
||||
"fffffffffffff"
|
||||
"ggggggggggggggg"
|
||||
"hhhh"
|
||||
):
|
||||
pass
|
||||
|
||||
##############################################################################
|
||||
# Assert statement
|
||||
##############################################################################
|
||||
|
||||
# Fits
|
||||
assert "aaaaaaaaabbbbbbbbbbbb", "ccccccccccccccccdddddddddddddddd"
|
||||
|
||||
# Wrap right
|
||||
assert "aaaaaaaaabbbbbbbbbbbb", (
|
||||
"ccccccccccccccccddddddddddddddddeeeeeeeeeeeeefffffffffff"
|
||||
)
|
||||
|
||||
# Right multiline
|
||||
assert "aaaaaaaaabbbbbbbbbbbb", (
|
||||
"cccccccccccccccc"
|
||||
"dddddddddddddddd"
|
||||
"eeeeeeeeeeeee"
|
||||
"fffffffffffffff"
|
||||
"ggggggggggggg"
|
||||
"hhhhhhhhhhh"
|
||||
)
|
||||
|
||||
# Wrap left
|
||||
assert (
|
||||
"aaaaaaaaabbbbbbbbbbbbccccccccccccccccddddddddddddddddeeeeeeeeeeeeefffffffffffffff"
|
||||
), "ggggggggggggghhhhhhhhhhh"
|
||||
|
||||
# Left multiline
|
||||
assert (
|
||||
"aaaaaaaaa"
|
||||
"bbbbbbbbbbbb"
|
||||
"cccccccccccccccc"
|
||||
"dddddddddddddddd"
|
||||
"eeeeeeeeeeeee"
|
||||
"fffffffffffffff"
|
||||
"ggggggggggggg"
|
||||
), "hhhhhhhhhhh"
|
||||
|
||||
# wrap both
|
||||
assert (
|
||||
"aaaaaaaaabbbbbbbbbbbbccccccccccccccccddddddddddddddddeeeeeeeeeeeeefffffffffffffff"
|
||||
), (
|
||||
"ggggggggggggg"
|
||||
"hhhhhhhhhhh"
|
||||
"iiiiiiiiiiiiiiiiii"
|
||||
"jjjjjjjjjjjjj"
|
||||
"kkkkkkkkkkkkkkkkk"
|
||||
"llllllllllll"
|
||||
)
|
||||
|
||||
# both multiline
|
||||
assert (
|
||||
"aaaaaaaaa"
|
||||
"bbbbbbbbbbbb"
|
||||
"cccccccccccccccc"
|
||||
"dddddddddddddddd"
|
||||
"eeeeeeeeeeeee"
|
||||
"fffffffffffffff"
|
||||
"ggggggggggggg"
|
||||
), (
|
||||
"hhhhhhhhhhh"
|
||||
"iiiiiiiiiiiiiiiiii"
|
||||
"jjjjjjjjjjjjj"
|
||||
"kkkkkkkkkkkkkkkkk"
|
||||
"llllllllllll"
|
||||
"mmmmmmmmmmmmmm"
|
||||
)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# In clause headers (can_omit_optional_parentheses)
|
||||
##############################################################################
|
||||
# Use can_omit_optional_parentheses layout to avoid an instability where the formatter
|
||||
# picks the can_omit_optional_parentheses layout when the strings are joined.
|
||||
if f"implicitconcatenatedstring" + f"implicitconcadddddddddddedring" * len([
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]):
|
||||
pass
|
||||
|
||||
# Keep parenthesizing multiline - implicit concatenated strings
|
||||
if (
|
||||
f"implicit"
|
||||
"""concatenate
|
||||
d"""
|
||||
"string" + f"implicit"
|
||||
"concaddddddddddded"
|
||||
"ring"
|
||||
* len([aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd])
|
||||
):
|
||||
pass
|
||||
|
||||
if (
|
||||
[
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]
|
||||
+ "implicitconcat"
|
||||
"enatedstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# In match statements
|
||||
match x:
|
||||
case "implicitconcatenatedstring" | [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]:
|
||||
pass
|
||||
|
||||
case [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
] | "implicitconcatenatedstring":
|
||||
pass
|
||||
|
||||
case (
|
||||
"implicitconcat"
|
||||
"enatedstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing"
|
||||
| [
|
||||
aaaaaa,
|
||||
bbbbbbbbbbbbbbbb,
|
||||
cccccccccccccccccc,
|
||||
ddddddddddddddddddddddddddd,
|
||||
]
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
##############################################################################
|
||||
# In docstring positions
|
||||
##############################################################################
|
||||
|
||||
|
||||
def short_docstring():
|
||||
"Implicitconcatenateddocstring"
|
||||
|
||||
|
||||
def long_docstring():
|
||||
"Loooooooooooooooooooooongdoooooooooooooooooooocstriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiingexceding the line widthbut it should be concatenated anyways because it is single line"
|
||||
|
||||
|
||||
def docstring_with_leading_whitespace():
|
||||
"This is a implicitconcatenateddocstring"
|
||||
|
||||
|
||||
def docstring_with_trailing_whitespace():
|
||||
"This is a implicitconcatenateddocstring"
|
||||
|
||||
|
||||
def docstring_with_leading_empty_parts():
|
||||
"This is a implicitconcatenateddocstring"
|
||||
|
||||
|
||||
def docstring_with_trailing_empty_parts():
|
||||
"This is a implicitconcatenateddocstring"
|
||||
|
||||
|
||||
def all_empty():
|
||||
""
|
||||
|
||||
|
||||
def byte_string_in_docstring_position():
|
||||
b" don't trim thebytes literal "
|
||||
|
||||
|
||||
def f_string_in_docstring_position():
|
||||
f" don't trim thef-string literal "
|
||||
|
||||
|
||||
def single_quoted():
|
||||
"content\ "
|
||||
return
|
||||
|
||||
|
||||
def implicit_with_comment():
|
||||
(
|
||||
"a"
|
||||
# leading
|
||||
"the comment above"
|
||||
)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Regressions
|
||||
##############################################################################
|
||||
|
||||
LEEEEEEEEEEEEEEEEEEEEEEFT = RRRRRRRRIIIIIIIIIIIIGGGGGHHHT | {
|
||||
"entityNameeeeeeeeeeeeeeeeee", # comment must be long enough to
|
||||
"some long implicit concatenated stringthat should join",
|
||||
}
|
||||
|
||||
# Ensure that flipping between Multiline and BestFit layout results in stable formatting
|
||||
# when using IfBreaksParenthesized layout.
|
||||
assert False, "Implicit concatenated stringuses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
|
||||
assert False, await "Implicit concatenated stringuses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
|
||||
assert False, "Implicit concatenated stringuses {} layout on {} format"[
|
||||
aaaaaaaaa, bbbbbb
|
||||
]
|
||||
|
||||
assert False, +"Implicit concatenated stringuses {} layout on {} format".format(
|
||||
"Multiline", "first"
|
||||
)
|
||||
```
|
|
@ -0,0 +1,637 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_assignment.py
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
## Implicit concatenated strings with a trailing comment but a non splittable target.
|
||||
|
||||
# Don't join the string because the joined string with the inlined comment exceeds the line length limit.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv"
|
||||
) # c
|
||||
|
||||
# This is the same string as above and should lead to the same formatting. The only difference is that we start
|
||||
# with an unparenthesized string.
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv" # c
|
||||
|
||||
# Again the same string as above but this time as non-implicit concatenated string.
|
||||
# It's okay if the formatting differs because it's an explicit choice to use implicit concatenation.
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv" # c
|
||||
|
||||
# Join the string because it's exactly in the line length limit when the comment is inlined.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv"
|
||||
) # c
|
||||
|
||||
# This is the same string as above and should lead to the same formatting. The only difference is that we start
|
||||
# with an unparenthesized string.
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv" # c
|
||||
|
||||
# Again the same string as above but as a non-implicit concatenated string. It should result in the same formatting
|
||||
# (for consistency).
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv" # c
|
||||
|
||||
# It should collapse the parentheses if the joined string and the comment fit on the same line.
|
||||
# This is required for stability.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvv" # c
|
||||
)
|
||||
|
||||
|
||||
#############################################################
|
||||
# Assignments where the target or annotations are splittable
|
||||
#############################################################
|
||||
|
||||
|
||||
# The target splits because of a magic trailing comma
|
||||
# The string is joined and not parenthesized because it just fits into the line length (including comment).
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = "ccccccccccccccccccccccccccccc" "cccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target splits because of the magic trailing comma
|
||||
# The string is **not** joined because it with the inlined comment exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = "ccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# The target should be flat
|
||||
# The string should be joined because it fits into the line length
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = (
|
||||
"ccccccccccccccccccccccccccccccccccc" "cccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target should be flat
|
||||
# The string gets parenthesized because it, with the inlined comment, exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = "ccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# Split an overlong target, but join the string if it fits
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccc" "cccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Split both if necessary and keep multiline
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
#########################################################
|
||||
# Leading or trailing own line comments:
|
||||
# Preserve the parentheses
|
||||
########################################################
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = (
|
||||
# test
|
||||
"ccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
)
|
||||
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = (
|
||||
"ccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
# test
|
||||
)
|
||||
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
# test
|
||||
)
|
||||
|
||||
|
||||
#############################################################
|
||||
# Type alias statements
|
||||
#############################################################
|
||||
|
||||
# First break the right, join the string
|
||||
type A[str, int, number] = "Literal[string, int] | None | " "CustomType" "| OtherCustomTypeExcee" # comment
|
||||
|
||||
# Keep multiline if overlong
|
||||
type A[str, int, number] = "Literal[string, int] | None | " "CustomTypeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" # comment
|
||||
|
||||
# Break the left if it is over-long, join the string
|
||||
type Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[stringgggggggggg, inttttttttttttttttttttttt, number] = "Literal[string, int] | None | " "CustomType" # comment
|
||||
|
||||
# Break both if necessary and keep multiline
|
||||
type Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[stringgggggggggg, inttttttttttttttttttttttt, number] = "Literal[string, int] | None | " "CustomTypeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" # comment
|
||||
|
||||
|
||||
#############################################################
|
||||
# F-Strings
|
||||
#############################################################
|
||||
|
||||
# Flatten and join the f-string
|
||||
aaaaaaaaaaa = f"test{
|
||||
expression}flat" f"cean beeeeeeee {joined} eeeeeeeeeeeeeeeee" # inline
|
||||
|
||||
# Parenthesize the value and join it, inline the comment
|
||||
aaaaaaaaaaa = f"test{
|
||||
expression}flat" f"cean beeeeeeee {joined} eeeeeeeeeeeeeeeeeeeeeeeeeee" # inline
|
||||
|
||||
# Parenthesize the f-string and keep it multiline because it doesn't fit on a single line including the comment
|
||||
aaaaaaaaaaa = f"test{
|
||||
expression
|
||||
}flat" f"cean beeeeeeee {
|
||||
joined
|
||||
} eeeeeeeeeeeeeeeeeeeeeeeeeeeee" # inline
|
||||
|
||||
|
||||
# The target splits because of a magic trailing comma
|
||||
# The string is joined and not parenthesized because it just fits into the line length (including comment).
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = f"ccccc{
|
||||
expression}ccccccccccc" f"cccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = f"ccccc{
|
||||
expression}ccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target splits because of the magic trailing comma
|
||||
# The string is **not** joined because it with the inlined comment exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = f"ccccc{
|
||||
expression}cccccccccccccccccccc" f"cccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# The target should be flat
|
||||
# The string should be joined because it fits into the line length
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = (
|
||||
f"ccccc{
|
||||
expression}ccccccccccc" "cccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = f"ccccc{
|
||||
expression}ccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target should be flat
|
||||
# The string gets parenthesized because it, with the inlined comment, exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
] = f"ccccc{
|
||||
expression}ccccccccccc" "ccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# Split an overlong target, but join the string if it fits
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
f"ccccc{
|
||||
expression}ccccccccccc" "cccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Split both if necessary and keep multiline
|
||||
a[
|
||||
aaaaaaa,
|
||||
b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
f"ccccc{
|
||||
expression}cccccccccccccccccccccccccccccccc" "ccccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Don't inline f-strings that contain expressions that are guaranteed to split, e.b. because of a magic trailing comma
|
||||
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[a,]
|
||||
}" "moreeeeeeeeeeeeeeeeeeee" "test" # comment
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[a,]
|
||||
}" "moreeeeeeeeeeeeeeeeeeee" "test" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[a,]
|
||||
}" "moreeeeeeeeeeeeeeeeeeee" "test" # comment
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[a,]
|
||||
}" "moreeeeeeeeeeeeeeeeeeee" "test" # comment
|
||||
)
|
||||
|
||||
# Don't inline f-strings that contain commented expressions
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{[
|
||||
a # comment
|
||||
]}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{[
|
||||
a # comment
|
||||
]}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
# Don't inline f-strings with multiline debug expressions:
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
a=}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a +
|
||||
b=}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a
|
||||
=}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
a=}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a
|
||||
=}" "moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Double
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py38
|
||||
source_type = Python
|
||||
```
|
||||
|
||||
```python
|
||||
## Implicit concatenated strings with a trailing comment but a non splittable target.
|
||||
|
||||
# Don't join the string because the joined string with the inlined comment exceeds the line length limit.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv"
|
||||
) # c
|
||||
|
||||
# This is the same string as above and should lead to the same formatting. The only difference is that we start
|
||||
# with an unparenthesized string.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv"
|
||||
) # c
|
||||
|
||||
# Again the same string as above but this time as non-implicit concatenated string.
|
||||
# It's okay if the formatting differs because it's an explicit choice to use implicit concatenation.
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvvvvvv" # c
|
||||
|
||||
# Join the string because it's exactly in the line length limit when the comment is inlined.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv" # c
|
||||
)
|
||||
|
||||
# This is the same string as above and should lead to the same formatting. The only difference is that we start
|
||||
# with an unparenthesized string.
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv" # c
|
||||
)
|
||||
|
||||
# Again the same string as above but as a non-implicit concatenated string. It should result in the same formatting
|
||||
# (for consistency).
|
||||
____aaa = (
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvvvvvvvvvvv" # c
|
||||
)
|
||||
|
||||
# It should collapse the parentheses if the joined string and the comment fit on the same line.
|
||||
# This is required for stability.
|
||||
____aaa = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbvv" # c
|
||||
|
||||
|
||||
#############################################################
|
||||
# Assignments where the target or annotations are splittable
|
||||
#############################################################
|
||||
|
||||
|
||||
# The target splits because of a magic trailing comma
|
||||
# The string is joined and not parenthesized because it just fits into the line length (including comment).
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target splits because of the magic trailing comma
|
||||
# The string is **not** joined because it with the inlined comment exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = (
|
||||
"ccccccccccccccccccccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
|
||||
# The target should be flat
|
||||
# The string should be joined because it fits into the line length
|
||||
a[aaaaaaa, b] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
a[aaaaaaa, b] = "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target should be flat
|
||||
# The string gets parenthesized because it, with the inlined comment, exceeds the line length limit.
|
||||
a[aaaaaaa, b] = (
|
||||
"ccccccccccccccccccccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
|
||||
# Split an overlong target, but join the string if it fits
|
||||
a[
|
||||
aaaaaaa, b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Split both if necessary and keep multiline
|
||||
a[
|
||||
aaaaaaa, b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
#########################################################
|
||||
# Leading or trailing own line comments:
|
||||
# Preserve the parentheses
|
||||
########################################################
|
||||
a[aaaaaaa, b] = (
|
||||
# test
|
||||
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
)
|
||||
|
||||
a[aaaaaaa, b] = (
|
||||
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
# test
|
||||
)
|
||||
|
||||
a[aaaaaaa, b] = (
|
||||
"ccccccccccccccccccccccccccccccccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
# test
|
||||
)
|
||||
|
||||
|
||||
#############################################################
|
||||
# Type alias statements
|
||||
#############################################################
|
||||
|
||||
# First break the right, join the string
|
||||
type A[str, int, number] = (
|
||||
"Literal[string, int] | None | CustomType| OtherCustomTypeExcee" # comment
|
||||
)
|
||||
|
||||
# Keep multiline if overlong
|
||||
type A[str, int, number] = (
|
||||
"Literal[string, int] | None | "
|
||||
"CustomTypeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
) # comment
|
||||
|
||||
# Break the left if it is over-long, join the string
|
||||
type Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[
|
||||
stringgggggggggg,
|
||||
inttttttttttttttttttttttt,
|
||||
number,
|
||||
] = "Literal[string, int] | None | CustomType" # comment
|
||||
|
||||
# Break both if necessary and keep multiline
|
||||
type Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[
|
||||
stringgggggggggg,
|
||||
inttttttttttttttttttttttt,
|
||||
number,
|
||||
] = (
|
||||
"Literal[string, int] | None | "
|
||||
"CustomTypeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
) # comment
|
||||
|
||||
|
||||
#############################################################
|
||||
# F-Strings
|
||||
#############################################################
|
||||
|
||||
# Flatten and join the f-string
|
||||
aaaaaaaaaaa = f"test{expression}flatcean beeeeeeee {joined} eeeeeeeeeeeeeeeee" # inline
|
||||
|
||||
# Parenthesize the value and join it, inline the comment
|
||||
aaaaaaaaaaa = (
|
||||
f"test{expression}flatcean beeeeeeee {joined} eeeeeeeeeeeeeeeeeeeeeeeeeee" # inline
|
||||
)
|
||||
|
||||
# Parenthesize the f-string and keep it multiline because it doesn't fit on a single line including the comment
|
||||
aaaaaaaaaaa = (
|
||||
f"test{expression}flat"
|
||||
f"cean beeeeeeee {joined} eeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
) # inline
|
||||
|
||||
|
||||
# The target splits because of a magic trailing comma
|
||||
# The string is joined and not parenthesized because it just fits into the line length (including comment).
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = f"ccccc{expression}ccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = f"ccccc{expression}ccccccccccccccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target splits because of the magic trailing comma
|
||||
# The string is **not** joined because it with the inlined comment exceeds the line length limit.
|
||||
a[
|
||||
aaaaaaa,
|
||||
b,
|
||||
] = (
|
||||
f"ccccc{expression}cccccccccccccccccccc"
|
||||
f"cccccccccccccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
|
||||
# The target should be flat
|
||||
# The string should be joined because it fits into the line length
|
||||
a[aaaaaaa, b] = f"ccccc{expression}ccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# Same but starting with a joined string. They should both result in the same formatting.
|
||||
a[aaaaaaa, b] = f"ccccc{expression}ccccccccccccccccccccccccccccccccccc" # comment
|
||||
|
||||
# The target should be flat
|
||||
# The string gets parenthesized because it, with the inlined comment, exceeds the line length limit.
|
||||
a[aaaaaaa, b] = (
|
||||
f"ccccc{expression}ccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
|
||||
# Split an overlong target, but join the string if it fits
|
||||
a[
|
||||
aaaaaaa, b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
f"ccccc{expression}ccccccccccccccccccccccccccccccccccccccccc" # comment
|
||||
)
|
||||
|
||||
# Split both if necessary and keep multiline
|
||||
a[
|
||||
aaaaaaa, b
|
||||
].bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = (
|
||||
f"ccccc{expression}cccccccccccccccccccccccccccccccc"
|
||||
"ccccccccccccccccccccccccccccccc"
|
||||
) # comment
|
||||
|
||||
# Don't inline f-strings that contain expressions that are guaranteed to split, e.b. because of a magic trailing comma
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a,
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeeee"
|
||||
"test"
|
||||
) # comment
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a,
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeeee"
|
||||
"test" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a,
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeeee"
|
||||
"test"
|
||||
) # comment
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a,
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeeee"
|
||||
"test" # comment
|
||||
)
|
||||
|
||||
# Don't inline f-strings that contain commented expressions
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a # comment
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
[
|
||||
a # comment
|
||||
]
|
||||
}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
# Don't inline f-strings with multiline debug expressions:
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
a=}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a +
|
||||
b=}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaaaaaaaaaaaaaaa = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a
|
||||
=}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||
a=}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
|
||||
aaaaa[aaaaaaaaaaa] = (
|
||||
f"testeeeeeeeeeeeeeeeeeeeeeeeee{a
|
||||
=}"
|
||||
"moreeeeeeeeeeeeeeeeeetest" # comment
|
||||
)
|
||||
```
|
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/join_implicit_concatenated_string_preserve.py
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
a = "different '" 'quote "are fine"' # join
|
||||
|
||||
# More single quotes
|
||||
"one single'" "two 'single'" ' two "double"'
|
||||
|
||||
# More double quotes
|
||||
'one double"' 'two "double"' " two 'single'"
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double"' " two 'single'"
|
||||
|
||||
# Already invalid Pre Python 312
|
||||
f"{'Hy "User"'}" f'{"Hy 'User'"}'
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Preserve
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py38
|
||||
source_type = Python
|
||||
```
|
||||
|
||||
```python
|
||||
a = "different 'quote \"are fine\"" # join
|
||||
|
||||
# More single quotes
|
||||
"one single'two 'single' two \"double\""
|
||||
|
||||
# More double quotes
|
||||
'one double"two "double" two \'single\''
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double" two \'single\''
|
||||
|
||||
# Already invalid Pre Python 312
|
||||
f"{'Hy "User"'}{"Hy 'User'"}"
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Preserve
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py312
|
||||
source_type = Python
|
||||
```
|
||||
|
||||
```python
|
||||
a = "different 'quote \"are fine\"" # join
|
||||
|
||||
# More single quotes
|
||||
"one single'two 'single' two \"double\""
|
||||
|
||||
# More double quotes
|
||||
'one double"two "double" two \'single\''
|
||||
|
||||
# Equal number of single and double quotes
|
||||
'two "double" two \'single\''
|
||||
|
||||
# Already invalid Pre Python 312
|
||||
f"{'Hy "User"'}{"Hy 'User'"}"
|
||||
```
|
|
@ -331,6 +331,34 @@ a = """\\\x1f"""
|
|||
```
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -70,9 +70,9 @@
|
||||
|
||||
# String continuation
|
||||
|
||||
-"Let's" "start" "with" "a" "simple" "example"
|
||||
+"Let'sstartwithasimpleexample"
|
||||
|
||||
-"Let's" "start" "with" "a" "simple" "example" "now repeat after me:" "I am confident" "I am confident" "I am confident" "I am confident" "I am confident"
|
||||
+"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident"
|
||||
|
||||
(
|
||||
"Let's"
|
||||
@@ -139,7 +139,7 @@
|
||||
]
|
||||
|
||||
# Parenthesized string continuation with messed up indentation
|
||||
-{"key": ([], "a" "b" "c")}
|
||||
+{"key": ([], "abc")}
|
||||
|
||||
|
||||
# Regression test for https://github.com/astral-sh/ruff/issues/5893
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = space
|
||||
|
@ -515,4 +543,29 @@ a = """\\\x1f"""
|
|||
```
|
||||
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -70,9 +70,9 @@
|
||||
|
||||
# String continuation
|
||||
|
||||
-"Let's" 'start' 'with' 'a' 'simple' 'example'
|
||||
+"Let'sstartwithasimpleexample"
|
||||
|
||||
-"Let's" 'start' 'with' 'a' 'simple' 'example' 'now repeat after me:' 'I am confident' 'I am confident' 'I am confident' 'I am confident' 'I am confident'
|
||||
+"Let'sstartwithasimpleexamplenow repeat after me:I am confidentI am confidentI am confidentI am confidentI am confident"
|
||||
|
||||
(
|
||||
"Let's"
|
||||
@@ -139,7 +139,7 @@
|
||||
]
|
||||
|
||||
# Parenthesized string continuation with messed up indentation
|
||||
-{'key': ([], 'a' 'b' 'c')}
|
||||
+{'key': ([], 'abc')}
|
||||
|
||||
|
||||
# Regression test for https://github.com/astral-sh/ruff/issues/5893
|
||||
```
|
||||
|
|
|
@ -279,4 +279,37 @@ print((yield x))
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -78,7 +78,7 @@
|
||||
)
|
||||
)
|
||||
|
||||
-yield "Cache key will cause errors if used with memcached: %r " "(longer than %s)" % (
|
||||
+yield "Cache key will cause errors if used with memcached: %r (longer than %s)" % (
|
||||
key,
|
||||
MEMCACHE_MAX_KEY_LENGTH,
|
||||
)
|
||||
@@ -96,8 +96,7 @@
|
||||
"Django to create, modify, and delete the table"
|
||||
)
|
||||
yield (
|
||||
- "# Feel free to rename the models, but don't rename db_table values or "
|
||||
- "field names."
|
||||
+ "# Feel free to rename the models, but don't rename db_table values or field names."
|
||||
)
|
||||
|
||||
yield (
|
||||
@@ -109,8 +108,7 @@
|
||||
"Django to create, modify, and delete the table"
|
||||
)
|
||||
yield (
|
||||
- "# Feel free to rename the models, but don't rename db_table values or "
|
||||
- "field names."
|
||||
+ "# Feel free to rename the models, but don't rename db_table values or field names."
|
||||
)
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7420
|
||||
```
|
||||
|
|
|
@ -188,6 +188,3 @@ f3 = { # f3
|
|||
{ # f 4
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -831,7 +831,51 @@ match x:
|
|||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
|
||||
ccccccccccccccccccccccccccccccccc,
|
||||
):
|
||||
@@ -246,63 +238,48 @@
|
||||
@@ -220,89 +212,80 @@
|
||||
|
||||
## Always use parentheses for implicitly concatenated strings
|
||||
match x:
|
||||
- case (
|
||||
- "implicit" "concatenated" "string"
|
||||
- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd]
|
||||
- ):
|
||||
+ case "implicitconcatenatedstring" | [
|
||||
+ aaaaaa,
|
||||
+ bbbbbbbbbbbbbbbb,
|
||||
+ cccccccccccccccccc,
|
||||
+ ddddddddddddddddddddddddddd,
|
||||
+ ]:
|
||||
pass
|
||||
|
||||
|
||||
match x:
|
||||
- case (
|
||||
- b"implicit" b"concatenated" b"string"
|
||||
- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd]
|
||||
- ):
|
||||
+ case b"implicitconcatenatedstring" | [
|
||||
+ aaaaaa,
|
||||
+ bbbbbbbbbbbbbbbb,
|
||||
+ cccccccccccccccccc,
|
||||
+ ddddddddddddddddddddddddddd,
|
||||
+ ]:
|
||||
pass
|
||||
|
||||
|
||||
match x:
|
||||
- case (
|
||||
- f"implicit" "concatenated" "string"
|
||||
- | [aaaaaa, bbbbbbbbbbbbbbbb, cccccccccccccccccc, ddddddddddddddddddddddddddd]
|
||||
- ):
|
||||
+ case f"implicitconcatenatedstring" | [
|
||||
+ aaaaaa,
|
||||
+ bbbbbbbbbbbbbbbb,
|
||||
+ cccccccccccccccccc,
|
||||
+ ddddddddddddddddddddddddddd,
|
||||
+ ]:
|
||||
pass
|
||||
|
||||
|
||||
## Complex number expressions and unary expressions
|
||||
|
||||
match x:
|
||||
|
|
|
@ -131,6 +131,28 @@ def docstring_single():
|
|||
```
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -33,10 +33,10 @@
|
||||
rb"""br single triple"""
|
||||
rb"""br double triple"""
|
||||
|
||||
-'single1' 'single2'
|
||||
-'single1' 'double2'
|
||||
-'double1' 'single2'
|
||||
-'double1' 'double2'
|
||||
+'single1single2'
|
||||
+'single1double2'
|
||||
+'double1single2'
|
||||
+'double1double2'
|
||||
|
||||
|
||||
def docstring_single_triple():
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = space
|
||||
|
@ -205,6 +227,28 @@ def docstring_single():
|
|||
```
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -33,10 +33,10 @@
|
||||
rb"""br single triple"""
|
||||
rb"""br double triple"""
|
||||
|
||||
-"single1" "single2"
|
||||
-"single1" "double2"
|
||||
-"double1" "single2"
|
||||
-"double1" "double2"
|
||||
+"single1single2"
|
||||
+"single1double2"
|
||||
+"double1single2"
|
||||
+"double1double2"
|
||||
|
||||
|
||||
def docstring_single_triple():
|
||||
```
|
||||
|
||||
|
||||
### Output 3
|
||||
```
|
||||
indent-style = space
|
||||
|
@ -279,4 +323,23 @@ def docstring_single():
|
|||
```
|
||||
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -33,10 +33,10 @@
|
||||
rb'''br single triple'''
|
||||
rb"""br double triple"""
|
||||
|
||||
-'single1' 'single2'
|
||||
-'single1' "double2"
|
||||
-"double1" 'single2'
|
||||
-"double1" "double2"
|
||||
+'single1single2'
|
||||
+'single1double2'
|
||||
+"double1single2"
|
||||
+"double1double2"
|
||||
|
||||
|
||||
def docstring_single_triple():
|
||||
```
|
||||
|
|
|
@ -362,4 +362,154 @@ assert package.files == [
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -30,50 +30,47 @@
|
||||
|
||||
|
||||
def test():
|
||||
- assert (
|
||||
- {
|
||||
- key1: value1,
|
||||
- key2: value2,
|
||||
- key3: value3,
|
||||
- key4: value4,
|
||||
- key5: value5,
|
||||
- key6: value6,
|
||||
- key7: value7,
|
||||
- key8: value8,
|
||||
- key9: value9,
|
||||
- }
|
||||
- == expected
|
||||
- ), "Not what we expected and the message is too long to fit ineeeeee one line"
|
||||
+ assert {
|
||||
+ key1: value1,
|
||||
+ key2: value2,
|
||||
+ key3: value3,
|
||||
+ key4: value4,
|
||||
+ key5: value5,
|
||||
+ key6: value6,
|
||||
+ key7: value7,
|
||||
+ key8: value8,
|
||||
+ key9: value9,
|
||||
+ } == expected, (
|
||||
+ "Not what we expected and the message is too long to fit ineeeeee one line"
|
||||
+ )
|
||||
|
||||
- assert (
|
||||
- {
|
||||
- key1: value1,
|
||||
- key2: value2,
|
||||
- key3: value3,
|
||||
- key4: value4,
|
||||
- key5: value5,
|
||||
- key6: value6,
|
||||
- key7: value7,
|
||||
- key8: value8,
|
||||
- key9: value9,
|
||||
- }
|
||||
- == expected
|
||||
- ), "Not what we expected and the message is too long to fit in one lineeeee"
|
||||
+ assert {
|
||||
+ key1: value1,
|
||||
+ key2: value2,
|
||||
+ key3: value3,
|
||||
+ key4: value4,
|
||||
+ key5: value5,
|
||||
+ key6: value6,
|
||||
+ key7: value7,
|
||||
+ key8: value8,
|
||||
+ key9: value9,
|
||||
+ } == expected, (
|
||||
+ "Not what we expected and the message is too long to fit in one lineeeee"
|
||||
+ )
|
||||
|
||||
- assert (
|
||||
- {
|
||||
- key1: value1,
|
||||
- key2: value2,
|
||||
- key3: value3,
|
||||
- key4: value4,
|
||||
- key5: value5,
|
||||
- key6: value6,
|
||||
- key7: value7,
|
||||
- key8: value8,
|
||||
- key9: value9,
|
||||
- }
|
||||
- == expected
|
||||
- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee"
|
||||
+ assert {
|
||||
+ key1: value1,
|
||||
+ key2: value2,
|
||||
+ key3: value3,
|
||||
+ key4: value4,
|
||||
+ key5: value5,
|
||||
+ key6: value6,
|
||||
+ key7: value7,
|
||||
+ key8: value8,
|
||||
+ key9: value9,
|
||||
+ } == expected, (
|
||||
+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeee"
|
||||
+ )
|
||||
|
||||
assert (
|
||||
{
|
||||
@@ -103,7 +100,9 @@
|
||||
key9: value9,
|
||||
}
|
||||
== expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||
- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee"
|
||||
+ ), (
|
||||
+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeee"
|
||||
+ )
|
||||
|
||||
assert expected == {
|
||||
key1: value1,
|
||||
@@ -117,20 +116,19 @@
|
||||
key9: value9,
|
||||
}, "Not what we expected and the message is too long to fit ineeeeee one line"
|
||||
|
||||
- assert (
|
||||
- expected
|
||||
- == {
|
||||
- key1: value1,
|
||||
- key2: value2,
|
||||
- key3: value3,
|
||||
- key4: value4,
|
||||
- key5: value5,
|
||||
- key6: value6,
|
||||
- key7: value7,
|
||||
- key8: value8,
|
||||
- key9: value9,
|
||||
- }
|
||||
- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee"
|
||||
+ assert expected == {
|
||||
+ key1: value1,
|
||||
+ key2: value2,
|
||||
+ key3: value3,
|
||||
+ key4: value4,
|
||||
+ key5: value5,
|
||||
+ key6: value6,
|
||||
+ key7: value7,
|
||||
+ key8: value8,
|
||||
+ key9: value9,
|
||||
+ }, (
|
||||
+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeeeeeeee"
|
||||
+ )
|
||||
|
||||
assert (
|
||||
expectedeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||
@@ -160,7 +158,9 @@
|
||||
key8: value8,
|
||||
key9: value9,
|
||||
}
|
||||
- ), "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee"
|
||||
+ ), (
|
||||
+ "Not what we expected and the message is too long to fit in one lineeeeeeeeeeeeeee"
|
||||
+ )
|
||||
|
||||
|
||||
# Test for https://github.com/astral-sh/ruff/issues/7246
|
||||
```
|
||||
|
|
|
@ -397,6 +397,21 @@ def f() -> (
|
|||
pass
|
||||
|
||||
|
||||
@@ -80,12 +78,12 @@
|
||||
#########################################################################################
|
||||
|
||||
|
||||
-def test_implicit_concatenated_string_return_type() -> "str" "bbbbbbbbbbbbbbbb":
|
||||
+def test_implicit_concatenated_string_return_type() -> "strbbbbbbbbbbbbbbbb":
|
||||
pass
|
||||
|
||||
|
||||
def test_overlong_implicit_concatenated_string_return_type() -> (
|
||||
- "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb"
|
||||
+ "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb"
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -108,9 +106,9 @@
|
||||
# 1. Black tries to keep the list flat by parenthesizing the list as shown below even when the `list` identifier
|
||||
# fits on the header line. IMO, this adds unnecessary parentheses that can be avoided
|
||||
|
|
|
@ -412,3 +412,26 @@ def test_return_multiline_string_binary_expression_return_type_annotation(
|
|||
]:
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
## Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -82,13 +82,13 @@
|
||||
#########################################################################################
|
||||
|
||||
|
||||
-def test_implicit_concatenated_string_return_type(a) -> "str" "bbbbbbbbbbbbbbbb":
|
||||
+def test_implicit_concatenated_string_return_type(a) -> "strbbbbbbbbbbbbbbbb":
|
||||
pass
|
||||
|
||||
|
||||
def test_overlong_implicit_concatenated_string_return_type(
|
||||
a,
|
||||
-) -> "liiiiiiiiiiiisssssst[str]" "bbbbbbbbbbbbbbbb":
|
||||
+) -> "liiiiiiiiiiiisssssst[str]bbbbbbbbbbbbbbbb":
|
||||
pass
|
||||
|
||||
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue