Skip to content

Commit

Permalink
Replace bespoke logging facility with logging module, available since…
Browse files Browse the repository at this point in the history
… Python 2.3.
  • Loading branch information
jaraco committed Oct 5, 2022
1 parent 45295fc commit 0a50760
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 242 deletions.
53 changes: 13 additions & 40 deletions conftest.py
Expand Up @@ -2,6 +2,7 @@
import sys
import platform
import pathlib
import logging

import pytest
import path
Expand Down Expand Up @@ -36,39 +37,20 @@ def needs_zlib():
pytest.importorskip('zlib')


# from jaraco.collections
class Everything:
def __contains__(self, other):
return True


class SavedLogs(list):
def render(self, *levels):
return [
msg % args for level, msg, args in self if level in (levels or Everything())
]


@pytest.fixture
def logs(monkeypatch):
from distutils import log

logs = SavedLogs()
log_levels = log.DEBUG, log.INFO, log.WARN, log.ERROR, log.FATAL

def _log(self, level, msg, args):
self.logs.append((level, msg, args))
@pytest.fixture(autouse=True)
def log_everything():
"""
For tests, set the level on the logger to log everything.
"""
logging.getLogger('distutils').setLevel(0)

def save_log(self, level, msg, args):
if level not in log_levels:
raise ValueError(f'invalid log level {level}')
if not isinstance(msg, str):
raise TypeError(f'msg should be str, not {type(msg).__name__!r}')
logs.append((level, msg, args))

monkeypatch.setattr(log.Log, '_log', save_log)
monkeypatch.setattr(log._global_log, 'threshold', log.FATAL)
return logs
@pytest.fixture(autouse=True)
def capture_log_at_info(caplog):
"""
By default, capture logs at INFO and greater.
"""
caplog.set_level(logging.INFO)


def _save_cwd():
Expand Down Expand Up @@ -111,15 +93,6 @@ def temp_cwd(tmp_path):
yield


@pytest.fixture
def threshold_warn():
from distutils.log import set_threshold, WARN

orig = set_threshold(WARN)
yield
set_threshold(orig)


@pytest.fixture
def pypirc(request, save_env, distutils_managed_tempdir):
from distutils.core import PyPIRCCommand
Expand Down
3 changes: 2 additions & 1 deletion distutils/cmd.py
Expand Up @@ -7,6 +7,7 @@
import sys
import os
import re
import logging

from .errors import DistutilsOptionError
from . import util, dir_util, file_util, archive_util, dep_util, log
Expand Down Expand Up @@ -179,7 +180,7 @@ def run(self):
"abstract method -- subclass %s must override" % self.__class__
)

def announce(self, msg, level=1):
def announce(self, msg, level=logging.INFO):
"""If the current verbosity level is of greater than or equal to
'level' print 'msg' to stdout.
"""
Expand Down
79 changes: 18 additions & 61 deletions distutils/log.py
@@ -1,80 +1,37 @@
"""A simple log mechanism styled after PEP 282."""
"""
A simple log mechanism styled after PEP 282.
# The class here is styled after PEP 282 so that it could later be
# replaced with a standard Python logging implementation.
Retained for compatibility and should not be used.
"""

import sys
import logging

DEBUG = 1
INFO = 2
WARN = 3
ERROR = 4
FATAL = 5

DEBUG = logging.DEBUG
INFO = logging.INFO
WARN = logging.WARN
ERROR = logging.ERROR
FATAL = logging.FATAL

class Log:
def __init__(self, threshold=WARN):
self.threshold = threshold

def _log(self, level, msg, args):
if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
raise ValueError('%s wrong log level' % str(level))

if level >= self.threshold:
if args:
msg = msg % args
if level in (WARN, ERROR, FATAL):
stream = sys.stderr
else:
stream = sys.stdout
try:
stream.write('%s\n' % msg)
except UnicodeEncodeError:
# emulate backslashreplace error handler
encoding = stream.encoding
msg = msg.encode(encoding, "backslashreplace").decode(encoding)
stream.write('%s\n' % msg)
stream.flush()

def log(self, level, msg, *args):
self._log(level, msg, args)

def debug(self, msg, *args):
self._log(DEBUG, msg, args)

def info(self, msg, *args):
self._log(INFO, msg, args)

def warn(self, msg, *args):
self._log(WARN, msg, args)

def error(self, msg, *args):
self._log(ERROR, msg, args)

def fatal(self, msg, *args):
self._log(FATAL, msg, args)


_global_log = Log()
_global_log = logging.getLogger('distutils')
log = _global_log.log
debug = _global_log.debug
info = _global_log.info
warn = _global_log.warn
warn = _global_log.warning
error = _global_log.error
fatal = _global_log.fatal


def set_threshold(level):
# return the old threshold for use from tests
old = _global_log.threshold
_global_log.threshold = level
return old
orig = _global_log.level
_global_log.setLevel(level)
return orig


def set_verbosity(v):
if v <= 0:
set_threshold(WARN)
set_threshold(logging.WARN)
elif v == 1:
set_threshold(INFO)
set_threshold(logging.INFO)
elif v >= 2:
set_threshold(DEBUG)
set_threshold(logging.DEBUG)
4 changes: 2 additions & 2 deletions distutils/tests/test_build_py.py
Expand Up @@ -150,7 +150,7 @@ def test_dir_in_package_data(self):
except DistutilsFileError:
self.fail("failed package_data when data dir includes a dir")

def test_dont_write_bytecode(self, logs):
def test_dont_write_bytecode(self, caplog):
# makes sure byte_compile is not used
dist = self.create_dist()[1]
cmd = build_py(dist)
Expand All @@ -164,7 +164,7 @@ def test_dont_write_bytecode(self, logs):
finally:
sys.dont_write_bytecode = old_dont_write_bytecode

assert 'byte-compiling is disabled' in logs.render()[0]
assert 'byte-compiling is disabled' in caplog.records[0].message

def test_namespace_package_does_not_warn(self, caplog):
"""
Expand Down
1 change: 0 additions & 1 deletion distutils/tests/test_config.py
Expand Up @@ -46,7 +46,6 @@


@support.combine_markers
@pytest.mark.usefixtures('threshold_warn')
@pytest.mark.usefixtures('pypirc')
class BasePyPIRCCommandTestCase(support.TempdirManager):
pass
Expand Down
24 changes: 11 additions & 13 deletions distutils/tests/test_dir_util.py
Expand Up @@ -26,21 +26,19 @@ def stuff(request, monkeypatch, distutils_managed_tempdir):


class TestDirUtil(support.TempdirManager):
def test_mkpath_remove_tree_verbosity(self, logs):

def test_mkpath_remove_tree_verbosity(self, caplog):
mkpath(self.target, verbose=0)
wanted = []
assert logs.render() == wanted
assert not caplog.records
remove_tree(self.root_target, verbose=0)

mkpath(self.target, verbose=1)
wanted = ['creating %s' % self.root_target, 'creating %s' % self.target]
assert logs.render() == wanted
logs.clear()
assert caplog.messages == wanted
caplog.clear()

remove_tree(self.root_target, verbose=1)
wanted = ["removing '%s' (and everything under it)" % self.root_target]
assert logs.render() == wanted
assert caplog.messages == wanted

@pytest.mark.skipif("platform.system() == 'Windows'")
def test_mkpath_with_custom_mode(self):
Expand All @@ -52,24 +50,24 @@ def test_mkpath_with_custom_mode(self):
mkpath(self.target2, 0o555)
assert stat.S_IMODE(os.stat(self.target2).st_mode) == 0o555 & ~umask

def test_create_tree_verbosity(self, logs):
def test_create_tree_verbosity(self, caplog):

create_tree(self.root_target, ['one', 'two', 'three'], verbose=0)
assert logs.render() == []
assert caplog.messages == []
remove_tree(self.root_target, verbose=0)

wanted = ['creating %s' % self.root_target]
create_tree(self.root_target, ['one', 'two', 'three'], verbose=1)
assert logs.render() == wanted
assert caplog.messages == wanted

remove_tree(self.root_target, verbose=0)

def test_copy_tree_verbosity(self, logs):
def test_copy_tree_verbosity(self, caplog):

mkpath(self.target, verbose=0)

copy_tree(self.target, self.target2, verbose=0)
assert logs.render() == []
assert caplog.messages == []

remove_tree(self.root_target, verbose=0)

Expand All @@ -80,7 +78,7 @@ def test_copy_tree_verbosity(self, logs):

wanted = ['copying {} -> {}'.format(a_file, self.target2)]
copy_tree(self.target, self.target2, verbose=1)
assert logs.render() == wanted
assert caplog.messages == wanted

remove_tree(self.root_target, verbose=0)
remove_tree(self.target2, verbose=0)
Expand Down
17 changes: 7 additions & 10 deletions distutils/tests/test_dist.py
Expand Up @@ -14,7 +14,6 @@
from distutils.cmd import Command

from distutils.tests import support
from distutils import log


pydistutils_cfg = '.' * (os.name == 'posix') + 'pydistutils.cfg'
Expand Down Expand Up @@ -236,7 +235,7 @@ def test_get_command_packages(self):
def test_announce(self):
# make sure the level is known
dist = Distribution()
with pytest.raises(ValueError):
with pytest.raises(TypeError):
dist.announce('ok', level='ok2')

def test_find_config_files_disable(self, temp_home):
Expand Down Expand Up @@ -367,15 +366,15 @@ def test_classifier(self):
meta = self.format_metadata(dist)
assert 'Metadata-Version: 1.1' in meta

def test_classifier_invalid_type(self, capsys):
def test_classifier_invalid_type(self, caplog):
attrs = {
'name': 'Boa',
'version': '3.0',
'classifiers': ('Programming Language :: Python :: 3',),
}
d = Distribution(attrs)
# should have warning about passing a non-list
assert 'should be a list' in capsys.readouterr().err
assert 'should be a list' in caplog.messages[0]
# should be converted to a list
assert isinstance(d.metadata.classifiers, list)
assert d.metadata.classifiers == list(attrs['classifiers'])
Expand All @@ -389,15 +388,15 @@ def test_keywords(self):
dist = Distribution(attrs)
assert dist.get_keywords() == ['spam', 'eggs', 'life of brian']

def test_keywords_invalid_type(self, capsys):
def test_keywords_invalid_type(self, caplog):
attrs = {
'name': 'Monty',
'version': '1.0',
'keywords': ('spam', 'eggs', 'life of brian'),
}
d = Distribution(attrs)
# should have warning about passing a non-list
assert 'should be a list' in capsys.readouterr().err
assert 'should be a list' in caplog.messages[0]
# should be converted to a list
assert isinstance(d.metadata.keywords, list)
assert d.metadata.keywords == list(attrs['keywords'])
Expand All @@ -411,15 +410,15 @@ def test_platforms(self):
dist = Distribution(attrs)
assert dist.get_platforms() == ['GNU/Linux', 'Some Evil Platform']

def test_platforms_invalid_types(self, capsys):
def test_platforms_invalid_types(self, caplog):
attrs = {
'name': 'Monty',
'version': '1.0',
'platforms': ('GNU/Linux', 'Some Evil Platform'),
}
d = Distribution(attrs)
# should have warning about passing a non-list
assert 'should be a list' in capsys.readouterr().err
assert 'should be a list' in caplog.messages[0]
# should be converted to a list
assert isinstance(d.metadata.platforms, list)
assert d.metadata.platforms == list(attrs['platforms'])
Expand Down Expand Up @@ -472,8 +471,6 @@ def test_fix_help_options(self):

def test_show_help(self, request, capsys):
# smoke test, just makes sure some help is displayed
reset_log = functools.partial(log.set_threshold, log._global_log.threshold)
request.addfinalizer(reset_log)
dist = Distribution()
sys.argv = []
dist.help = 1
Expand Down

0 comments on commit 0a50760

Please sign in to comment.