From ceb4d7fd687ddbb94dc6c62fb6a2c46c383da54f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 15 Jun 2023 21:32:37 +0100 Subject: [PATCH] Fix re-added file with errors in mypy daemon (#15440) Fixes #5343 Fixes #12249 This can potentially slow down runs in situations where multiple unchanged files are re-added to source list. Potentially, we can track whether modules had errors permanently (not just from previous run), and then only re-check those unchanged re-added files that had errors before. I am not sure if this is important. --- mypy/dmypy_server.py | 15 +++++++++++++++ test-data/unit/daemon.test | 22 ++++++++++++++++++++++ test-data/unit/fine-grained.test | 21 +++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 631aab4ccbc8..54544f4c01ce 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -857,6 +857,21 @@ def _find_changed( assert path removed.append((source.module, path)) + # Always add modules that were (re-)added, since they may be detected as not changed by + # fswatcher (if they were actually not changed), but they may still need to be checked + # in case they had errors before they were deleted from sources on previous runs. + previous_modules = {source.module for source in self.previous_sources} + changed_set = set(changed) + changed.extend( + [ + (source.module, source.path) + for source in sources + if source.path + and source.module not in previous_modules + and (source.module, source.path) not in changed_set + ] + ) + # Find anything that has had its module path change because of added or removed __init__s last = {s.path: s.module for s in self.previous_sources} for s in sources: diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 424fc6d2b167..1fae1514111c 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -62,6 +62,28 @@ Daemon started \[mypy] files = ./foo.py +[case testDaemonRunMultipleStrict] +$ dmypy run -- foo.py --strict --follow-imports=error +Daemon started +foo.py:1: error: Function is missing a return type annotation +foo.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run -- bar.py --strict --follow-imports=error +bar.py:1: error: Function is missing a return type annotation +bar.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +$ dmypy run -- foo.py --strict --follow-imports=error +foo.py:1: error: Function is missing a return type annotation +foo.py:1: note: Use "-> None" if function does not return a value +Found 1 error in 1 file (checked 1 source file) +== Return code: 1 +[file foo.py] +def f(): pass +[file bar.py] +def f(): pass + [case testDaemonRunRestart] $ dmypy run -- foo.py --follow-imports=error Daemon started diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 88a11be31f34..a0d7d302fd08 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10340,3 +10340,24 @@ reveal_type(x) [out] == a.py:3: note: Revealed type is "Union[def (x: builtins.int) -> builtins.int, def (*x: builtins.int) -> builtins.int]" + +[case testErrorInReAddedModule] +# flags: --disallow-untyped-defs --follow-imports=error +# cmd: mypy a.py +# cmd2: mypy b.py +# cmd3: mypy a.py + +[file a.py] +def f(): pass +[file b.py] +def f(): pass +[file unrelated.txt.3] +[out] +a.py:1: error: Function is missing a return type annotation +a.py:1: note: Use "-> None" if function does not return a value +== +b.py:1: error: Function is missing a return type annotation +b.py:1: note: Use "-> None" if function does not return a value +== +a.py:1: error: Function is missing a return type annotation +a.py:1: note: Use "-> None" if function does not return a value