Skip to content

Commit c2ba292

Browse files
committed
Move configure_logging() from rimport to shared.
1 parent 0d83ad5 commit c2ba292

3 files changed

Lines changed: 71 additions & 82 deletions

File tree

rimport

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -303,37 +303,6 @@ def print_can_file_be_downloaded(file_can_be_downloaded: bool):
303303
logger.info("%sFile is not (yet) available for download.", INDENT)
304304

305305

306-
def configure_logging(log_level: int) -> None:
307-
"""Configure logging to send INFO/WARNING to stdout and ERROR/CRITICAL to stderr.
308-
309-
Sets up two handlers:
310-
- INFO handler: Sends INFO, WARNING, and DEBUG level messages to stdout
311-
- ERROR handler: Sends ERROR and CRITICAL level messages to stderr
312-
313-
Both handlers use simple message-only formatting without timestamps or level names.
314-
315-
Args:
316-
log_level: Minimum logging level (DEBUG, INFO, or WARNING).
317-
"""
318-
logger.setLevel(log_level)
319-
320-
# Handler for INFO, WARNING, and DEBUG level messages -> stdout
321-
info_handler = logging.StreamHandler(sys.stdout)
322-
info_handler.setLevel(logging.DEBUG) # Accept all levels, filter will handle it
323-
info_handler.addFilter(lambda record: record.levelno < logging.ERROR)
324-
info_handler.setFormatter(logging.Formatter("%(message)s"))
325-
326-
# Handler for ERROR and CRITICAL level messages -> stderr
327-
error_handler = logging.StreamHandler(sys.stderr)
328-
error_handler.setLevel(logging.ERROR)
329-
error_handler.setFormatter(logging.Formatter("%(message)s"))
330-
331-
# Clear any existing handlers and add our custom ones
332-
logger.handlers.clear()
333-
logger.addHandler(info_handler)
334-
logger.addHandler(error_handler)
335-
336-
337306
def main(argv: List[str] | None = None) -> int:
338307
"""Main entry point for the rimport tool.
339308
@@ -361,7 +330,7 @@ def main(argv: List[str] | None = None) -> int:
361330

362331
# Configure logging based on verbosity flags
363332
log_level = shared.get_log_level(quiet=args.quiet, verbose=args.verbose)
364-
configure_logging(log_level)
333+
shared.configure_logging(logger, log_level)
365334

366335
# Ensure we are running as the STAGE_OWNER account before touching the tree
367336
# Set env var RIMPORT_SKIP_USER_CHECK=1 if you prefer to run `sudox -u STAGE_OWNER rimport …`

shared.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
import argparse
77
import os
8+
import sys
89

910
DEFAULT_INPUTDATA_ROOT = "/glade/campaign/cesm/cesmdata/cseg/inputdata/"
1011
DEFAULT_STAGING_ROOT = (
@@ -32,6 +33,37 @@ def get_log_level(quiet: bool = False, verbose: bool = False) -> int:
3233
return logging.INFO
3334

3435

36+
def configure_logging(logger, log_level: int) -> None:
37+
"""Configure logging to send INFO/WARNING to stdout and ERROR/CRITICAL to stderr.
38+
39+
Sets up two handlers:
40+
- INFO handler: Sends INFO, WARNING, and DEBUG level messages to stdout
41+
- ERROR handler: Sends ERROR and CRITICAL level messages to stderr
42+
43+
Both handlers use simple message-only formatting without timestamps or level names.
44+
45+
Args:
46+
log_level: Minimum logging level (DEBUG, INFO, or WARNING).
47+
"""
48+
logger.setLevel(log_level)
49+
50+
# Handler for INFO, WARNING, and DEBUG level messages -> stdout
51+
info_handler = logging.StreamHandler(sys.stdout)
52+
info_handler.setLevel(logging.DEBUG) # Accept all levels, filter will handle it
53+
info_handler.addFilter(lambda record: record.levelno < logging.ERROR)
54+
info_handler.setFormatter(logging.Formatter("%(message)s"))
55+
56+
# Handler for ERROR and CRITICAL level messages -> stderr
57+
error_handler = logging.StreamHandler(sys.stderr)
58+
error_handler.setLevel(logging.ERROR)
59+
error_handler.setFormatter(logging.Formatter("%(message)s"))
60+
61+
# Clear any existing handlers and add our custom ones
62+
logger.handlers.clear()
63+
logger.addHandler(info_handler)
64+
logger.addHandler(error_handler)
65+
66+
3567
def add_inputdata_root(parser: argparse.ArgumentParser):
3668
"""Add inputdata_root option to an argument parser.
3769
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
"""
2-
Tests for configure_logging() function in rimport script.
2+
Tests for shared configure_logging() function.
33
"""
44

5-
import os
6-
import sys
7-
import importlib.util
85
import logging
9-
from importlib.machinery import SourceFileLoader
106

117
import pytest
128

13-
# Import rimport module from file without .py extension
14-
rimport_path = os.path.join(
15-
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
16-
"rimport",
17-
)
18-
loader = SourceFileLoader("rimport", rimport_path)
19-
spec = importlib.util.spec_from_loader("rimport", loader)
20-
if spec is None:
21-
raise ImportError(f"Could not create spec for rimport from {rimport_path}")
22-
rimport = importlib.util.module_from_spec(spec)
23-
loader.exec_module(rimport)
9+
import shared
10+
11+
logger = logging.getLogger(__name__)
2412

2513

2614
class TestConfigureLogging:
@@ -31,49 +19,49 @@ def cleanup_logger(self):
3119
"""Clean up logger handlers after each test."""
3220
yield
3321
# Clear handlers after test
34-
rimport.logger.handlers.clear()
22+
logger.handlers.clear()
3523

3624
def test_sets_logger_level_to_info(self):
3725
"""Test that configure_logging sets the logger level to INFO."""
38-
rimport.configure_logging(logging.INFO)
39-
assert rimport.logger.level == logging.INFO
26+
shared.configure_logging(logger, logging.INFO)
27+
assert logger.level == logging.INFO
4028

4129
def test_creates_two_handlers(self):
4230
"""Test that configure_logging creates exactly two handlers."""
43-
rimport.configure_logging(logging.INFO)
44-
assert len(rimport.logger.handlers) == 2
31+
shared.configure_logging(logger, logging.INFO)
32+
assert len(logger.handlers) == 2
4533

4634
def test_info_handler_goes_to_stdout(self, capsys):
4735
"""Test that INFO level messages go to stdout."""
48-
rimport.configure_logging(logging.INFO)
49-
rimport.logger.info("Test info message")
36+
shared.configure_logging(logger, logging.INFO)
37+
logger.info("Test info message")
5038

5139
captured = capsys.readouterr()
5240
assert "Test info message" in captured.out
5341
assert captured.err == ""
5442

5543
def test_warning_handler_goes_to_stdout(self, capsys):
5644
"""Test that WARNING level messages go to stdout."""
57-
rimport.configure_logging(logging.INFO)
58-
rimport.logger.warning("Test warning message")
45+
shared.configure_logging(logger, logging.INFO)
46+
logger.warning("Test warning message")
5947

6048
captured = capsys.readouterr()
6149
assert "Test warning message" in captured.out
6250
assert captured.err == ""
6351

6452
def test_error_handler_goes_to_stderr(self, capsys):
6553
"""Test that ERROR level messages go to stderr."""
66-
rimport.configure_logging(logging.INFO)
67-
rimport.logger.error("Test error message")
54+
shared.configure_logging(logger, logging.INFO)
55+
logger.error("Test error message")
6856

6957
captured = capsys.readouterr()
7058
assert captured.out == ""
7159
assert "Test error message" in captured.err
7260

7361
def test_critical_handler_goes_to_stderr(self, capsys):
7462
"""Test that CRITICAL level messages go to stderr."""
75-
rimport.configure_logging(logging.INFO)
76-
rimport.logger.critical("Test critical message")
63+
shared.configure_logging(logger, logging.INFO)
64+
logger.critical("Test critical message")
7765

7866
captured = capsys.readouterr()
7967
assert captured.out == ""
@@ -84,20 +72,20 @@ def test_clears_existing_handlers(self):
8472
# Add a dummy handler
8573
from io import StringIO
8674
dummy_handler = logging.StreamHandler(StringIO())
87-
rimport.logger.addHandler(dummy_handler)
88-
assert len(rimport.logger.handlers) >= 1
75+
logger.addHandler(dummy_handler)
76+
assert len(logger.handlers) >= 1
8977

9078
# Configure logging
91-
rimport.configure_logging(logging.INFO)
79+
shared.configure_logging(logger, logging.INFO)
9280

9381
# Verify old handlers were cleared and new ones added
94-
assert len(rimport.logger.handlers) == 2
95-
assert dummy_handler not in rimport.logger.handlers
82+
assert len(logger.handlers) == 2
83+
assert dummy_handler not in logger.handlers
9684

9785
def test_formatter_uses_message_only(self, capsys):
9886
"""Test that the formatter outputs only the message without level/timestamp."""
99-
rimport.configure_logging(logging.INFO)
100-
rimport.logger.info("Simple message")
87+
shared.configure_logging(logger, logging.INFO)
88+
logger.info("Simple message")
10189

10290
captured = capsys.readouterr()
10391
output = captured.out.strip()
@@ -106,34 +94,34 @@ def test_formatter_uses_message_only(self, capsys):
10694

10795
def test_multiple_calls_dont_duplicate_handlers(self):
10896
"""Test that calling configure_logging multiple times doesn't duplicate handlers."""
109-
rimport.configure_logging(logging.INFO)
110-
assert len(rimport.logger.handlers) == 2
97+
shared.configure_logging(logger, logging.INFO)
98+
assert len(logger.handlers) == 2
11199

112-
rimport.configure_logging(logging.INFO)
113-
assert len(rimport.logger.handlers) == 2 # Still 2, not 4
100+
shared.configure_logging(logger, logging.INFO)
101+
assert len(logger.handlers) == 2 # Still 2, not 4
114102

115-
rimport.configure_logging(logging.INFO)
116-
assert len(rimport.logger.handlers) == 2 # Still 2, not 6
103+
shared.configure_logging(logger, logging.INFO)
104+
assert len(logger.handlers) == 2 # Still 2, not 6
117105

118106
def test_configure_with_debug_level(self, capsys):
119107
"""Test that configure_logging accepts DEBUG level."""
120-
rimport.configure_logging(logging.DEBUG)
108+
shared.configure_logging(logger, logging.DEBUG)
121109

122110
# DEBUG messages should now be logged
123-
rimport.logger.debug("Debug message")
111+
logger.debug("Debug message")
124112

125113
captured = capsys.readouterr()
126114
assert "Debug message" in captured.out
127115
assert captured.err == ""
128116

129117
def test_configure_with_warning_level(self, capsys):
130118
"""Test that configure_logging accepts WARNING level."""
131-
rimport.configure_logging(logging.WARNING)
119+
shared.configure_logging(logger, logging.WARNING)
132120

133121
# INFO messages should be suppressed
134-
rimport.logger.info("Info message")
122+
logger.info("Info message")
135123
# WARNING messages should be logged
136-
rimport.logger.warning("Warning message")
124+
logger.warning("Warning message")
137125

138126
captured = capsys.readouterr()
139127
assert "Info message" not in captured.out
@@ -142,10 +130,10 @@ def test_configure_with_warning_level(self, capsys):
142130

143131
def test_configure_with_info_level_suppresses_debug(self, capsys):
144132
"""Test that INFO level suppresses DEBUG messages."""
145-
rimport.configure_logging(logging.INFO)
133+
shared.configure_logging(logger, logging.INFO)
146134

147-
rimport.logger.debug("Debug message")
148-
rimport.logger.info("Info message")
135+
logger.debug("Debug message")
136+
logger.info("Info message")
149137

150138
captured = capsys.readouterr()
151139
assert "Debug message" not in captured.out

0 commit comments

Comments
 (0)