Skip to content

Commit

Permalink
Remove the use of attrs/dataclasses to help mypyc
Browse files Browse the repository at this point in the history
mypyc currently falls back to creating non-extension classes for
dataclasses, which prevents most of the useful optimizations from
happening. Rewrite all the classes to not rely on dataclass.

Co-authored-by: Sanjit Kalapatapu <sanjitkal@gmail.com>
Co-authored-by: Michael J. Sullivan <sully@msully.net>
  • Loading branch information
msullivan and SanjitKal committed Oct 1, 2019
1 parent 53bb1a8 commit 32f62e4
Showing 1 changed file with 80 additions and 33 deletions.
113 changes: 80 additions & 33 deletions black.py
Expand Up @@ -40,7 +40,6 @@
from typing_extensions import Final

from appdirs import user_cache_dir
from attr import dataclass, evolve, Factory
import click
import toml
from typed_ast import ast3, ast27
Expand Down Expand Up @@ -185,12 +184,23 @@ class Feature(Enum):
}


@dataclass
class FileMode:
target_versions: Set[TargetVersion] = Factory(set)
line_length: int = DEFAULT_LINE_LENGTH
string_normalization: bool = True
is_pyi: bool = False
target_versions: Set[TargetVersion]
line_length: int
string_normalization: bool
is_pyi: bool

def __init__(
self,
target_versions: Optional[Set[TargetVersion]] = None,
line_length: int = DEFAULT_LINE_LENGTH,
string_normalization: bool = True,
is_pyi: bool = False,
) -> None:
self.target_versions = target_versions if target_versions is not None else set()
self.line_length = line_length
self.string_normalization = string_normalization
self.is_pyi = is_pyi

def get_cache_key(self) -> str:
if self.target_versions:
Expand Down Expand Up @@ -642,7 +652,9 @@ def format_file_in_place(
`mode` and `fast` options are passed to :func:`format_file_contents`.
"""
if src.suffix == ".pyi":
mode = evolve(mode, is_pyi=True)
mode = FileMode(
mode.target_versions, mode.line_length, mode.string_normalization, True
)

then = datetime.utcfromtimestamp(src.stat().st_mtime)
with open(src, "rb") as buf:
Expand Down Expand Up @@ -895,9 +907,11 @@ def visit_default(self, node: LN) -> Iterator[T]:
yield from self.visit(child)


@dataclass
class DebugVisitor(Visitor[T]):
tree_depth: int = 0
tree_depth: int

def __init__(self) -> None:
self.tree_depth = 0

def visit_default(self, node: LN) -> Iterator[T]:
indent = " " * (2 * self.tree_depth)
Expand Down Expand Up @@ -1043,16 +1057,22 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
DOT_PRIORITY: Final = 1


@dataclass
class BracketTracker:
"""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)
previous: Optional[Leaf] = None
_for_loop_depths: List[int] = Factory(list)
_lambda_argument_depths: List[int] = Factory(list)
bracket_match: Dict[Tuple[Depth, NodeType], Leaf]
delimiters: Dict[LeafID, Priority]
previous: Optional[Leaf]
_for_loop_depths: List[int]
_lambda_argument_depths: List[int]

def __init__(self) -> None:
self.bracket_match = {}
self.delimiters = {}
self.previous = None
self._for_loop_depths = []
self._lambda_argument_depths = []

def mark(self, leaf: Leaf) -> None:
"""Mark `leaf` with bracket-related metadata. Keep track of delimiters.
Expand Down Expand Up @@ -1175,17 +1195,23 @@ def get_open_lsqb(self) -> Optional[Leaf]:
return self.bracket_match.get((self.depth - 1, token.RSQB))


@dataclass
class Line:
"""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)
inside_brackets: bool = False
depth: int
leaves: List[Leaf]
comments: Dict[LeafID, List[Leaf]] # keys ordered like `leaves`
bracket_tracker: BracketTracker
inside_brackets: bool
should_explode: bool = False

def __init__(self, *, depth: int = 0, inside_brackets: bool = False) -> None:
self.depth = depth
self.inside_brackets = inside_brackets
self.leaves = []
self.comments = {}
self.bracket_tracker = BracketTracker()

def append(self, leaf: Leaf, preformatted: bool = False) -> None:
"""Add a new `leaf` to the end of the line.
Expand Down Expand Up @@ -1497,7 +1523,6 @@ def __bool__(self) -> bool:
return bool(self.leaves or self.comments)


@dataclass
class EmptyLineTracker:
"""Provides a stateful method that returns the number of potential extra
empty lines needed before and after the currently processed line.
Expand All @@ -1507,10 +1532,14 @@ class EmptyLineTracker:
are consumed by `maybe_empty_lines()` and included in the computation.
"""

is_pyi: bool = False
is_pyi: bool
previous_line: Optional[Line] = None
previous_after: int = 0
previous_defs: List[int] = Factory(list)
previous_defs: List[int]

def __init__(self, *, is_pyi: bool = False) -> None:
self.is_pyi = is_pyi
self.previous_defs = []

def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
"""Return the number of extra empty lines before and after the `current_line`.
Expand Down Expand Up @@ -1614,18 +1643,25 @@ def _maybe_empty_lines_for_class_or_def(
return newlines, 0


@dataclass
class LineGenerator(Visitor[Line]):
"""Generates reformatted Line objects. Empty lines are not emitted.
Note: destroys the tree it's visiting by mutating prefixes of its leaves
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 = Factory(Line)
remove_u_prefix: bool = False
def __init__(
self,
is_pyi: bool = False,
normalize_strings: bool = True,
current_line: Optional[Line] = None,
remove_u_prefix: bool = False,
) -> None:
self.is_pyi = is_pyi
self.normalize_strings = normalize_strings
self.current_line = current_line if current_line is not None else Line()
self.remove_u_prefix = remove_u_prefix
self._setup_visit_methods()

def line(self, indent: int = 0) -> Iterator[Line]:
"""Generate a line.
Expand Down Expand Up @@ -1805,7 +1841,7 @@ def visit_STANDALONE_COMMENT(self, leaf: Leaf) -> Iterator[Line]:
yield from self.line()
yield from self.visit_default(leaf)

def __attrs_post_init__(self) -> None:
def _setup_visit_methods(self) -> None:
"""You are in a twisty little maze of passages."""
v = self.visit_stmt
Ø: Set[str] = set()
Expand Down Expand Up @@ -2256,8 +2292,7 @@ def generate_comments(leaf: LN) -> Iterator[Leaf]:
yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)


@dataclass
class ProtoComment:
class ProtoComment: # noqa # flake8 wants this to use __slots__ but mypyc will
"""Describes a piece of syntax that is a comment.
It's not a :class:`blib2to3.pytree.Leaf` so that:
Expand All @@ -2273,6 +2308,12 @@ class ProtoComment:
newlines: int # how many newlines before the comment
consumed: int # how many characters of the original leaf's prefix did we consume

def __init__(self, type: int, value: str, newlines: int, consumed: int) -> None:
self.type = type
self.value = value
self.newlines = newlines
self.consumed = consumed


@lru_cache(maxsize=4096)
def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
Expand Down Expand Up @@ -3501,7 +3542,6 @@ def find_project_root(srcs: Iterable[str]) -> Path:
return directory


@dataclass
class Report:
"""Provides a reformatting counter. Can be rendered with `str(report)`."""

Expand All @@ -3512,6 +3552,13 @@ class Report:
same_count: int = 0
failure_count: int = 0

def __init__(
self, *, check: bool = False, quiet: bool = False, verbose: bool = False
) -> None:
self.check = check
self.quiet = quiet
self.verbose = verbose

def done(self, src: Path, changed: Changed) -> None:
"""Increment the counter for successful reformatting. Write out a message."""
if changed is Changed.YES:
Expand Down

0 comments on commit 32f62e4

Please sign in to comment.