Repository: python/mypy
Pull request: Add success msg for only notes output
Closes #12302 

The `messages` here could be neither error nor note, thus I modify the `count_stats ` to also count `note`.

Problem statement:
Success message missing when reveal_type() used.
**Bug Report**


I have something like `if TYPE_CHECKING: reveal_type(foo)` in a module (in 5 places).
mypy finds no errors, but the line that says "success" is missing.

Here's two sets of output from mypy, first with reveal_type() in the module, and second without.

```
D:\Solutions\trunk\Python\Sass>mypy .
solver\common.py:299: note: Revealed type is "Any"
solver\common.py:366: note: Revealed type is "Any"
solver\common.py:521: note: Revealed type is "solver.instruct.InstrTab"
solver\common.py:522: note: Revealed type is "Any"
solver\common.py:523: note: Revealed type is "Any"

D:\Solutions\trunk\Python\Sass>mypy .
Success: no issues found in 52 source files
```

What I think is correct output, in the first case, is
```
D:\Solutions\trunk\Python\Sass>mypy .
solver\common.py:299: note: Revealed type is "Any"
solver\common.py:366: note: Revealed type is "Any"
solver\common.py:521: note: Revealed type is "solver.instruct.InstrTab"
solver\common.py:522: note: Revealed type is "Any"
solver\common.py:523: note: Revealed type is "Any"
Success: no issues found in 52 source files
```

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: 0,92`
- Python version used: 3.7
- Operating system and version: Windows 10

Hints from issue discussion:
This happens if there are only notes (notes still cause failure, but no error count) in the output:
playground https://mypy-play.net/?mypy=latest&python=3.10&gist=3d64dc19218346533773b8f84b9535fa

Commit message: Allow success msg for only notes output

Patch to review:
diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py
index 2f8fc7f119ba..266243f964e5 100644
--- a/mypy/dmypy_server.py
+++ b/mypy/dmypy_server.py
@@ -773,10 +773,12 @@ def pretty_messages(self, messages: List[str], n_sources: int,
         if self.options.error_summary:
             summary: Optional[str] = None
             if messages:
-                n_errors, n_files = count_stats(messages)
+                n_errors, n_notes, n_files = count_stats(messages)
                 if n_errors:
                     summary = self.formatter.format_error(n_errors, n_files, n_sources,
                                                           use_color=use_color)
+                elif n_notes == len(messages):
+                    summary = self.formatter.format_success(n_sources, use_color)
             else:
                 summary = self.formatter.format_success(n_sources, use_color)
             if summary:
diff --git a/mypy/main.py b/mypy/main.py
index d765781838cf..1a47399cc23e 100644
--- a/mypy/main.py
+++ b/mypy/main.py
@@ -114,13 +114,16 @@ def main(script_path: Optional[str],
         code = 2 if blockers else 1
     if options.error_summary:
         if messages:
-            n_errors, n_files = util.count_stats(messages)
+            n_errors, n_notes, n_files = util.count_stats(messages)
             if n_errors:
                 summary = formatter.format_error(
                     n_errors, n_files, len(sources), blockers=blockers,
                     use_color=options.color_output
                 )
                 stdout.write(summary + '\n')
+            # Only notes should also output success
+            elif n_notes == len(messages):
+                stdout.write(formatter.format_success(len(sources), options.color_output) + '\n')
         else:
             stdout.write(formatter.format_success(len(sources), options.color_output) + '\n')
         stdout.flush()
diff --git a/mypy/util.py b/mypy/util.py
index 2d6888dd0a2e..c02e5dcfc9b4 100644
--- a/mypy/util.py
+++ b/mypy/util.py
@@ -435,11 +435,12 @@ def check_python_version(program: str) -> None:
                  "please upgrade to 3.6 or newer".format(name=program))
 
 
-def count_stats(errors: List[str]) -> Tuple[int, int]:
-    """Count total number of errors and files in error list."""
-    errors = [e for e in errors if ': error:' in e]
-    files = {e.split(':')[0] for e in errors}
-    return len(errors), len(files)
+def count_stats(messages: List[str]) -> Tuple[int, int, int]:
+    """Count total number of errors, notes and error_files in message list."""
+    errors = [e for e in messages if ': error:' in e]
+    error_files = {e.split(':')[0] for e in errors}
+    notes = [e for e in messages if ': note:' in e]
+    return len(errors), len(notes), len(error_files)
 
 
 def split_words(msg: str) -> List[str]:


Code review comments:
diff_hunk: @@ -114,13 +114,16 @@ def main(script_path: Optional[str],
         code = 2 if blockers else 1
     if options.error_summary:
         if messages:
-            n_errors, n_files = util.count_stats(messages)
+            n_errors, n_notes, n_files = util.count_stats(messages)
path: mypy/main.py
text: @user1:
I don't think the outer `if` remains necessary. If `messages` is empty, then this returns `(0, 0, 0)`. So we can just always count the stats and then condition on them.

Also, is `n_notes == len(messages)` actually different from `not n_errors`? Is there a third type of message that is neither an error nor a note?

@author:
There is a test case [testErrorSummaryOnBadUsage], which generates 'mypy: cannot read file 'missing.py': No such file or directory'

@user1:
Okay, I still think the comment about not needing the outer `if` applies.