Priority = int
Index = int
LN = Union[Leaf, Node]
-SplitFunc = Callable[['Line', bool], Iterator['Line']]
+SplitFunc = Callable[["Line", bool], Iterator["Line"]]
out = partial(click.secho, bold=True, err=True)
-err = partial(click.secho, fg='red', err=True)
+err = partial(click.secho, fg="red", err=True)
class NothingChanged(UserWarning):
@click.command()
@click.option(
- '-l',
- '--line-length',
+ "-l",
+ "--line-length",
type=int,
default=DEFAULT_LINE_LENGTH,
- help='How many character per line to allow.',
+ help="How many character per line to allow.",
show_default=True,
)
@click.option(
- '--check',
+ "--check",
is_flag=True,
help=(
"Don't write back the files, just return the status. Return code 0 "
),
)
@click.option(
- '--fast/--safe',
+ "--fast/--safe",
is_flag=True,
- help='If --fast given, skip temporary sanity checks. [default: --safe]',
+ help="If --fast given, skip temporary sanity checks. [default: --safe]",
)
@click.version_option(version=__version__)
@click.argument(
- 'src',
+ "src",
nargs=-1,
type=click.Path(
exists=True, file_okay=True, dir_okay=True, readable=True, allow_dash=True
elif p.is_file():
# if a file was explicitly given, we don't care about its extension
sources.append(p)
- elif s == '-':
- sources.append(Path('-'))
+ elif s == "-":
+ sources.append(Path("-"))
else:
- err(f'invalid path: {s}')
+ err(f"invalid path: {s}")
if len(sources) == 0:
ctx.exit(0)
elif len(sources) == 1:
p = sources[0]
report = Report(check=check)
try:
- if not p.is_file() and str(p) == '-':
+ if not p.is_file() and str(p) == "-":
changed = format_stdin_to_stdout(
line_length=line_length, fast=fast, write_back=not check
)
report = Report(check=not write_back)
for src, task in tasks.items():
if not task.done():
- report.failed(src, 'timed out, cancelling')
+ report.failed(src, "timed out, cancelling")
task.cancel()
cancelled.append(task)
elif task.cancelled():
if cancelled:
await asyncio.gather(*cancelled, loop=loop, return_exceptions=True)
else:
- out('All done! ✨ 🍰 ✨')
+ out("All done! ✨ 🍰 ✨")
click.echo(str(report))
return report.return_code
valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
`line_length` is passed to :func:`format_str`.
"""
- if src_contents.strip() == '':
+ if src_contents.strip() == "":
raise NothingChanged
dst_contents = format_str(src_contents, line_length=line_length)
def lib2to3_parse(src_txt: str) -> Node:
"""Given a string with source, return the lib2to3 Node."""
grammar = pygram.python_grammar_no_print_statement
- if src_txt[-1] != '\n':
- nl = '\r\n' if '\r\n' in src_txt[:1024] else '\n'
+ if src_txt[-1] != "\n":
+ nl = "\r\n" if "\r\n" in src_txt[:1024] else "\n"
src_txt += nl
for grammar in GRAMMARS:
drv = driver.Driver(grammar, pytree.convert)
return code
-T = TypeVar('T')
+T = TypeVar("T")
class Visitor(Generic[T]):
name = token.tok_name[node.type]
else:
name = type_repr(node.type)
- yield from getattr(self, f'visit_{name}', self.visit_default)(node)
+ yield from getattr(self, f"visit_{name}", self.visit_default)(node)
def visit_default(self, node: LN) -> Iterator[T]:
"""Default `visit_*()` implementation. Recurses to children of `node`."""
tree_depth: int = 0
def visit_default(self, node: LN) -> Iterator[T]:
- indent = ' ' * (2 * self.tree_depth)
+ indent = " " * (2 * self.tree_depth)
if isinstance(node, Node):
_type = type_repr(node.type)
- out(f'{indent}{_type}', fg='yellow')
+ out(f"{indent}{_type}", fg="yellow")
self.tree_depth += 1
for child in node.children:
yield from self.visit(child)
self.tree_depth -= 1
- out(f'{indent}/{_type}', fg='yellow', bold=False)
+ out(f"{indent}/{_type}", fg="yellow", bold=False)
else:
_type = token.tok_name.get(node.type, str(node.type))
- out(f'{indent}{_type}', fg='blue', nl=False)
+ out(f"{indent}{_type}", fg="blue", nl=False)
if node.prefix:
# We don't have to handle prefixes for `Node` objects since
# that delegates to the first child anyway.
- out(f' {node.prefix!r}', fg='green', bold=False, nl=False)
- out(f' {node.value!r}', fg='blue', bold=False)
+ out(f" {node.prefix!r}", fg="green", bold=False, nl=False)
+ out(f" {node.value!r}", fg="blue", bold=False)
@classmethod
def show(cls, code: str) -> None:
KEYWORDS = set(keyword.kwlist)
WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}
-FLOW_CONTROL = {'return', 'raise', 'break', 'continue'}
+FLOW_CONTROL = {"return", "raise", "break", "continue"}
STATEMENT = {
syms.if_stmt,
syms.while_stmt,
syms.classdef,
}
STANDALONE_COMMENT = 153
-LOGIC_OPERATORS = {'and', 'or'}
+LOGIC_OPERATORS = {"and", "or"}
COMPARATORS = {
token.LESS,
token.GREATER,
self.delimiters[id(self.previous)] = STRING_PRIORITY
elif (
leaf.type == token.NAME
- and leaf.value == 'for'
+ and leaf.value == "for"
and leaf.parent
and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
):
self.delimiters[id(self.previous)] = COMPREHENSION_PRIORITY
elif (
leaf.type == token.NAME
- and leaf.value == 'if'
+ and leaf.value == "if"
and leaf.parent
and leaf.parent.type in {syms.comp_if, syms.old_comp_if}
):
return (
bool(self)
and self.leaves[0].type == token.NAME
- and self.leaves[0].value == 'class'
+ and self.leaves[0].value == "class"
)
@property
except IndexError:
second_leaf = None
return (
- (first_leaf.type == token.NAME and first_leaf.value == 'def')
+ (first_leaf.type == token.NAME and first_leaf.value == "def")
or (
first_leaf.type == token.ASYNC
and second_leaf is not None
and second_leaf.type == token.NAME
- and second_leaf.value == 'def'
+ and second_leaf.value == "def"
)
)
return (
bool(self)
and self.leaves[0].type == token.NAME
- and self.leaves[0].value == 'yield'
+ and self.leaves[0].value == "yield"
)
@property
To avoid splitting on the comma in this situation, increase the depth of
tokens between `for` and `in`.
"""
- if leaf.type == token.NAME and leaf.value == 'for':
+ if leaf.type == token.NAME and leaf.value == "for":
self.has_for = True
self.bracket_tracker.depth += 1
self._for_loop_variable = True
def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool:
"""See `maybe_increment_for_loop_variable` above for explanation."""
- if self._for_loop_variable and leaf.type == token.NAME and leaf.value == 'in':
+ if self._for_loop_variable and leaf.type == token.NAME and leaf.value == "in":
self.bracket_tracker.depth -= 1
self._for_loop_variable = False
return True
comment.type == STANDALONE_COMMENT
and self.bracket_tracker.any_open_brackets()
):
- comment.prefix = ''
+ comment.prefix = ""
return False
if comment.type != token.COMMENT:
after = len(self.leaves) - 1
if after == -1:
comment.type = STANDALONE_COMMENT
- comment.prefix = ''
+ comment.prefix = ""
return False
else:
def __str__(self) -> str:
"""Render the line."""
if not self:
- return '\n'
+ return "\n"
- indent = ' ' * self.depth
+ indent = " " * self.depth
leaves = iter(self.leaves)
first = next(leaves)
- res = f'{first.prefix}{indent}{first.value}'
+ res = f"{first.prefix}{indent}{first.value}"
for leaf in leaves:
res += str(leaf)
for _, comment in self.comments:
res += str(comment)
- return res + '\n'
+ return res + "\n"
def __bool__(self) -> bool:
"""Return True if the line has leaves or comments."""
`depth` is not used for indentation in this case.
"""
if not self:
- return '\n'
+ return "\n"
- res = ''
+ res = ""
for leaf in self.leaves:
res += str(leaf)
return res
if current_line.leaves:
# Consume the first leaf's extra newlines.
first_leaf = current_line.leaves[0]
- before = first_leaf.prefix.count('\n')
+ before = first_leaf.prefix.count("\n")
before = min(before, max_allowed)
- first_leaf.prefix = ''
+ first_leaf.prefix = ""
else:
before = 0
depth = current_line.depth
else:
normalize_prefix(node, inside_brackets=any_open_brackets)
+ if node.type == token.STRING:
+ normalize_string_quotes(node)
if node.type not in WHITESPACE:
self.current_line.append(node)
yield from super().visit_default(node)
def __attrs_post_init__(self) -> None:
"""You are in a twisty little maze of passages."""
v = self.visit_stmt
- self.visit_if_stmt = partial(v, keywords={'if', 'else', 'elif'})
- self.visit_while_stmt = partial(v, keywords={'while', 'else'})
- self.visit_for_stmt = partial(v, keywords={'for', 'else'})
- self.visit_try_stmt = partial(v, keywords={'try', 'except', 'else', 'finally'})
- self.visit_except_clause = partial(v, keywords={'except'})
- self.visit_funcdef = partial(v, keywords={'def'})
- self.visit_with_stmt = partial(v, keywords={'with'})
- self.visit_classdef = partial(v, keywords={'class'})
+ self.visit_if_stmt = partial(v, keywords={"if", "else", "elif"})
+ self.visit_while_stmt = partial(v, keywords={"while", "else"})
+ self.visit_for_stmt = partial(v, keywords={"for", "else"})
+ self.visit_try_stmt = partial(v, keywords={"try", "except", "else", "finally"})
+ self.visit_except_clause = partial(v, keywords={"except"})
+ self.visit_funcdef = partial(v, keywords={"def"})
+ self.visit_with_stmt = partial(v, keywords={"with"})
+ self.visit_classdef = partial(v, keywords={"class"})
self.visit_async_funcdef = self.visit_async_stmt
self.visit_decorated = self.visit_decorators
def whitespace(leaf: Leaf) -> str: # noqa C901
"""Return whitespace prefix if needed for the given `leaf`."""
- NO = ''
- SPACE = ' '
- DOUBLESPACE = ' '
+ NO = ""
+ SPACE = " "
+ DOUBLESPACE = " "
t = leaf.type
p = leaf.parent
v = leaf.value
and prevp.parent.type == syms.shift_expr
and prevp.prev_sibling
and prevp.prev_sibling.type == token.NAME
- and prevp.prev_sibling.value == 'print' # type: ignore
+ and prevp.prev_sibling.value == "print" # type: ignore
):
# Python 2 print chevron
return NO
return NO
elif t == token.NAME:
- if v == 'import':
+ if v == "import":
return SPACE
if prev and prev.type == token.DOT:
if not p:
return
- if '#' not in p:
+ if "#" not in p:
return
consumed = 0
nlines = 0
- for index, line in enumerate(p.split('\n')):
+ for index, line in enumerate(p.split("\n")):
consumed += len(line) + 1 # adding the length of the split '\n'
line = line.lstrip()
if not line:
nlines += 1
- if not line.startswith('#'):
+ if not line.startswith("#"):
continue
if index == 0 and leaf.type != token.ENDMARKER:
else:
comment_type = STANDALONE_COMMENT
comment = make_comment(line)
- yield Leaf(comment_type, comment, prefix='\n' * nlines)
+ yield Leaf(comment_type, comment, prefix="\n" * nlines)
- if comment in {'# fmt: on', '# yapf: enable'}:
+ if comment in {"# fmt: on", "# yapf: enable"}:
raise FormatOn(consumed)
- if comment in {'# fmt: off', '# yapf: disable'}:
+ if comment in {"# fmt: off", "# yapf: disable"}:
raise FormatOff(consumed)
nlines = 0
"""
content = content.rstrip()
if not content:
- return '#'
+ return "#"
- if content[0] == '#':
+ if content[0] == "#":
content = content[1:]
- if content and content[0] not in ' !:#':
- content = ' ' + content
- return '#' + content
+ if content and content[0] not in " !:#":
+ content = " " + content
+ return "#" + content
def split_line(
yield line
return
- line_str = str(line).strip('\n')
+ line_str = str(line).strip("\n")
if (
len(line_str) <= line_length
- and '\n' not in line_str # multiline strings
+ and "\n" not in line_str # multiline strings
and not line.contains_standalone_comments
):
yield line
result: List[Line] = []
try:
for l in split_func(line, py36):
- if str(l).strip('\n') == line_str:
+ if str(l).strip("\n") == line_str:
raise CannotSplit("Split function returned an unchanged result")
result.extend(
and current_line.leaves[-1].type != token.COMMA
and trailing_comma_safe
):
- current_line.append(Leaf(token.COMMA, ','))
+ current_line.append(Leaf(token.COMMA, ","))
yield current_line
return bool(
t == token.NAME
and (
- (v == 'import' and p and p.type == syms.import_name)
- or (v == 'from' and p and p.type == syms.import_from)
+ (v == "import" and p and p.type == syms.import_name)
+ or (v == "from" and p and p.type == syms.import_from)
)
)
Note: don't use backslashes for formatting or you'll lose your voting rights.
"""
if not inside_brackets:
- spl = leaf.prefix.split('#')
- if '\\' not in spl[0]:
- nl_count = spl[-1].count('\n')
+ spl = leaf.prefix.split("#")
+ if "\\" not in spl[0]:
+ nl_count = spl[-1].count("\n")
if len(spl) > 1:
nl_count -= 1
- leaf.prefix = '\n' * nl_count
+ leaf.prefix = "\n" * nl_count
return
- leaf.prefix = ''
+ leaf.prefix = ""
+
+
+def normalize_string_quotes(leaf: Leaf) -> None:
+ value = leaf.value.lstrip("furbFURB")
+ if value[:3] == '"""':
+ return
+
+ elif value[:3] == "'''":
+ orig_quote = "'''"
+ new_quote = '"""'
+ elif value[0] == '"':
+ orig_quote = '"'
+ new_quote = "'"
+ else:
+ orig_quote = "'"
+ new_quote = '"'
+ first_quote_pos = leaf.value.find(orig_quote)
+ if first_quote_pos == -1:
+ return # There's an internal error
+
+ body = leaf.value[first_quote_pos + len(orig_quote):-len(orig_quote)]
+ new_body = body.replace(f"\\{orig_quote}", orig_quote).replace(
+ new_quote, f"\\{new_quote}"
+ )
+ if new_quote == '"""' and new_body[-1] == '"':
+ # edge case:
+ new_body = new_body[:-1] + '\\"'
+ orig_escape_count = body.count("\\")
+ new_escape_count = new_body.count("\\")
+ if new_escape_count > orig_escape_count:
+ return # Do not introduce more escaping
+
+ if new_escape_count == orig_escape_count and orig_quote == '"':
+ return # Prefer double quotes
+
+ prefix = leaf.value[:first_quote_pos]
+ leaf.value = f"{prefix}{new_quote}{new_body}{new_quote}"
def is_python36(node: Node) -> bool:
for n in node.pre_order():
if n.type == token.STRING:
value_head = n.value[:2] # type: ignore
- if value_head in {'f"', 'F"', "f'", "F'", 'rf', 'fr', 'RF', 'FR'}:
+ if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}:
return True
elif (
return False
-PYTHON_EXTENSIONS = {'.py'}
+PYTHON_EXTENSIONS = {".py"}
BLACKLISTED_DIRECTORIES = {
- 'build', 'buck-out', 'dist', '_build', '.git', '.hg', '.mypy_cache', '.tox', '.venv'
+ "build", "buck-out", "dist", "_build", ".git", ".hg", ".mypy_cache", ".tox", ".venv"
}
def done(self, src: Path, changed: bool) -> None:
"""Increment the counter for successful reformatting. Write out a message."""
if changed:
- reformatted = 'would reformat' if self.check else 'reformatted'
- out(f'{reformatted} {src}')
+ reformatted = "would reformat" if self.check else "reformatted"
+ out(f"{reformatted} {src}")
self.change_count += 1
else:
- out(f'{src} already well formatted, good job.', bold=False)
+ out(f"{src} already well formatted, good job.", bold=False)
self.same_count += 1
def failed(self, src: Path, message: str) -> None:
"""Increment the counter for failed reformatting. Write out a message."""
- err(f'error: cannot format {src}: {message}')
+ err(f"error: cannot format {src}: {message}")
self.failure_count += 1
@property
failed = "failed to reformat"
report = []
if self.change_count:
- s = 's' if self.change_count > 1 else ''
+ s = "s" if self.change_count > 1 else ""
report.append(
- click.style(f'{self.change_count} file{s} {reformatted}', bold=True)
+ click.style(f"{self.change_count} file{s} {reformatted}", bold=True)
)
if self.same_count:
- s = 's' if self.same_count > 1 else ''
- report.append(f'{self.same_count} file{s} {unchanged}')
+ s = "s" if self.same_count > 1 else ""
+ report.append(f"{self.same_count} file{s} {unchanged}")
if self.failure_count:
- s = 's' if self.failure_count > 1 else ''
+ s = "s" if self.failure_count > 1 else ""
report.append(
- click.style(f'{self.failure_count} file{s} {failed}', fg='red')
+ click.style(f"{self.failure_count} file{s} {failed}", fg="red")
)
- return ', '.join(report) + '.'
+ return ", ".join(report) + "."
def assert_equivalent(src: str, dst: str) -> None:
try:
dst_ast = ast.parse(dst)
except Exception as exc:
- log = dump_to_file(''.join(traceback.format_tb(exc.__traceback__)), dst)
+ log = dump_to_file("".join(traceback.format_tb(exc.__traceback__)), dst)
raise AssertionError(
f"INTERNAL ERROR: Black produced invalid code: {exc}. "
f"Please report a bug on https://github.com/ambv/black/issues. "
f"This invalid output might be helpful: {log}"
) from None
- src_ast_str = '\n'.join(_v(src_ast))
- dst_ast_str = '\n'.join(_v(dst_ast))
+ src_ast_str = "\n".join(_v(src_ast))
+ dst_ast_str = "\n".join(_v(dst_ast))
if src_ast_str != dst_ast_str:
- log = dump_to_file(diff(src_ast_str, dst_ast_str, 'src', 'dst'))
+ log = dump_to_file(diff(src_ast_str, dst_ast_str, "src", "dst"))
raise AssertionError(
f"INTERNAL ERROR: Black produced code that is not equivalent to "
f"the source. "
newdst = format_str(dst, line_length=line_length)
if dst != newdst:
log = dump_to_file(
- diff(src, dst, 'source', 'first pass'),
- diff(dst, newdst, 'first pass', 'second pass'),
+ diff(src, dst, "source", "first pass"),
+ diff(dst, newdst, "first pass", "second pass"),
)
raise AssertionError(
f"INTERNAL ERROR: Black produced different code on the second pass "
import tempfile
with tempfile.NamedTemporaryFile(
- mode='w', prefix='blk_', suffix='.log', delete=False
+ mode="w", prefix="blk_", suffix=".log", delete=False
) as f:
for lines in output:
f.write(lines)
- f.write('\n')
+ f.write("\n")
return f.name
"""Return a unified diff string between strings `a` and `b`."""
import difflib
- a_lines = [line + '\n' for line in a.split('\n')]
- b_lines = [line + '\n' for line in b.split('\n')]
- return ''.join(
+ a_lines = [line + "\n" for line in a.split("\n")]
+ b_lines = [line + "\n" for line in b.split("\n")]
+ return "".join(
difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
)
loop.close()
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
def get_long_description():
- readme_md = CURRENT_DIR / 'README.md'
- with open(readme_md, encoding='utf8') as ld_file:
+ readme_md = CURRENT_DIR / "README.md"
+ with open(readme_md, encoding="utf8") as ld_file:
return ld_file.read()
def get_version():
- black_py = CURRENT_DIR / 'black.py'
- _version_re = re.compile(r'__version__\s+=\s+(?P<version>.*)')
- with open(black_py, 'r', encoding='utf8') as f:
- version = _version_re.search(f.read()).group('version')
+ black_py = CURRENT_DIR / "black.py"
+ _version_re = re.compile(r"__version__\s+=\s+(?P<version>.*)")
+ with open(black_py, "r", encoding="utf8") as f:
+ version = _version_re.search(f.read()).group("version")
return str(ast.literal_eval(version))
setup(
- name='black',
+ name="black",
version=get_version(),
description="The uncompromising code formatter.",
long_description=get_long_description(),
long_description_content_type="text/markdown",
- keywords='automation formatter yapf autopep8 pyfmt gofmt rustfmt',
- author='Łukasz Langa',
- author_email='lukasz@langa.pl',
- url='https://github.com/ambv/black',
- license='MIT',
- py_modules=['black'],
- packages=['blib2to3', 'blib2to3.pgen2'],
- package_data={'blib2to3': ['*.txt']},
+ keywords="automation formatter yapf autopep8 pyfmt gofmt rustfmt",
+ author="Łukasz Langa",
+ author_email="lukasz@langa.pl",
+ url="https://github.com/ambv/black",
+ license="MIT",
+ py_modules=["black"],
+ packages=["blib2to3", "blib2to3.pgen2"],
+ package_data={"blib2to3": ["*.txt"]},
python_requires=">=3.6",
zip_safe=False,
- install_requires=['click', 'attrs>=17.4.0'],
- test_suite='tests.test_black',
+ install_requires=["click", "attrs>=17.4.0"],
+ test_suite="tests.test_black",
classifiers=[
- 'Development Status :: 3 - Alpha',
- 'Environment :: Console',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3 :: Only',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Topic :: Software Development :: Quality Assurance',
+ "Development Status :: 3 - Alpha",
+ "Environment :: Console",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3 :: Only",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Software Development :: Quality Assurance",
],
- entry_points={'console_scripts': ['black=black:main']},
+ entry_points={"console_scripts": ["black=black:main"]},
)
# Explains why we use global state.
-GLOBAL_STATE = {'a': a(1), 'b': a(2), 'c': a(3)}
+GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)}
# Another comment!
result = await x.method1()
# Comment after ending a block.
if result:
- print('A OK', file=sys.stdout)
+ print("A OK", file=sys.stdout)
# Comment between things.
print()
__all__ = [
# Super-special typing primitives.
- 'Any',
- 'Callable',
- 'ClassVar',
+ "Any",
+ "Callable",
+ "ClassVar",
# ABCs (from collections.abc).
- 'AbstractSet', # collections.abc.Set.
- 'ByteString',
- 'Container',
+ "AbstractSet", # collections.abc.Set.
+ "ByteString",
+ "Container",
# Concrete collection types.
- 'Counter',
- 'Deque',
- 'Dict',
- 'DefaultDict',
- 'List',
- 'Set',
- 'FrozenSet',
- 'NamedTuple', # Not really a type.
- 'Generator',
+ "Counter",
+ "Deque",
+ "Dict",
+ "DefaultDict",
+ "List",
+ "Set",
+ "FrozenSet",
+ "NamedTuple", # Not really a type.
+ "Generator",
]
# Comment before function.
]
lcomp3 = [
# This one is actually too long to fit in a single line.
- element.split('\n', 1)[0]
+ element.split("\n", 1)[0]
# yup
for element in collection.select_elements()
# right
# 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?
)
def func():
lcomp3 = [
# This one is actually too long to fit in a single line.
- element.split('\n', 1)[0]
+ element.split("\n", 1)[0]
# yup
for element in collection.select_elements()
# right
def foo(list_a, list_b):
results = (
- User.query.filter(User.foo == 'bar').filter( # Because foo.
+ User.query.filter(User.foo == "bar").filter( # Because foo.
db.or_(User.field_a.astext.in_(list_a), User.field_b.astext.in_(list_b))
).filter(
User.xyz.is_(None)
def test(self) -> None:
with patch("black.out", print):
self.assertEqual(
- unstyle(str(report)), '1 file reformatted, 1 file failed to reformat.'
+ unstyle(str(report)), "1 file reformatted, 1 file failed to reformat."
)
self.assertEqual(
unstyle(str(report)),
- '1 file reformatted, 1 file left unchanged, 1 file failed to reformat.',
+ "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.",
)
self.assertEqual(
unstyle(str(report)),
- '2 files reformatted, 1 file left unchanged, '
- '1 file failed to reformat.',
+ "2 files reformatted, 1 file left unchanged, "
+ "1 file failed to reformat.",
)
self.assertEqual(
unstyle(str(report)),
- '2 files reformatted, 2 files left unchanged, '
- '2 files failed to reformat.',
+ "2 files reformatted, 2 files left unchanged, "
+ "2 files failed to reformat.",
)
return DOUBLESPACE
# Another comment because more comments
- assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
+ assert p is not None, f'INTERNAL ERROR: hand-made leaf without parent: {leaf!r}'
prev = leaf.prev_sibling
if not prev:
def f():
- NO = ''
- SPACE = ' '
- DOUBLESPACE = ' '
+ NO = ""
+ SPACE = " "
+ DOUBLESPACE = " "
t = leaf.type
p = leaf.parent # trailing comment
def g():
- NO = ''
- SPACE = ' '
- DOUBLESPACE = ' '
+ NO = ""
+ SPACE = " "
+ DOUBLESPACE = " "
t = leaf.type
p = leaf.parent
...
-'some_string'
-b'\\xa3'
+"some_string"
+b"\\xa3"
Name
None
True
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
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)
-{'2.7': dead, '3.7': (long_live or die_hard)}
-{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
+{"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]
{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()}
{
Life is Life
call()
call(arg)
-call(kwarg='hey')
-call(arg, kwarg='hey')
-call(arg, another, kwarg='hey', **kwargs)
+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',
+ kwarg="hey",
**kwargs
) # note: no trailing comma pre-3.6
call(*gidgets[: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)
a = (1,)
for i in range(10):
print(i)
continue
- exec("new-style exec", {}, {})
+ exec('new-style exec', {}, {})
return None
async def coroutine(arg, exec=False):
- "Single-line docstring. Multiline is harder to reformat."
+ '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)
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:
+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''):
from library import some_connection, some_decorator
-f'trigger 3.6 mode'
+f"trigger 3.6 mode"
# fmt: off
def func_no_args():
a; b; c
for i in range(10):
print(i)
continue
- exec("new-style exec", {}, {})
+ exec('new-style exec', {}, {})
return None
async def coroutine(arg, exec=False):
- "Single-line docstring. Multiline is harder to reformat."
+ '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)
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:
+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''):
+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(10000, 200000)))
assert task._cancel_stack[:len(old_stack)] == old_stack
f: int = -1,
g: int = 1 if False else 2,
h: str = "",
- i: str = r'',
+ i: str = r"",
):
...
-f'f-string without formatted values is just a string'
-f'{{NOT a formatted value}}'
-f'some f-string with {a} {few():.2f} {formatted.values!r}'
+f"f-string without formatted values is just a string"
+f"{{NOT a formatted value}}"
+f"some f-string with {a} {few():.2f} {formatted.values!r}"
f"{f'{nested} inner'} outer"
-f'space between opening braces: { {a for a in (1, 2, 3)}}'
+f"space between opening braces: { {a for a in (1, 2, 3)}}"
from library import some_connection, some_decorator
-f'trigger 3.6 mode'
+f"trigger 3.6 mode"
def func_no_args():
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 conn.do_what_i_mean("SELECT bobby, tables FROM xkcd", timeout=2)
await asyncio.sleep(1)
return text[number:-1]
-def spaces(a=1, b=(), c=[], d={}, e=True, f=-1, g=1 if False else 2, h="", i=r''):
+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(10000, 200000)))
assert task._cancel_stack[:len(old_stack)] == old_stack
f: int = -1,
g: int = 1 if False else 2,
h: str = "",
- i: str = r'',
+ i: str = r"",
):
...
--- /dev/null
+"Hello"
+"Don't do that"
+'Here is a "'
+'What\'s the deal here?'
+"What's the deal \"here\"?"
+"And \"here\"?"
+"""Strings with "" in them"""
+'''Strings with "" in them'''
+'''Here's a "'''
+'''Here's a " '''
+'''Just a normal triple
+quote'''
+f"just a normal {f} string"
+f'''This is a triple-quoted {f}-string'''
+f'MOAR {" ".join([])}'
+f"MOAR {' '.join([])}"
+r"raw string ftw"
+
+# output
+
+"Hello"
+"Don't do that"
+'Here is a "'
+"What's the deal here?"
+'What\'s the deal "here"?'
+'And "here"?'
+"""Strings with "" in them"""
+"""Strings with "" in them"""
+'''Here's a "'''
+"""Here's a " """
+"""Just a normal triple
+quote"""
+f"just a normal {f} string"
+f"""This is a triple-quoted {f}-string"""
+f'MOAR {" ".join([])}'
+f"MOAR {' '.join([])}"
+r"raw string ftw"
fs = partial(black.format_str, line_length=ll)
THIS_FILE = Path(__file__)
THIS_DIR = THIS_FILE.parent
-EMPTY_LINE = '# EMPTY LINE WITH WHITESPACE' + ' (this comment will be removed)'
+EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
def dump_to_stderr(*output: str) -> str:
- return '\n' + '\n'.join(output) + '\n'
+ return "\n" + "\n".join(output) + "\n"
def read_data(name: str) -> Tuple[str, str]:
"""read_data('test_name') -> 'input', 'output'"""
- if not name.endswith(('.py', '.out')):
- name += '.py'
+ if not name.endswith((".py", ".out")):
+ name += ".py"
_input: List[str] = []
_output: List[str] = []
- with open(THIS_DIR / name, 'r', encoding='utf8') as test:
+ with open(THIS_DIR / name, "r", encoding="utf8") as test:
lines = test.readlines()
result = _input
for line in lines:
- line = line.replace(EMPTY_LINE, '')
- if line.rstrip() == '# output':
+ line = line.replace(EMPTY_LINE, "")
+ if line.rstrip() == "# output":
result = _output
continue
if _input and not _output:
# If there's no output marker, treat the entire file as already pre-formatted.
_output = _input[:]
- return ''.join(_input).strip() + '\n', ''.join(_output).strip() + '\n'
+ return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
class BlackTestCase(unittest.TestCase):
maxDiff = None
def assertFormatEqual(self, expected: str, actual: str) -> None:
- if actual != expected and not os.environ.get('SKIP_AST_PRINT'):
+ if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
bdv: black.DebugVisitor[Any]
- black.out('Expected tree:', fg='green')
+ black.out("Expected tree:", fg="green")
try:
exp_node = black.lib2to3_parse(expected)
bdv = black.DebugVisitor()
list(bdv.visit(exp_node))
except Exception as ve:
black.err(str(ve))
- black.out('Actual tree:', fg='red')
+ black.out("Actual tree:", fg="red")
try:
exp_node = black.lib2to3_parse(actual)
bdv = black.DebugVisitor()
@patch("black.dump_to_file", dump_to_stderr)
def test_self(self) -> None:
- source, expected = read_data('test_black')
+ source, expected = read_data("test_black")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_black(self) -> None:
- source, expected = read_data('../black')
+ source, expected = read_data("../black")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, line_length=ll)
- self.assertFalse(ff(THIS_DIR / '..' / 'black.py'))
+ self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
def test_piping(self) -> None:
- source, expected = read_data('../black')
+ source, expected = read_data("../black")
hold_stdin, hold_stdout = sys.stdin, sys.stdout
try:
sys.stdin, sys.stdout = StringIO(source), StringIO()
- sys.stdin.name = '<stdin>'
+ sys.stdin.name = "<stdin>"
black.format_stdin_to_stdout(line_length=ll, fast=True, write_back=True)
sys.stdout.seek(0)
actual = sys.stdout.read()
@patch("black.dump_to_file", dump_to_stderr)
def test_setup(self) -> None:
- source, expected = read_data('../setup')
+ source, expected = read_data("../setup")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, line_length=ll)
- self.assertFalse(ff(THIS_DIR / '..' / 'setup.py'))
+ self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
@patch("black.dump_to_file", dump_to_stderr)
def test_function(self) -> None:
- source, expected = read_data('function')
+ source, expected = read_data("function")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_expression(self) -> None:
- source, expected = read_data('expression')
+ source, expected = read_data("expression")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_fstring(self) -> None:
- source, expected = read_data('fstring')
+ source, expected = read_data("fstring")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, line_length=ll)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_string_quotes(self) -> None:
+ source, expected = read_data("string_quotes")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments(self) -> None:
- source, expected = read_data('comments')
+ source, expected = read_data("comments")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments2(self) -> None:
- source, expected = read_data('comments2')
+ source, expected = read_data("comments2")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments3(self) -> None:
- source, expected = read_data('comments3')
+ source, expected = read_data("comments3")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments4(self) -> None:
- source, expected = read_data('comments4')
+ source, expected = read_data("comments4")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_cantfit(self) -> None:
- source, expected = read_data('cantfit')
+ source, expected = read_data("cantfit")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_import_spacing(self) -> None:
- source, expected = read_data('import_spacing')
+ source, expected = read_data("import_spacing")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_composition(self) -> None:
- source, expected = read_data('composition')
+ source, expected = read_data("composition")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_empty_lines(self) -> None:
- source, expected = read_data('empty_lines')
+ source, expected = read_data("empty_lines")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2(self) -> None:
- source, expected = read_data('python2')
+ source, expected = read_data("python2")
actual = fs(source)
self.assertFormatEqual(expected, actual)
# black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff(self) -> None:
- source, expected = read_data('fmtonoff')
+ source, expected = read_data("fmtonoff")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
err_lines.append(msg)
with patch("black.out", out), patch("black.err", err):
- report.done(Path('f1'), changed=False)
+ report.done(Path("f1"), changed=False)
self.assertEqual(len(out_lines), 1)
self.assertEqual(len(err_lines), 0)
- self.assertEqual(out_lines[-1], 'f1 already well formatted, good job.')
- self.assertEqual(unstyle(str(report)), '1 file left unchanged.')
+ self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
+ self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
self.assertEqual(report.return_code, 0)
- report.done(Path('f2'), changed=True)
+ report.done(Path("f2"), changed=True)
self.assertEqual(len(out_lines), 2)
self.assertEqual(len(err_lines), 0)
- self.assertEqual(out_lines[-1], 'reformatted f2')
+ self.assertEqual(out_lines[-1], "reformatted f2")
self.assertEqual(
- unstyle(str(report)), '1 file reformatted, 1 file left unchanged.'
+ unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
)
self.assertEqual(report.return_code, 0)
report.check = True
self.assertEqual(report.return_code, 1)
report.check = False
- report.failed(Path('e1'), 'boom')
+ report.failed(Path("e1"), "boom")
self.assertEqual(len(out_lines), 2)
self.assertEqual(len(err_lines), 1)
- self.assertEqual(err_lines[-1], 'error: cannot format e1: boom')
+ self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
self.assertEqual(
unstyle(str(report)),
- '1 file reformatted, 1 file left unchanged, '
- '1 file failed to reformat.',
+ "1 file reformatted, 1 file left unchanged, "
+ "1 file failed to reformat.",
)
self.assertEqual(report.return_code, 123)
- report.done(Path('f3'), changed=True)
+ report.done(Path("f3"), changed=True)
self.assertEqual(len(out_lines), 3)
self.assertEqual(len(err_lines), 1)
- self.assertEqual(out_lines[-1], 'reformatted f3')
+ self.assertEqual(out_lines[-1], "reformatted f3")
self.assertEqual(
unstyle(str(report)),
- '2 files reformatted, 1 file left unchanged, '
- '1 file failed to reformat.',
+ "2 files reformatted, 1 file left unchanged, "
+ "1 file failed to reformat.",
)
self.assertEqual(report.return_code, 123)
- report.failed(Path('e2'), 'boom')
+ report.failed(Path("e2"), "boom")
self.assertEqual(len(out_lines), 3)
self.assertEqual(len(err_lines), 2)
- self.assertEqual(err_lines[-1], 'error: cannot format e2: boom')
+ self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
self.assertEqual(
unstyle(str(report)),
- '2 files reformatted, 1 file left unchanged, '
- '2 files failed to reformat.',
+ "2 files reformatted, 1 file left unchanged, "
+ "2 files failed to reformat.",
)
self.assertEqual(report.return_code, 123)
- report.done(Path('f4'), changed=False)
+ report.done(Path("f4"), changed=False)
self.assertEqual(len(out_lines), 4)
self.assertEqual(len(err_lines), 2)
- self.assertEqual(out_lines[-1], 'f4 already well formatted, good job.')
+ self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
self.assertEqual(
unstyle(str(report)),
- '2 files reformatted, 2 files left unchanged, '
- '2 files failed to reformat.',
+ "2 files reformatted, 2 files left unchanged, "
+ "2 files failed to reformat.",
)
self.assertEqual(report.return_code, 123)
report.check = True
self.assertEqual(
unstyle(str(report)),
- '2 files would be reformatted, 2 files would be left unchanged, '
- '2 files would fail to reformat.',
+ "2 files would be reformatted, 2 files would be left unchanged, "
+ "2 files would fail to reformat.",
)
def test_is_python36(self) -> None:
self.assertTrue(black.is_python36(node))
node = black.lib2to3_parse("def f(*, arg): f'string'\n")
self.assertTrue(black.is_python36(node))
- source, expected = read_data('function')
+ source, expected = read_data("function")
node = black.lib2to3_parse(source)
self.assertTrue(black.is_python36(node))
node = black.lib2to3_parse(expected)
self.assertTrue(black.is_python36(node))
- source, expected = read_data('expression')
+ source, expected = read_data("expression")
node = black.lib2to3_parse(source)
self.assertFalse(black.is_python36(node))
node = black.lib2to3_parse(expected)
self.assertFalse(black.is_python36(node))
def test_debug_visitor(self) -> None:
- source, _ = read_data('debug_visitor.py')
- expected, _ = read_data('debug_visitor.out')
+ source, _ = read_data("debug_visitor.py")
+ expected, _ = read_data("debug_visitor.out")
out_lines = []
err_lines = []
with patch("black.out", out), patch("black.err", err):
black.DebugVisitor.show(source)
- actual = '\n'.join(out_lines) + '\n'
- log_name = ''
+ actual = "\n".join(out_lines) + "\n"
+ log_name = ""
if expected != actual:
log_name = black.dump_to_file(*out_lines)
self.assertEqual(
)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()