Repository: iterative/dvc

Issue:
tests: Logs are throwing duplicated output
You can review any log (this one, for example: https://travis-ci.org/iterative/dvc/jobs/457244685#L1544-L1571)


Discussion:
https://ci.appveyor.com/project/iterative/dvc/builds/20527652#L8269

Resolving patch:
diff --git a/dvc/logger.py b/dvc/logger.py
--- a/dvc/logger.py
+++ b/dvc/logger.py
@@ -59,6 +59,9 @@ class LogLevelFilter(logging.Filter):
             def filter(self, record):
                 return record.levelno <= logging.WARNING
 
+        if Logger._already_initialized():
+            return
+
         sh_out = logging.StreamHandler(sys.stdout)
         sh_out.setFormatter(logging.Formatter(Logger.FMT))
         sh_out.setLevel(logging.DEBUG)
@@ -243,3 +246,7 @@ def box(cls, msg, border_color=''):
             )
 
         print(box)
+
+    @staticmethod
+    def _already_initialized():
+        return bool(Logger.logger().handlers)


Test patch:
diff --git a/tests/test_logger.py b/tests/test_logger.py
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -47,9 +47,16 @@ def test_colorize(self):
             # This is not a tty, so it should not colorize anything
             self.assertEqual(msg, name)
 
+    def test_handlers(self):
+        Logger.init()
+        Logger.init()
+        self.assertEqual(len(Logger.logger().handlers), 2)
+
+
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_stderr(self, mock_stdout, mock_stderr):
+    def test_stderr(self, mock_stdout, mock_stderr, mock_logger):
         error_message = 'error msg'
         Logger.init()
         Logger.error(error_message)
@@ -59,9 +66,10 @@ def test_stderr(self, mock_stdout, mock_stderr):
                                    'are always happy to help!\n'.format(error_message),
                                    mock_stderr.getvalue())
 
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_stdout(self, mock_stdout, mock_stderr):
+    def test_stdout(self, mock_stdout, mock_stderr, mock_logger):
         non_error_message = 'non-error message'
         Logger.init()
         Logger.info(non_error_message)
@@ -71,39 +79,42 @@ def test_stdout(self, mock_stdout, mock_stderr):
 
 
 class TestLoggerException(TestDvc):
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_empty_err_msg(self, mock_stdout, mock_stderr):
+    def test_empty_err_msg(self, mock_stdout, mock_stderr, mock_logger):
         from dvc.exceptions import DvcException
 
         Logger.init()
         exc_msg = 'msg'
         Logger.error("", exc=DvcException(exc_msg))
-        
+
         self.assertEqual('', mock_stdout.getvalue())
         self.assertEqual('Error: {}\n\nHaving any troubles? '
                          'Hit us up at dvc.org/support, we '
                          'are always happy to help!\n'.format(exc_msg),
                          mock_stderr.getvalue())
 
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_empty_exc_msg(self, mock_stdout, mock_stderr):
+    def test_empty_exc_msg(self, mock_stdout, mock_stderr, mock_logger):
         from dvc.exceptions import DvcException
 
         Logger.init()
         err_msg = 'msg'
         Logger.error(err_msg, exc=DvcException(""))
-        
+
         self.assertEqual('', mock_stdout.getvalue())
         self.assertEqual('Error: {}\n\nHaving any troubles? '
                          'Hit us up at dvc.org/support, we '
                          'are always happy to help!\n'.format(err_msg),
                          mock_stderr.getvalue())
 
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_msg_and_exc(self, mock_stdout, mock_stderr):
+    def test_msg_and_exc(self, mock_stdout, mock_stderr, mock_logger):
         from dvc.exceptions import DvcException
 
         Logger.init()
@@ -126,17 +137,19 @@ class A(object):
             verbose = False
         CmdBase._set_loglevel(A())
 
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_progress(self, mock_stdout, mock_stderr):
+    def test_progress(self, mock_stdout, mock_stderr, mock_logger):
         from dvc.progress import progress
         progress.update_target('target', 50, 100)
         self.assertEqual('', mock_stdout.getvalue())
         self.assertEqual('', mock_stderr.getvalue())
 
+    @patch('dvc.logger.Logger._already_initialized', return_value=False)
     @patch('sys.stderr', new_callable=StringIO)
     @patch('sys.stdout', new_callable=StringIO)
-    def test_box(self, mock_stdout, mock_stderr):
+    def test_box(self, mock_stdout, mock_stderr, mock_logger):
         Logger.box('message')
         self.assertEqual('', mock_stdout.getvalue())
         self.assertEqual('', mock_stderr.getvalue())