Add --skip-magic-trailing-comma (#1824)

This commit is contained in:
Shantanu 2021-01-17 16:59:06 -08:00 committed by GitHub
parent de510478d9
commit 692c0f50d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 531 additions and 36 deletions

View File

@ -97,6 +97,10 @@ Options:
-S, --skip-string-normalization
Don't normalize string quotes or prefixes.
-C, --skip-magic-trailing-comma
Don't use trailing commas as a reason to
split lines.
--check Don't write the files back, just return the
status. Return code 0 means nothing would
change. Return code 1 means some files
@ -127,18 +131,19 @@ Options:
paths are excluded. Use forward slashes for
directories on all platforms (Windows, too).
Exclusions are calculated first, inclusions
later. [default: /(\.eggs|\.git|\.hg|\.mypy
_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-
out|build|dist)/]
later. [default: /(\.direnv|\.eggs|\.git|\.
hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_bu
ild|buck-out|build|dist)/]
--force-exclude TEXT Like --exclude, but files and directories
matching this regex will be excluded even
when they are passed explicitly as arguments.
when they are passed explicitly as
arguments.
--stdin-filename TEXT The name of the file when passing it through
stdin. Useful to make sure Black will respect
--force-exclude option on some editors that
rely on using stdin.
stdin. Useful to make sure Black will
respect --force-exclude option on some
editors that rely on using stdin.
-q, --quiet Don't emit non-error messages to stderr.
Errors are still emitted; silence those with

View File

@ -54,6 +54,9 @@ The headers controlling how source code is formatted are:
- `X-Skip-String-Normalization`: corresponds to the `--skip-string-normalization`
command line flag. If present and its value is not the empty string, no string
normalization will be performed.
- `X-Skip-Magic-Trailing-Comma`: corresponds to the `--skip-magic-trailing-comma`
command line flag. If present and its value is not the empty string, trailing commas
will not be used as a reason to split lines.
- `X-Fast-Or-Safe`: if set to `fast`, `blackd` will act as _Black_ does when passed the
`--fast` command line flag.
- `X-Python-Variant`: if set to `pyi`, `blackd` will act as _Black_ does when passed the

View File

@ -52,6 +52,10 @@ Options:
-S, --skip-string-normalization
Don't normalize string quotes or prefixes.
-C, --skip-magic-trailing-comma
Don't use trailing commas as a reason to
split lines.
--check Don't write the files back, just return the
status. Return code 0 means nothing would
change. Return code 1 means some files
@ -82,13 +86,19 @@ Options:
paths are excluded. Use forward slashes for
directories on all platforms (Windows, too).
Exclusions are calculated first, inclusions
later. [default: /(\.eggs|\.git|\.hg|\.mypy
_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-
out|build|dist)/]
later. [default: /(\.direnv|\.eggs|\.git|\.
hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_bu
ild|buck-out|build|dist)/]
--force-exclude TEXT Like --exclude, but files and directories
matching this regex will be excluded even
when they are passed explicitly as arguments.
when they are passed explicitly as
arguments.
--stdin-filename TEXT The name of the file when passing it through
stdin. Useful to make sure Black will
respect --force-exclude option on some
editors that rely on using stdin.
--stdin-filename TEXT The name of the file when passing it through
stdin. Useful to make sure Black will respect

View File

@ -438,6 +438,9 @@ into one item per line.
How do you make it stop? Just delete that trailing comma and _Black_ will collapse your
collection into one line if it fits.
If you must, you can recover the behaviour of early versions of Black with the option
`--skip-magic-trailing-comma` / `-C`.
### r"strings" and R"strings"
_Black_ normalizes string quotes as well as string prefixes, making them lowercase. One

View File

@ -260,6 +260,7 @@ class Mode:
target_versions: Set[TargetVersion] = field(default_factory=set)
line_length: int = DEFAULT_LINE_LENGTH
string_normalization: bool = True
magic_trailing_comma: bool = True
experimental_string_processing: bool = False
is_pyi: bool = False
@ -397,6 +398,12 @@ def target_version_option_callback(
is_flag=True,
help="Don't normalize string quotes or prefixes.",
)
@click.option(
"-C",
"--skip-magic-trailing-comma",
is_flag=True,
help="Don't use trailing commas as a reason to split lines.",
)
@click.option(
"--experimental-string-processing",
is_flag=True,
@ -524,6 +531,7 @@ def main(
fast: bool,
pyi: bool,
skip_string_normalization: bool,
skip_magic_trailing_comma: bool,
experimental_string_processing: bool,
quiet: bool,
verbose: bool,
@ -546,6 +554,7 @@ def main(
line_length=line_length,
is_pyi=pyi,
string_normalization=not skip_string_normalization,
magic_trailing_comma=not skip_magic_trailing_comma,
experimental_string_processing=experimental_string_processing,
)
if config and verbose:
@ -1022,13 +1031,12 @@ def f(
versions = detect_target_versions(src_node)
normalize_fmt_off(src_node)
lines = LineGenerator(
mode=mode,
remove_u_prefix="unicode_literals" in future_imports
or supports_feature(versions, Feature.UNICODE_LITERALS),
is_pyi=mode.is_pyi,
normalize_strings=mode.string_normalization,
)
elt = EmptyLineTracker(is_pyi=mode.is_pyi)
empty_line = Line()
empty_line = Line(mode=mode)
after = 0
split_line_features = {
feature
@ -1464,6 +1472,7 @@ def get_open_lsqb(self) -> Optional[Leaf]:
class Line:
"""Holds leaves and comments. Can be printed with `str(line)`."""
mode: Mode
depth: int = 0
leaves: List[Leaf] = field(default_factory=list)
# keys ordered like `leaves`
@ -1496,8 +1505,11 @@ def append(self, leaf: Leaf, preformatted: bool = False) -> None:
)
if self.inside_brackets or not preformatted:
self.bracket_tracker.mark(leaf)
if self.maybe_should_explode(leaf):
if self.mode.magic_trailing_comma:
if self.has_magic_trailing_comma(leaf):
self.should_explode = True
elif self.has_magic_trailing_comma(leaf, ensure_removable=True):
self.remove_trailing_comma()
if not self.append_comment(leaf):
self.leaves.append(leaf)
@ -1673,10 +1685,14 @@ def contains_unsplittable_type_ignore(self) -> bool:
def contains_multiline_strings(self) -> bool:
return any(is_multiline_string(leaf) for leaf in self.leaves)
def maybe_should_explode(self, closing: Leaf) -> bool:
"""Return True if this line should explode (always be split), that is when:
- there's a trailing comma here; and
- it's not a one-tuple.
def has_magic_trailing_comma(
self, closing: Leaf, ensure_removable: bool = False
) -> bool:
"""Return True if we have a magic trailing comma, that is when:
- there's a trailing comma here
- it's not a one-tuple
Additionally, if ensure_removable:
- it's not from square bracket indexing
"""
if not (
closing.type in CLOSING_BRACKETS
@ -1685,9 +1701,15 @@ def maybe_should_explode(self, closing: Leaf) -> bool:
):
return False
if closing.type in {token.RBRACE, token.RSQB}:
if closing.type == token.RBRACE:
return True
if closing.type == token.RSQB:
if not ensure_removable:
return True
comma = self.leaves[-1]
return bool(comma.parent and comma.parent.type == syms.listmaker)
if self.is_import:
return True
@ -1765,6 +1787,7 @@ def is_complex_subscript(self, leaf: Leaf) -> bool:
def clone(self) -> "Line":
return Line(
mode=self.mode,
depth=self.depth,
inside_brackets=self.inside_brackets,
should_explode=self.should_explode,
@ -1923,10 +1946,9 @@ class LineGenerator(Visitor[Line]):
in ways that will no longer stringify to valid Python code on the tree.
"""
is_pyi: bool = False
normalize_strings: bool = True
current_line: Line = field(default_factory=Line)
mode: Mode
remove_u_prefix: bool = False
current_line: Line = field(init=False)
def line(self, indent: int = 0) -> Iterator[Line]:
"""Generate a line.
@ -1941,7 +1963,7 @@ def line(self, indent: int = 0) -> Iterator[Line]:
return # Line is empty, don't emit. Creating a new one unnecessary.
complete_line = self.current_line
self.current_line = Line(depth=complete_line.depth + indent)
self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent)
yield complete_line
def visit_default(self, node: LN) -> Iterator[Line]:
@ -1965,7 +1987,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
yield from self.line()
normalize_prefix(node, inside_brackets=any_open_brackets)
if self.normalize_strings and node.type == token.STRING:
if self.mode.string_normalization and node.type == token.STRING:
normalize_string_prefix(node, remove_u_prefix=self.remove_u_prefix)
normalize_string_quotes(node)
if node.type == token.NUMBER:
@ -2017,7 +2039,7 @@ def visit_stmt(
def visit_suite(self, node: Node) -> Iterator[Line]:
"""Visit a suite."""
if self.is_pyi and is_stub_suite(node):
if self.mode.is_pyi and is_stub_suite(node):
yield from self.visit(node.children[2])
else:
yield from self.visit_default(node)
@ -2026,7 +2048,7 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
"""Visit a statement without nested statements."""
is_suite_like = node.parent and node.parent.type in STATEMENT
if is_suite_like:
if self.is_pyi and is_stub_body(node):
if self.mode.is_pyi and is_stub_body(node):
yield from self.visit_default(node)
else:
yield from self.line(+1)
@ -2034,7 +2056,11 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
yield from self.line(-1)
else:
if not self.is_pyi or not node.parent or not is_stub_suite(node.parent):
if (
not self.mode.is_pyi
or not node.parent
or not is_stub_suite(node.parent)
):
yield from self.line()
yield from self.visit_default(node)
@ -2110,6 +2136,8 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
def __post_init__(self) -> None:
"""You are in a twisty little maze of passages."""
self.current_line = Line(mode=self.mode)
v = self.visit_stmt
Ø: Set[str] = set()
self.visit_assert_stmt = partial(v, keywords={"assert"}, parens={"assert", ","})
@ -4350,6 +4378,7 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
# `StringSplitter` will break it down further if necessary.
string_value = LL[string_idx].value
string_line = Line(
mode=line.mode,
depth=line.depth + 1,
inside_brackets=True,
should_explode=line.should_explode,
@ -4943,7 +4972,7 @@ def bracket_split_build_line(
If `is_body` is True, the result line is one-indented inside brackets and as such
has its first leaf's prefix normalized and a trailing comma added when expected.
"""
result = Line(depth=original.depth)
result = Line(mode=original.mode, depth=original.depth)
if is_body:
result.inside_brackets = True
result.depth += 1
@ -5015,7 +5044,9 @@ def delimiter_split(line: Line, features: Collection[Feature] = ()) -> Iterator[
if bt.delimiter_count_with_priority(delimiter_priority) == 1:
raise CannotSplit("Splitting a single attribute from its owner looks wrong")
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
current_line = Line(
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
lowest_depth = sys.maxsize
trailing_comma_safe = True
@ -5027,7 +5058,9 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
except ValueError:
yield current_line
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
current_line = Line(
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
current_line.append(leaf)
for leaf in line.leaves:
@ -5051,7 +5084,9 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
if leaf_priority == delimiter_priority:
yield current_line
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
current_line = Line(
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
if current_line:
if (
trailing_comma_safe
@ -5072,7 +5107,9 @@ def standalone_comment_split(
if not line.contains_standalone_comments(0):
raise CannotSplit("Line does not have any standalone comments")
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
current_line = Line(
mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
def append_to_line(leaf: Leaf) -> Iterator[Line]:
"""Append `leaf` to current line or to new line if appending impossible."""
@ -5082,7 +5119,9 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
except ValueError:
yield current_line
current_line = Line(depth=line.depth, inside_brackets=line.inside_brackets)
current_line = Line(
line.mode, depth=line.depth, inside_brackets=line.inside_brackets
)
current_line.append(leaf)
for leaf in line.leaves:
@ -5767,7 +5806,7 @@ def should_split_body_explode(line: Line, opening_bracket: Leaf) -> bool:
return False
return max_priority == COMMA_PRIORITY and (
trailing_comma
(line.mode.magic_trailing_comma and trailing_comma)
# always explode imports
or opening_bracket.parent.type in {syms.atom, syms.import_from}
)

View File

@ -32,6 +32,7 @@
LINE_LENGTH_HEADER = "X-Line-Length"
PYTHON_VARIANT_HEADER = "X-Python-Variant"
SKIP_STRING_NORMALIZATION_HEADER = "X-Skip-String-Normalization"
SKIP_MAGIC_TRAILING_COMMA = "X-Skip-Magic-Trailing-Comma"
FAST_OR_SAFE_HEADER = "X-Fast-Or-Safe"
DIFF_HEADER = "X-Diff"
@ -40,6 +41,7 @@
LINE_LENGTH_HEADER,
PYTHON_VARIANT_HEADER,
SKIP_STRING_NORMALIZATION_HEADER,
SKIP_MAGIC_TRAILING_COMMA,
FAST_OR_SAFE_HEADER,
DIFF_HEADER,
]
@ -114,6 +116,9 @@ async def handle(request: web.Request, executor: Executor) -> web.Response:
skip_string_normalization = bool(
request.headers.get(SKIP_STRING_NORMALIZATION_HEADER, False)
)
skip_magic_trailing_comma = bool(
request.headers.get(SKIP_MAGIC_TRAILING_COMMA, False)
)
fast = False
if request.headers.get(FAST_OR_SAFE_HEADER, "safe") == "fast":
fast = True
@ -122,6 +127,7 @@ async def handle(request: web.Request, executor: Executor) -> web.Response:
is_pyi=pyi,
line_length=line_length,
string_normalization=not skip_string_normalization,
magic_trailing_comma=not skip_magic_trailing_comma,
)
req_bytes = await request.content.read()
charset = request.charset if request.charset is not None else "utf8"

View File

@ -0,0 +1,404 @@
--- [Deterministic header]
+++ [Deterministic header]
@@ -1,8 +1,8 @@
...
-'some_string'
-b'\\xa3'
+"some_string"
+b"\\xa3"
Name
None
True
False
1
@@ -29,63 +29,84 @@
~great
+value
-1
~int and not v1 ^ 123 + v2 | True
(~int) and (not ((v1 ^ (123 + v2)) | True))
-+really ** -confusing ** ~operator ** -precedence
-flags & ~ select.EPOLLIN and waiters.write_task is not None
++(really ** -(confusing ** ~(operator ** -precedence)))
+flags & ~select.EPOLLIN and waiters.write_task is not None
lambda arg: None
lambda a=True: a
lambda a, b, c=True: a
-lambda a, b, c=True, *, d=(1 << v2), e='str': a
-lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b
+lambda a, b, c=True, *, d=(1 << v2), e="str": a
+lambda a, b, c=True, *vararg, d=(v1 << 2), e="str", **kwargs: a + b
manylambdas = lambda x=lambda y=lambda z=1: z: y(): x()
-foo = (lambda port_id, ignore_missing: {"port1": port1_resource, "port2": port2_resource}[port_id])
+foo = lambda port_id, ignore_missing: {
+ "port1": port1_resource,
+ "port2": port2_resource,
+}[port_id]
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)
str or None if (1 if True else 2) else str or bytes or None
(str or None) if (1 if True else 2) else (str or bytes or None)
-((super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None))
-{'2.7': dead, '3.7': (long_live or die_hard)}
-{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
+(
+ (super_long_variable_name or None)
+ if (1 if super_long_test_name else 2)
+ else (str or bytes or None)
+)
+{"2.7": dead, "3.7": (long_live or die_hard)}
+{"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
+{"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
()
(1,)
(1, 2)
(1, 2, 3)
[]
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
-[1, 2, 3,]
+[1, 2, 3]
[*a]
[*range(10)]
-[*a, 4, 5,]
-[4, *a, 5,]
-[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
+[*a, 4, 5]
+[4, *a, 5]
+[
+ this_is_a_very_long_variable_which_will_force_a_delimiter_split,
+ element,
+ another,
+ *more,
+]
{i for i in (1, 2, 3)}
{(i ** 2) for i in (1, 2, 3)}
-{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
+{(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))}
{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)}
[i for i in (1, 2, 3)]
[(i ** 2) for i in (1, 2, 3)]
-[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]
+[(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))]
[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)]
{i: 0 for i in (1, 2, 3)}
-{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}
+{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))}
{a: b * 2 for a, b in dictionary.items()}
{a: b * -2 for a, b in dictionary.items()}
-{k: v for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension}
+{
+ k: v
+ for k, v in this_is_a_very_long_variable_which_will_cause_a_trailing_comma_which_breaks_the_comprehension
+}
Python3 > Python2 > COBOL
Life is Life
call()
call(arg)
-call(kwarg='hey')
-call(arg, kwarg='hey')
-call(arg, another, kwarg='hey', **kwargs)
-call(this_is_a_very_long_variable_which_will_force_a_delimiter_split, arg, another, kwarg='hey', **kwargs) # note: no trailing comma pre-3.6
+call(kwarg="hey")
+call(arg, kwarg="hey")
+call(arg, another, kwarg="hey", **kwargs)
+call(
+ this_is_a_very_long_variable_which_will_force_a_delimiter_split,
+ arg,
+ another,
+ kwarg="hey",
+ **kwargs
+) # note: no trailing comma pre-3.6
call(*gidgets[:2])
call(a, *gidgets[:2])
call(**self.screen_kwargs)
call(b, **self.screen_kwargs)
lukasz.langa.pl
@@ -94,26 +115,24 @@
1.0 .real
....__class__
list[str]
dict[str, int]
tuple[str, ...]
-tuple[
- str, int, float, dict[str, int]
-]
+tuple[str, int, float, dict[str, int]]
tuple[str, int, float, dict[str, int],]
very_long_variable_name_filters: t.List[
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
]
xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
)
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
)
-xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[
- ..., List[SomeClass]
-] = classmethod(sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)) # type: ignore
+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod(
+ sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
+) # type: ignore
slice[0]
slice[0:1]
slice[0:1:2]
slice[:]
slice[:-1]
@@ -137,113 +156,178 @@
numpy[-(c + 1) :, d]
numpy[:, l[-2]]
numpy[:, ::-1]
numpy[np.newaxis, :]
(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
-{'2.7': dead, '3.7': long_live or die_hard}
-{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
+{"2.7": dead, "3.7": long_live or die_hard}
+{"2.7", "3.6", "3.7", "3.8", "3.9", "4.0" if gilectomy else "3.10"}
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
(SomeName)
SomeName
(Good, Bad, Ugly)
(i for i in (1, 2, 3))
((i ** 2) for i in (1, 2, 3))
-((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))
+((i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c")))
(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3))
(*starred,)
-{"id": "1","type": "type","started_at": now(),"ended_at": now() + timedelta(days=10),"priority": 1,"import_session_id": 1,**kwargs}
+{
+ "id": "1",
+ "type": "type",
+ "started_at": now(),
+ "ended_at": now() + timedelta(days=10),
+ "priority": 1,
+ "import_session_id": 1,
+ **kwargs,
+}
a = (1,)
-b = 1,
+b = (1,)
c = 1
d = (1,) + a + (2,)
e = (1,).count(1)
f = 1, *range(10)
g = 1, *"ten"
-what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(vars_to_remove)
-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(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()
+what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(
+ vars_to_remove
+)
+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(
+ 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()
+)
Ø = set()
authors.łukasz.say_thanks()
mapping = {
A: 0.25 * (10.0 / 12),
B: 0.1 * (10.0 / 12),
C: 0.1 * (10.0 / 12),
D: 0.1 * (10.0 / 12),
}
+
def gen():
yield from outside_of_generator
- a = (yield)
- b = ((yield))
- c = (((yield)))
+ a = yield
+ b = yield
+ c = yield
+
async def f():
await some.complicated[0].call(with_args=(True or (1 is not 1)))
-print(* [] or [1])
+
+
+print(*[] or [1])
print(**{1: 3} if False else {x: x for x in range(3)})
-print(* lambda x: x)
-assert(not Test),("Short message")
-assert this is ComplexTest and not requirements.fit_in_a_single_line(force=False), "Short message"
-assert(((parens is TooMany)))
-for x, in (1,), (2,), (3,): ...
-for y in (): ...
-for z in (i for i in (1, 2, 3)): ...
-for i in (call()): ...
-for j in (1 + (2 + 3)): ...
-while(this and that): ...
-for addr_family, addr_type, addr_proto, addr_canonname, addr_sockaddr in socket.getaddrinfo('google.com', 'http'):
+print(*lambda x: x)
+assert not Test, "Short message"
+assert this is ComplexTest and not requirements.fit_in_a_single_line(
+ force=False
+), "Short message"
+assert parens is TooMany
+for (x,) in (1,), (2,), (3,):
+ ...
+for y in ():
+ ...
+for z in (i for i in (1, 2, 3)):
+ ...
+for i in call():
+ ...
+for j in 1 + (2 + 3):
+ ...
+while this and that:
+ ...
+for (
+ addr_family,
+ addr_type,
+ addr_proto,
+ addr_canonname,
+ addr_sockaddr,
+) in socket.getaddrinfo("google.com", "http"):
pass
-a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
-a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
-a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
-a = aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
-if (
- threading.current_thread() != threading.main_thread() and
- threading.current_thread() != threading.main_thread() or
- signal.getsignal(signal.SIGINT) != signal.default_int_handler
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa /
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-):
- return True
-if (
- ~ aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n
-):
- return True
-if (
- ~ aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n
-):
- return True
-if (
- ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
+a = (
+ aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
+ in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
+)
+a = (
+ aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
+ not in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
+)
+a = (
+ aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
+ is qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
+)
+a = (
+ aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp
+ is not qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz
+)
+if (
+ threading.current_thread() != threading.main_thread()
+ and threading.current_thread() != threading.main_thread()
+ or signal.getsignal(signal.SIGINT) != signal.default_int_handler
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ * aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ / aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+):
+ return True
+if (
+ ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e
+ | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n
+):
+ return True
+if (
+ ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e
+ | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h
+ ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n
+):
+ return True
+if (
+ ~aaaaaaaaaaaaaaaa.a
+ + aaaaaaaaaaaaaaaa.b
+ - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e
+ | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h
+ ^ aaaaaaaaaaaaaaaa.i
+ << aaaaaaaaaaaaaaaa.k
+ >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
):
return True
last_call()
# standalone comment at ENDMARKER

View File

@ -395,6 +395,31 @@ def test_numeric_literals_ignoring_underscores(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, mode)
def test_skip_magic_trailing_comma(self) -> None:
source, _ = read_data("expression.py")
expected, _ = read_data("expression_skip_magic_trailing_comma.diff")
tmp_file = Path(black.dump_to_file(source))
diff_header = re.compile(
rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
)
try:
result = BlackRunner().invoke(black.main, ["-C", "--diff", str(tmp_file)])
self.assertEqual(result.exit_code, 0)
finally:
os.unlink(tmp_file)
actual = result.output
actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
actual = actual.rstrip() + "\n" # the diff output has a trailing space
if expected != actual:
dump = black.dump_to_file(actual)
msg = (
"Expected diff isn't equal to the actual. If you made changes to"
" expression.py and this is an anticipated difference, overwrite"
f" tests/data/expression_skip_magic_trailing_comma.diff with {dump}"
)
self.assertEqual(expected, actual, msg)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2_print_function(self) -> None:
source, expected = read_data("python2_print_function")