Repository: akaihola/darker

Issue:
mypy fails with "baseline-revision is not a valid Python package name" in the baseline worktree if the git repo toplevel contains __init__.py
**Describe the bug**

If the top-level of a git repo has an `__init__.py` in it, then the step that does
```shell
git worktree add --quiet --force --force /tmp/tmpF00/baseline-revision HEAD
```
will create a file `baseline-revision/__init__.py`. When mypy sees this, it expects "baseline-revision" to be a valid python package name. But it's not because of the hyphen.

https://github.com/python/mypy/issues/8400 talks a bit about this issue, and there are folks asking for a way to suppress it.

But there's a simple fix in Darker, like

```diff
diff --git a/src/darker/linting.py b/src/darker/linting.py
index bc2055f..1e285d6 100644
--- a/src/darker/linting.py
+++ b/src/darker/linting.py
@@ -524,7 +524,7 @@ def _get_messages_from_linters_for_baseline(

     """
     with TemporaryDirectory() as tmpdir:
-        tmp_path = Path(tmpdir) / "baseline-revision"
+        tmp_path = Path(tmpdir) / "baseline-revision" / root.name
         with git_clone_local(root, revision, tmp_path) as clone_root:
             rev1_commit = git_rev_parse(revision, root)
             result = _get_messages_from_linters(
```

i.e., always create the worktree in a subfolder of "baseline-revision". Sharing the same name as where the repo is checked out should be most reliable. (E.g. if the project config assumes the top-level of the git repo is in PYTHONPATH).

**To Reproduce**
Steps to reproduce the behavior:
1. Clone this repository: 'https://chromium.googlesource.com/chromiumos/chromite'
    - (or just make a git repo with an `__init__.py` at the top level and any other)
2. Run Darker with `darker -vvv --lint --check --lint "mypy --follow-imports=silent" api/controller/sysroot.py`
3. See error / wrong behavior

"-vvv" will show the output, "baseline-revision is not a valid Python package name".

Otherwise, the effect is just to treat the baseline as having no errors, so all mypy errors on the files are emitted, without filtering.

**Expected behavior**

Just new mypy errors to be emitted.

**Tested with:**

  - darker@a7f586428f44d07795dba601fb21db0fb5ede22b (May 1 2023)
  - mypy 1.3.0
  - Python 3.10.9

Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index fe4a4fa..acd1927 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -11,6 +11,9 @@ Fixed
 - Revert running ``commit-range`` from the repository itself. This broke the GitHub
   action.
 - Python 3.12 compatibility in multi-line string scanning.
+- Use the original repository working directory name as the name of the temporary
+  directory for getting the linter baseline. This avoids issues with Mypy when there's
+  an ``__init__.py`` in the repository root.
 
 
 1.7.1_ - 2023-03-26
diff --git a/constraints-oldest.txt b/constraints-oldest.txt
index 8313173..ef36c84 100644
--- a/constraints-oldest.txt
+++ b/constraints-oldest.txt
@@ -10,6 +10,7 @@ flake8-2020==1.6.1
 flake8-bugbear==22.1.11
 flake8-comprehensions==3.7.0
 flynt==0.76
+mypy==0.990
 Pygments==2.4.0
 pytest==6.2.0
 pytest-kwparametrize==0.0.3
diff --git a/setup.cfg b/setup.cfg
index f34135e..2dc4b8f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -60,6 +60,7 @@ test =
     defusedxml>=0.7.1
     flynt>=0.76,<0.78
     isort>=5.0.1
+    mypy>=0.990
     pygments
     pytest>=6.2.0
     pytest-darker
diff --git a/src/darker/linting.py b/src/darker/linting.py
index bc2055f..1e285d6 100644
--- a/src/darker/linting.py
+++ b/src/darker/linting.py
@@ -524,7 +524,7 @@ def _get_messages_from_linters_for_baseline(
 
     """
     with TemporaryDirectory() as tmpdir:
-        tmp_path = Path(tmpdir) / "baseline-revision"
+        tmp_path = Path(tmpdir) / "baseline-revision" / root.name
         with git_clone_local(root, revision, tmp_path) as clone_root:
             rev1_commit = git_rev_parse(revision, root)
             result = _get_messages_from_linters(


Test patch:
diff --git a/src/darker/tests/test_linting.py b/src/darker/tests/test_linting.py
index 7ddc273..7849401 100644
--- a/src/darker/tests/test_linting.py
+++ b/src/darker/tests/test_linting.py
@@ -4,8 +4,10 @@
 
 import os
 from pathlib import Path
+from subprocess import PIPE, Popen  # nosec
 from textwrap import dedent
-from typing import Dict, Iterable, List, Tuple, Union
+from typing import Any, Dict, Iterable, List, Tuple, Union
+from unittest.mock import patch
 
 import pytest
 
@@ -567,3 +569,35 @@ def test_get_messages_from_linters_for_baseline(git_repo):
         MessageLocation(a_py, 3): [LinterMessage("python", "Third line")],
     }
     assert result == expect
+
+
+class AssertEmptyStderrPopen(
+    Popen  # type: ignore[type-arg]
+):  # pylint: disable=too-few-public-methods
+    # When support for Python 3.8 is dropped, inherit `Popen[str]` instead and remove
+    # the `type-arg` ignore above.
+    """A Popen to use for the following test; asserts that its stderr is empty"""
+
+    def __init__(self, args: List[str], **kwargs: Any):  # type: ignore[misc]
+        super().__init__(args, stderr=PIPE, **kwargs)
+        assert self.stderr is not None
+        assert self.stderr.read() == ""
+
+
+def test_get_messages_from_linters_for_baseline_no_mypy_errors(git_repo):
+    """Ensure Mypy does not fail early when ``__init__.py`` is at the repository root
+
+    Regression test for #498
+
+    """
+    git_repo.add({"__init__.py": ""}, commit="Initial commit")
+    initial = git_repo.get_hash()
+    with patch.object(linting, "Popen", AssertEmptyStderrPopen):
+        # end of test setup
+
+        _ = linting._get_messages_from_linters_for_baseline(
+            linter_cmdlines=["mypy"],
+            root=git_repo.root,
+            paths=[Path("__init__.py")],
+            revision=initial,
+        )