Skip to content

Commit

Permalink
Merge pull request #9 from databio/dev
Browse files Browse the repository at this point in the history
reduce message  width
  • Loading branch information
nsheff authored Jun 4, 2019
2 parents a7d11aa + f83edfd commit 951a07c
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 44 deletions.
15 changes: 11 additions & 4 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# Changelog

## *v0.1* (2019-04-30):
## [0.2.0] -- (2019-06-02)

### Changed
- Deprecated `setup_logger` in favor of `init_logger`
- Changed argparser help interface to fit each arg on one line

## [0.1.0] -- (2019-04-30)

### Added
- Parameter to `setup_logger` to pass argument to `style` parameter of `logging.Formatter`

## *v0.0.2* (2019-04-14):
## [0.0.2] -- (2019-04-14)
### Changed
- Lessen level of some messages
### Fixed
- Avoid erroneous missing-option exception when adding standard logging options

## *v0.0.1* (2019-04-09):
## [0.0.1] -- (2019-04-09)
### Fixed
- Fixed a bug preventing installation

## *v0.0* (2019-04-08):
## [0.0.0] -- (2019-04-08):
- Initial release

3 changes: 1 addition & 2 deletions logmuse/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
__version__ = "0.1"

__version__ = "0.2.0"
43 changes: 30 additions & 13 deletions logmuse/est.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Project configuration, particularly for logging.
"""Project logging configuration.
Project-scope constants may reside here, but more importantly, some setup here
will provide a logging infrastructure for all of the project's modules.
Expand All @@ -10,12 +10,14 @@
import logging
import os
import sys
import warnings
from ._version import __version__

__author__ = "Vince Reuter"
__email__ = "[email protected]"

__all__ = ["add_logging_options", "logger_via_cli", "setup_logger", "AbsentOptionException"]
__all__ = ["add_logging_options", "logger_via_cli", "init_logger",
"setup_logger", "AbsentOptionException"]


BASIC_LOGGING_FORMAT = "%(message)s"
Expand All @@ -38,17 +40,23 @@
# in logging level, making verbosity a more intuitive specification mechanism.
_WARN_REPR = "WARN"
LEVEL_BY_VERBOSITY = ["CRITICAL", "ERROR", _WARN_REPR, "INFO", "DEBUG"]
_MIN_VERBOSITY = 1
_MAX_VERBOSITY = len(LEVEL_BY_VERBOSITY)
_VERBOSITY_CHOICES = [str(x) for x in
range(_MIN_VERBOSITY, len(LEVEL_BY_VERBOSITY) + 1)] + \
LEVEL_BY_VERBOSITY + ["WARNING"]

LOGGING_CLI_OPTDATA = {
SILENCE_LOGS_OPTNAME: {
"action": "store_true", "help": "Silence logging"},
"action": "store_true",
"help": "Silence logging. Overrides {}.".format(VERBOSITY_OPTNAME)},
VERBOSITY_OPTNAME: {
"help": "Relative measure of interest in logs; this can be an "
"integer in [0, 5], or a Python builtin logging name)"},
"metavar": "V", "choices": _VERBOSITY_CHOICES,
"help": "Set logging level ({}-{} or logging module level name)".
format(_MIN_VERBOSITY, len(LEVEL_BY_VERBOSITY))},
DEVMODE_OPTNAME: {
"action": "store_true",
"help": "Handle logging in development mode; perhaps among other "
"facets, make the format more information-rich."}
"help": "Expand content of logging message format."}
}


Expand Down Expand Up @@ -98,10 +106,10 @@ def logger_via_cli(opts, **kwargs):
# between the CLI version and the logger setup signature).
logs_cli_args[PARAM_BY_OPTNAME.get(optname, name)] = optval
logs_cli_args.update(kwargs)
return setup_logger(**logs_cli_args)
return init_logger(**logs_cli_args)


def setup_logger(
def init_logger(
name="", level=None, stream=None, logfile=None,
make_root=None, propagate=False, silent=False, devmode=False,
verbosity=None, fmt=None, datefmt=None, plain_format=False, style=None):
Expand Down Expand Up @@ -252,6 +260,18 @@ def setup_logger(
return logger


def setup_logger(
name="", level=None, stream=None, logfile=None,
make_root=None, propagate=False, silent=False, devmode=False,
verbosity=None, fmt=None, datefmt=None, plain_format=False, style=None):
""" Old alias for init_logger for backwards compatibility """
warnings.warn("Please use init_logger in place of setup_logger",
DeprecationWarning)
return init_logger(
name, level, stream, logfile, make_root, propagate,
silent, devmode, verbosity, fmt, datefmt, plain_format, style)


def _level_from_verbosity(verbosity):
"""
Translation of verbosity into logging level.
Expand Down Expand Up @@ -279,10 +299,7 @@ def _level_from_verbosity(verbosity):
"{}".format(verbosity, ", ".join(LEVEL_BY_VERBOSITY)))
return getattr(logging, v)
elif isinstance(verbosity, int):
# Allow negative value to mute even ERROR level but not CRITICAL.
# Also handle excessively high verbosity request.
v = min(max(verbosity, 0), len(LEVEL_BY_VERBOSITY) - 1)
return LEVEL_BY_VERBOSITY[v]
return LEVEL_BY_VERBOSITY[verbosity - 1] # 1-based user, 0-based internal
else:
raise TypeError("Verbosity must be string or int; got {} ({})"
.format(verbosity, type(verbosity)))
Expand Down
12 changes: 9 additions & 3 deletions tests/test_add_logging_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


def pytest_generate_tests(metafunc):
""" Generation and parameterization of tests in this module. """
if "opt" in metafunc.fixturenames:
metafunc.parametrize("opt", list(LOGGING_CLI_OPTDATA.keys()))

Expand Down Expand Up @@ -58,15 +59,20 @@ def _build_action_usage(act_kind):
"""
Determine function to create command chunks needed to test action.
:param type act_kind:
:param type act_kind: the type of option action (e.g. StoreTrueAction)
:return function(argparse._StoreAction) -> list[str]: function that when
given a CLI action will create the representative command line chunks
"""
from logmuse.est import _VERBOSITY_CHOICES, VERBOSITY_OPTNAME
def get_general_use(act):
name = _get_opt_first_name(act)
arg = random.choice(_VERBOSITY_CHOICES) \
if name == VERBOSITY_OPTNAME else _random_chars_option()
return [name, arg]
strategies = [
((argparse._StoreTrueAction, argparse._StoreFalseAction),
lambda a: [a.option_strings[0]]),
((argparse._StoreAction),
lambda a: [_get_opt_first_name(a), _random_chars_option()])
((argparse._StoreAction), get_general_use)
]
for kinds, strat in strategies:
if issubclass(act_kind, kinds):
Expand Down
18 changes: 9 additions & 9 deletions tests/test_basic_setup_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import string
import sys
import pytest
from logmuse import setup_logger
from logmuse import init_logger
from logmuse.est import DEFAULT_STREAM, LOGGING_LEVEL, PACKAGE_NAME

__author__ = "Vince Reuter"
Expand All @@ -28,7 +28,7 @@ def _random_filename():
("handlers", lambda h: _check_handler(h, loc=DEFAULT_STREAM))])
def test_all_defaults(attr, check):
""" Check the values on the logger that result from all default arguments. """
logger = setup_logger()
logger = init_logger()
if hasattr(check, "__call__"):
fails = list(itertools.chain(*[check(obj) for obj in getattr(logger, attr)]))
if fails:
Expand All @@ -51,20 +51,20 @@ def test_all_defaults(attr, check):
[("level", x) for x in range(0, 50, 10)])
def test_single_attr(att, val):
""" Test successful setting of a simple, particular logger attribute. """
assert val == getattr(setup_logger(**{att: val}), att)
assert val == getattr(init_logger(**{att: val}), att)


def test_make_non_root_name_root():
""" Non-root name for root logger is prohibited. """
with pytest.raises(ValueError):
setup_logger("root", make_root=False)
init_logger("root", make_root=False)


@pytest.mark.parametrize(["make_root", "exp"],
[(None, PACKAGE_NAME), (False, PACKAGE_NAME), (True, "root")])
def test_make_root(make_root, exp):
""" Root status for logger has a couple of implications. """
log = setup_logger(make_root=make_root)
log = init_logger(make_root=make_root)
assert exp == log.name
assert log.propagate is False

Expand All @@ -79,7 +79,7 @@ def test_make_root(make_root, exp):
({"make_root": True, "propagate": True}, True)])
def test_propagate(kwargs, exp):
""" Determination of propagation flag considers root status and propagation. """
assert setup_logger(**kwargs).propagate is exp
assert init_logger(**kwargs).propagate is exp


@pytest.mark.parametrize(
Expand All @@ -88,7 +88,7 @@ def test_propagate(kwargs, exp):
("not_a_real_stream", DEFAULT_STREAM)])
def test_stream(stream, exp):
""" Validate stream handler setting for created logger. """
log = setup_logger(stream=stream)
log = init_logger(stream=stream)
assert 1 == len(log.handlers)
h = _check_hdlr_kind(log, logging.StreamHandler)
assert exp == h.stream
Expand All @@ -98,7 +98,7 @@ def test_stream(stream, exp):
def test_logfile(tmpdir, filename):
""" Validate file handler setting for created logger. """
fp = tmpdir.join(filename).strpath
log = setup_logger(logfile=fp)
log = init_logger(logfile=fp)
assert 1 == len(log.handlers)
h = _check_hdlr_kind(log, logging.FileHandler)
assert fp == h.stream.name
Expand All @@ -109,7 +109,7 @@ def test_logfile(tmpdir, filename):
def test_logfile_and_stream(filename, stream, tmpdir):
""" Logging can be both stream and file. """
fp = tmpdir.join(filename).strpath
log = setup_logger(logfile=fp, stream=stream)
log = init_logger(logfile=fp, stream=stream)
assert 2 == len(log.handlers)
fh = _check_hdlr_kind(log, logging.FileHandler)
sh = _check_hdlr_kind(log, logging.StreamHandler, omit=logging.FileHandler)
Expand Down
24 changes: 11 additions & 13 deletions tests/test_logger_via_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import pytest
from logmuse import add_logging_options, logger_via_cli
from logmuse.est import AbsentOptionException, LEVEL_BY_VERBOSITY, \
LOGGING_CLI_OPTDATA, SILENCE_LOGS_OPTNAME, VERBOSITY_OPTNAME
LOGGING_CLI_OPTDATA, SILENCE_LOGS_OPTNAME, VERBOSITY_OPTNAME, \
_MIN_VERBOSITY, _MAX_VERBOSITY

__author__ = "Vince Reuter"
__email__ = "[email protected]"
Expand Down Expand Up @@ -58,37 +59,34 @@ def test_silence(parser, cmdl, flag, hdlr_type):
assert isinstance(hs[0], hdlr_type)


@pytest.mark.parametrize("verbosity", list(range(5)))
@pytest.mark.parametrize("verbosity", range(_MIN_VERBOSITY, _MAX_VERBOSITY + 1))
def test_typical_verbosity(parser, verbosity):
""" Typical verbosity specifications yield logger with expected level. """
opts = parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])
logger = logger_via_cli(opts)
exp = getattr(logging, LEVEL_BY_VERBOSITY[verbosity])
exp = getattr(logging, LEVEL_BY_VERBOSITY[verbosity - 1])
_assert_level(logger, exp)


@given(verbosity=st.integers(-sys.maxsize, -1))
def test_negative_verbosity(parser, verbosity):
""" Verbosity is pulled up to min logging level. """
opts = parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])
logger = logger_via_cli(opts)
_assert_level(logger, logging.CRITICAL)
with pytest.raises(SystemExit):
parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])


@given(verbosity=st.integers(len(LEVEL_BY_VERBOSITY), sys.maxsize))
@given(verbosity=st.integers(len(LEVEL_BY_VERBOSITY) + 1, sys.maxsize))
def test_excess_verbosity(parser, verbosity):
""" Verbosity saturates / maxes out. """
opts = parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])
logger = logger_via_cli(opts)
_assert_level(logger, logging.DEBUG)
with pytest.raises(SystemExit):
parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])


@pytest.mark.parametrize("verbosity", ["a", "NOTALEVEL", 2.5])
def test_invalid_verbosity_is_exceptional(parser, verbosity):
""" Verbosity must be a valid level name or an integer. """
opts = parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])
with pytest.raises(Exception):
logger_via_cli(opts)
with pytest.raises(SystemExit):
parser.parse_args([VERBOSITY_OPTNAME, str(verbosity)])


def _assert_level(log, lev):
Expand Down

0 comments on commit 951a07c

Please sign in to comment.