"""This module provides functions for validating and potentially creating
file and directory paths. It aims to ensure consistent path handling
throughout a larger system, preventing errors related to missing or
incorrectly specified paths. The module leverages the `IPathValidator`
interface for flexibility and maintainability.
Functions:
- validate_path:
Validates a path and optionally creates it if it doesn't exist.
Classes:
- FileValidator:
Validates file paths, checking for existence
and potentially raising exceptions.
- DirectoryValidator:
Validates directory paths, checking for
existence and potentially creating directories.
- IPathValidator:
Abstract base class defining the contract for path validation.
"""
# region Imports
import argparse
import logging
import os
import sys
from os import PathLike
from typing import Protocol
from typing import AnyStr
from src.core.configurator.path_validator import IPathValidator
# endregion
[docs]
class ILoggingConfigurator(Protocol):
"""Protocol for configuring logging.
Implementations should provide a method to set a logger instance.
"""
[docs]
def set_logger(
self,
silent: bool = False
) -> logging.Logger:
"""Configures and returns a logger instance.
Args:
silent (bool, optional):
If True, suppresses output to console.
Defaults to False.
Returns:
logging.Logger:
Configured logger instance.
"""
[docs]
class LoggingConfigurator(ILoggingConfigurator):
"""Configures logging to a specified file or console."""
def __init__(
self,
path_validator: IPathValidator,
log_path: PathLike[AnyStr] = os.curdir,
args: argparse.Namespace = None
):
"""Initializes the LoggingConfigurator with deps and parameters.
Args:
path_validator (IPathValidator):
Instance for verifying log file path.
log_path (PathLike[AnyStr], optional):
Base directory for logs. Defaults to current directory.
args (argparse.Namespace, optional):
Parsed command-line arguments.
"""
self.args = args
if args is not None:
self.log_path = args.logFilename
else:
self.log_path = log_path
self.path_validator = path_validator
self.set_logger()
[docs]
def set_logger(
self,
silent: bool = False
) -> logging.Logger:
"""Sets up logging configuration, creating log files and handlers.
Args:
silent (bool):
If True, disables console output. Defaults to False.
Returns:
logging.Logger:
Configured logger instance.
Raises:
SystemExit:
If verification or creation of log path fails.
"""
try:
if self.args is not None:
base_logpath = os.path.abspath(os.path.join(
self.args.outputDir, self.log_path or self.args.logFilename
))
else:
base_logpath = os.path.abspath(os.path.join(
self.log_path, 'default_analyzer.log'))
if not self.path_validator.verify_path(
base_logpath,
create_if_missing=True
):
sys.exit(os.EX_SOFTWARE)
handlers = [logging.FileHandler(filename=base_logpath)]
if not silent:
handlers.append(logging.StreamHandler(
stream=sys.stdout))
logging.basicConfig(
level=logging.INFO,
format=r'%(asctime)s - %(levelname)s - %(message)s',
handlers=handlers)
configuration_logger = logging.getLogger()
configuration_logger.propagate = False
return configuration_logger
except (IOError, FileNotFoundError, SystemError, OSError) as e:
print(
f"A fatal error '{repr(e)}' occurred at "
f"'{e.__traceback__.tb_frame}'", file=sys.stdout)
sys.exit(os.EX_SOFTWARE)