Source code for libdebug.utils.libcontext
#
# This file is part of libdebug Python library (https://github.com/libdebug/libdebug).
# Copyright (c) 2023-2024 Gabriele Digregorio, Roberto Alessandro Bertolini. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for details.
#
from __future__ import annotations
import sys
from contextlib import contextmanager
from copy import deepcopy
from libdebug.liblog import liblog
[docs]
class LibContext:
"""A class that holds the global context of the library."""
_instance = None
def __new__(cls: type):
"""Create a new instance of the class if it does not exist yet.
Returns:
LibContext: the instance of the class.
"""
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self: LibContext) -> None:
"""Initializes the context."""
if self._initialized:
return
self._sym_lvl = 3
self._debugger_logger = "INFO"
self._pipe_logger = "INFO"
self._general_logger = "INFO"
# Adjust log levels based on command-line arguments
if len(sys.argv) > 1:
if "debugger" in sys.argv:
liblog.debugger_logger.setLevel("DEBUG")
self._debugger_logger = "DEBUG"
elif "pipe" in sys.argv:
liblog.pipe_logger.setLevel("DEBUG")
self._pipe_logger = "DEBUG"
elif "dbg" in sys.argv:
self._set_debug_level_for_all()
self._debugger_logger = "DEBUG"
self._pipe_logger = "DEBUG"
self._general_logger = "DEBUG"
self._initialized = True
self._arch = "amd64"
self._terminal = []
def _set_debug_level_for_all(self: LibContext) -> None:
"""Set the debug level for all the loggers to DEBUG."""
for logger in [
liblog.general_logger,
liblog.debugger_logger,
liblog.pipe_logger,
]:
logger.setLevel("DEBUG")
@property
def sym_lvl(self: LibContext) -> int:
"""Property getter for sym_lvl.
Returns:
_sym_lvl (int): the current symbol level.
"""
return self._sym_lvl
@sym_lvl.setter
def sym_lvl(self: LibContext, value: int) -> None:
"""Property setter for sym_lvl, ensuring it's between 0 and 5."""
if 0 <= value <= 5:
self._sym_lvl = value
else:
raise ValueError("sym_lvl must be between 0 and 5")
@property
def debugger_logger(self: LibContext) -> str:
"""Property getter for debugger_logger.
Returns:
_debugger_logger (str): the current debugger logger level.
"""
return self._debugger_logger
@debugger_logger.setter
def debugger_logger(self: LibContext, value: str) -> None:
"""Property setter for debugger_logger, ensuring it's a valid logging level."""
if value in ["DEBUG", "INFO"]:
self._debugger_logger = value
liblog.debugger_logger.setLevel(value)
else:
raise ValueError("debugger_logger must be a valid logging level")
@property
def pipe_logger(self: LibContext) -> str:
"""Property getter for pipe_logger.
Returns:
_pipe_logger (str): the current pipe logger level.
"""
return self._pipe_logger
@pipe_logger.setter
def pipe_logger(self: LibContext, value: str) -> None:
"""Property setter for pipe_logger, ensuring it's a valid logging level."""
if value in ["DEBUG", "INFO"]:
self._pipe_logger = value
liblog.pipe_logger.setLevel(value)
else:
raise ValueError("pipe_logger must be a valid logging level")
@property
def general_logger(self: LibContext) -> str:
"""Property getter for general_logger.
Returns:
_general_logger (str): the current general logger level.
"""
return self._general_logger
@general_logger.setter
def general_logger(self: LibContext, value: str) -> None:
"""Property setter for general_logger, ensuring it's a valid logging level."""
if value in ["DEBUG", "INFO"]:
self._general_logger = value
liblog.general_logger.setLevel(value)
else:
raise ValueError("general_logger must be a valid logging level")
@property
def arch(self: LibContext) -> str:
"""Property getter for architecture.
Returns:
_arch (str): the current architecture.
"""
return self._arch
@arch.setter
def arch(self: LibContext, value: str) -> None:
"""Property setter for arch, ensuring it's a valid architecture."""
if value in ["amd64"]:
self._arch = value
else:
raise RuntimeError("The specified architecture is not supported")
@property
def terminal(self: LibContext) -> list[str]:
"""Property getter for terminal.
Returns:
_terminal (str): the current terminal.
"""
return self._terminal
@terminal.setter
def terminal(self: LibContext, value: list[str] | str) -> None:
"""Property setter for terminal, ensuring it's a valid terminal."""
if isinstance(value, str):
value = [value]
self._terminal = value
[docs]
def update(self: LibContext, **kwargs: ...) -> None:
"""Update the context with the given values."""
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
[docs]
@contextmanager
def tmp(self: LibContext, **kwargs: ...) -> ...:
"""Context manager that temporarily changes the library context. Use "with" statement."""
# Make a deep copy of the current state
old_context = deepcopy(self.__dict__)
self.update(**kwargs)
try:
yield
finally:
# Restore the original state
self.__dict__.update(old_context)
liblog.debugger_logger.setLevel(self.debugger_logger)
liblog.pipe_logger.setLevel(self.pipe_logger)
# Global context instance
libcontext = LibContext()