Compare commits

...

4 Commits

Author SHA1 Message Date
Charlie Marsh
6783d8a53b Don't expand groups for trailing end-of-line comments 2023-08-09 20:17:31 -04:00
Charlie Marsh
0252995973 Document FormatSpec fields (#6458) 2023-08-09 18:13:29 -04:00
Charlie Marsh
627f475b91 Avoid applying PYI055 to runtime-evaluated annotations (#6457)
## Summary

The use of `|` as a union operator is not always safe, if a type
annotation is evaluated in a runtime context. For example, this code
errors at runtime:

```python
import httpretty
import requests_mock

item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker
```

However, it's fine in a `.pyi` file, with `__future__` annotations`, or
if the annotation is in a non-evaluated context, like:

```python
def func():
    item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker
```

This PR modifies the rule to avoid enforcing in those invalid,
runtime-evaluated contexts.

Closes https://github.com/astral-sh/ruff/issues/6455.
2023-08-09 16:46:41 -04:00
Charlie Marsh
395bb31247 Improve counting of message arguments when msg is provided as a keyword (#6456)
Closes https://github.com/astral-sh/ruff/issues/6454.
2023-08-09 20:39:10 +00:00
28 changed files with 180 additions and 305 deletions

View File

@@ -1,7 +1,6 @@
import builtins
from typing import Union
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
x: type[int] | type[str] | type[float]
y: builtins.type[int] | type[str] | builtins.type[complex]
@@ -9,7 +8,9 @@ z: Union[type[float], type[complex]]
z: Union[type[float, int], type[complex]]
def func(arg: type[int] | str | type[float]) -> None: ...
def func(arg: type[int] | str | type[float]) -> None:
...
# OK
x: type[int, str, float]
@@ -17,4 +18,14 @@ y: builtins.type[int, str, complex]
z: Union[float, complex]
def func(arg: type[int, float] | str) -> None: ...
def func(arg: type[int, float] | str) -> None:
...
# OK
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
def func():
# PYI055
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker

View File

@@ -1,14 +1,12 @@
import builtins
from typing import Union
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
x: type[int] | type[str] | type[float]
y: builtins.type[int] | type[str] | builtins.type[complex]
z: Union[type[float], type[complex]]
z: Union[type[float, int], type[complex]]
def func(arg: type[int] | str | type[float]) -> None: ...
# OK
@@ -16,5 +14,11 @@ x: type[int, str, float]
y: builtins.type[int, str, complex]
z: Union[float, complex]
def func(arg: type[int, float] | str) -> None: ...
# OK
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
def func():
# PYI055
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker

View File

@@ -19,6 +19,10 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
# do not handle keyword arguments
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d")
logging.info(msg="Hello %s")
logging.info(msg="Hello %s %s")
import warning
warning.warning("Hello %s %s", "World!")

View File

@@ -15,6 +15,10 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
# do not handle keyword arguments
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d", {"objects": 1, "modifications": 1, "errors": 1})
logging.info(msg="Hello")
logging.info(msg="Hello", something="else")
import warning
warning.warning("Hello %s", "World!", "again")

View File

@@ -43,6 +43,11 @@ impl Violation for UnnecessaryTypeUnion {
/// PYI055
pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr) {
// The `|` operator isn't always safe to allow to runtime-evaluated annotations.
if checker.semantic().execution_context().is_runtime() {
return;
}
let mut type_exprs = Vec::new();
// Check if `union` is a PEP604 union (e.g. `float | int`) or a `typing.Union[float, int]`

View File

@@ -1,56 +1,12 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI055.py:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
PYI055.py:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
6 | x: type[int] | type[str] | type[float]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
|
PYI055.py:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
8 | z: Union[type[float], type[complex]]
9 | z: Union[type[float, int], type[complex]]
|
PYI055.py:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
|
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
9 | z: Union[type[float, int], type[complex]]
|
PYI055.py:9:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
9 | z: Union[type[float, int], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
PYI055.py:12:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
PYI055.py:31:11: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
12 | def func(arg: type[int] | str | type[float]) -> None: ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
13 |
14 | # OK
29 | def func():
30 | # PYI055
31 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|

View File

@@ -1,56 +1,79 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI055.pyi:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
PYI055.pyi:4:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
2 | from typing import Union
3 |
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
5 | x: type[int] | type[str] | type[float]
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
|
PYI055.pyi:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
PYI055.pyi:5:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
6 | x: type[int] | type[str] | type[float]
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | x: type[int] | type[str] | type[float]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
7 | z: Union[type[float], type[complex]]
|
PYI055.pyi:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
PYI055.pyi:6:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`.
|
5 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
4 | w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
5 | x: type[int] | type[str] | type[float]
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
8 | z: Union[type[float], type[complex]]
9 | z: Union[type[float, int], type[complex]]
7 | z: Union[type[float], type[complex]]
8 | z: Union[type[float, int], type[complex]]
|
PYI055.pyi:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
PYI055.pyi:7:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`.
|
6 | x: type[int] | type[str] | type[float]
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
5 | x: type[int] | type[str] | type[float]
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
7 | z: Union[type[float], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
9 | z: Union[type[float, int], type[complex]]
8 | z: Union[type[float, int], type[complex]]
|
PYI055.pyi:9:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
7 | y: builtins.type[int] | type[str] | builtins.type[complex]
8 | z: Union[type[float], type[complex]]
9 | z: Union[type[float, int], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|
PYI055.pyi:12:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
PYI055.pyi:8:4: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`.
|
12 | def func(arg: type[int] | str | type[float]) -> None: ...
6 | y: builtins.type[int] | type[str] | builtins.type[complex]
7 | z: Union[type[float], type[complex]]
8 | z: Union[type[float, int], type[complex]]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
9 |
10 | def func(arg: type[int] | str | type[float]) -> None: ...
|
PYI055.pyi:10:15: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`.
|
8 | z: Union[type[float, int], type[complex]]
9 |
10 | def func(arg: type[int] | str | type[float]) -> None: ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
13 |
14 | # OK
11 |
12 | # OK
|
PYI055.pyi:20:7: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
19 | # OK
20 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
21 |
22 | def func():
|
PYI055.pyi:24:11: PYI055 Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`.
|
22 | def func():
23 | # PYI055
24 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
|

View File

@@ -111,8 +111,8 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) {
let Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
})) = call.arguments.find_argument("msg", 0)
else {
})) = call.arguments
.find_positional( 0) else {
return;
};

View File

@@ -2126,23 +2126,21 @@ impl Arguments {
})
}
/// Return the positional argument at the given index, or `None` if no such argument exists.
pub fn find_positional(&self, position: usize) -> Option<&Expr> {
self.args
.iter()
.take_while(|expr| !expr.is_starred_expr())
.nth(position)
}
/// Return the argument with the given name or at the given position, or `None` if no such
/// argument exists. Used to retrieve arguments that can be provided _either_ as keyword or
/// positional arguments.
pub fn find_argument(&self, name: &str, position: usize) -> Option<&Expr> {
self.keywords
.iter()
.find(|keyword| {
let Keyword { arg, .. } = keyword;
arg.as_ref().is_some_and(|arg| arg == name)
})
self.find_keyword(name)
.map(|keyword| &keyword.value)
.or_else(|| {
self.args
.iter()
.take_while(|expr| !expr.is_starred_expr())
.nth(position)
})
.or_else(|| self.find_positional(position))
}
}

View File

@@ -161,10 +161,11 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
} else {
write!(
f,
[
line_suffix(&format_args![space(), space(), format_comment(trailing)]),
expand_parent()
]
[line_suffix(&format_args![
space(),
space(),
format_comment(trailing)
])]
)?;
}
@@ -210,13 +211,17 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
write!(f, [space(), space()])?;
}
write!(
f,
[
format_comment(comment),
empty_lines(lines_after(comment.slice().end(), f.context().source()))
]
)?;
if comment.line_position.is_end_of_line() {
write!(f, [format_comment(comment)])?;
} else {
write!(
f,
[
format_comment(comment),
empty_lines(lines_after(comment.slice().end(), f.context().source()))
]
)?;
}
comment.mark_formatted();

View File

@@ -65,7 +65,7 @@ match match(
```diff
--- Black
+++ Ruff
@@ -1,35 +1,34 @@
@@ -1,16 +1,11 @@
match something:
- case b():
+ case NOT_YET_IMPLEMENTED_Pattern:
@@ -85,29 +85,9 @@ match match(
+ case NOT_YET_IMPLEMENTED_Pattern:
pass
-match(arg) # comment
+match(
+ arg # comment
+)
match()
match()
-case(arg) # comment
+case(
+ arg # comment
+)
case()
case()
-re.match(something) # fast
+re.match(
+ something # fast
+)
match(arg) # comment
@@ -29,7 +24,5 @@
re.match(something) # fast
re.match()
match match():
- case case(
@@ -130,26 +110,20 @@ match something:
case NOT_YET_IMPLEMENTED_Pattern:
pass
match(
arg # comment
)
match(arg) # comment
match()
match()
case(
arg # comment
)
case(arg) # comment
case()
case()
re.match(
something # fast
)
re.match(something) # fast
re.match()
match match():
case NOT_YET_IMPLEMENTED_Pattern:

View File

@@ -191,21 +191,7 @@ instruction()#comment with bad spacing
)
# Please keep __all__ alphabetized within each category.
@@ -60,8 +60,12 @@
# Comment before function.
def inline_comments_in_brackets_ruin_everything():
if typedargslist:
- parameters.children = [children[0], body, children[-1]] # (1 # )1
parameters.children = [
+ children[0], # (1
+ body,
+ children[-1], # )1
+ ]
+ parameters.children = [
children[0],
body,
children[-1], # type: ignore
@@ -72,7 +76,11 @@
@@ -72,7 +72,11 @@
body,
parameters.children[-1], # )2
]
@@ -218,41 +204,17 @@ instruction()#comment with bad spacing
if (
self._proc is not None
# has the child process finished?
@@ -115,7 +123,9 @@
@@ -114,9 +118,7 @@
# yup
arg3=True,
)
lcomp = [
- lcomp = [
- element for element in collection if element is not None # yup # yup # right
+ element # yup
+ for element in collection # yup
+ if element is not None # right
]
- ]
+ lcomp = [element for element in collection if element is not None] # yup # yup # right
lcomp2 = [
# hello
@@ -143,7 +153,10 @@
# let's return
return Node(
syms.simple_stmt,
- [Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n?
+ [
+ Node(statement, result),
+ Leaf(token.NEWLINE, "\n"), # FIXME: \r\n?
+ ],
)
@@ -158,7 +171,10 @@
class Test:
def _init_host(self, parsed) -> None:
- if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore
+ if (
+ parsed.hostname is None # type: ignore
+ or not parsed.hostname.strip()
+ ):
pass
element
```
## Ruff Output
@@ -320,11 +282,7 @@ else:
# Comment before function.
def inline_comments_in_brackets_ruin_everything():
if typedargslist:
parameters.children = [
children[0], # (1
body,
children[-1], # )1
]
parameters.children = [children[0], body, children[-1]] # (1 # )1
parameters.children = [
children[0],
body,
@@ -382,11 +340,7 @@ short
# yup
arg3=True,
)
lcomp = [
element # yup
for element in collection # yup
if element is not None # right
]
lcomp = [element for element in collection if element is not None] # yup # yup # right
lcomp2 = [
# hello
element
@@ -413,10 +367,7 @@ short
# let's return
return Node(
syms.simple_stmt,
[
Node(statement, result),
Leaf(token.NEWLINE, "\n"), # FIXME: \r\n?
],
[Node(statement, result), Leaf(token.NEWLINE, "\n")], # FIXME: \r\n?
)
@@ -431,10 +382,7 @@ CONFIG_FILES = (
class Test:
def _init_host(self, parsed) -> None:
if (
parsed.hostname is None # type: ignore
or not parsed.hostname.strip()
):
if parsed.hostname is None or not parsed.hostname.strip(): # type: ignore
pass

View File

@@ -21,26 +21,24 @@ else:
```diff
--- Black
+++ Ruff
@@ -1,7 +1,7 @@
@@ -1,9 +1,5 @@
a, b, c = 3, 4, 5
if (
a == 3
-if (
- a == 3
- and b != 9 # fmt: skip
+ and b != 9 # fmt: skip
and c is not None
):
- and c is not None
-):
+if a == 3 and b != 9 and c is not None: # fmt: skip
print("I'm good!")
else:
print("I'm bad")
```
## Ruff Output
```py
a, b, c = 3, 4, 5
if (
a == 3
and b != 9 # fmt: skip
and c is not None
):
if a == 3 and b != 9 and c is not None: # fmt: skip
print("I'm good!")
else:
print("I'm bad")

View File

@@ -100,20 +100,7 @@ def foo() -> tuple[int, int, int,]:
```diff
--- Black
+++ Ruff
@@ -26,7 +26,11 @@
return 2 * a
-def double(a: int) -> int: # Hello
+def double(
+ a: int
+) -> (
+ int # Hello
+):
return 2 * a
@@ -54,7 +58,9 @@
@@ -54,7 +54,9 @@
a: int,
b: int,
c: int,
@@ -157,11 +144,7 @@ def double(a: int) -> int: # Hello
return 2 * a
def double(
a: int
) -> (
int # Hello
):
def double(a: int) -> int: # Hello
return 2 * a

View File

@@ -255,11 +255,7 @@ c1 = (
# Fits, either style
d11 = x.e().e().e() #
d12 = x.e().e().e() #
d13 = (
x.e() #
.e()
.e()
)
d13 = x.e().e().e() #
# Doesn't fit, default
d2 = x.e().esadjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkfsdddd() #

View File

@@ -506,12 +506,8 @@ x = (
- ( #
)
)
x = (
() - () #
)
x = (
() - () #
)
x = () - () #
x = () - () #
```

View File

@@ -195,11 +195,7 @@ result = (
# TODO(konstin): Black has this special case for comment placement where everything stays in one line
f("aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa")
f(
session,
b=1,
**dict(), # oddly placed end-of-line comment
)
f(session, b=1, **dict()) # oddly placed end-of-line comment
f(
session,
b=1,

View File

@@ -138,10 +138,7 @@ a not in b
== b
)
(
a # comment
== b
)
(a == b) # comment
a < b > c == d

View File

@@ -78,14 +78,9 @@ x={ # dangling end of line comment
{**d}
{
**a, # leading
**b, # middle # trailing
}
{**a, **b} # leading # middle # trailing
{
**b # middle with single item
}
{**b} # middle with single item
{
# before

View File

@@ -128,10 +128,7 @@ aaaaaaaaaaaaaaaaaaaaa = {
]
}
{
a: a # a
for c in e # for # c # in # e
}
{a: a for c in e} # a # for # c # in # e
{
# above a

View File

@@ -158,9 +158,7 @@ lambda x: lambda y: lambda z: (
# Trailing
a = (
lambda: 1 # Dangling
)
a = lambda: 1 # Dangling
# Regression test: lambda empty arguments ranges were too long, leading to unstable
# formatting

View File

@@ -92,10 +92,7 @@ aaaaaaaaaaaaaaaaaaaaa = [
]
]
[
a # a
for c in e # for # c # in # e
]
[a for c in e] # a # for # c # in # e
[
# above a

View File

@@ -72,10 +72,7 @@ selected_choices = {
]
}
{
a # a
for c in e # for # c # in # e
}
{a for c in e} # a # for # c # in # e
{
# above a

View File

@@ -111,8 +111,7 @@ a1 = "a"[
a2 = "a"[
# a
# b
: # c
# d
: # c# d
]
# Check all places where comments can exist
@@ -151,9 +150,7 @@ c4 = "c"[
]
# End of line comments
d1 = "d"[ # comment
:
]
d1 = "d"[:] # comment
d2 = "d"[ # comment
1:
]

View File

@@ -635,9 +635,7 @@ def f(arg1=1, *, kwonlyarg1, kwonlyarg2=2):
# Regression test for https://github.com/astral-sh/ruff/issues/5176#issuecomment-1598171989
def foo(
b=3 + 2, # comment
):
def foo(b=3 + 2): # comment
...
@@ -1039,9 +1037,7 @@ def handleMatch( # type: ignore[override] # https://github.com/python/mypy/issu
...
def double(
a: int, # Hello
) -> int:
def double(a: int) -> int: # Hello
return 2 * a

View File

@@ -61,11 +61,7 @@ while some_condition(unformatted, args) and anotherCondition or aThirdCondition:
print("Do something")
while (
some_condition(unformatted, args) # trailing some condition
and anotherCondition
or aThirdCondition # trailing third condition
): # comment
while some_condition(unformatted, args) and anotherCondition or aThirdCondition: # trailing some condition # trailing third condition # comment
print("Do something")
```

View File

@@ -142,10 +142,7 @@ with (
# trailing
with (
a, # a # comma
b, # c
): # colon
with a, b: # a # comma # c # colon
...
@@ -218,11 +215,9 @@ with (
...
with (
(
a
# trailing own line comment
) as b # trailing as same line comment # trailing b same line comment
):
a
# trailing own line comment
) as b: # trailing as same line comment # trailing b same line comment
...
with (

View File

@@ -188,14 +188,23 @@ impl FormatParse for FormatType {
#[derive(Debug, PartialEq)]
pub struct FormatSpec {
// Ex) `!s` in `'{!s}'`
conversion: Option<FormatConversion>,
// Ex) `*` in `'{:*^30}'`
fill: Option<char>,
// Ex) `<` in `'{:<30}'`
align: Option<FormatAlign>,
// Ex) `+` in `'{:+f}'`
sign: Option<FormatSign>,
// Ex) `#` in `'{:#x}'`
alternate_form: bool,
// Ex) `30` in `'{:<30}'`
width: Option<usize>,
// Ex) `,` in `'{:,}'`
grouping_option: Option<FormatGrouping>,
// Ex) `2` in `'{:.2}'`
precision: Option<usize>,
// Ex) `f` in `'{:+f}'`
format_type: Option<FormatType>,
}