Coverage for bugscpp/message/message.py : 93%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import logging
2import tempfile
3from collections.abc import MutableMapping
4from logging.handlers import RotatingFileHandler
5from os.path import dirname
6from pathlib import Path
7from pkgutil import iter_modules
8from textwrap import TextWrapper, dedent, fill, indent
9from typing import Dict, Optional, Tuple
11from colorama import Fore, init
13init()
16class _MessageWhiteList(logging.Filter):
17 _modules: Optional[Tuple[str]] = None
19 def filter(self, record):
20 if not _MessageWhiteList._modules:
21 pkgpath = dirname(dirname(__file__))
22 modules = tuple(name for _, name, _ in iter_modules([pkgpath]))
23 _MessageWhiteList._modules = (
24 tuple(
25 "__main__",
26 )
27 + modules
28 )
30 return record.name.startswith(_MessageWhiteList._modules)
33class _MessageConfig(MutableMapping):
34 def __init__(self, *args, **kwargs):
35 self._store: Dict = dict(*args, **kwargs)
37 def __getitem__(self, key: str):
38 return self._store[self._keytransform(key)]
40 def __setitem__(self, key: str, value):
41 self._store[self._keytransform(key)] = value
42 # (python 3.8) 'force' option should be preferred.
43 logging.root.handlers = []
44 logging.basicConfig(**self._store)
46 def __delitem__(self, key: str):
47 del self._store[self._keytransform(key)]
49 def __iter__(self):
50 return iter(self._store)
52 def __len__(self):
53 return len(self._store)
55 def _keytransform(self, key: str):
56 return key
59class _Message:
60 def __init__(self):
61 temp = Path(tempfile.gettempdir()) / "defects4cpp.log"
62 self._config = _MessageConfig(
63 {
64 "format": "%(levelname)s[%(name)s]: %(message)s",
65 "level": logging.INFO,
66 "handlers": [self._create_file_handler(temp)],
67 # "force": True, # (python 3.8) Some third party libraries might have touched it.
68 }
69 )
70 logging.root.handlers = []
71 logging.basicConfig(**self._config)
72 self._wrapper = TextWrapper()
74 # Disable information about where calls were made from.
75 logging._srcfile = None
76 # Disable threading information.
77 logging.logThreads = 0
78 # Disable process information.
79 logging.logProcesses = 0
81 @property
82 def path(self) -> str:
83 return logging.getLoggerClass().root.handlers[0].baseFilename
85 @path.setter
86 def path(self, path: Path):
87 if path.is_dir():
88 path /= "defects4cpp.log"
89 self._config["handlers"] = [self._create_file_handler(path)]
91 @staticmethod
92 def _create_file_handler(path: Path) -> RotatingFileHandler:
93 handler = RotatingFileHandler(path, maxBytes=100_000, backupCount=5)
94 handler.addFilter(_MessageWhiteList())
95 return handler
97 @staticmethod
98 def critical(module_name: str, msg: str):
99 logging.getLogger(module_name).critical(msg)
101 @staticmethod
102 def error(module_name: str, msg: str):
103 logging.getLogger(module_name).error(msg)
105 @staticmethod
106 def warning(module_name: str, msg: str):
107 logging.getLogger(module_name).warning(msg)
109 @staticmethod
110 def debug(module_name: str, msg: str):
111 logging.getLogger(module_name).debug(msg)
113 @staticmethod
114 def info(module_name: str, msg: str):
115 logging.getLogger(module_name).info(msg)
117 @staticmethod
118 def stdout_title(msg: str):
119 print(f"{Fore.BLUE}{msg}{Fore.RESET}")
121 def stdout_bullet(self, msg: str):
122 print(
123 f"{Fore.GREEN}"
124 f"{fill(dedent(msg), initial_indent='* ', subsequent_indent=' ', width=self._wrapper.width)}"
125 f"{Fore.RESET}"
126 )
128 def stdout_paragraph(self, msg: str):
129 print(
130 f"{Fore.WHITE}"
131 f"{indent(self._wrapper.fill(msg), prefix=' ', predicate=lambda line: True)}"
132 f"{Fore.RESET}"
133 )
135 @staticmethod
136 def stdout_progress(msg: str):
137 print(f"{Fore.CYAN}{msg}{Fore.RESET}")
139 @staticmethod
140 def stdout_progress_detail(msg: str):
141 print(f" {Fore.GREEN}{msg}{Fore.RESET}")
143 @staticmethod
144 def stdout_progress_error(msg: str):
145 print(f"{Fore.RED}{msg}{Fore.RESET}")
147 @staticmethod
148 def stdout_argparse_error(msg: str):
149 print(f"{Fore.YELLOW}{msg}{Fore.RESET}")
151 @staticmethod
152 def stdout_search_error(msg: str):
153 print(f"{Fore.YELLOW}{msg}{Fore.RESET}")
155 @staticmethod
156 def stdout_error(msg: str):
157 print(f"{Fore.RED}{msg}{Fore.RESET}")
159 @staticmethod
160 def stdout_stream(msg: str):
161 print(f"{Fore.LIGHTBLUE_EX}{msg}{Fore.RESET}", end="")
164message = _Message()