]>
git.madduck.net Git - etc/vim.git/blobdiff - black.py
madduck's git repository
Every one of the projects in this repository is available at the canonical
URL git://git.madduck.net/madduck/pub/<projectpath> — see
each project's metadata for the exact URL.
All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@ git. madduck. net .
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
SSH access, as well as push access can be individually
arranged .
If you use my repositories frequently, consider adding the following
snippet to ~/.gitconfig and using the third clone URL listed for each
project:
[url "git://git.madduck.net/madduck/"]
insteadOf = madduck:
class NothingChanged(UserWarning):
class NothingChanged(UserWarning):
- """Raised by :func:`format_file` when reformatted code is the same as source."""
+ """Raised when reformatted code is the same as source."""
class CannotSplit(Exception):
class CannotSplit(Exception):
- """A readable split that fits the allotted line length is impossible.
+ """A readable split that fits the allotted line length is impossible."""
- Raised by :func:`left_hand_split`, :func:`right_hand_split`, and
- :func:`delimiter_split`.
- """
+
+class InvalidInput(ValueError):
+ """Raised when input source code fails all parse attempts."""
faulty_line = lines[lineno - 1]
except IndexError:
faulty_line = "<line number missing in source>"
faulty_line = lines[lineno - 1]
except IndexError:
faulty_line = "<line number missing in source>"
- exc = ValueError (f"Cannot parse: {lineno}:{column}: {faulty_line}")
+ exc = InvalidInput (f"Cannot parse: {lineno}:{column}: {faulty_line}")
else:
raise exc from None
else:
raise exc from None
bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = Factory(dict)
delimiters: Dict[LeafID, Priority] = Factory(dict)
previous: Optional[Leaf] = None
bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = Factory(dict)
delimiters: Dict[LeafID, Priority] = Factory(dict)
previous: Optional[Leaf] = None
- _for_loop_variable: int = 0
- _lambda_arguments: int = 0
+ _for_loop_depths: List[int] = Factory(list)
+ _lambda_argument_depths: List[int] = Factory(list)
def mark(self, leaf: Leaf) -> None:
"""Mark `leaf` with bracket-related metadata. Keep track of delimiters.
def mark(self, leaf: Leaf) -> None:
"""Mark `leaf` with bracket-related metadata. Keep track of delimiters.
"""
if leaf.type == token.NAME and leaf.value == "for":
self.depth += 1
"""
if leaf.type == token.NAME and leaf.value == "for":
self.depth += 1
- self._for_loop_variable += 1
+ self._for_loop_depths.append(self.depth)
return True
return False
def maybe_decrement_after_for_loop_variable(self, leaf: Leaf) -> bool:
"""See `maybe_increment_for_loop_variable` above for explanation."""
return True
return False
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_depths
+ and self._for_loop_depths[-1] == self.depth
+ and leaf.type == token.NAME
+ and leaf.value == "in"
+ ):
- self._for_loop_variable -= 1
+ self._for_loop_depths.pop()
"""
if leaf.type == token.NAME and leaf.value == "lambda":
self.depth += 1
"""
if leaf.type == token.NAME and leaf.value == "lambda":
self.depth += 1
- self._lambda_arguments += 1
+ self._lambda_argument_depths.append(self.depth)
return True
return False
def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool:
"""See `maybe_increment_lambda_arguments` above for explanation."""
return True
return False
def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool:
"""See `maybe_increment_lambda_arguments` above for explanation."""
- if self._lambda_arguments and leaf.type == token.COLON:
+ if (
+ self._lambda_argument_depths
+ and self._lambda_argument_depths[-1] == self.depth
+ and leaf.type == token.COLON
+ ):
- self._lambda_arguments -= 1
+ self._lambda_argument_depths.pop()
def is_split_before_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
def is_split_before_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
- """Return the priority of the `leaf` delimiter, given a line before after it.
+ """Return the priority of the `leaf` delimiter, given a line break before it.
The delimiter priorities returned here are from those delimiters that would
cause a line break before themselves.
The delimiter priorities returned here are from those delimiters that would
cause a line break before themselves.
):
return STRING_PRIORITY
):
return STRING_PRIORITY
- if leaf.type != token.NAME :
+ if leaf.type not in {token.NAME, token.ASYNC} :
return 0
if (
leaf.value == "for"
and leaf.parent
and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
return 0
if (
leaf.value == "for"
and leaf.parent
and leaf.parent.type in {syms.comp_for, syms.old_comp_for}
+ or leaf.type == token.ASYNC
- return COMPREHENSION_PRIORITY
+ if (
+ not isinstance(leaf.prev_sibling, Leaf)
+ or leaf.prev_sibling.value != "async"
+ ):
+ return COMPREHENSION_PRIORITY
@dataclass
class ProtoComment:
@dataclass
class ProtoComment:
+ """Describes a piece of syntax that is a comment.
+
+ It's not a :class:`blib2to3.pytree.Leaf` so that:
+
+ * it can be cached (`Leaf` objects should not be reused more than once as
+ they store their lineno, column, prefix, and parent information);
+ * `newlines` and `consumed` fields are kept separate from the `value`. This
+ simplifies handling of special marker comments like ``# fmt: off/on``.
+ """
+
type: int # token.COMMENT or STANDALONE_COMMENT
value: str # content of the comment
newlines: int # how many newlines before the comment
type: int # token.COMMENT or STANDALONE_COMMENT
value: str # content of the comment
newlines: int # how many newlines before the comment
@lru_cache(maxsize=4096)
def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
@lru_cache(maxsize=4096)
def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
+ """Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
result: List[ProtoComment] = []
if not prefix or "#" not in prefix:
return result
result: List[ProtoComment] = []
if not prefix or "#" not in prefix:
return result
def normalize_numeric_literal(leaf: Leaf, allow_underscores: bool) -> None:
"""Normalizes numeric (float, int, and complex) literals.
def normalize_numeric_literal(leaf: Leaf, allow_underscores: bool) -> None:
"""Normalizes numeric (float, int, and complex) literals.
- All letters used in the representation are normalized to lowercase, long number
- literals are split using underscores.
+ All letters used in the representation are normalized to lowercase (except
+ in Python 2 long literals), and long number literals are split using underscores.
"""
text = leaf.value.lower()
if text.startswith(("0o", "0x", "0b")):
"""
text = leaf.value.lower()
if text.startswith(("0o", "0x", "0b")):
elif text.endswith(("j", "l")):
number = text[:-1]
suffix = text[-1]
elif text.endswith(("j", "l")):
number = text[:-1]
suffix = text[-1]
+ # Capitalize in "2L" because "l" looks too similar to "1".
+ if suffix == "l":
+ suffix = "L"
text = f"{format_float_or_int_string(number, allow_underscores)}{suffix}"
else:
text = format_float_or_int_string(text, allow_underscores)
text = f"{format_float_or_int_string(number, allow_underscores)}{suffix}"
else:
text = format_float_or_int_string(text, allow_underscores)
before, after = text.split(".")
before = format_int_string(before, allow_underscores) if before else "0"
before, after = text.split(".")
before = format_int_string(before, allow_underscores) if before else "0"
- after = format_int_string(after, allow_underscores) if after else "0"
+ if after:
+ after = format_int_string(after, allow_underscores, count_from_end=False)
+ else:
+ after = "0"
return f"{before}.{after}"
return f"{before}.{after}"
-def format_int_string(text: str, allow_underscores: bool) -> str:
+def format_int_string(
+ text: str, allow_underscores: bool, count_from_end: bool = True
+) -> str:
"""Normalizes underscores in a string to e.g. 1_000_000.
"""Normalizes underscores in a string to e.g. 1_000_000.
- Input must be a string of at least six digits and optional underscores.
+ Input must be a string of digits and optional underscores.
+ If count_from_end is False, we add underscores after groups of three digits
+ counting from the beginning instead of the end of the strings. This is used
+ for the fractional part of float literals.
"""
if not allow_underscores:
return text
"""
if not allow_underscores:
return text
# No underscores for numbers <= 6 digits long.
return text
# No underscores for numbers <= 6 digits long.
return text
- # Avoid removing leading zeros, which are important if we're formatting
- # part of a number like "0.001".
- return format(int("1" + text), "3_")[1:].lstrip("_")
+ if count_from_end:
+ # Avoid removing leading zeros, which are important if we're formatting
+ # part of a number like "0.001".
+ return format(int("1" + text), "3_")[1:].lstrip("_")
+ else:
+ return "_".join(text[i : i + 3] for i in range(0, len(text), 3))
def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
for index, child in enumerate(list(node.children)):
if check_lpar:
if child.type == syms.atom:
for index, child in enumerate(list(node.children)):
if check_lpar:
if child.type == syms.atom:
- maybe_make_parens_invisible_in_atom(child)
+ if maybe_make_parens_invisible_in_atom(child):
+ lpar = Leaf(token.LPAR, "")
+ rpar = Leaf(token.RPAR, "")
+ index = child.remove() or 0
+ node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
elif is_one_tuple(child):
# wrap child in visible parentheses
lpar = Leaf(token.LPAR, "(")
elif is_one_tuple(child):
# wrap child in visible parentheses
lpar = Leaf(token.LPAR, "(")
def maybe_make_parens_invisible_in_atom(node: LN) -> bool:
def maybe_make_parens_invisible_in_atom(node: LN) -> bool:
- """If it's safe, make the parens in the atom `node` invisible, recursively."""
+ """If it's safe, make the parens in the atom `node` invisible, recursively.
+
+ Returns whether the node should itself be wrapped in invisible parentheses.
+
+ """
if (
node.type != syms.atom
or is_empty_tuple(node)
if (
node.type != syms.atom
or is_empty_tuple(node)
last.value = "" # type: ignore
if len(node.children) > 1:
maybe_make_parens_invisible_in_atom(node.children[1])
last.value = "" # type: ignore
if len(node.children) > 1:
maybe_make_parens_invisible_in_atom(node.children[1])
def is_empty_tuple(node: LN) -> bool:
def is_empty_tuple(node: LN) -> bool:
length = 4 * line.depth
opening_bracket = None
closing_bracket = None
length = 4 * line.depth
opening_bracket = None
closing_bracket = None
- optional_brackets: Set[LeafID] = set()
inner_brackets: Set[LeafID] = set()
for index, leaf, leaf_length in enumerate_with_length(line, reversed=True):
length += leaf_length
inner_brackets: Set[LeafID] = set()
for index, leaf, leaf_length in enumerate_with_length(line, reversed=True):
length += leaf_length
if leaf.type == STANDALONE_COMMENT or has_inline_comment:
break
if leaf.type == STANDALONE_COMMENT or has_inline_comment:
break
- optional_brackets.discard(id(leaf))
if opening_bracket:
if leaf is opening_bracket:
opening_bracket = None
elif leaf.type in CLOSING_BRACKETS:
inner_brackets.add(id(leaf))
elif leaf.type in CLOSING_BRACKETS:
if opening_bracket:
if leaf is opening_bracket:
opening_bracket = None
elif leaf.type in CLOSING_BRACKETS:
inner_brackets.add(id(leaf))
elif leaf.type in CLOSING_BRACKETS:
- if not leaf.value:
- optional_brackets.add(id(opening_bracket))
- continue
-
if index > 0 and line.leaves[index - 1].type in OPENING_BRACKETS:
# Empty brackets would fail a split so treat them as "inner"
# brackets (e.g. only add them to the `omit` set if another
if index > 0 and line.leaves[index - 1].type in OPENING_BRACKETS:
# Empty brackets would fail a split so treat them as "inner"
# brackets (e.g. only add them to the `omit` set if another
inner_brackets.add(id(leaf))
continue
inner_brackets.add(id(leaf))
continue
- opening_bracket = leaf.opening_bracket
if closing_bracket:
omit.add(id(closing_bracket))
omit.update(inner_brackets)
inner_brackets.clear()
yield omit
if closing_bracket:
omit.add(id(closing_bracket))
omit.update(inner_brackets)
inner_brackets.clear()
yield omit
+
+ if leaf.value:
+ opening_bracket = leaf.opening_bracket
+ closing_bracket = leaf
def get_future_imports(node: Node) -> Set[str]:
def get_future_imports(node: Node) -> Set[str]: