Format bytes string (#6166)

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Format bytes string

Closes #6064

## Test Plan

Added a fixture based on string's one
This commit is contained in:
Luc Khai Hai 2023-07-31 17:46:40 +09:00 committed by GitHub
parent de898c52eb
commit b95fc6d162
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 577 additions and 89 deletions

View file

@ -0,0 +1,8 @@
[
{
"quote_style": "double"
},
{
"quote_style": "single"
}
]

View file

@ -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'
)
}

View file

@ -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<ExprConstant> 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 =

View file

@ -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 }
}
}

View file

@ -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:

View file

@ -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)

View file

@ -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():

View file

@ -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()
```

View file

@ -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')}
```