Move logging into its own file

This commit is contained in:
I-Al-Istannen 2020-04-25 19:59:58 +02:00
parent 80247400a4
commit 8a42a2a396
8 changed files with 157 additions and 117 deletions

View File

@ -5,33 +5,4 @@ This module exports only what you need for a basic configuration. If you want a
more complex configuration, you need to import the other submodules manually.
"""
import logging
from .pferd import Pferd
STYLE = "{"
FORMAT = "[{levelname:<7}] {message}"
DATE_FORMAT = "%F %T"
FORMATTER = logging.Formatter(
fmt=FORMAT,
datefmt=DATE_FORMAT,
style=STYLE,
)
def enable_logging(name: str = "PFERD", level: int = logging.INFO) -> None:
"""
Enable and configure logging via the logging module.
"""
handler = logging.StreamHandler()
handler.setFormatter(FORMATTER)
logger = logging.getLogger(name)
logger.setLevel(level)
logger.addHandler(handler)
# This should be logged by our own handler, and not the root logger's
# default handler, so we don't pass it on to the root logger.
logger.propagate = False

View File

@ -14,7 +14,8 @@ from urllib.parse import (parse_qs, urlencode, urljoin, urlparse, urlsplit,
import bs4
import requests
from ..utils import PrettyLogger, soupify
from ..logging import PrettyLogger
from ..utils import soupify
from .authenticators import IliasAuthenticator
from .date_demangler import demangle_date
from .downloader import IliasDownloadInfo

View File

@ -9,10 +9,11 @@ from typing import Callable, List, Optional
import bs4
import requests
from ..logging import PrettyLogger
from ..organizer import Organizer
from ..tmp_dir import TmpDir
from ..transform import Transformable
from ..utils import PrettyLogger, soupify, stream_to_path
from ..utils import soupify, stream_to_path
from .authenticators import IliasAuthenticator
LOGGER = logging.getLogger(__name__)

133
PFERD/logging.py Normal file
View File

@ -0,0 +1,133 @@
"""
Contains a few logger utility functions and implementations.
"""
import logging
from typing import Optional
import colorama
from colorama import Fore, Style
from .utils import PathLike, to_path
STYLE = "{"
FORMAT = "[{levelname:<7}] {message}"
DATE_FORMAT = "%F %T"
FORMATTER = logging.Formatter(
fmt=FORMAT,
datefmt=DATE_FORMAT,
style=STYLE,
)
def enable_logging(name: str = "PFERD", level: int = logging.INFO) -> None:
"""
Enable and configure logging via the logging module.
"""
handler = logging.StreamHandler()
handler.setFormatter(FORMATTER)
logger = logging.getLogger(name)
logger.setLevel(level)
logger.addHandler(handler)
# This should be logged by our own handler, and not the root logger's
# default handler, so we don't pass it on to the root logger.
logger.propagate = False
colorama.init()
class PrettyLogger:
"""
A logger that prints some specially formatted log messages in color.
"""
def __init__(self, logger: logging.Logger) -> None:
self.logger = logger
@staticmethod
def _format_path(path: PathLike) -> str:
return repr(str(to_path(path)))
def error(self, message: str) -> None:
"""
Print an error message indicating some operation fatally failed.
"""
self.logger.error(
f"{Fore.RED}{Style.BRIGHT}{message}{Style.RESET_ALL}"
)
def warn(self, message: str) -> None:
"""
Print a warning message indicating some operation failed, but the error can be recovered
or ignored.
"""
self.logger.warning(
f"{Fore.YELLOW}{Style.BRIGHT}{message}{Style.RESET_ALL}"
)
def modified_file(self, path: PathLike) -> None:
"""
An existing file has changed.
"""
self.logger.info(
f"{Fore.MAGENTA}{Style.BRIGHT}Modified {self._format_path(path)}.{Style.RESET_ALL}"
)
def new_file(self, path: PathLike) -> None:
"""
A new file has been downloaded.
"""
self.logger.info(
f"{Fore.GREEN}{Style.BRIGHT}Created {self._format_path(path)}.{Style.RESET_ALL}"
)
def ignored_file(self, path: PathLike, reason: str) -> None:
"""
File was not downloaded or modified.
"""
self.logger.info(
f"{Style.DIM}Ignored {self._format_path(path)} "
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
)
def searching(self, path: PathLike) -> None:
"""
A crawler searches a particular object.
"""
self.logger.info(f"Searching {self._format_path(path)}")
def not_searching(self, path: PathLike, reason: str) -> None:
"""
A crawler does not search a particular object.
"""
self.logger.info(
f"{Style.DIM}Not searching {self._format_path(path)} "
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
)
def starting_synchronizer(
self,
target_directory: PathLike,
synchronizer_name: str,
subject: Optional[str] = None,
) -> None:
"""
A special message marking that a synchronizer has been started.
"""
subject_str = f"{subject} " if subject else ""
self.logger.info("")
self.logger.info((
f"{Fore.CYAN}{Style.BRIGHT}Synchronizing "
f"{subject_str}to {self._format_path(target_directory)} "
f"using the {synchronizer_name} synchronizer.{Style.RESET_ALL}"
))

View File

@ -10,7 +10,8 @@ from pathlib import Path, PurePath
from typing import List, Set
from .location import Location
from .utils import PrettyLogger, prompt_yes_no
from .logging import PrettyLogger
from .utils import prompt_yes_no
LOGGER = logging.getLogger(__name__)
PRETTY = PrettyLogger(LOGGER)

View File

@ -11,10 +11,11 @@ from .ilias import (IliasAuthenticator, IliasCrawler, IliasDirectoryFilter,
IliasDownloader, IliasDownloadStrategy,
KitShibbolethAuthenticator, download_modified_or_new)
from .location import Location
from .logging import PrettyLogger
from .organizer import Organizer
from .tmp_dir import TmpDir
from .transform import TF, Transform, apply_transform
from .utils import PrettyLogger, PathLike, to_path
from .utils import PathLike, to_path
# TODO save known-good cookies as soon as possible

View File

@ -2,19 +2,20 @@
A few utility bobs and bits.
"""
import logging
import re
from pathlib import Path, PurePath
from typing import Optional, Tuple, Union
import bs4
import requests
from colorama import Fore, Style
PathLike = Union[PurePath, str, Tuple[str, ...]]
def to_path(pathlike: PathLike) -> Path:
"""
Convert a given PathLike into a Path.
"""
if isinstance(pathlike, tuple):
return Path(*pathlike)
return Path(pathlike)
@ -24,6 +25,9 @@ Regex = Union[str, re.Pattern]
def to_pattern(regex: Regex) -> re.Pattern:
"""
Convert a regex to a re.Pattern.
"""
if isinstance(regex, re.Pattern):
return regex
return re.compile(regex)
@ -74,79 +78,3 @@ def prompt_yes_no(question: str, default: Optional[bool] = None) -> bool:
if response == "" and default is not None:
return default
print(wrong_reply)
class PrettyLogger:
"""
A logger that prints some specially formatted log messages in color.
"""
def __init__(self, logger: logging.Logger) -> None:
self.logger = logger
@staticmethod
def _format_path(path: PathLike) -> str:
return repr(str(to_path(path)))
def modified_file(self, path: PathLike) -> None:
"""
An existing file has changed.
"""
self.logger.info(
f"{Fore.MAGENTA}{Style.BRIGHT}Modified {self._format_path(path)}.{Style.RESET_ALL}"
)
def new_file(self, path: PathLike) -> None:
"""
A new file has been downloaded.
"""
self.logger.info(
f"{Fore.GREEN}{Style.BRIGHT}Created {self._format_path(path)}.{Style.RESET_ALL}"
)
def ignored_file(self, path: PathLike, reason: str) -> None:
"""
File was not downloaded or modified.
"""
self.logger.info(
f"{Style.DIM}Ignored {self._format_path(path)} "
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
)
def searching(self, path: PathLike) -> None:
"""
A crawler searches a particular object.
"""
self.logger.info(f"Searching {self._format_path(path)}")
def not_searching(self, path: PathLike, reason: str) -> None:
"""
A crawler does not search a particular object.
"""
self.logger.info(
f"{Style.DIM}Not searching {self._format_path(path)} "
f"({Style.NORMAL}{reason}{Style.DIM}).{Style.RESET_ALL}"
)
def starting_synchronizer(
self,
target_directory: PathLike,
synchronizer_name: str,
subject: Optional[str] = None,
) -> None:
"""
A special message marking that a synchronizer has been started.
"""
subject_str = f"{subject} " if subject else ""
self.logger.info("")
self.logger.info((
f"{Fore.CYAN}{Style.BRIGHT}Synchronizing "
f"{subject_str}to {self._format_path(target_directory)} "
f"using the {synchronizer_name} synchronizer.{Style.RESET_ALL}"
))

View File

@ -1,7 +1,8 @@
import argparse
from pathlib import Path, PurePath
import PFERD
from PFERD import Pferd
from PFERD.logging import enable_logging
from PFERD.transform import (attempt, glob, keep, move, move_dir, optionally,
re_move)
@ -45,9 +46,12 @@ tf_ss_2020_pg = attempt(
def df_ss_2020_or1(path: PurePath) -> bool:
if glob("Tutorien/")(path): return True
if glob("Tutorien/Tutorium 10, dienstags 15:45 Uhr/")(path): return True
if glob("Tutorien/*")(path): return False
if glob("Tutorien/")(path):
return True
if glob("Tutorien/Tutorium 10, dienstags 15:45 Uhr/")(path):
return True
if glob("Tutorien/*")(path):
return False
return True
@ -64,8 +68,8 @@ def main() -> None:
parser.add_argument("synchronizers", nargs="*")
args = parser.parse_args()
PFERD.enable_logging()
pferd = PFERD.Pferd(Path(__file__).parent, test_run=args.test_run)
enable_logging()
pferd = Pferd(Path(__file__).parent, test_run=args.test_run)
if not args.synchronizers or "numerik" in args.synchronizers:
pferd.ilias_kit(