Repository: psf/black

Issue:
INTERNAL ERROR: *.pyi file + function + comment + ellipsis
Seems like a minimal example requires:
* a `.pyi` file
* a comment after the function declaration
* an ellipsis in the function body 

`example.pyi`:
```python
def f():
    # hi
    ...
```

```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G95

$ python --version
Python 3.7.4

$ python -m black --version
black.py, version 19.3b0

$ cat example.pyi
def f():
    # hi
    ...

$ python -m black example.pyi
error: cannot format example.pyi: INTERNAL ERROR: Black produced invalid code: expected an indented block (<unknown>, line 3). Please report a bug on https://github.com/ambv/black/issues.  This invalid output might be helpful: /var/folders/m_/ffctv9r113lb285667lc7fh800k0_0/T/blk_2fvut1n6.log
All done! 💥 💔 💥
1 file failed to reformat.

$ cat /var/folders/m_/ffctv9r113lb285667lc7fh800k0_0/T/blk_iqek_luy.log
  File "/Users/dustiningram/.pyenv/versions/3.7.4/lib/python3.7/site-packages/black.py", line 3388, in assert_equivalent
    dst_ast = ast.parse(dst)
  File "/Users/dustiningram/.pyenv/versions/3.7.4/lib/python3.7/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
def foobar():  # hi

...
```

Does also happen on master: Yes

```
$ python -m black --version
black.py, version 19.3b0+82.gae5588c

$ python -m black example.pyi
error: cannot format example.pyi: INTERNAL ERROR: Black produced invalid code: invalid syntax (<unknown>, line 2). Please report a bug on https://github.com/psf/black/issues.  This invalid output might be helpful: /var/folders/m_/ffctv9r113lb285667lc7fh800k0_0/T/blk_yzbb8_6s.log
Oh no! 💥 💔 💥
1 file failed to reformat.
```


Discussion:
Also possibly related, `black` will remove the comment entirely if the file is formatted a certain way:

```
$ cat example.pyi
def f(
    ):  # hi
    ...

$ python -m black example.pyi
reformatted example.pyi
All done! ✨ 🍰 ✨
1 file reformatted.

$ cat example.pyi
def f(): ...
```
Thanks for reporting, I can also repro this
Just ran into this as well. Note that black behaves appropriately when `pass` is used instead of `...`, but that of course contravenes the preferred syntax in PEP 484.
Still crashes. The behavior where comments are sometimes removed is tracked separately in #1239.

Resolving patch:
diff --git a/src/black/nodes.py b/src/black/nodes.py
--- a/src/black/nodes.py
+++ b/src/black/nodes.py
@@ -714,6 +714,11 @@ def is_multiline_string(leaf: Leaf) -> bool:
 
 def is_stub_suite(node: Node) -> bool:
     """Return True if `node` is a suite with a stub body."""
+
+    # If there is a comment, we want to keep it.
+    if node.prefix.strip():
+        return False
+
     if (
         len(node.children) != 4
         or node.children[0].type != token.NEWLINE
@@ -722,6 +727,9 @@ def is_stub_suite(node: Node) -> bool:
     ):
         return False
 
+    if node.children[3].prefix.strip():
+        return False
+
     return is_stub_body(node.children[2])
 
 
@@ -735,7 +743,8 @@ def is_stub_body(node: LN) -> bool:
 
     child = node.children[0]
     return (
-        child.type == syms.atom
+        not child.prefix.strip()
+        and child.type == syms.atom
         and len(child.children) == 3
         and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
     )


Test patch:
diff --git a/tests/data/simple_cases/ignore_pyi.py b/tests/data/simple_cases/ignore_pyi.py
new file mode 100644
--- /dev/null
+++ b/tests/data/simple_cases/ignore_pyi.py
@@ -0,0 +1,41 @@
+def f():  # type: ignore
+    ...
+
+class x:  # some comment
+    ...
+
+class y:
+    ...  # comment
+
+# whitespace doesn't matter (note the next line has a trailing space and tab)
+class z:        
+    ...
+
+def g():
+    # hi
+    ...
+
+def h():
+    ...
+    # bye
+
+# output
+
+def f():  # type: ignore
+    ...
+
+class x:  # some comment
+    ...
+
+class y: ...  # comment
+
+# whitespace doesn't matter (note the next line has a trailing space and tab)
+class z: ...
+
+def g():
+    # hi
+    ...
+
+def h():
+    ...
+    # bye
diff --git a/tests/test_format.py b/tests/test_format.py
--- a/tests/test_format.py
+++ b/tests/test_format.py
@@ -33,9 +33,10 @@ def check_file(
 @pytest.mark.parametrize("filename", all_data_cases("simple_cases"))
 def test_simple_format(filename: str) -> None:
     magic_trailing_comma = filename != "skip_magic_trailing_comma"
-    check_file(
-        "simple_cases", filename, black.Mode(magic_trailing_comma=magic_trailing_comma)
+    mode = black.Mode(
+        magic_trailing_comma=magic_trailing_comma, is_pyi=filename.endswith("_pyi")
     )
+    check_file("simple_cases", filename, mode)
 
 
 @pytest.mark.parametrize("filename", all_data_cases("preview"))