)
import click
+from click.core import ParameterSource
from dataclasses import replace
from mypy_extensions import mypyc_attr
from black.const import DEFAULT_LINE_LENGTH, DEFAULT_INCLUDES, DEFAULT_EXCLUDES
from black.const import STDIN_PLACEHOLDER
from black.nodes import STARS, syms, is_simple_decorator_expression
+from black.nodes import is_string_token
from black.lines import Line, EmptyLineTracker
from black.linegen import transform_line, LineGenerator, LN
from black.comments import normalize_fmt_off
config: Optional[str],
) -> None:
"""The uncompromising code formatter."""
- if config and verbose:
- out(f"Using configuration from {config}.", bold=False, fg="blue")
+ ctx.ensure_object(dict)
+ root, method = find_project_root(src) if code is None else (None, None)
+ ctx.obj["root"] = root
+
+ if verbose:
+ if root:
+ out(
+ f"Identified `{root}` as project root containing a {method}.",
+ fg="blue",
+ )
+
+ normalized = [
+ (normalize_path_maybe_ignore(Path(source), root), source)
+ for source in src
+ ]
+ srcs_string = ", ".join(
+ [
+ f'"{_norm}"'
+ if _norm
+ else f'\033[31m"{source} (skipping - invalid)"\033[34m'
+ for _norm, source in normalized
+ ]
+ )
+ out(f"Sources to be formatted: {srcs_string}", fg="blue")
+
+ if config:
+ config_source = ctx.get_parameter_source("config")
+ if config_source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP):
+ out("Using configuration from project root.", fg="blue")
+ else:
+ out(f"Using configuration in '{config}'.", fg="blue")
error_msg = "Oh no! 💥 💔 💥"
if required_version and required_version != __version__:
)
if verbose or not quiet:
+ if code is None and (verbose or report.change_count or report.failure_count):
+ out()
out(error_msg if report.return_code else "All done! ✨ 🍰 ✨")
if code is None:
click.echo(str(report), err=True)
stdin_filename: Optional[str],
) -> Set[Path]:
"""Compute the set of files to be formatted."""
-
- root = find_project_root(src)
sources: Set[Path] = set()
path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx)
if exclude is None:
exclude = re_compile_maybe_verbose(DEFAULT_EXCLUDES)
- gitignore = get_gitignore(root)
+ gitignore = get_gitignore(ctx.obj["root"])
else:
gitignore = None
is_stdin = False
if is_stdin or p.is_file():
- normalized_path = normalize_path_maybe_ignore(p, root, report)
+ normalized_path = normalize_path_maybe_ignore(p, ctx.obj["root"], report)
if normalized_path is None:
continue
sources.update(
gen_python_files(
p.iterdir(),
- root,
+ ctx.obj["root"],
include,
exclude,
extend_exclude,
else:
versions = detect_target_versions(src_node, future_imports=future_imports)
- # TODO: fully drop support and this code hopefully in January 2022 :D
- if TargetVersion.PY27 in mode.target_versions or versions == {TargetVersion.PY27}:
- msg = (
- "DEPRECATION: Python 2 support will be removed in the first stable release "
- "expected in January 2022."
- )
- err(msg, fg="yellow", bold=True)
-
normalize_fmt_off(src_node)
- lines = LineGenerator(
- mode=mode,
- remove_u_prefix="unicode_literals" in future_imports
- or supports_feature(versions, Feature.UNICODE_LITERALS),
- )
+ lines = LineGenerator(mode=mode)
elt = EmptyLineTracker(is_pyi=mode.is_pyi)
empty_line = Line(mode=mode)
after = 0
}
for n in node.pre_order():
- if n.type == token.STRING:
- value_head = n.value[:2] # type: ignore
+ if is_string_token(n):
+ value_head = n.value[:2]
if value_head in {'f"', 'F"', "f'", "F'", "rf", "fr", "RF", "FR"}:
features.add(Feature.F_STRINGS)
assert isinstance(n, Leaf)
if "_" in n.value:
features.add(Feature.NUMERIC_UNDERSCORES)
- elif n.value.endswith(("L", "l")):
- # Python 2: 10L
- features.add(Feature.LONG_INT_LITERAL)
- elif len(n.value) >= 2 and n.value[0] == "0" and n.value[1].isdigit():
- # Python 2: 0123; 00123; ...
- if not all(char == "0" for char in n.value):
- # although we don't want to match 0000 or similar
- features.add(Feature.OCTAL_INT_LITERAL)
elif n.type == token.SLASH:
if n.parent and n.parent.type in {
):
features.add(Feature.UNPACKING_ON_FLOW)
- # Python 2 only features (for its deprecation) except for integers, see above
- elif n.type == syms.print_stmt:
- features.add(Feature.PRINT_STMT)
- elif n.type == syms.exec_stmt:
- features.add(Feature.EXEC_STMT)
- elif n.type == syms.tfpdef:
- # def set_position((x, y), value):
- # ...
- features.add(Feature.AUTOMATIC_PARAMETER_UNPACKING)
- elif n.type == syms.except_clause:
- # try:
- # ...
- # except Exception, err:
- # ...
- if len(n.children) >= 4:
- if n.children[-2].type == token.COMMA:
- features.add(Feature.COMMA_STYLE_EXCEPT)
- elif n.type == syms.raise_stmt:
- # raise Exception, "msg"
- if len(n.children) >= 4:
- if n.children[-2].type == token.COMMA:
- features.add(Feature.COMMA_STYLE_RAISE)
- elif n.type == token.BACKQUOTE:
- # `i'm surprised this ever existed`
- features.add(Feature.BACKQUOTE_REPR)
+ elif (
+ n.type == syms.annassign
+ and len(n.children) >= 4
+ and n.children[3].type == syms.testlist_star_expr
+ ):
+ features.add(Feature.ANN_ASSIGN_EXTENDED_RHS)
return features