Repository: Parquery/pyicontract-lint

Issue:
Running pyicontract-lint without linting errors should notify success
Hi @mristin , it's me again :)
Just an enhancement proposal: when I got the package to run I had no linting errors and therefore the package would not give any output, even in `--verbose` mode. I had to run it with `--verbose json` mode to get the following output:
```bash
> pyicontract-lint --format verbose telereddit  # no output
> pyicontract-lint --format json telereddit
[]
```

It would be great if we could get some sort of feedback on the successful run (at least in `--verbose` mode). I'm thinking of something along the line of:
```bash
> pyicontract-lint --format verbose telereddit
--------------------------------------------------------------------
Checked xx files. OK
```

Discussion:
coveralls: ## Pull Request Test Coverage Report for [Build 95](https://coveralls.io/builds/32995171)

* **7** of **8**   **(87.5%)**  changed or added relevant lines in **2** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.1%**) to **91.525%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [icontract_lint/__init__.py](https://coveralls.io/builds/32995171/source?filename=icontract_lint%2F__init__.py#L596) | 2 | 3 | 66.67%
<!-- | **Total:** | **7** | **8** | **87.5%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/32995171/badge)](https://coveralls.io/builds/32995171) |
| :-- | --: |
| Change from base [Build 93](https://coveralls.io/builds/32994375): |  0.1% |
| Covered Lines: | 270 |
| Relevant Lines: | 295 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/icontract_lint/__init__.py b/icontract_lint/__init__.py
index b35a405..8639113 100644
--- a/icontract_lint/__init__.py
+++ b/icontract_lint/__init__.py
@@ -2,6 +2,7 @@
 import collections
 import enum
 import json
+import os
 import pathlib
 import re
 import sys
@@ -589,9 +590,10 @@ def output_verbose(errors: List[Error], stream: TextIO) -> None:
     """
     for err in errors:
         if err.lineno is not None:
-            stream.write("{}:{}: {} ({})\n".format(err.filename, err.lineno, err.description, err.identifier.value))
+            stream.write("{}:{}: {} ({}){}".format(err.filename, err.lineno, err.description, err.identifier.value,
+                                                   os.linesep))
         else:
-            stream.write("{}: {} ({})\n".format(err.filename, err.description, err.identifier.value))
+            stream.write("{}: {} ({}){}".format(err.filename, err.description, err.identifier.value, os.linesep))
 
 
 def output_json(errors: List[Error], stream: TextIO) -> None:
diff --git a/icontract_lint/main.py b/icontract_lint/main.py
index aeca12b..d5028f7 100644
--- a/icontract_lint/main.py
+++ b/icontract_lint/main.py
@@ -4,6 +4,7 @@
 # This file is necessary so that we can specify the entry point for pex.
 
 import argparse
+import os
 import pathlib
 import sys
 from typing import List, Any, TextIO
@@ -42,13 +43,16 @@ def parse_args(sys_argv: List[str]) -> Args:
 def _main(args: Args, stream: TextIO) -> int:
     """Execute the main routine."""
     if args.version:
-        stream.write("{}\n".format(pyicontract_lint_meta.__version__))
+        stream.write("{}{}".format(pyicontract_lint_meta.__version__, os.linesep))
         return 0
 
     errors = icontract_lint.check_paths(paths=args.paths)
 
     if args.format == 'verbose':
-        icontract_lint.output_verbose(errors=errors, stream=stream)
+        if not errors:
+            stream.write("No errors detected.{}".format(os.linesep))
+        else:
+            icontract_lint.output_verbose(errors=errors, stream=stream)
     elif args.format == 'json':
         icontract_lint.output_json(errors=errors, stream=stream)
     else:


Test patch:
diff --git a/tests/test_icontract_lint.py b/tests/test_icontract_lint.py
index f66931e..be91d62 100644
--- a/tests/test_icontract_lint.py
+++ b/tests/test_icontract_lint.py
@@ -2,6 +2,7 @@
 
 # pylint: disable=missing-docstring
 import io
+import os
 import pathlib
 import sys
 import tempfile
@@ -725,8 +726,9 @@ class TestOutputVerbose(unittest.TestCase):
 
         icontract_lint.output_verbose(errors=errs, stream=stream)
 
-        self.assertEqual('/path/to/some/file.py:123: The contract decorator lacks the condition. (no-condition)\n',
-                         buf.getvalue())
+        self.assertEqual(
+            '/path/to/some/file.py:123: The contract decorator lacks the condition. (no-condition){}'.format(
+                os.linesep), buf.getvalue())
 
 
 class TestOutputJson(unittest.TestCase):
diff --git a/tests/test_main.py b/tests/test_main.py
index ae4f23a..fca1281 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python3
 """Test the main routine."""
 import io
+import os
 import pathlib
 import sys
 import tempfile
@@ -93,6 +94,20 @@ class TestMain(unittest.TestCase):
                 ]""".format(pth=str(pth).replace("\\", "\\\\"))),
                     buf.getvalue())
 
+    def test_verbose_no_errors(self):
+        with tempfile.TemporaryDirectory() as tmp:
+            tmp_path = pathlib.Path(tmp)
+            pth = tmp_path / "some-executable.py"
+            pth.write_text('"""all ok"""')
+
+            buf = io.StringIO()
+            stream = cast(TextIO, buf)
+            args = icontract_lint.main.parse_args(sys_argv=[str(pth)])
+            retcode = icontract_lint.main._main(args=args, stream=stream)
+
+            self.assertEqual(0, retcode)
+            self.assertEqual(("No errors detected.{}").format(os.linesep), buf.getvalue())
+
     def test_verbose(self):
         with tempfile.TemporaryDirectory() as tmp:
             tmp_path = pathlib.Path(tmp)
@@ -107,10 +122,9 @@ class TestMain(unittest.TestCase):
                 retcode = icontract_lint.main._main(args=args, stream=stream)
 
                 self.assertEqual(1, retcode)
-                self.assertEqual(
-                    ("{pth}:3: Precondition argument(s) are missing in "
-                     "the function signature: x (pre-invalid-arg)\n").format(pth=str(pth)),
-                    buf.getvalue())
+                self.assertEqual(("{}:3: Precondition argument(s) are missing in "
+                                  "the function signature: x (pre-invalid-arg){}").format(str(pth), os.linesep),
+                                 buf.getvalue())
 
     def test_dont_panic(self):
         with tempfile.TemporaryDirectory() as tmp:
@@ -134,7 +148,7 @@ class TestMain(unittest.TestCase):
 
         retcode = icontract_lint.main._main(args=args, stream=stream)
         self.assertEqual(0, retcode)
-        self.assertEqual('{}\n'.format(pyicontract_lint_meta.__version__), buf.getvalue())
+        self.assertEqual('{}{}'.format(pyicontract_lint_meta.__version__, os.linesep), buf.getvalue())
 
 
 if __name__ == '__main__':