Repository: PyCQA/docformatter (python)

Issue:
[regression] removing whitespace below shebang
```python
#!/usr/bin/env python

"""a.py"""
```

`docformatter==1.6.3` (also may possibly be `1.6.2` per https://github.com/PyCQA/docformatter/issues/186) is removing whitespace below a shebang.

`> docformatter a.py` leads to:

```python
#!/usr/bin/env python
"""a.py"""
```

`docformatter==1.5.1` didn't use to do that.

Pull request description:
fix: removing newline after shebang
Closes #187.

Patch:
diff --git a/src/docformatter/format.py b/src/docformatter/format.py
index d8bbbb9..88df426 100644
--- a/src/docformatter/format.py
+++ b/src/docformatter/format.py
@@ -528,14 +528,20 @@ class Formatter:
         """
         for _idx, _token in enumerate(modified_tokens):
             if _token[0] == 3:
-                # Remove newline between variable definition and docstring.
                 j = 1
-                while modified_tokens[_idx - j][
-                    4
-                ] == "\n" and not modified_tokens[_idx - j - 1][
-                    4
-                ].strip().endswith(
-                    '"""'
+
+                # Remove newline between variable definition and docstring
+                # unless is separating docstring from:
+                #     * A previous docstring.
+                #     * The file's shebang.
+                while (
+                    modified_tokens[_idx - j][4] == "\n"
+                    and not (
+                        modified_tokens[_idx - j - 1][4]
+                        .strip()
+                        .endswith('"""')
+                    )
+                    and not modified_tokens[_idx - j - 1][4].startswith("#!/")
                 ):
                     modified_tokens.pop(_idx - j)
                     j += 1
diff --git a/src/docformatter/strings.py b/src/docformatter/strings.py
index c42d2ee..1a4e076 100644
--- a/src/docformatter/strings.py
+++ b/src/docformatter/strings.py
@@ -104,8 +104,7 @@ def normalize_summary(summary: str) -> str:
     # Remove trailing whitespace
     summary = summary.rstrip()
 
-    # Add period at end of sentence and capitalize the first word of the
-    # summary.
+    # Add period at end of sentence.
     if (
         summary
         and (summary[-1].isalnum() or summary[-1] in ['"', "'"])
@@ -114,10 +113,11 @@ def normalize_summary(summary: str) -> str:
         summary += "."
 
     with contextlib.suppress(IndexError):
-        # Look for underscores in the first word, this would typically
-        # indicate the first word is a variable name or some other
-        # non-standard English word.
-        if "_" not in summary.split(" ", 1)[0]:
+        # Look for underscores, periods in the first word, this would typically
+        # indicate the first word is a variable name, file name, or some other
+        # non-standard English word.  If none of these exist capitalize the
+        # first word of the summary.
+        if all(char not in summary.split(" ", 1)[0] for char in ["_", "."]):
             summary = summary[0].upper() + summary[1:]
 
     return summary


Test patch:
diff --git a/tests/test_format_code.py b/tests/test_format_code.py
index 132c304..73aa9f1 100644
--- a/tests/test_format_code.py
+++ b/tests/test_format_code.py
@@ -853,9 +853,9 @@ def foo():\r
         )
 
         assert (
-            '\n\n\ndef my_func():\n"""Summary of my function."""\npass'
+            'def my_func():\n"""Summary of my function."""\npass'
             == uut._do_format_code(
-                '\n\n\ndef my_func():\n\n"""Summary of my function."""\npass'
+                'def my_func():\n\n"""Summary of my function."""\npass'
             )
         )
 
@@ -1107,7 +1107,7 @@ class TestClass:
         test_args,
         args,
     ):
-        """Strip newlines between module variable defintiion and docstring."""
+        """Strip newlines between module variable definition and docstring."""
         uut = Formatter(
             test_args,
             sys.stderr,
@@ -1157,6 +1157,34 @@ def test_wps3_process_step_io_data_or_href():
 '''
         assert docstring == uut._do_format_code(docstring)
 
+    @pytest.mark.unit
+    @pytest.mark.parametrize("args", [[""]])
+    def test_format_code_keep_newline_after_shebang(
+        self,
+        test_args,
+        args,
+    ):
+        """Do not remove newlines following the shebang.
+
+        See issue #187.
+        """
+        uut = Formatter(
+            test_args,
+            sys.stderr,
+            sys.stdin,
+            sys.stdout,
+        )
+
+        docstring = '''\
+#!/usr/bin/env python
+
+"""a.py."""
+'''
+        assert docstring == uut._do_format_code('''\
+#!/usr/bin/env python
+
+"""a.py"""
+''')
 
 class TestFormatCodeRanges:
     """Class for testing _format_code() with the line_range or length_range