Allow tests to override "global" `log_level` (rebased) (#7340)
Co-authored-by: Ruaridh Williamson <ruaridh.williamson@flexciton.com>
This commit is contained in:
		
							parent
							
								
									e78207c936
								
							
						
					
					
						commit
						fcbaab8b0b
					
				
							
								
								
									
										1
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										1
									
								
								AUTHORS
								
								
								
								
							|  | @ -245,6 +245,7 @@ Romain Dorgueil | ||||||
| Roman Bolshakov | Roman Bolshakov | ||||||
| Ronny Pfannschmidt | Ronny Pfannschmidt | ||||||
| Ross Lawley | Ross Lawley | ||||||
|  | Ruaridh Williamson | ||||||
| Russel Winder | Russel Winder | ||||||
| Ryan Wooden | Ryan Wooden | ||||||
| Samuel Dion-Girardeau | Samuel Dion-Girardeau | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | ``caplog.set_level()`` will now override any :confval:`log_level` set via the CLI or ``.ini``. | ||||||
|  | @ -250,6 +250,9 @@ made in ``3.4`` after community feedback: | ||||||
| 
 | 
 | ||||||
| * Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration | * Log levels are no longer changed unless explicitly requested by the :confval:`log_level` configuration | ||||||
|   or ``--log-level`` command-line options. This allows users to configure logger objects themselves. |   or ``--log-level`` command-line options. This allows users to configure logger objects themselves. | ||||||
|  |   Setting :confval:`log_level` will set the level that is captured globally so if a specific test requires | ||||||
|  |   a lower level than this, use the ``caplog.set_level()`` functionality otherwise that test will be prone to | ||||||
|  |   failure. | ||||||
| * :ref:`Live Logs <live_logs>` is now disabled by default and can be enabled setting the | * :ref:`Live Logs <live_logs>` is now disabled by default and can be enabled setting the | ||||||
|   :confval:`log_cli` configuration option to ``true``. When enabled, the verbosity is increased so logging for each |   :confval:`log_cli` configuration option to ``true``. When enabled, the verbosity is increased so logging for each | ||||||
|   test is visible. |   test is visible. | ||||||
|  |  | ||||||
|  | @ -343,7 +343,7 @@ class LogCaptureFixture: | ||||||
|         """Creates a new funcarg.""" |         """Creates a new funcarg.""" | ||||||
|         self._item = item |         self._item = item | ||||||
|         # dict of log name -> log level |         # dict of log name -> log level | ||||||
|         self._initial_log_levels = {}  # type: Dict[Optional[str], int] |         self._initial_logger_levels = {}  # type: Dict[Optional[str], int] | ||||||
| 
 | 
 | ||||||
|     def _finalize(self) -> None: |     def _finalize(self) -> None: | ||||||
|         """Finalizes the fixture. |         """Finalizes the fixture. | ||||||
|  | @ -351,7 +351,7 @@ class LogCaptureFixture: | ||||||
|         This restores the log levels changed by :meth:`set_level`. |         This restores the log levels changed by :meth:`set_level`. | ||||||
|         """ |         """ | ||||||
|         # restore log levels |         # restore log levels | ||||||
|         for logger_name, level in self._initial_log_levels.items(): |         for logger_name, level in self._initial_logger_levels.items(): | ||||||
|             logger = logging.getLogger(logger_name) |             logger = logging.getLogger(logger_name) | ||||||
|             logger.setLevel(level) |             logger.setLevel(level) | ||||||
| 
 | 
 | ||||||
|  | @ -430,8 +430,9 @@ class LogCaptureFixture: | ||||||
|         """ |         """ | ||||||
|         logger_obj = logging.getLogger(logger) |         logger_obj = logging.getLogger(logger) | ||||||
|         # save the original log-level to restore it during teardown |         # save the original log-level to restore it during teardown | ||||||
|         self._initial_log_levels.setdefault(logger, logger_obj.level) |         self._initial_logger_levels.setdefault(logger, logger_obj.level) | ||||||
|         logger_obj.setLevel(level) |         logger_obj.setLevel(level) | ||||||
|  |         self.handler.setLevel(level) | ||||||
| 
 | 
 | ||||||
|     @contextmanager |     @contextmanager | ||||||
|     def at_level( |     def at_level( | ||||||
|  | @ -446,10 +447,13 @@ class LogCaptureFixture: | ||||||
|         logger_obj = logging.getLogger(logger) |         logger_obj = logging.getLogger(logger) | ||||||
|         orig_level = logger_obj.level |         orig_level = logger_obj.level | ||||||
|         logger_obj.setLevel(level) |         logger_obj.setLevel(level) | ||||||
|  |         handler_orig_level = self.handler.level | ||||||
|  |         self.handler.setLevel(level) | ||||||
|         try: |         try: | ||||||
|             yield |             yield | ||||||
|         finally: |         finally: | ||||||
|             logger_obj.setLevel(orig_level) |             logger_obj.setLevel(orig_level) | ||||||
|  |             self.handler.setLevel(handler_orig_level) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.fixture | @pytest.fixture | ||||||
|  |  | ||||||
|  | @ -138,3 +138,100 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow | ||||||
| 
 | 
 | ||||||
|     # This reaches into private API, don't use this type of thing in real tests! |     # This reaches into private API, don't use this type of thing in real tests! | ||||||
|     assert set(caplog._item._store[catch_log_records_key]) == {"setup", "call"} |     assert set(caplog._item._store[catch_log_records_key]) == {"setup", "call"} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_ini_controls_global_log_level(testdir): | ||||||
|  |     testdir.makepyfile( | ||||||
|  |         """ | ||||||
|  |         import pytest | ||||||
|  |         import logging | ||||||
|  |         def test_log_level_override(request, caplog): | ||||||
|  |             plugin = request.config.pluginmanager.getplugin('logging-plugin') | ||||||
|  |             assert plugin.log_level == logging.ERROR | ||||||
|  |             logger = logging.getLogger('catchlog') | ||||||
|  |             logger.warning("WARNING message won't be shown") | ||||||
|  |             logger.error("ERROR message will be shown") | ||||||
|  |             assert 'WARNING' not in caplog.text | ||||||
|  |             assert 'ERROR' in caplog.text | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  |     testdir.makeini( | ||||||
|  |         """ | ||||||
|  |         [pytest] | ||||||
|  |         log_level=ERROR | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     # make sure that that we get a '0' exit code for the testsuite | ||||||
|  |     assert result.ret == 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_caplog_can_override_global_log_level(testdir): | ||||||
|  |     testdir.makepyfile( | ||||||
|  |         """ | ||||||
|  |         import pytest | ||||||
|  |         import logging | ||||||
|  |         def test_log_level_override(request, caplog): | ||||||
|  |             logger = logging.getLogger('catchlog') | ||||||
|  |             plugin = request.config.pluginmanager.getplugin('logging-plugin') | ||||||
|  |             assert plugin.log_level == logging.WARNING | ||||||
|  | 
 | ||||||
|  |             logger.info("INFO message won't be shown") | ||||||
|  | 
 | ||||||
|  |             caplog.set_level(logging.INFO, logger.name) | ||||||
|  | 
 | ||||||
|  |             with caplog.at_level(logging.DEBUG, logger.name): | ||||||
|  |                 logger.debug("DEBUG message will be shown") | ||||||
|  | 
 | ||||||
|  |             logger.debug("DEBUG message won't be shown") | ||||||
|  | 
 | ||||||
|  |             with caplog.at_level(logging.CRITICAL, logger.name): | ||||||
|  |                 logger.warning("WARNING message won't be shown") | ||||||
|  | 
 | ||||||
|  |             logger.debug("DEBUG message won't be shown") | ||||||
|  |             logger.info("INFO message will be shown") | ||||||
|  | 
 | ||||||
|  |             assert "message won't be shown" not in caplog.text | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  |     testdir.makeini( | ||||||
|  |         """ | ||||||
|  |         [pytest] | ||||||
|  |         log_level=WARNING | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     assert result.ret == 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_caplog_captures_despite_exception(testdir): | ||||||
|  |     testdir.makepyfile( | ||||||
|  |         """ | ||||||
|  |         import pytest | ||||||
|  |         import logging | ||||||
|  |         def test_log_level_override(request, caplog): | ||||||
|  |             logger = logging.getLogger('catchlog') | ||||||
|  |             plugin = request.config.pluginmanager.getplugin('logging-plugin') | ||||||
|  |             assert plugin.log_level == logging.WARNING | ||||||
|  | 
 | ||||||
|  |             logger.info("INFO message won't be shown") | ||||||
|  | 
 | ||||||
|  |             caplog.set_level(logging.INFO, logger.name) | ||||||
|  | 
 | ||||||
|  |             with caplog.at_level(logging.DEBUG, logger.name): | ||||||
|  |                 logger.debug("DEBUG message will be shown") | ||||||
|  |                 raise Exception() | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  |     testdir.makeini( | ||||||
|  |         """ | ||||||
|  |         [pytest] | ||||||
|  |         log_level=WARNING | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     result.stdout.fnmatch_lines(["*DEBUG message will be shown*"]) | ||||||
|  |     assert result.ret == 1 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue