Enable more non-panicking formatter tests (#3262)

This commit is contained in:
Charlie Marsh 2023-02-27 18:21:53 -05:00 committed by GitHub
parent 270015865b
commit 16be691712
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 3282 additions and 7 deletions

View file

@ -75,13 +75,7 @@ mod tests {
pattern = "resources/test/fixtures/black/**/*.py",
// Excluded tests because they reach unreachable when attaching tokens
exclude = [
"*comments.py",
"*comments[3,5,8].py",
"*comments_non_breaking_space.py",
"*docstring_preview.py",
"*docstring.py",
"*fmtonoff.py",
"*fmtskip8.py",
"*comments8.py",
])
]
#[test]

View file

@ -0,0 +1,213 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py
---
## Input
```py
# The percent-percent comments are Spyder IDE cells.
# %%
def func():
x = """
a really long string
"""
lcomp3 = [
# This one is actually too long to fit in a single line.
element.split("\n", 1)[0]
# yup
for element in collection.select_elements()
# right
if element is not None
]
# Capture each of the exceptions in the MultiError along with each of their causes and contexts
if isinstance(exc_value, MultiError):
embedded = []
for exc in exc_value.exceptions:
if exc not in _seen:
embedded.append(
# This should be left alone (before)
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
_seen=set(_seen),
)
# This should be left alone (after)
)
# everything is fine if the expression isn't nested
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
_seen=set(_seen),
)
# %%
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -9,10 +9,11 @@
lcomp3 = [
# This one is actually too long to fit in a single line.
element.split("\n", 1)[0]
- # yup
- for element in collection.select_elements()
- # right
- if element is not None
+ for # yup
+ element in collection.select_elements()
+ if # right
+ element
+ is not None
]
# Capture each of the exceptions in the MultiError along with each of their causes and contexts
if isinstance(exc_value, MultiError):
@@ -26,9 +27,9 @@
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
- # copy the set of _seen exceptions so that duplicates
+ _seen=# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
- _seen=set(_seen),
+ set(_seen),
)
# This should be left alone (after)
)
@@ -39,9 +40,9 @@
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
- # copy the set of _seen exceptions so that duplicates
+ _seen=# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
- _seen=set(_seen),
+ set(_seen),
)
```
## Ruff Output
```py
# The percent-percent comments are Spyder IDE cells.
# %%
def func():
x = """
a really long string
"""
lcomp3 = [
# This one is actually too long to fit in a single line.
element.split("\n", 1)[0]
for # yup
element in collection.select_elements()
if # right
element
is not None
]
# Capture each of the exceptions in the MultiError along with each of their causes and contexts
if isinstance(exc_value, MultiError):
embedded = []
for exc in exc_value.exceptions:
if exc not in _seen:
embedded.append(
# This should be left alone (before)
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
_seen=# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
set(_seen),
)
# This should be left alone (after)
)
# everything is fine if the expression isn't nested
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
_seen=# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
set(_seen),
)
# %%
```
## Black Output
```py
# The percent-percent comments are Spyder IDE cells.
# %%
def func():
x = """
a really long string
"""
lcomp3 = [
# This one is actually too long to fit in a single line.
element.split("\n", 1)[0]
# yup
for element in collection.select_elements()
# right
if element is not None
]
# Capture each of the exceptions in the MultiError along with each of their causes and contexts
if isinstance(exc_value, MultiError):
embedded = []
for exc in exc_value.exceptions:
if exc not in _seen:
embedded.append(
# This should be left alone (before)
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
_seen=set(_seen),
)
# This should be left alone (after)
)
# everything is fine if the expression isn't nested
traceback.TracebackException.from_exception(
exc,
limit=limit,
lookup_lines=lookup_lines,
capture_locals=capture_locals,
# copy the set of _seen exceptions so that duplicates
# shared between sub-exceptions are not omitted
_seen=set(_seen),
)
# %%
```

View file

@ -0,0 +1,279 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py
---
## Input
```py
while True:
if something.changed:
do.stuff() # trailing comment
# Comment belongs to the `if` block.
# This one belongs to the `while` block.
# Should this one, too? I guess so.
# This one is properly standalone now.
for i in range(100):
# first we do this
if i % 33 == 0:
break
# then we do this
print(i)
# and finally we loop around
with open(some_temp_file) as f:
data = f.read()
try:
with open(some_other_file) as w:
w.write(data)
except OSError:
print("problems")
import sys
# leading function comment
def wat():
...
# trailing function comment
# SECTION COMMENT
# leading 1
@deco1
# leading 2
@deco2(with_args=True)
# leading 3
@deco3
def decorated1():
...
# leading 1
@deco1
# leading 2
@deco2(with_args=True)
# leading function comment
def decorated1():
...
# Note: this is fixed in
# Preview.empty_lines_before_class_or_def_with_leading_comments.
# In the current style, the user will have to split those lines by hand.
some_instruction
# This comment should be split from `some_instruction` by two lines but isn't.
def g():
...
if __name__ == "__main__":
main()
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -23,7 +23,6 @@
try:
with open(some_other_file) as w:
w.write(data)
-
except OSError:
print("problems")
@@ -41,18 +40,18 @@
# leading 1
@deco1
-# leading 2
-@deco2(with_args=True)
-# leading 3
-@deco3
+@# leading 2
+deco2(with_args=True)
+@# leading 3
+deco3
def decorated1():
...
# leading 1
@deco1
-# leading 2
-@deco2(with_args=True)
+@# leading 2
+deco2(with_args=True)
# leading function comment
def decorated1():
...
```
## Ruff Output
```py
while True:
if something.changed:
do.stuff() # trailing comment
# Comment belongs to the `if` block.
# This one belongs to the `while` block.
# Should this one, too? I guess so.
# This one is properly standalone now.
for i in range(100):
# first we do this
if i % 33 == 0:
break
# then we do this
print(i)
# and finally we loop around
with open(some_temp_file) as f:
data = f.read()
try:
with open(some_other_file) as w:
w.write(data)
except OSError:
print("problems")
import sys
# leading function comment
def wat():
...
# trailing function comment
# SECTION COMMENT
# leading 1
@deco1
@# leading 2
deco2(with_args=True)
@# leading 3
deco3
def decorated1():
...
# leading 1
@deco1
@# leading 2
deco2(with_args=True)
# leading function comment
def decorated1():
...
# Note: this is fixed in
# Preview.empty_lines_before_class_or_def_with_leading_comments.
# In the current style, the user will have to split those lines by hand.
some_instruction
# This comment should be split from `some_instruction` by two lines but isn't.
def g():
...
if __name__ == "__main__":
main()
```
## Black Output
```py
while True:
if something.changed:
do.stuff() # trailing comment
# Comment belongs to the `if` block.
# This one belongs to the `while` block.
# Should this one, too? I guess so.
# This one is properly standalone now.
for i in range(100):
# first we do this
if i % 33 == 0:
break
# then we do this
print(i)
# and finally we loop around
with open(some_temp_file) as f:
data = f.read()
try:
with open(some_other_file) as w:
w.write(data)
except OSError:
print("problems")
import sys
# leading function comment
def wat():
...
# trailing function comment
# SECTION COMMENT
# leading 1
@deco1
# leading 2
@deco2(with_args=True)
# leading 3
@deco3
def decorated1():
...
# leading 1
@deco1
# leading 2
@deco2(with_args=True)
# leading function comment
def decorated1():
...
# Note: this is fixed in
# Preview.empty_lines_before_class_or_def_with_leading_comments.
# In the current style, the user will have to split those lines by hand.
some_instruction
# This comment should be split from `some_instruction` by two lines but isn't.
def g():
...
if __name__ == "__main__":
main()
```

View file

@ -0,0 +1,126 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py
---
## Input
```py
from .config import ( ConfigTypeAttributes, Int, Path, # String,
# DEFAULT_TYPE_ATTRIBUTES,
)
result = 1 # A simple comment
result = ( 1, ) # Another one
result = 1 # type: ignore
result = 1# This comment is talking about type: ignore
square = Square(4) # type: Optional[Square]
def function(a:int=42):
""" This docstring is already formatted
a
b
"""
#  There's a NBSP + 3 spaces before
# And 4 spaces on the next line
pass
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,23 +1,22 @@
from .config import (
ConfigTypeAttributes,
Int,
- Path, # String,
- # DEFAULT_TYPE_ATTRIBUTES,
+ Path, # String,
)
-result = 1 # A simple comment
-result = (1,) # Another one
+result = 1 # A simple comment
+result = (1,) # Another one
-result = 1 #  type: ignore
-result = 1 # This comment is talking about type: ignore
-square = Square(4) #  type: Optional[Square]
+result = 1 # type: ignore
+result = 1 # This comment is talking about type: ignore
+square = Square(4) # type: Optional[Square]
def function(a: int = 42):
- """This docstring is already formatted
- a
- b
+ """ This docstring is already formatted
+ a
+ b
"""
- # There's a NBSP + 3 spaces before
+ #  There's a NBSP + 3 spaces before
# And 4 spaces on the next line
pass
```
## Ruff Output
```py
from .config import (
ConfigTypeAttributes,
Int,
Path, # String,
)
result = 1 # A simple comment
result = (1,) # Another one
result = 1 # type: ignore
result = 1 # This comment is talking about type: ignore
square = Square(4) # type: Optional[Square]
def function(a: int = 42):
""" This docstring is already formatted
a
b
"""
#  There's a NBSP + 3 spaces before
# And 4 spaces on the next line
pass
```
## Black Output
```py
from .config import (
ConfigTypeAttributes,
Int,
Path, # String,
# DEFAULT_TYPE_ATTRIBUTES,
)
result = 1 # A simple comment
result = (1,) # Another one
result = 1 #  type: ignore
result = 1 # This comment is talking about type: ignore
square = Square(4) #  type: Optional[Square]
def function(a: int = 42):
"""This docstring is already formatted
a
b
"""
# There's a NBSP + 3 spaces before
# And 4 spaces on the next line
pass
```

View file

@ -0,0 +1,322 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments.py
---
## Input
```py
#!/usr/bin/env python3
# fmt: on
# Some license here.
#
# Has many lines. Many, many lines.
# Many, many, many lines.
"""Module docstring.
Possibly also many, many lines.
"""
import os.path
import sys
import a
from b.c import X # some noqa comment
try:
import fast
except ImportError:
import slow as fast
# Some comment before a function.
y = 1
(
# some strings
y # type: ignore
)
def function(default=None):
"""Docstring comes first.
Possibly many lines.
"""
# FIXME: Some comment about why this function is crap but still in production.
import inner_imports
if inner_imports.are_evil():
# Explains why we have this if.
# In great detail indeed.
x = X()
return x.method1() # type: ignore
# This return is also commented for some reason.
return default
# Explains why we use global state.
GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
# Another comment!
# This time two lines.
class Foo:
"""Docstring for class Foo. Example from Sphinx docs."""
#: Doc comment for class attribute Foo.bar.
#: It can have multiple lines.
bar = 1
flox = 1.5 #: Doc comment for Foo.flox. One line only.
baz = 2
"""Docstring for class attribute Foo.baz."""
def __init__(self):
#: Doc comment for instance attribute qux.
self.qux = 3
self.spam = 4
"""Docstring for instance attribute spam."""
#' <h1>This is pweave!</h1>
@fast(really=True)
async def wat():
# This comment, for some reason \
# contains a trailing backslash.
async with X.open_async() as x: # Some more comments
result = await x.method1()
# Comment after ending a block.
if result:
print("A OK", file=sys.stdout)
# Comment between things.
print()
# Some closing comments.
# Maybe Vim or Emacs directives for formatting.
# Who knows.
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -93,4 +93,4 @@
# Some closing comments.
# Maybe Vim or Emacs directives for formatting.
-# Who knows.
\ No newline at end of file
+# Who knows.
```
## Ruff Output
```py
#!/usr/bin/env python3
# fmt: on
# Some license here.
#
# Has many lines. Many, many lines.
# Many, many, many lines.
"""Module docstring.
Possibly also many, many lines.
"""
import os.path
import sys
import a
from b.c import X # some noqa comment
try:
import fast
except ImportError:
import slow as fast
# Some comment before a function.
y = 1
(
# some strings
y # type: ignore
)
def function(default=None):
"""Docstring comes first.
Possibly many lines.
"""
# FIXME: Some comment about why this function is crap but still in production.
import inner_imports
if inner_imports.are_evil():
# Explains why we have this if.
# In great detail indeed.
x = X()
return x.method1() # type: ignore
# This return is also commented for some reason.
return default
# Explains why we use global state.
GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
# Another comment!
# This time two lines.
class Foo:
"""Docstring for class Foo. Example from Sphinx docs."""
#: Doc comment for class attribute Foo.bar.
#: It can have multiple lines.
bar = 1
flox = 1.5 #: Doc comment for Foo.flox. One line only.
baz = 2
"""Docstring for class attribute Foo.baz."""
def __init__(self):
#: Doc comment for instance attribute qux.
self.qux = 3
self.spam = 4
"""Docstring for instance attribute spam."""
#' <h1>This is pweave!</h1>
@fast(really=True)
async def wat():
# This comment, for some reason \
# contains a trailing backslash.
async with X.open_async() as x: # Some more comments
result = await x.method1()
# Comment after ending a block.
if result:
print("A OK", file=sys.stdout)
# Comment between things.
print()
# Some closing comments.
# Maybe Vim or Emacs directives for formatting.
# Who knows.
```
## Black Output
```py
#!/usr/bin/env python3
# fmt: on
# Some license here.
#
# Has many lines. Many, many lines.
# Many, many, many lines.
"""Module docstring.
Possibly also many, many lines.
"""
import os.path
import sys
import a
from b.c import X # some noqa comment
try:
import fast
except ImportError:
import slow as fast
# Some comment before a function.
y = 1
(
# some strings
y # type: ignore
)
def function(default=None):
"""Docstring comes first.
Possibly many lines.
"""
# FIXME: Some comment about why this function is crap but still in production.
import inner_imports
if inner_imports.are_evil():
# Explains why we have this if.
# In great detail indeed.
x = X()
return x.method1() # type: ignore
# This return is also commented for some reason.
return default
# Explains why we use global state.
GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
# Another comment!
# This time two lines.
class Foo:
"""Docstring for class Foo. Example from Sphinx docs."""
#: Doc comment for class attribute Foo.bar.
#: It can have multiple lines.
bar = 1
flox = 1.5 #: Doc comment for Foo.flox. One line only.
baz = 2
"""Docstring for class attribute Foo.baz."""
def __init__(self):
#: Doc comment for instance attribute qux.
self.qux = 3
self.spam = 4
"""Docstring for instance attribute spam."""
#' <h1>This is pweave!</h1>
@fast(really=True)
async def wat():
# This comment, for some reason \
# contains a trailing backslash.
async with X.open_async() as x: # Some more comments
result = await x.method1()
# Comment after ending a block.
if result:
print("A OK", file=sys.stdout)
# Comment between things.
print()
# Some closing comments.
# Maybe Vim or Emacs directives for formatting.
# Who knows.```

View file

@ -0,0 +1,225 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py
---
## Input
```py
def docstring_almost_at_line_limit():
"""long docstring.................................................................
"""
def docstring_almost_at_line_limit_with_prefix():
f"""long docstring................................................................
"""
def mulitline_docstring_almost_at_line_limit():
"""long docstring.................................................................
..................................................................................
"""
def mulitline_docstring_almost_at_line_limit_with_prefix():
f"""long docstring................................................................
..................................................................................
"""
def docstring_at_line_limit():
"""long docstring................................................................"""
def docstring_at_line_limit_with_prefix():
f"""long docstring..............................................................."""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def multiline_docstring_at_line_limit_with_prefix():
f"""first line----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def single_quote_docstring_over_line_limit():
"We do not want to put the closing quote on a new line as that is invalid (see GH-3141)."
def single_quote_docstring_over_line_limit2():
'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).'
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,9 +1,13 @@
def docstring_almost_at_line_limit():
- """long docstring................................................................."""
+ """long docstring.................................................................
+ """
def docstring_almost_at_line_limit_with_prefix():
- f"""long docstring................................................................"""
+ (
+ f"""long docstring................................................................
+ """
+ )
def mulitline_docstring_almost_at_line_limit():
@@ -14,10 +18,12 @@
def mulitline_docstring_almost_at_line_limit_with_prefix():
- f"""long docstring................................................................
+ (
+ f"""long docstring................................................................
..................................................................................
"""
+ )
def docstring_at_line_limit():
@@ -35,9 +41,11 @@
def multiline_docstring_at_line_limit_with_prefix():
- f"""first line----------------------------------------------------------------------
+ (
+ f"""first line----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
+ )
def single_quote_docstring_over_line_limit():
```
## Ruff Output
```py
def docstring_almost_at_line_limit():
"""long docstring.................................................................
"""
def docstring_almost_at_line_limit_with_prefix():
(
f"""long docstring................................................................
"""
)
def mulitline_docstring_almost_at_line_limit():
"""long docstring.................................................................
..................................................................................
"""
def mulitline_docstring_almost_at_line_limit_with_prefix():
(
f"""long docstring................................................................
..................................................................................
"""
)
def docstring_at_line_limit():
"""long docstring................................................................"""
def docstring_at_line_limit_with_prefix():
f"""long docstring..............................................................."""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def multiline_docstring_at_line_limit_with_prefix():
(
f"""first line----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
)
def single_quote_docstring_over_line_limit():
"We do not want to put the closing quote on a new line as that is invalid (see GH-3141)."
def single_quote_docstring_over_line_limit2():
"We do not want to put the closing quote on a new line as that is invalid (see GH-3141)."
```
## Black Output
```py
def docstring_almost_at_line_limit():
"""long docstring................................................................."""
def docstring_almost_at_line_limit_with_prefix():
f"""long docstring................................................................"""
def mulitline_docstring_almost_at_line_limit():
"""long docstring.................................................................
..................................................................................
"""
def mulitline_docstring_almost_at_line_limit_with_prefix():
f"""long docstring................................................................
..................................................................................
"""
def docstring_at_line_limit():
"""long docstring................................................................"""
def docstring_at_line_limit_with_prefix():
f"""long docstring..............................................................."""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def multiline_docstring_at_line_limit_with_prefix():
f"""first line----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def single_quote_docstring_over_line_limit():
"We do not want to put the closing quote on a new line as that is invalid (see GH-3141)."
def single_quote_docstring_over_line_limit2():
"We do not want to put the closing quote on a new line as that is invalid (see GH-3141)."
```

View file

@ -0,0 +1,925 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py
---
## Input
```py
class MyClass:
""" Multiline
class docstring
"""
def method(self):
"""Multiline
method docstring
"""
pass
def foo():
"""This is a docstring with
some lines of text here
"""
return
def bar():
'''This is another docstring
with more lines of text
'''
return
def baz():
'''"This" is a string with some
embedded "quotes"'''
return
def troz():
'''Indentation with tabs
is just as OK
'''
return
def zort():
"""Another
multiline
docstring
"""
pass
def poit():
"""
Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
- dolore magna aliqua
- enim ad minim veniam
- quis nostrud exercitation ullamco laboris nisi
- aliquip ex ea commodo consequat
"""
pass
def under_indent():
"""
These lines are indented in a way that does not
make sense.
"""
pass
def over_indent():
"""
This has a shallow indent
- But some lines are deeper
- And the closing quote is too deep
"""
pass
def single_line():
"""But with a newline after it!
"""
pass
def this():
r"""
'hey ho'
"""
def that():
""" "hey yah" """
def and_that():
"""
"hey yah" """
def and_this():
'''
"hey yah"'''
def multiline_whitespace():
'''
'''
def oneline_whitespace():
''' '''
def empty():
""""""
def single_quotes():
'testing'
def believe_it_or_not_this_is_in_the_py_stdlib(): '''
"hey yah"'''
def ignored_docstring():
"""a => \
b"""
def single_line_docstring_with_whitespace():
""" This should be stripped """
def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass
def backslash_space():
"""\ """
def multiline_backslash_1():
'''
hey\there\
\ '''
def multiline_backslash_2():
'''
hey there \ '''
# Regression test for #3425
def multiline_backslash_really_long_dont_crash():
"""
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
def multiline_backslash_3():
'''
already escaped \\ '''
def my_god_its_full_of_stars_1():
"I'm sorry Dave\u2001"
# the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2():
"I'm sorry Dave"
def docstring_almost_at_line_limit():
"""long docstring................................................................."""
def docstring_almost_at_line_limit2():
"""long docstring.................................................................
..................................................................................
"""
def docstring_at_line_limit():
"""long docstring................................................................"""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def stable_quote_normalization_with_immediate_inner_single_quote(self):
''''<text here>
<text here, since without another non-empty line black is stable>
'''
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,83 +1,85 @@
class MyClass:
- """Multiline
- class docstring
- """
+ """ Multiline
+ class docstring
+ """
def method(self):
"""Multiline
- method docstring
- """
+ method docstring
+ """
pass
def foo():
- """This is a docstring with
- some lines of text here
- """
+ """This is a docstring with
+ some lines of text here
+ """
return
def bar():
"""This is another docstring
- with more lines of text
- """
+ with more lines of text
+ """
return
def baz():
'''"This" is a string with some
- embedded "quotes"'''
+ embedded "quotes"'''
return
def troz():
"""Indentation with tabs
- is just as OK
- """
+ is just as OK
+ """
return
def zort():
"""Another
- multiline
- docstring
- """
+ multiline
+ docstring
+ """
pass
def poit():
"""
- Lorem ipsum dolor sit amet.
+ Lorem ipsum dolor sit amet.
- Consectetur adipiscing elit:
- - sed do eiusmod tempor incididunt ut labore
- - dolore magna aliqua
- - enim ad minim veniam
- - quis nostrud exercitation ullamco laboris nisi
- - aliquip ex ea commodo consequat
- """
+ Consectetur adipiscing elit:
+ - sed do eiusmod tempor incididunt ut labore
+ - dolore magna aliqua
+ - enim ad minim veniam
+ - quis nostrud exercitation ullamco laboris nisi
+ - aliquip ex ea commodo consequat
+ """
pass
def under_indent():
"""
- These lines are indented in a way that does not
- make sense.
- """
+ These lines are indented in a way that does not
+make sense.
+ """
pass
def over_indent():
"""
- This has a shallow indent
- - But some lines are deeper
- - And the closing quote is too deep
+ This has a shallow indent
+ - But some lines are deeper
+ - And the closing quote is too deep
"""
pass
def single_line():
- """But with a newline after it!"""
+ """But with a newline after it!
+
+ """
pass
@@ -93,20 +95,25 @@
def and_that():
"""
- "hey yah" """
+ "hey yah" """
def and_this():
- '''
- "hey yah"'''
+ '''
+ "hey yah"'''
def multiline_whitespace():
- """ """
+ """
+
+
+
+
+ """
def oneline_whitespace():
- """ """
+ """ """
def empty():
@@ -118,8 +125,8 @@
def believe_it_or_not_this_is_in_the_py_stdlib():
- '''
- "hey yah"'''
+ '''
+"hey yah"'''
def ignored_docstring():
@@ -128,31 +135,31 @@
def single_line_docstring_with_whitespace():
- """This should be stripped"""
+ """ This should be stripped """
def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
- tab at start of line and then a tab separated value
- multiple tabs at the beginning and inline
- mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
-
- line ends with some tabs
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
"""
def docstring_with_inline_tabs_and_tab_indentation():
"""hey
- tab separated value
- tab at start of line and then a tab separated value
- multiple tabs at the beginning and inline
- mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
-
- line ends with some tabs
- """
+ tab separated value
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
+ """
pass
@@ -168,7 +175,7 @@
def multiline_backslash_2():
"""
- hey there \ """
+ hey there \ """
# Regression test for #3425
@@ -179,7 +186,7 @@
def multiline_backslash_3():
"""
- already escaped \\"""
+ already escaped \\ """
def my_god_its_full_of_stars_1():
@@ -188,7 +195,7 @@
# the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2():
- "I'm sorry Dave"
+ "I'm sorry Dave"
def docstring_almost_at_line_limit():
```
## Ruff Output
```py
class MyClass:
""" Multiline
class docstring
"""
def method(self):
"""Multiline
method docstring
"""
pass
def foo():
"""This is a docstring with
some lines of text here
"""
return
def bar():
"""This is another docstring
with more lines of text
"""
return
def baz():
'''"This" is a string with some
embedded "quotes"'''
return
def troz():
"""Indentation with tabs
is just as OK
"""
return
def zort():
"""Another
multiline
docstring
"""
pass
def poit():
"""
Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
- dolore magna aliqua
- enim ad minim veniam
- quis nostrud exercitation ullamco laboris nisi
- aliquip ex ea commodo consequat
"""
pass
def under_indent():
"""
These lines are indented in a way that does not
make sense.
"""
pass
def over_indent():
"""
This has a shallow indent
- But some lines are deeper
- And the closing quote is too deep
"""
pass
def single_line():
"""But with a newline after it!
"""
pass
def this():
r"""
'hey ho'
"""
def that():
""" "hey yah" """
def and_that():
"""
"hey yah" """
def and_this():
'''
"hey yah"'''
def multiline_whitespace():
"""
"""
def oneline_whitespace():
""" """
def empty():
""""""
def single_quotes():
"testing"
def believe_it_or_not_this_is_in_the_py_stdlib():
'''
"hey yah"'''
def ignored_docstring():
"""a => \
b"""
def single_line_docstring_with_whitespace():
""" This should be stripped """
def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass
def backslash_space():
"""\ """
def multiline_backslash_1():
"""
hey\there\
\ """
def multiline_backslash_2():
"""
hey there \ """
# Regression test for #3425
def multiline_backslash_really_long_dont_crash():
"""
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
def multiline_backslash_3():
"""
already escaped \\ """
def my_god_its_full_of_stars_1():
"I'm sorry Dave\u2001"
# the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2():
"I'm sorry Dave"
def docstring_almost_at_line_limit():
"""long docstring................................................................."""
def docstring_almost_at_line_limit2():
"""long docstring.................................................................
..................................................................................
"""
def docstring_at_line_limit():
"""long docstring................................................................"""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def stable_quote_normalization_with_immediate_inner_single_quote(self):
"""'<text here>
<text here, since without another non-empty line black is stable>
"""
```
## Black Output
```py
class MyClass:
"""Multiline
class docstring
"""
def method(self):
"""Multiline
method docstring
"""
pass
def foo():
"""This is a docstring with
some lines of text here
"""
return
def bar():
"""This is another docstring
with more lines of text
"""
return
def baz():
'''"This" is a string with some
embedded "quotes"'''
return
def troz():
"""Indentation with tabs
is just as OK
"""
return
def zort():
"""Another
multiline
docstring
"""
pass
def poit():
"""
Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
- dolore magna aliqua
- enim ad minim veniam
- quis nostrud exercitation ullamco laboris nisi
- aliquip ex ea commodo consequat
"""
pass
def under_indent():
"""
These lines are indented in a way that does not
make sense.
"""
pass
def over_indent():
"""
This has a shallow indent
- But some lines are deeper
- And the closing quote is too deep
"""
pass
def single_line():
"""But with a newline after it!"""
pass
def this():
r"""
'hey ho'
"""
def that():
""" "hey yah" """
def and_that():
"""
"hey yah" """
def and_this():
'''
"hey yah"'''
def multiline_whitespace():
""" """
def oneline_whitespace():
""" """
def empty():
""""""
def single_quotes():
"testing"
def believe_it_or_not_this_is_in_the_py_stdlib():
'''
"hey yah"'''
def ignored_docstring():
"""a => \
b"""
def single_line_docstring_with_whitespace():
"""This should be stripped"""
def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass
def backslash_space():
"""\ """
def multiline_backslash_1():
"""
hey\there\
\ """
def multiline_backslash_2():
"""
hey there \ """
# Regression test for #3425
def multiline_backslash_really_long_dont_crash():
"""
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
def multiline_backslash_3():
"""
already escaped \\"""
def my_god_its_full_of_stars_1():
"I'm sorry Dave\u2001"
# the space below is actually a \u2001, removed in output
def my_god_its_full_of_stars_2():
"I'm sorry Dave"
def docstring_almost_at_line_limit():
"""long docstring................................................................."""
def docstring_almost_at_line_limit2():
"""long docstring.................................................................
..................................................................................
"""
def docstring_at_line_limit():
"""long docstring................................................................"""
def multiline_docstring_at_line_limit():
"""first line-----------------------------------------------------------------------
second line----------------------------------------------------------------------"""
def stable_quote_normalization_with_immediate_inner_single_quote(self):
"""'<text here>
<text here, since without another non-empty line black is stable>
"""
```

View file

@ -0,0 +1,898 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py
---
## Input
```py
#!/usr/bin/env python3
import asyncio
import sys
from third_party import X, Y, Z
from library import some_connection, \
some_decorator
# fmt: off
from third_party import (X,
Y, Z)
# fmt: on
f'trigger 3.6 mode'
# Comment 1
# Comment 2
# fmt: off
def func_no_args():
a; b; c
if True: raise RuntimeError
if False: ...
for i in range(10):
print(i)
continue
exec('new-style exec', {}, {})
return None
async def coroutine(arg, exec=False):
'Single-line docstring. Multiline is harder to reformat.'
async with some_connection() as conn:
await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2)
await asyncio.sleep(1)
@asyncio.coroutine
@some_decorator(
with_args=True,
many_args=[1,2,3]
)
def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
return text[number:-1]
# fmt: on
def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''):
offset = attr.ib(default=attr.Factory( lambda: _r.uniform(1, 2)))
assert task._cancel_stack[:len(old_stack)] == old_stack
def spaces_types(a: int = 1, b: tuple = (), c: list = [], d: dict = {}, e: bool = True, f: int = -1, g: int = 1 if False else 2, h: str = "", i: str = r''): ...
def spaces2(result= _core.Value(None)):
...
something = {
# fmt: off
key: 'value',
}
def subscriptlist():
atom[
# fmt: off
'some big and',
'complex subscript',
# fmt: on
goes + here, andhere,
]
def import_as_names():
# fmt: off
from hello import a, b
'unformatted'
# fmt: on
def testlist_star_expr():
# fmt: off
a , b = *hello
'unformatted'
# fmt: on
def yield_expr():
# fmt: off
yield hello
'unformatted'
# fmt: on
'formatted'
# fmt: off
( yield hello )
'unformatted'
# fmt: on
def example(session):
# fmt: off
result = session\
.query(models.Customer.id)\
.filter(models.Customer.account_id == account_id,
models.Customer.email == email_address)\
.order_by(models.Customer.id.asc())\
.all()
# fmt: on
def off_and_on_without_data():
"""All comments here are technically on the same prefix.
The comments between will be formatted. This is a known limitation.
"""
# fmt: off
#hey, that won't work
# fmt: on
pass
def on_and_off_broken():
"""Another known limitation."""
# fmt: on
# fmt: off
this=should.not_be.formatted()
and_=indeed . it is not formatted
because . the . handling . inside . generate_ignored_nodes()
now . considers . multiple . fmt . directives . within . one . prefix
# fmt: on
# fmt: off
# ...but comments still get reformatted even though they should not be
# fmt: on
def long_lines():
if True:
typedargslist.extend(
gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True)
)
# fmt: off
a = (
unnecessary_bracket()
)
# fmt: on
_type_comment_re = re.compile(
r"""
^
[\t ]*
\#[ ]type:[ ]*
(?P<type>
[^#\t\n]+?
)
(?<!ignore) # note: this will force the non-greedy + in <type> to match
# a trailing space which is why we need the silliness below
(?<!ignore[ ]{1})(?<!ignore[ ]{2})(?<!ignore[ ]{3})(?<!ignore[ ]{4})
(?<!ignore[ ]{5})(?<!ignore[ ]{6})(?<!ignore[ ]{7})(?<!ignore[ ]{8})
(?<!ignore[ ]{9})(?<!ignore[ ]{10})
[\t ]*
(?P<nl>
(?:\#[^\n]*)?
\n?
)
$
""",
# fmt: off
re.MULTILINE|re.VERBOSE
# fmt: on
)
def single_literal_yapf_disable():
"""Black does not support this."""
BAZ = {
(1, 2, 3, 4),
(5, 6, 7, 8),
(9, 10, 11, 12)
} # yapf: disable
cfg.rule(
"Default", "address",
xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"],
xxxxxx="xx_xxxxx", xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
xxxxxxxxx_xxxx=True, xxxxxxxx_xxxxxxxxxx=False,
xxxxxx_xxxxxx=2, xxxxxx_xxxxx_xxxxxxxx=70, xxxxxx_xxxxxx_xxxxx=True,
# fmt: off
xxxxxxx_xxxxxxxxxxxx={
"xxxxxxxx": {
"xxxxxx": False,
"xxxxxxx": False,
"xxxx_xxxxxx": "xxxxx",
},
"xxxxxxxx-xxxxx": {
"xxxxxx": False,
"xxxxxxx": True,
"xxxx_xxxxxx": "xxxxxx",
},
},
# fmt: on
xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5
)
# fmt: off
yield 'hello'
# No formatting to the end of the file
l=[1,2,3]
d={'a':1,
'b':2}
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -5,39 +5,53 @@
from third_party import X, Y, Z
from library import some_connection, some_decorator
+# fmt: off
+from third_party import X, Y, Z
-# fmt: off
-from third_party import (X,
- Y, Z)
# fmt: on
-f"trigger 3.6 mode"
+f'trigger 3.6 mode'
+
+
# Comment 1
# Comment 2
-
# fmt: off
def func_no_args():
- a; b; c
- if True: raise RuntimeError
- if False: ...
- for i in range(10):
- print(i)
- continue
- exec('new-style exec', {}, {})
- return None
+ a
+ b
+ c
+ if True:
+ raise RuntimeError
+ if False:
+ ...
+ for i in range(10):
+ print(i)
+ continue
+ exec("new-style exec", {}, {})
+ return None
+
+
async def coroutine(arg, exec=False):
- 'Single-line docstring. Multiline is harder to reformat.'
- async with some_connection() as conn:
- await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2)
- await asyncio.sleep(1)
+ "Single-line docstring. Multiline is harder to reformat."
+ async with some_connection() as conn:
+ await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2)
+ await asyncio.sleep(1)
+
+
@asyncio.coroutine
-@some_decorator(
-with_args=True,
-many_args=[1,2,3]
-)
-def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
- return text[number:-1]
+@some_decorator(with_args=True, many_args=[1, 2, 3])
+def function_signature_stress_test(
+ number: int,
+ no_annotation=None,
+ text: str = "default",
+ *,
+ debug: bool = False,
+ **kwargs,
+) -> str:
+ return text[number:-1]
+
+
# fmt: on
def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""):
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
@@ -64,55 +78,55 @@
something = {
# fmt: off
- key: 'value',
+ key: "value",
}
def subscriptlist():
atom[
# fmt: off
- 'some big and',
- 'complex subscript',
+ "some big and",
+ "complex subscript",
# fmt: on
- goes + here,
+ goes
+ + here,
andhere,
]
def import_as_names():
# fmt: off
- from hello import a, b
- 'unformatted'
+ from hello import a, b
+
+ "unformatted"
# fmt: on
def testlist_star_expr():
# fmt: off
- a , b = *hello
- 'unformatted'
+ a, b = *hello
+ "unformatted"
# fmt: on
def yield_expr():
# fmt: off
yield hello
- 'unformatted'
+ "unformatted"
# fmt: on
"formatted"
# fmt: off
- ( yield hello )
- 'unformatted'
+ (yield hello)
+ "unformatted"
# fmt: on
def example(session):
# fmt: off
- result = session\
- .query(models.Customer.id)\
- .filter(models.Customer.account_id == account_id,
- models.Customer.email == email_address)\
- .order_by(models.Customer.id.asc())\
- .all()
+ result = session.query(models.Customer.id).filter(
+ models.Customer.account_id == account_id,
+ models.Customer.email == email_address,
+ ).order_by(models.Customer.id.asc()).all()
# fmt: on
@@ -123,7 +137,7 @@
"""
# fmt: off
- # hey, that won't work
+ #hey, that won't work
# fmt: on
pass
@@ -133,10 +147,10 @@
"""Another known limitation."""
# fmt: on
# fmt: off
- this=should.not_be.formatted()
- and_=indeed . it is not formatted
- because . the . handling . inside . generate_ignored_nodes()
- now . considers . multiple . fmt . directives . within . one . prefix
+ this = should.not_be.formatted()
+ and_ = indeed.it is not formatted
+ because.the.handling.inside.generate_ignored_nodes()
+ now.considers.multiple.fmt.directives.within.one.prefix
# fmt: on
# fmt: off
# ...but comments still get reformatted even though they should not be
@@ -154,9 +168,7 @@
)
)
# fmt: off
- a = (
- unnecessary_bracket()
- )
+ a = unnecessary_bracket()
# fmt: on
_type_comment_re = re.compile(
r"""
@@ -179,7 +191,8 @@
$
""",
# fmt: off
- re.MULTILINE|re.VERBOSE
+ re.MULTILINE
+ | re.VERBOSE,
# fmt: on
)
@@ -200,8 +213,8 @@
xxxxxx_xxxxxx=2,
xxxxxx_xxxxx_xxxxxxxx=70,
xxxxxx_xxxxxx_xxxxx=True,
- # fmt: off
- xxxxxxx_xxxxxxxxxxxx={
+ xxxxxxx_xxxxxxxxxxxx=# fmt: off
+ {
"xxxxxxxx": {
"xxxxxx": False,
"xxxxxxx": False,
@@ -213,12 +226,11 @@
"xxxx_xxxxxx": "xxxxxx",
},
},
- # fmt: on
- xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5,
+ xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=# fmt: on
+ 5,
)
# fmt: off
-yield 'hello'
+yield "hello"
# No formatting to the end of the file
-l=[1,2,3]
-d={'a':1,
- 'b':2}
+l = [1, 2, 3]
+d = {"a": 1, "b": 2}
```
## Ruff Output
```py
#!/usr/bin/env python3
import asyncio
import sys
from third_party import X, Y, Z
from library import some_connection, some_decorator
# fmt: off
from third_party import X, Y, Z
# fmt: on
f'trigger 3.6 mode'
# Comment 1
# Comment 2
# fmt: off
def func_no_args():
a
b
c
if True:
raise RuntimeError
if False:
...
for i in range(10):
print(i)
continue
exec("new-style exec", {}, {})
return None
async def coroutine(arg, exec=False):
"Single-line docstring. Multiline is harder to reformat."
async with some_connection() as conn:
await conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2)
await asyncio.sleep(1)
@asyncio.coroutine
@some_decorator(with_args=True, many_args=[1, 2, 3])
def function_signature_stress_test(
number: int,
no_annotation=None,
text: str = "default",
*,
debug: bool = False,
**kwargs,
) -> str:
return text[number:-1]
# fmt: on
def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""):
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
assert task._cancel_stack[: len(old_stack)] == old_stack
def spaces_types(
a: int = 1,
b: tuple = (),
c: list = [],
d: dict = {},
e: bool = True,
f: int = -1,
g: int = 1 if False else 2,
h: str = "",
i: str = r"",
):
...
def spaces2(result=_core.Value(None)):
...
something = {
# fmt: off
key: "value",
}
def subscriptlist():
atom[
# fmt: off
"some big and",
"complex subscript",
# fmt: on
goes
+ here,
andhere,
]
def import_as_names():
# fmt: off
from hello import a, b
"unformatted"
# fmt: on
def testlist_star_expr():
# fmt: off
a, b = *hello
"unformatted"
# fmt: on
def yield_expr():
# fmt: off
yield hello
"unformatted"
# fmt: on
"formatted"
# fmt: off
(yield hello)
"unformatted"
# fmt: on
def example(session):
# fmt: off
result = session.query(models.Customer.id).filter(
models.Customer.account_id == account_id,
models.Customer.email == email_address,
).order_by(models.Customer.id.asc()).all()
# fmt: on
def off_and_on_without_data():
"""All comments here are technically on the same prefix.
The comments between will be formatted. This is a known limitation.
"""
# fmt: off
#hey, that won't work
# fmt: on
pass
def on_and_off_broken():
"""Another known limitation."""
# fmt: on
# fmt: off
this = should.not_be.formatted()
and_ = indeed.it is not formatted
because.the.handling.inside.generate_ignored_nodes()
now.considers.multiple.fmt.directives.within.one.prefix
# fmt: on
# fmt: off
# ...but comments still get reformatted even though they should not be
# fmt: on
def long_lines():
if True:
typedargslist.extend(
gen_annotated_params(
ast_args.kwonlyargs,
ast_args.kw_defaults,
parameters,
implicit_default=True,
)
)
# fmt: off
a = unnecessary_bracket()
# fmt: on
_type_comment_re = re.compile(
r"""
^
[\t ]*
\#[ ]type:[ ]*
(?P<type>
[^#\t\n]+?
)
(?<!ignore) # note: this will force the non-greedy + in <type> to match
# a trailing space which is why we need the silliness below
(?<!ignore[ ]{1})(?<!ignore[ ]{2})(?<!ignore[ ]{3})(?<!ignore[ ]{4})
(?<!ignore[ ]{5})(?<!ignore[ ]{6})(?<!ignore[ ]{7})(?<!ignore[ ]{8})
(?<!ignore[ ]{9})(?<!ignore[ ]{10})
[\t ]*
(?P<nl>
(?:\#[^\n]*)?
\n?
)
$
""",
# fmt: off
re.MULTILINE
| re.VERBOSE,
# fmt: on
)
def single_literal_yapf_disable():
"""Black does not support this."""
BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable
cfg.rule(
"Default",
"address",
xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"],
xxxxxx="xx_xxxxx",
xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
xxxxxxxxx_xxxx=True,
xxxxxxxx_xxxxxxxxxx=False,
xxxxxx_xxxxxx=2,
xxxxxx_xxxxx_xxxxxxxx=70,
xxxxxx_xxxxxx_xxxxx=True,
xxxxxxx_xxxxxxxxxxxx=# fmt: off
{
"xxxxxxxx": {
"xxxxxx": False,
"xxxxxxx": False,
"xxxx_xxxxxx": "xxxxx",
},
"xxxxxxxx-xxxxx": {
"xxxxxx": False,
"xxxxxxx": True,
"xxxx_xxxxxx": "xxxxxx",
},
},
xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=# fmt: on
5,
)
# fmt: off
yield "hello"
# No formatting to the end of the file
l = [1, 2, 3]
d = {"a": 1, "b": 2}
```
## Black Output
```py
#!/usr/bin/env python3
import asyncio
import sys
from third_party import X, Y, Z
from library import some_connection, some_decorator
# fmt: off
from third_party import (X,
Y, Z)
# fmt: on
f"trigger 3.6 mode"
# Comment 1
# Comment 2
# fmt: off
def func_no_args():
a; b; c
if True: raise RuntimeError
if False: ...
for i in range(10):
print(i)
continue
exec('new-style exec', {}, {})
return None
async def coroutine(arg, exec=False):
'Single-line docstring. Multiline is harder to reformat.'
async with some_connection() as conn:
await conn.do_what_i_mean('SELECT bobby, tables FROM xkcd', timeout=2)
await asyncio.sleep(1)
@asyncio.coroutine
@some_decorator(
with_args=True,
many_args=[1,2,3]
)
def function_signature_stress_test(number:int,no_annotation=None,text:str='default',* ,debug:bool=False,**kwargs) -> str:
return text[number:-1]
# fmt: on
def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r""):
offset = attr.ib(default=attr.Factory(lambda: _r.uniform(1, 2)))
assert task._cancel_stack[: len(old_stack)] == old_stack
def spaces_types(
a: int = 1,
b: tuple = (),
c: list = [],
d: dict = {},
e: bool = True,
f: int = -1,
g: int = 1 if False else 2,
h: str = "",
i: str = r"",
):
...
def spaces2(result=_core.Value(None)):
...
something = {
# fmt: off
key: 'value',
}
def subscriptlist():
atom[
# fmt: off
'some big and',
'complex subscript',
# fmt: on
goes + here,
andhere,
]
def import_as_names():
# fmt: off
from hello import a, b
'unformatted'
# fmt: on
def testlist_star_expr():
# fmt: off
a , b = *hello
'unformatted'
# fmt: on
def yield_expr():
# fmt: off
yield hello
'unformatted'
# fmt: on
"formatted"
# fmt: off
( yield hello )
'unformatted'
# fmt: on
def example(session):
# fmt: off
result = session\
.query(models.Customer.id)\
.filter(models.Customer.account_id == account_id,
models.Customer.email == email_address)\
.order_by(models.Customer.id.asc())\
.all()
# fmt: on
def off_and_on_without_data():
"""All comments here are technically on the same prefix.
The comments between will be formatted. This is a known limitation.
"""
# fmt: off
# hey, that won't work
# fmt: on
pass
def on_and_off_broken():
"""Another known limitation."""
# fmt: on
# fmt: off
this=should.not_be.formatted()
and_=indeed . it is not formatted
because . the . handling . inside . generate_ignored_nodes()
now . considers . multiple . fmt . directives . within . one . prefix
# fmt: on
# fmt: off
# ...but comments still get reformatted even though they should not be
# fmt: on
def long_lines():
if True:
typedargslist.extend(
gen_annotated_params(
ast_args.kwonlyargs,
ast_args.kw_defaults,
parameters,
implicit_default=True,
)
)
# fmt: off
a = (
unnecessary_bracket()
)
# fmt: on
_type_comment_re = re.compile(
r"""
^
[\t ]*
\#[ ]type:[ ]*
(?P<type>
[^#\t\n]+?
)
(?<!ignore) # note: this will force the non-greedy + in <type> to match
# a trailing space which is why we need the silliness below
(?<!ignore[ ]{1})(?<!ignore[ ]{2})(?<!ignore[ ]{3})(?<!ignore[ ]{4})
(?<!ignore[ ]{5})(?<!ignore[ ]{6})(?<!ignore[ ]{7})(?<!ignore[ ]{8})
(?<!ignore[ ]{9})(?<!ignore[ ]{10})
[\t ]*
(?P<nl>
(?:\#[^\n]*)?
\n?
)
$
""",
# fmt: off
re.MULTILINE|re.VERBOSE
# fmt: on
)
def single_literal_yapf_disable():
"""Black does not support this."""
BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable
cfg.rule(
"Default",
"address",
xxxx_xxxx=["xxx-xxxxxx-xxxxxxxxxx"],
xxxxxx="xx_xxxxx",
xxxxxxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
xxxxxxxxx_xxxx=True,
xxxxxxxx_xxxxxxxxxx=False,
xxxxxx_xxxxxx=2,
xxxxxx_xxxxx_xxxxxxxx=70,
xxxxxx_xxxxxx_xxxxx=True,
# fmt: off
xxxxxxx_xxxxxxxxxxxx={
"xxxxxxxx": {
"xxxxxx": False,
"xxxxxxx": False,
"xxxx_xxxxxx": "xxxxx",
},
"xxxxxxxx-xxxxx": {
"xxxxxx": False,
"xxxxxxx": True,
"xxxx_xxxxxx": "xxxxxx",
},
},
# fmt: on
xxxxxxxxxx_xxxxxxxxxxx_xxxxxxx_xxxxxxxxx=5,
)
# fmt: off
yield 'hello'
# No formatting to the end of the file
l=[1,2,3]
d={'a':1,
'b':2}
```

View file

@ -0,0 +1,293 @@
---
source: crates/ruff_python_formatter/src/lib.rs
expression: snapshot
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py
---
## Input
```py
# Make sure a leading comment is not removed.
def some_func( unformatted, args ): # fmt: skip
print("I am some_func")
return 0
# Make sure this comment is not removed.
# Make sure a leading comment is not removed.
async def some_async_func( unformatted, args): # fmt: skip
print("I am some_async_func")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
class SomeClass( Unformatted, SuperClasses ): # fmt: skip
def some_method( self, unformatted, args ): # fmt: skip
print("I am some_method")
return 0
async def some_async_method( self, unformatted, args ): # fmt: skip
print("I am some_async_method")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
if unformatted_call( args ): # fmt: skip
print("First branch")
# Make sure this is not removed.
elif another_unformatted_call( args ): # fmt: skip
print("Second branch")
else : # fmt: skip
print("Last branch")
while some_condition( unformatted, args ): # fmt: skip
print("Do something")
for i in some_iter( unformatted, args ): # fmt: skip
print("Do something")
async def test_async_for():
async for i in some_async_iter( unformatted, args ): # fmt: skip
print("Do something")
try : # fmt: skip
some_call()
except UnformattedError as ex: # fmt: skip
handle_exception()
finally : # fmt: skip
finally_call()
with give_me_context( unformatted, args ): # fmt: skip
print("Do something")
async def test_async_with():
async with give_me_async_context( unformatted, args ): # fmt: skip
print("Do something")
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -1,62 +1,62 @@
# Make sure a leading comment is not removed.
-def some_func( unformatted, args ): # fmt: skip
+def some_func(unformatted, args): # fmt: skip
print("I am some_func")
return 0
# Make sure this comment is not removed.
# Make sure a leading comment is not removed.
-async def some_async_func( unformatted, args): # fmt: skip
+async def some_async_func(unformatted, args): # fmt: skip
print("I am some_async_func")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
-class SomeClass( Unformatted, SuperClasses ): # fmt: skip
- def some_method( self, unformatted, args ): # fmt: skip
+class SomeClass(Unformatted, SuperClasses): # fmt: skip
+ def some_method(self, unformatted, args): # fmt: skip
print("I am some_method")
return 0
- async def some_async_method( self, unformatted, args ): # fmt: skip
+ async def some_async_method(self, unformatted, args): # fmt: skip
print("I am some_async_method")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
-if unformatted_call( args ): # fmt: skip
+if unformatted_call(args): # fmt: skip
print("First branch")
# Make sure this is not removed.
-elif another_unformatted_call( args ): # fmt: skip
+elif another_unformatted_call(args): # fmt: skip
print("Second branch")
-else : # fmt: skip
+else: # fmt: skip
print("Last branch")
-while some_condition( unformatted, args ): # fmt: skip
+while some_condition(unformatted, args): # fmt: skip
print("Do something")
-for i in some_iter( unformatted, args ): # fmt: skip
+for i in some_iter(unformatted, args): # fmt: skip
print("Do something")
async def test_async_for():
- async for i in some_async_iter( unformatted, args ): # fmt: skip
+ async for i in some_async_iter(unformatted, args): # fmt: skip
print("Do something")
-try : # fmt: skip
+try: # fmt: skip
some_call()
-except UnformattedError as ex: # fmt: skip
+except UnformattedError as ex:
handle_exception()
-finally : # fmt: skip
+finally: # fmt: skip
finally_call()
-with give_me_context( unformatted, args ): # fmt: skip
+with give_me_context(unformatted, args): # fmt: skip
print("Do something")
async def test_async_with():
- async with give_me_async_context( unformatted, args ): # fmt: skip
+ async with give_me_async_context(unformatted, args): # fmt: skip
print("Do something")
```
## Ruff Output
```py
# Make sure a leading comment is not removed.
def some_func(unformatted, args): # fmt: skip
print("I am some_func")
return 0
# Make sure this comment is not removed.
# Make sure a leading comment is not removed.
async def some_async_func(unformatted, args): # fmt: skip
print("I am some_async_func")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
class SomeClass(Unformatted, SuperClasses): # fmt: skip
def some_method(self, unformatted, args): # fmt: skip
print("I am some_method")
return 0
async def some_async_method(self, unformatted, args): # fmt: skip
print("I am some_async_method")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
if unformatted_call(args): # fmt: skip
print("First branch")
# Make sure this is not removed.
elif another_unformatted_call(args): # fmt: skip
print("Second branch")
else: # fmt: skip
print("Last branch")
while some_condition(unformatted, args): # fmt: skip
print("Do something")
for i in some_iter(unformatted, args): # fmt: skip
print("Do something")
async def test_async_for():
async for i in some_async_iter(unformatted, args): # fmt: skip
print("Do something")
try: # fmt: skip
some_call()
except UnformattedError as ex:
handle_exception()
finally: # fmt: skip
finally_call()
with give_me_context(unformatted, args): # fmt: skip
print("Do something")
async def test_async_with():
async with give_me_async_context(unformatted, args): # fmt: skip
print("Do something")
```
## Black Output
```py
# Make sure a leading comment is not removed.
def some_func( unformatted, args ): # fmt: skip
print("I am some_func")
return 0
# Make sure this comment is not removed.
# Make sure a leading comment is not removed.
async def some_async_func( unformatted, args): # fmt: skip
print("I am some_async_func")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
class SomeClass( Unformatted, SuperClasses ): # fmt: skip
def some_method( self, unformatted, args ): # fmt: skip
print("I am some_method")
return 0
async def some_async_method( self, unformatted, args ): # fmt: skip
print("I am some_async_method")
await asyncio.sleep(1)
# Make sure a leading comment is not removed.
if unformatted_call( args ): # fmt: skip
print("First branch")
# Make sure this is not removed.
elif another_unformatted_call( args ): # fmt: skip
print("Second branch")
else : # fmt: skip
print("Last branch")
while some_condition( unformatted, args ): # fmt: skip
print("Do something")
for i in some_iter( unformatted, args ): # fmt: skip
print("Do something")
async def test_async_for():
async for i in some_async_iter( unformatted, args ): # fmt: skip
print("Do something")
try : # fmt: skip
some_call()
except UnformattedError as ex: # fmt: skip
handle_exception()
finally : # fmt: skip
finally_call()
with give_me_context( unformatted, args ): # fmt: skip
print("Do something")
async def test_async_with():
async with give_me_async_context( unformatted, args ): # fmt: skip
print("Do something")
```