Skip to content

Commit b37b261

Browse files
authored
Changes proposed in #885. Don't register handlers by default. (#889)
* Changes proposed in #885. Don't register handlers by default. * Delay file creation until log emit. Correctly read from config. * Remove loading/storing log level references. * _create_log_handlers now returns early if called a second time * Fix type errors. * Update changelog. * Test remove register file log handler to see if CI works. * Undo last change. test server ssl works agian. * Bump scikit-learn version to 0.22 * Scikit-learn 0.22 does not install properly. * Install scikit-learn through pip instead.
1 parent 371911f commit b37b261

3 files changed

Lines changed: 58 additions & 28 deletions

File tree

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ install:
3535
# Install the build and runtime dependencies of the project.
3636
- "cd C:\\projects\\openml-python"
3737
- "pip install .[examples,test]"
38-
- conda install --quiet --yes scikit-learn=0.20.0
38+
- "pip install scikit-learn==0.21"
3939

4040

4141
# Not a .NET project, we build scikit-learn in the install step instead

doc/progress.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Changelog
1111

1212
* FIX #873: Fixes an issue which resulted in incorrect URLs when printing OpenML objects after
1313
switching the server
14+
* FIX #885: Logger no longer registered by default. Added utility functions to easily register
15+
logging to console and file.
1416
* MAINT #767: Source distribution installation is now unit-tested.
1517
* MAINT #865: OpenML no longer bundles test files in the source distribution.
1618

openml/config.py

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,79 @@
77
import logging
88
import logging.handlers
99
import os
10-
from typing import cast
10+
from typing import Tuple, cast
1111

1212
from io import StringIO
1313
import configparser
1414
from urllib.parse import urlparse
1515

1616
logger = logging.getLogger(__name__)
17+
openml_logger = logging.getLogger('openml')
18+
console_handler = None
19+
file_handler = None
1720

1821

19-
def configure_logging(console_output_level: int, file_output_level: int):
20-
""" Sets the OpenML logger to DEBUG, with attached Stream- and FileHandler. """
21-
# Verbosity levels as defined (https://github.com/openml/OpenML/wiki/Client-API-Standards)
22-
# don't match Python values directly:
23-
verbosity_map = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
22+
def _create_log_handlers():
23+
""" Creates but does not attach the log handlers. """
24+
global console_handler, file_handler
25+
if console_handler is not None or file_handler is not None:
26+
logger.debug("Requested to create log handlers, but they are already created.")
27+
return
2428

25-
openml_logger = logging.getLogger('openml')
26-
openml_logger.setLevel(logging.DEBUG)
2729
message_format = '[%(levelname)s] [%(asctime)s:%(name)s] %(message)s'
2830
output_formatter = logging.Formatter(message_format, datefmt='%H:%M:%S')
2931

30-
console_stream = logging.StreamHandler()
31-
console_stream.setFormatter(output_formatter)
32-
console_stream.setLevel(verbosity_map[console_output_level])
32+
console_handler = logging.StreamHandler()
33+
console_handler.setFormatter(output_formatter)
3334

34-
one_mb = 2**20
35+
one_mb = 2 ** 20
3536
log_path = os.path.join(cache_directory, 'openml_python.log')
36-
file_stream = logging.handlers.RotatingFileHandler(log_path, maxBytes=one_mb, backupCount=1)
37-
file_stream.setLevel(verbosity_map[file_output_level])
38-
file_stream.setFormatter(output_formatter)
37+
file_handler = logging.handlers.RotatingFileHandler(
38+
log_path, maxBytes=one_mb, backupCount=1, delay=True
39+
)
40+
file_handler.setFormatter(output_formatter)
3941

40-
openml_logger.addHandler(console_stream)
41-
openml_logger.addHandler(file_stream)
42-
return console_stream, file_stream
42+
43+
def _convert_log_levels(log_level: int) -> Tuple[int, int]:
44+
""" Converts a log level that's either defined by OpenML/Python to both specifications. """
45+
# OpenML verbosity level don't match Python values directly:
46+
openml_to_python = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
47+
python_to_openml = {logging.DEBUG: 2, logging.INFO: 1, logging.WARNING: 0,
48+
logging.CRITICAL: 0, logging.ERROR: 0}
49+
# Because the dictionaries share no keys, we use `get` to convert as necessary:
50+
openml_level = python_to_openml.get(log_level, log_level)
51+
python_level = openml_to_python.get(log_level, log_level)
52+
return openml_level, python_level
53+
54+
55+
def _set_level_register_and_store(handler: logging.Handler, log_level: int):
56+
""" Set handler log level, register it if needed, save setting to config file if specified. """
57+
oml_level, py_level = _convert_log_levels(log_level)
58+
handler.setLevel(py_level)
59+
60+
if openml_logger.level > py_level or openml_logger.level == logging.NOTSET:
61+
openml_logger.setLevel(py_level)
62+
63+
if handler not in openml_logger.handlers:
64+
openml_logger.addHandler(handler)
65+
66+
67+
def set_console_log_level(console_output_level: int):
68+
""" Set console output to the desired level and register it with openml logger if needed. """
69+
global console_handler
70+
_set_level_register_and_store(cast(logging.Handler, console_handler), console_output_level)
71+
72+
73+
def set_file_log_level(file_output_level: int):
74+
""" Set file output to the desired level and register it with openml logger if needed. """
75+
global file_handler
76+
_set_level_register_and_store(cast(logging.Handler, file_handler), file_output_level)
4377

4478

4579
# Default values (see also https://github.com/openml/OpenML/wiki/Client-API-Standards)
4680
_defaults = {
4781
'apikey': None,
4882
'server': "https://www.openml.org/api/v1/xml",
49-
'verbosity': 0, # WARNING
50-
'file_verbosity': 2, # DEBUG
5183
'cachedir': os.path.expanduser(os.path.join('~', '.openml', 'cache')),
5284
'avoid_duplicate_runs': 'True',
5385
'connection_n_retries': 2,
@@ -176,9 +208,7 @@ def _setup():
176208

177209

178210
def _parse_config():
179-
"""Parse the config file, set up defaults.
180-
"""
181-
211+
""" Parse the config file, set up defaults. """
182212
config = configparser.RawConfigParser(defaults=_defaults)
183213

184214
if not os.path.exists(config_file):
@@ -189,6 +219,7 @@ def _parse_config():
189219
"create an empty file there." % config_file)
190220

191221
try:
222+
# The ConfigParser requires a [SECTION_HEADER], which we do not expect in our config file.
192223
# Cheat the ConfigParser module by adding a fake section header
193224
config_file_ = StringIO()
194225
config_file_.write("[FAKE_SECTION]\n")
@@ -255,7 +286,4 @@ def set_cache_directory(cachedir):
255286
]
256287

257288
_setup()
258-
259-
_console_log_level = cast(int, _defaults['verbosity'])
260-
_file_log_level = cast(int, _defaults['file_verbosity'])
261-
console_log, file_log = configure_logging(_console_log_level, _file_log_level)
289+
_create_log_handlers()

0 commit comments

Comments
 (0)