Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add level option to setup_logging #116

Merged
merged 21 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Changelog
=========

* Replace Boolean ``debug`` option in ``setup_logging`` by more flexible integer
``level`` parameter.

v0.1.29 (2024-03-21)
------------------------------------------
Expand Down
15 changes: 11 additions & 4 deletions src/mdacli/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2021 Authors and contributors
# Copyright (c) 2024 Authors and contributors
#
# Released under the GNU Public Licence, v2 or any higher version
# SPDX-License-Identifier: GPL-2.0-or-later
Expand Down Expand Up @@ -104,13 +104,20 @@ def cli(name,

args = ap.parse_args()

# Set the logging level based on the verbose argument
# If verbose is not an argument, default to WARNING
if not hasattr(args, "verbose") or not args.verbose:
level = logging.WARNING
else:
level = logging.INFO

if args.debug:
args.verbose = True
level = logging.DEBUG
else:
# Ignore all warnings if not in debug mode
# Ignore all warnings if not in debug mode, because MDA is noisy
warnings.filterwarnings("ignore")

with setup_logging(logger, logfile=args.logfile, debug=args.debug):
with setup_logging(logger, logfile=args.logfile, level=level):
# Execute the main client interface.
try:
analysis_callable = args.analysis_callable
Expand Down
23 changes: 12 additions & 11 deletions src/mdacli/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


@contextlib.contextmanager
def setup_logging(logobj, logfile=None, debug=False):
def setup_logging(logobj, logfile=None, level=logging.WARNING):
"""
Create a logging environment for a given logobj.

Expand All @@ -25,19 +25,20 @@ def setup_logging(logobj, logfile=None, debug=False):
A logging instance
logfile : str
Name of the log file
debug : bool
If ``True`` detailed debug logs inludcing filename and function name
are displayed. If ``False`` only the message logged from
errors, warnings and infos will be displayed.
level : int
Set the root logger level to the specified level. If for example set
to :py:obj:`logging.DEBUG` detailed debug logs inludcing filename and
function name are displayed. For :py:obj:`logging.INFO only the message
logged from errors, warnings and infos will be displayed.
"""
try:
format = '{message}'
if debug:
format = "[{levelname}] {filename}:{name}:{funcName}:{lineno}: " \
+ format
level = logging.DEBUG
if level == logging.DEBUG:
format = (
"[{levelname}] {filename}:{name}:{funcName}:{lineno}: "
"{message}"
)
else:
level = logging.INFO
format = "{message}"

logging.basicConfig(format=format,
handlers=[logging.StreamHandler(sys.stdout)],
Expand Down
16 changes: 16 additions & 0 deletions tests/run_tester
hejamu marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2024 Authors and contributors
#
# Released under the GNU Public Licence, v2 or any higher version
# SPDX-License-Identifier: GPL-2.0-or-later
import re
import sys

from tester.__main__ import main


if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
71 changes: 63 additions & 8 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,38 @@
"""Test mdacli cli."""

import subprocess
import sys
from pathlib import Path

import pytest
from MDAnalysisTests.datafiles import TPR, XTC


tester_class = (Path('.').absolute() / 'tests/run_tester').as_posix()


def test_required_args():
"""Test that there is a module given."""
with pytest.raises(subprocess.CalledProcessError):
subprocess.check_call(['mda'])
subprocess.check_call(["mda"])


def test_wrong_module():
"""Test for a non existent module."""
with pytest.raises(subprocess.CalledProcessError):
subprocess.check_call(['mda', 'foo'])
subprocess.check_call(["mda", "foo"])


@pytest.mark.parametrize('args', ("version", "debug", "help"))
@pytest.mark.parametrize("args", ("version", "debug", "help"))
def test_extra_options(args):
"""Test for a ab extra option."""
subprocess.check_call(['mda', '--' + args])
subprocess.check_call(["mda", "--" + args])


@pytest.mark.parametrize('args', ("RMSF", "rmsf"))
@pytest.mark.parametrize("args", ("RMSF", "rmsf"))
def test_case_insensitive(args):
"""Test for beeing case insensitive."""
subprocess.check_call(['mda', args, "-h"])
"""Test for being case insensitive."""
subprocess.check_call(["mda", args, "-h"])


@pytest.mark.parametrize('args', ("RMSF", "rmsf"))
Expand All @@ -48,4 +53,54 @@ def test_running_analysis(tmpdir):
"""Test running a complete analysis."""
with tmpdir.as_cwd():
subprocess.check_call(
['mda', "rmsf", "-s", TPR, "-f", XTC, "-atomgroup", "all"])
["mda", "rmsf", "-s", TPR, "-f", XTC, "-atomgroup", "all"]
)


def test_verbosity_level_warning(caplog):
"""Test the log level warning."""
# This should only print warning messages
output = subprocess.check_output(
[sys.executable, tester_class,
"tester", "-s", TPR, "-f", XTC, "-atomgroup", "all"],
text=True,
)
assert "This is a warning" in output
# Cross-check that info and debug messages are not printed
assert "This is a debug message" not in output
assert "This is an info message" not in output


def test_verbosity_level_info(caplog):
"""Test the log level info."""
# This should only print warning and info messages
output = subprocess.check_output(
[
sys.executable, tester_class,
"tester", "-s", TPR, "-f", XTC,
"-atomgroup", "all",
"-v",
],
text=True,
)
assert "This is an info message" in output
assert "This is a warning" in output
# Cross-check that debug messages are not printed
assert "This is a debug message" not in output


def test_verbosity_level_debug(caplog):
"""Test the log level debug."""
# This should print all messages
output = subprocess.check_output(
[
sys.executable, tester_class, "--debug",
"tester", "-s", TPR, "-f", XTC,
"-atomgroup", "all",
"-v",
],
text=True,
)
assert "This is an info message" in output
assert "This is a warning" in output
assert "This is a debug message" in output
8 changes: 4 additions & 4 deletions tests/test_logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2021 Authors and contributors
# Copyright (c) 2024 Authors and contributors
#
# Released under the GNU Public Licence, v2 or any higher version
# SPDX-License-Identifier: GPL-2.0-or-later
Expand All @@ -20,7 +20,7 @@ def test_default_log(self, caplog):
logger = logging.getLogger("test")
with mdacli.logger.setup_logging(logger,
logfile=None,
debug=False):
level=logging.INFO):
logger.info("foo")
assert "foo" in caplog.text

Expand All @@ -33,7 +33,7 @@ def test_info_log(self, tmpdir, caplog):
# is created by the function.
with mdacli.logger.setup_logging(logger,
logfile="logfile",
debug=False):
level=logging.INFO):
logger.info("foo")
assert "foo" in caplog.text
with open("logfile.log", "r") as f:
Expand All @@ -48,7 +48,7 @@ def test_debug_log(self, tmpdir, caplog):
with tmpdir.as_cwd():
with mdacli.logger.setup_logging(logger,
logfile="logfile",
debug=True):
level=logging.DEBUG):
logger.info("foo")
assert "test:test_logger.py:52 foo\n" in caplog.text

Expand Down
10 changes: 10 additions & 0 deletions tests/tester/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2024 Authors and contributors
#
# Released under the GNU Public Licence, v2 or any higher version
# SPDX-License-Identifier: GPL-2.0-or-later
"""Make the tester module available to mdacli."""

from .tester_class import Tester # noqa
29 changes: 29 additions & 0 deletions tests/tester/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2024 Authors and contributors
# (see the AUTHORS.rst file for the full list of names)
#
# Released under the GNU Public Licence, v3 or any higher version
# SPDX-License-Identifier: GPL-3.0-or-later
"""Test module for mdacli."""
hejamu marked this conversation as resolved.
Show resolved Hide resolved

from MDAnalysis.analysis.base import AnalysisBase

from mdacli import cli


def main():
"""Execute main CLI entry point."""
cli(
name="Tester",
module_list=["tester"],
base_class=AnalysisBase,
version=0.0,
description="test",
ignore_warnings=True,
)


if __name__ == "__main__":
main()
46 changes: 46 additions & 0 deletions tests/tester/tester_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; coding:utf-8 -*-
#
# Copyright (c) 2024 Authors and contributors
#
# Released under the GNU Public Licence, v2 or any higher version
# SPDX-License-Identifier: GPL-2.0-or-later
"""Mock module for mdacli to test logging."""

import logging

from MDAnalysis.analysis.base import AnalysisBase


logger = logging.getLogger(__name__)


class Tester(AnalysisBase):
"""Mock class for mdacli. Implements only the minimum requirements.

Currently only logs messages at different levels to check the verbosity and
debug flags in the CLI.

Parameters
----------
atomgroup : AtomGroup or Universe
"""

def __init__(self, atomgroup, **kwargs):
"""Initialise the Tester class."""
super(Tester, self).__init__(atomgroup.universe.trajectory, **kwargs)
logger.info("This is an info message")
logger.warn("This is a warning")
logger.debug("This is a debug message")

def _prepare(self):
"""Prepare the analysis."""
pass

def _single_frame(self):
"""Analyse a single frame."""
pass

def _conclude(self):
"""Conclude the analysis."""
pass
Loading