Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support compilation with mypyc #1009

Merged
merged 3 commits into from Oct 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .appveyor.yml
Expand Up @@ -7,7 +7,7 @@ build: off

test_script:
- C:\Python36\python.exe tests/test_black.py
- C:\Python36\python.exe -m mypy black.py blackd.py tests/test_black.py
- C:\Python36\python.exe -m mypy black.py blackd.py tests/test_black.py blib2to3

after_test:
- C:\Python36\python.exe -m pip install pyinstaller
Expand Down
4 changes: 4 additions & 0 deletions .flake8
Expand Up @@ -3,3 +3,7 @@ ignore = E203, E266, E501, W503
max-line-length = 80
max-complexity = 18
select = B,C,E,F,W,T4,B9
# We need to configure the mypy.ini because the flake8-mypy's default
# options don't properly override it, so if we don't specify it we get
# half of the config from mypy.ini and half from flake8-mypy.
mypy_config = mypy.ini
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -12,3 +12,4 @@ pip-wheel-metadata/
_black_version.py
.idea
.eggs
.dmypy.json
2 changes: 2 additions & 0 deletions Pipfile
Expand Up @@ -11,6 +11,8 @@ toml = ">=0.9.4"
black = {path = ".",extras = ["d"],editable = true}
aiohttp-cors = "*"
typed-ast = "==1.4.0"
typing_extensions = ">=3.7.4"
mypy_extensions = ">=0.4.3"
regex = ">=2019.8"
pathspec = ">=0.6"
dataclasses = {version = ">=0.6", python_version = "< 3.7"}
Expand Down
28 changes: 23 additions & 5 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 61 additions & 37 deletions black.py
Expand Up @@ -37,6 +37,8 @@
Union,
cast,
)
from typing_extensions import Final
from mypy_extensions import mypyc_attr

from appdirs import user_cache_dir
from dataclasses import dataclass, field, replace
Expand Down Expand Up @@ -247,6 +249,17 @@ def read_pyproject_toml(
return value


def target_version_option_callback(
c: click.Context, p: Union[click.Option, click.Parameter], v: Tuple[str, ...]
) -> List[TargetVersion]:
"""Compute the target versions from a --target-version flag.

This is its own function because mypy couldn't infer the type correctly
when it was a lambda, causing mypyc trouble.
"""
return [TargetVersion[val.upper()] for val in v]


@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.option("-c", "--code", type=str, help="Format the code passed in as a string.")
@click.option(
Expand All @@ -261,7 +274,7 @@ def read_pyproject_toml(
"-t",
"--target-version",
type=click.Choice([v.name.lower() for v in TargetVersion]),
callback=lambda c, p, v: [TargetVersion[val.upper()] for val in v],
callback=target_version_option_callback,
multiple=True,
help=(
"Python versions that should be supported by Black's output. [default: "
Expand Down Expand Up @@ -388,7 +401,7 @@ def main(
verbose: bool,
include: str,
exclude: str,
src: Tuple[str],
src: Tuple[str, ...],
config: Optional[str],
) -> None:
"""The uncompromising code formatter."""
Expand Down Expand Up @@ -470,7 +483,9 @@ def main(
ctx.exit(report.return_code)


def path_empty(src: Tuple[str], quiet: bool, verbose: bool, ctx: click.Context) -> None:
def path_empty(
src: Tuple[str, ...], quiet: bool, verbose: bool, ctx: click.Context
) -> None:
"""
Exit if there is no `src` provided for formatting
"""
Expand Down Expand Up @@ -585,7 +600,7 @@ async def schedule_formatting(
): src
for src in sorted(sources)
}
pending: Iterable[asyncio.Future] = tasks.keys()
pending: Iterable["asyncio.Future[bool]"] = tasks.keys()
try:
loop.add_signal_handler(signal.SIGINT, cancel, pending)
loop.add_signal_handler(signal.SIGTERM, cancel, pending)
Expand Down Expand Up @@ -639,10 +654,10 @@ def format_file_in_place(
except NothingChanged:
return False

if write_back == write_back.YES:
if write_back == WriteBack.YES:
with open(src, "w", encoding=encoding, newline=newline) as f:
f.write(dst_contents)
elif write_back == write_back.DIFF:
elif write_back == WriteBack.DIFF:
now = datetime.utcnow()
src_name = f"{src}\t{then} +0000"
dst_name = f"{src}\t{now} +0000"
Expand Down Expand Up @@ -865,8 +880,16 @@ def visit(self, node: LN) -> Iterator[T]:
if node.type < 256:
name = token.tok_name[node.type]
else:
name = type_repr(node.type)
yield from getattr(self, f"visit_{name}", self.visit_default)(node)
name = str(type_repr(node.type))
# We explicitly branch on whether a visitor exists (instead of
# using self.visit_default as the default arg to getattr) in order
# to save needing to create a bound method object and so mypyc can
# generate a native call to visit_default.
visitf = getattr(self, f"visit_{name}", None)
if visitf:
yield from visitf(node)
else:
yield from self.visit_default(node)

def visit_default(self, node: LN) -> Iterator[T]:
"""Default `visit_*()` implementation. Recurses to children of `node`."""
Expand Down Expand Up @@ -911,8 +934,8 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
list(v.visit(code))


WHITESPACE = {token.DEDENT, token.INDENT, token.NEWLINE}
STATEMENT = {
WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
STATEMENT: Final = {
syms.if_stmt,
syms.while_stmt,
syms.for_stmt,
Expand All @@ -922,18 +945,18 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
syms.funcdef,
syms.classdef,
}
STANDALONE_COMMENT = 153
STANDALONE_COMMENT: Final = 153
token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
LOGIC_OPERATORS = {"and", "or"}
COMPARATORS = {
LOGIC_OPERATORS: Final = {"and", "or"}
COMPARATORS: Final = {
token.LESS,
token.GREATER,
token.EQEQUAL,
token.NOTEQUAL,
token.LESSEQUAL,
token.GREATEREQUAL,
}
MATH_OPERATORS = {
MATH_OPERATORS: Final = {
token.VBAR,
token.CIRCUMFLEX,
token.AMPER,
Expand All @@ -949,23 +972,23 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
token.TILDE,
token.DOUBLESTAR,
}
STARS = {token.STAR, token.DOUBLESTAR}
VARARGS_SPECIALS = STARS | {token.SLASH}
VARARGS_PARENTS = {
STARS: Final = {token.STAR, token.DOUBLESTAR}
VARARGS_SPECIALS: Final = STARS | {token.SLASH}
VARARGS_PARENTS: Final = {
syms.arglist,
syms.argument, # double star in arglist
syms.trailer, # single argument to call
syms.typedargslist,
syms.varargslist, # lambdas
}
UNPACKING_PARENTS = {
UNPACKING_PARENTS: Final = {
syms.atom, # single element of a list or set literal
syms.dictsetmaker,
syms.listmaker,
syms.testlist_gexp,
syms.testlist_star_expr,
}
TEST_DESCENDANTS = {
TEST_DESCENDANTS: Final = {
syms.test,
syms.lambdef,
syms.or_test,
Expand All @@ -982,7 +1005,7 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
syms.term,
syms.power,
}
ASSIGNMENTS = {
ASSIGNMENTS: Final = {
"=",
"+=",
"-=",
Expand All @@ -998,13 +1021,13 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
"**=",
"//=",
}
COMPREHENSION_PRIORITY = 20
COMMA_PRIORITY = 18
TERNARY_PRIORITY = 16
LOGIC_PRIORITY = 14
STRING_PRIORITY = 12
COMPARATOR_PRIORITY = 10
MATH_PRIORITIES = {
COMPREHENSION_PRIORITY: Final = 20
COMMA_PRIORITY: Final = 18
TERNARY_PRIORITY: Final = 16
LOGIC_PRIORITY: Final = 14
STRING_PRIORITY: Final = 12
COMPARATOR_PRIORITY: Final = 10
MATH_PRIORITIES: Final = {
token.VBAR: 9,
token.CIRCUMFLEX: 8,
token.AMPER: 7,
Expand All @@ -1020,7 +1043,7 @@ def show(cls, code: Union[str, Leaf, Node]) -> None:
token.TILDE: 3,
token.DOUBLESTAR: 2,
}
DOT_PRIORITY = 1
DOT_PRIORITY: Final = 1


@dataclass
Expand Down Expand Up @@ -1729,13 +1752,13 @@ def visit_default(self, node: LN) -> Iterator[Line]:
self.current_line.append(node)
yield from super().visit_default(node)

def visit_INDENT(self, node: Node) -> Iterator[Line]:
def visit_INDENT(self, node: Leaf) -> Iterator[Line]:
"""Increase indentation level, maybe yield a line."""
# In blib2to3 INDENT never holds comments.
yield from self.line(+1)
yield from self.visit_default(node)

def visit_DEDENT(self, node: Node) -> Iterator[Line]:
def visit_DEDENT(self, node: Leaf) -> Iterator[Line]:
"""Decrease indentation level, maybe yield a line."""
# The current line might still wait for trailing comments. At DEDENT time
# there won't be any (they would be prefixes on the preceding NEWLINE).
Expand Down Expand Up @@ -2463,7 +2486,7 @@ def left_hand_split(line: Line, features: Collection[Feature] = ()) -> Iterator[
body_leaves: List[Leaf] = []
head_leaves: List[Leaf] = []
current_leaves = head_leaves
matching_bracket = None
matching_bracket: Optional[Leaf] = None
for leaf in line.leaves:
if (
current_leaves is body_leaves
Expand Down Expand Up @@ -2506,8 +2529,8 @@ def right_hand_split(
body_leaves: List[Leaf] = []
head_leaves: List[Leaf] = []
current_leaves = tail_leaves
opening_bracket = None
closing_bracket = None
opening_bracket: Optional[Leaf] = None
closing_bracket: Optional[Leaf] = None
for leaf in reversed(line.leaves):
if current_leaves is body_leaves:
if leaf is opening_bracket:
Expand Down Expand Up @@ -3028,7 +3051,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
# That happens when one of the `ignored_nodes` ended with a NEWLINE
# leaf (possibly followed by a DEDENT).
hidden_value = hidden_value[:-1]
first_idx = None
first_idx: Optional[int] = None
for ignored in ignored_nodes:
index = ignored.remove()
if first_idx is None:
Expand Down Expand Up @@ -3399,8 +3422,8 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
yield omit

length = 4 * line.depth
opening_bracket = None
closing_bracket = None
opening_bracket: Optional[Leaf] = None
closing_bracket: Optional[Leaf] = None
inner_brackets: Set[LeafID] = set()
for index, leaf, leaf_length in enumerate_with_length(line, reversed=True):
length += leaf_length
Expand Down Expand Up @@ -3797,6 +3820,7 @@ def assert_stable(src: str, dst: str, mode: FileMode) -> None:
) from None


@mypyc_attr(patchable=True)
def dump_to_file(*output: str) -> str:
"""Dump `output` to a temporary file. Return path to the file."""
with tempfile.NamedTemporaryFile(
Expand Down Expand Up @@ -3829,7 +3853,7 @@ def diff(a: str, b: str, a_name: str, b_name: str) -> str:
)


def cancel(tasks: Iterable[asyncio.Task]) -> None:
def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None:
"""asyncio signal handler that cancels all `tasks` and reports to stderr."""
err("Aborted!")
for task in tasks:
Expand Down
1 change: 0 additions & 1 deletion blib2to3/__init__.pyi

This file was deleted.

10 changes: 0 additions & 10 deletions blib2to3/pgen2/__init__.pyi

This file was deleted.

2 changes: 2 additions & 0 deletions blib2to3/pgen2/conv.py
@@ -1,6 +1,8 @@
# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

# mypy: ignore-errors

"""Convert graminit.[ch] spit out by pgen to Python code.

Pgen is the Python parser generator. It is useful to quickly create a
Expand Down