Passed
Push — main ( cdf0f7...3de8e8 )
by Douglas
01:40
created

mandos.MandosLogging._add_path_logger()   B

Complexity

Conditions 6

Size

Total Lines 17
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 17
nop 2
dl 0
loc 17
rs 8.6166
c 0
b 0
f 0
1
"""
2
Metadata for this project.
3
"""
4
5
import logging
6
import re
7
import sys
8
from importlib.metadata import PackageNotFoundError
0 ignored issues
show
introduced by
Unable to import 'importlib.metadata'
Loading history...
Bug introduced by
The name metadata does not seem to exist in module importlib.
Loading history...
9
from importlib.metadata import metadata as __load
0 ignored issues
show
introduced by
Unable to import 'importlib.metadata'
Loading history...
Bug introduced by
The name metadata does not seem to exist in module importlib.
Loading history...
10
from pathlib import Path
11
from typing import Optional
12
13
from loguru import logger
0 ignored issues
show
introduced by
Unable to import 'loguru'
Loading history...
14
15
pkg = "mandos"
0 ignored issues
show
Coding Style Naming introduced by
Constant name "pkg" doesn't conform to UPPER_CASE naming style ('([^\\W\\da-z][^\\Wa-z]*|__.*__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
16
_metadata = None
0 ignored issues
show
Coding Style Naming introduced by
Constant name "_metadata" doesn't conform to UPPER_CASE naming style ('([^\\W\\da-z][^\\Wa-z]*|__.*__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
17
try:
18
    _metadata = __load(Path(__file__).absolute().parent.name)
19
    __status__ = "Development"
20
    __copyright__ = "Copyright 2020–2021"
21
    __date__ = "2020-08-14"
22
    __uri__ = _metadata["home-page"]
23
    __title__ = _metadata["name"]
24
    __summary__ = _metadata["summary"]
25
    __license__ = _metadata["license"]
26
    __version__ = _metadata["version"]
27
    __author__ = _metadata["author"]
28
    __maintainer__ = _metadata["maintainer"]
29
    __contact__ = _metadata["maintainer"]
30
except PackageNotFoundError:  # pragma: no cover
31
    logger.error(f"Could not load package metadata for {pkg}. Is it installed?")
32
33
34
class MandosMetadata:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
35
    version = __version__
0 ignored issues
show
introduced by
The variable __version__ does not seem to be defined for all execution paths.
Loading history...
36
37
38
class InterceptHandler(logging.Handler):
39
    """
40
    Redirects standard logging to loguru.
41
    """
42
43
    def emit(self, record):
44
        # Get corresponding Loguru level if it exists
45
        try:
46
            level = logger.level(record.levelname).name
47
        except ValueError:
48
            level = record.levelno
49
        # Find caller from where originated the logged message
50
        frame, depth = logging.currentframe(), 2
51
        while frame.f_code.co_filename == logging.__file__:
52
            frame = frame.f_back
53
            depth += 1
54
        logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
55
56
57
def _notice(__message: str, *args, **kwargs):
58
    return logger.log("NOTICE", __message, *args, **kwargs)
59
60
61
class MandosLogging:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
62
    # this is required for mandos to run
63
    try:
64
        logger.level("NOTICE", no=35)
65
    except TypeError:
66
        # this happens if it's already been added (e.g. from an outside library)
67
        # if we don't have it set after this, we'll find out soon enough
68
        logger.debug("Could not add 'NOTICE' loguru level. Did you already set it?")
69
    logger.notice = _notice
70
71
    @classmethod
72
    def init(cls) -> None:
73
        """
74
        Sets an initial configuration.
75
        """
76
        cls.redirect_std_logging()
77
        cls.set_log_level("INFO", None)
78
79
    @classmethod
80
    def redirect_std_logging(cls, level: int = 10) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
81
        # 10 b/c we're really never going to want trace output
82
        logging.basicConfig(handlers=[InterceptHandler()], level=level)
83
84
    @classmethod
85
    def set_log_level(cls, level: str, path: Optional[Path]) -> None:
86
        """
87
        This function will control all aspects of the logging as set via command-line.
88
89
        Args:
90
            level: The level to use for output to stderr
91
            path: If set, the path to a file. Can be prefixed with ``:level:`` to set the level
92
                  (e.g. ``:INFO:mandos-run.log.gz``). Can serialize to JSON if .json is used
93
                  instead of .log.
94
        """
95
        logger.remove()
96
        logger.add(sys.stderr, level=level)
97
        cls._add_path_logger(path)
98
99
    @classmethod
100
    def _add_path_logger(cls, path: Path) -> None:
101
        if path is None:
102
            return
103
        match = re.compile(r"(?:[A-Z]+:)??(.*)").match(str(path))
104
        level = "DEBUG" if match.group(1) is None else match.group(1)
0 ignored issues
show
Unused Code introduced by
The variable level seems to be unused.
Loading history...
105
        path = Path(match.group(2))
106
        for e, c in dict(gz="gzip", zip="zip", bz2="bzip2", xz="xz"):
0 ignored issues
show
Coding Style Naming introduced by
Variable name "c" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Variable name "e" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
107
            if str(path).endswith("." + e):
108
                serialize = True if path.suffix == f".json.{e}" else False
0 ignored issues
show
Unused Code introduced by
The if expression can be replaced with 'test'
Loading history...
109
                logger.add(
110
                    str(path),
111
                    level="DEBUG",
112
                    compression=c,
113
                    serialize=serialize,
114
                    backtrace=True,
115
                    diagnose=True,
116
                )
117
118
119
if __name__ == "__main__":  # pragma: no cover
120
    if _metadata is not None:
121
        print(f"{pkg} (v{_metadata['version']})")
122
    else:
123
        print("Unknown project info")
124
125
126
__all__ = ["MandosMetadata", "logger", "MandosLogging"]
127