Skip to content

Commit 3ea222e

Browse files
Copilotbittner
andcommitted
Fix ArgvContext to isolate logging configuration
Co-authored-by: bittner <[email protected]>
1 parent 77acbd8 commit 3ea222e

File tree

5 files changed

+69
-50
lines changed

5 files changed

+69
-50
lines changed

cli_test_helpers/decorators.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import contextlib
6+
import logging
67
import os
78
import sys
89
from tempfile import TemporaryDirectory
@@ -18,17 +19,27 @@ class ArgvContext:
1819
Use it to mimic the command line arguments of the CLI application.
1920
Note that the first argument (index ``0``) is always the script or
2021
application name.
22+
23+
This context manager also isolates logging configuration by temporarily
24+
clearing logging handlers when entering the context and restoring them
25+
when exiting. This allows code under test to call ``logging.basicConfig()``
26+
successfully even when test frameworks (like pytest) have already
27+
configured logging handlers.
2128
"""
2229

2330
def __init__(self, *new_args):
2431
self._old = sys.argv
2532
self.args = type(self._old)(new_args)
33+
self._old_handlers = None
2634

2735
def __enter__(self):
2836
sys.argv = self.args
37+
self._old_handlers = logging.root.handlers[:]
38+
logging.root.handlers.clear()
2939

3040
def __exit__(self, exc_type, exc_val, exc_tb):
3141
sys.argv = self._old
42+
logging.root.handlers = self._old_handlers
3243

3344

3445
class EnvironContext(patch.dict):

cli_test_logging.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

test_cli.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

test_cli_workaround.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/test_decorators.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""Tests for our tests helpers. 8-}."""
22

3+
import logging
34
import os
45
import sys
6+
import tempfile
7+
from pathlib import Path
58

69
from cli_test_helpers import ArgvContext, EnvironContext, RandomDirectoryContext
710

@@ -23,6 +26,61 @@ def test_argv_context():
2326
assert sys.argv == old, "sys.argv wasn't correctly reset"
2427

2528

29+
def test_argv_context_logging_isolation():
30+
"""
31+
Does ArgvContext isolate logging configuration?
32+
33+
This test verifies that ArgvContext temporarily clears logging handlers,
34+
allowing code under test to call logging.basicConfig() successfully even
35+
when test frameworks have already configured logging.
36+
"""
37+
# Setup: Create a mock handler to simulate pytest's handlers
38+
original_handlers = logging.root.handlers[:]
39+
mock_handler = logging.NullHandler()
40+
logging.root.handlers.append(mock_handler)
41+
42+
try:
43+
# Verify the mock handler is present
44+
assert mock_handler in logging.root.handlers, "Test setup failed"
45+
46+
with tempfile.TemporaryDirectory() as tmpdir:
47+
logfile = Path(tmpdir) / "test.log"
48+
49+
with ArgvContext("test_script.py", str(logfile)):
50+
# Inside context: handlers should be cleared
51+
handlers_before = logging.root.handlers[:]
52+
assert len(handlers_before) == 0, (
53+
"Handlers should be cleared inside ArgvContext"
54+
)
55+
56+
# Configure logging (this should work now)
57+
logging.basicConfig(
58+
filename=str(logfile),
59+
level=logging.INFO,
60+
force=False, # Should work without force since handlers are cleared
61+
)
62+
63+
# Log a message
64+
logger = logging.getLogger(__name__)
65+
logger.info("Test message")
66+
67+
# After context: handlers should be restored
68+
assert mock_handler in logging.root.handlers, (
69+
"Original handlers should be restored after ArgvContext"
70+
)
71+
72+
# Verify the log file was created and contains the message
73+
assert logfile.exists(), "Log file should have been created"
74+
log_content = logfile.read_text()
75+
assert "Test message" in log_content, (
76+
"Log file should contain the test message"
77+
)
78+
79+
finally:
80+
# Cleanup: Restore original handlers
81+
logging.root.handlers = original_handlers
82+
83+
2684
def test_environ_context():
2785
"""
2886
Does EnvironContext set new environ values and reset old ones correctly?

0 commit comments

Comments
 (0)