)
from appdirs import user_cache_dir
-from attr import dataclass, evolve, Factory
+from dataclasses import dataclass, field, replace
import click
import toml
from typed_ast import ast3, ast27
@dataclass
class FileMode:
- target_versions: Set[TargetVersion] = Factory(set)
+ target_versions: Set[TargetVersion] = field(default_factory=set)
line_length: int = DEFAULT_LINE_LENGTH
string_normalization: bool = True
is_pyi: bool = False
`mode` and `fast` options are passed to :func:`format_file_contents`.
"""
if src.suffix == ".pyi":
- mode = evolve(mode, is_pyi=True)
+ mode = replace(mode, is_pyi=True)
then = datetime.utcfromtimestamp(src.stat().st_mtime)
with open(src, "rb") as buf:
"""Keeps track of brackets on a line."""
depth: int = 0
- bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = Factory(dict)
- delimiters: Dict[LeafID, Priority] = Factory(dict)
+ bracket_match: Dict[Tuple[Depth, NodeType], Leaf] = field(default_factory=dict)
+ delimiters: Dict[LeafID, Priority] = field(default_factory=dict)
previous: Optional[Leaf] = None
- _for_loop_depths: List[int] = Factory(list)
- _lambda_argument_depths: List[int] = Factory(list)
+ _for_loop_depths: List[int] = field(default_factory=list)
+ _lambda_argument_depths: List[int] = field(default_factory=list)
def mark(self, leaf: Leaf) -> None:
"""Mark `leaf` with bracket-related metadata. Keep track of delimiters.
"""Holds leaves and comments. Can be printed with `str(line)`."""
depth: int = 0
- leaves: List[Leaf] = Factory(list)
- comments: Dict[LeafID, List[Leaf]] = Factory(dict) # keys ordered like `leaves`
- bracket_tracker: BracketTracker = Factory(BracketTracker)
+ leaves: List[Leaf] = field(default_factory=list)
+ # keys ordered like `leaves`
+ comments: Dict[LeafID, List[Leaf]] = field(default_factory=dict)
+ bracket_tracker: BracketTracker = field(default_factory=BracketTracker)
inside_brackets: bool = False
should_explode: bool = False
is_pyi: bool = False
previous_line: Optional[Line] = None
previous_after: int = 0
- previous_defs: List[int] = Factory(list)
+ previous_defs: List[int] = field(default_factory=list)
def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
"""Return the number of extra empty lines before and after the `current_line`.
is_pyi: bool = False
normalize_strings: bool = True
- current_line: Line = Factory(Line)
+ current_line: Line = field(default_factory=Line)
remove_u_prefix: bool = False
def line(self, indent: int = 0) -> Iterator[Line]:
node.insert_child(index, Node(syms.atom, [lpar, operand, rpar]))
yield from self.visit_default(node)
- def __attrs_post_init__(self) -> None:
+ def __post_init__(self) -> None:
"""You are in a twisty little maze of passages."""
v = self.visit_stmt
Ø: Set[str] = set()
if child.type == syms.atom:
if maybe_make_parens_invisible_in_atom(child, parent=node):
- lpar = Leaf(token.LPAR, "")
- rpar = Leaf(token.RPAR, "")
- index = child.remove() or 0
- node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
+ wrap_in_parentheses(node, child, visible=False)
elif is_one_tuple(child):
- # wrap child in visible parentheses
- lpar = Leaf(token.LPAR, "(")
- rpar = Leaf(token.RPAR, ")")
- child.remove()
- node.insert_child(index, Node(syms.atom, [lpar, child, rpar]))
+ wrap_in_parentheses(node, child, visible=True)
elif node.type == syms.import_from:
# "import from" nodes store parentheses directly as part of
# the statement
break
elif not (isinstance(child, Leaf) and is_multiline_string(child)):
- # wrap child in invisible parentheses
- lpar = Leaf(token.LPAR, "")
- rpar = Leaf(token.RPAR, "")
- index = child.remove() or 0
- prefix = child.prefix
- child.prefix = ""
- new_child = Node(syms.atom, [lpar, child, rpar])
- new_child.prefix = prefix
- node.insert_child(index, new_child)
+ wrap_in_parentheses(node, child, visible=False)
check_lpar = isinstance(child, Leaf) and child.value in parens_after
"""
container: Optional[LN] = container_of(leaf)
while container is not None and container.type != token.ENDMARKER:
+ is_fmt_on = False
for comment in list_comments(container.prefix, is_endmarker=False):
if comment.value in FMT_ON:
- return
+ is_fmt_on = True
+ elif comment.value in FMT_OFF:
+ is_fmt_on = False
+ if is_fmt_on:
+ return
yield container
return wrapped
+def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
+ """Wrap `child` in parentheses.
+
+ This replaces `child` with an atom holding the parentheses and the old
+ child. That requires moving the prefix.
+
+ If `visible` is False, the leaves will be valueless (and thus invisible).
+ """
+ lpar = Leaf(token.LPAR, "(" if visible else "")
+ rpar = Leaf(token.RPAR, ")" if visible else "")
+ prefix = child.prefix
+ child.prefix = ""
+ index = child.remove() or 0
+ new_child = Node(syms.atom, [lpar, child, rpar])
+ new_child.prefix = prefix
+ parent.insert_child(index, new_child)
+
+
def is_one_tuple(node: LN) -> bool:
"""Return True if `node` holds a tuple with one element, with or without parens."""
if node.type == syms.atom:
yield f"{' ' * depth}{node.__class__.__name__}("
- for field in sorted(node._fields):
+ for field in sorted(node._fields): # noqa: F402
# TypeIgnore has only one field 'lineno' which breaks this comparison
type_ignore_classes = (ast3.TypeIgnore, ast27.TypeIgnore)
if sys.version_info >= (3, 8):