Skip to content

Feature: verbosity setting #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 12, 2024
11 changes: 7 additions & 4 deletions src/thread/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ def wrapper(

except SystemExit:
self.status = 'Killed'
print('KILLED ident: %s' % self.ident)
if Settings.VERBOSITY > 'quiet':
print('KILLED ident: %s' % self.ident)
return

return wrapper
Expand Down Expand Up @@ -532,8 +533,9 @@ def start(self) -> None:
# Handle abrupt exit
def service_shutdown(signum, frame):
if Settings.GRACEFUL_EXIT_ENABLED:
print('\nCaught signal %d' % signum)
print('Gracefully killing active threads')
if Settings.VERBOSITY > 'quiet':
print('\nCaught signal %d' % signum)
print('Gracefully killing active threads')

for thread in Threads:
if isinstance(thread, Thread):
Expand All @@ -545,7 +547,8 @@ def service_shutdown(signum, frame):
):
pass
except Exception:
print('Failed to kill ident: %d' % thread.ident or 0)
if Settings.VERBOSITY > 'quiet':
print('Failed to kill ident: %d' % thread.ident or 0)
sys.exit(0)


Expand Down
1 change: 1 addition & 0 deletions src/thread/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
from .config import Settings

from . import (
config,
algorithm,
)
143 changes: 142 additions & 1 deletion src/thread/utils/config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,155 @@
from typing import Any, Callable, Literal, Union

_Verbosity_Num = Literal[0, 1, 2]
_Verbosity_Enum = Literal['quiet', 'normal', 'verbose']
VerbosityLevel = Union[_Verbosity_Num, _Verbosity_Enum]
VerbosityMapping: dict[_Verbosity_Enum, _Verbosity_Num] = {
'quiet': 0,
'normal': 1,
'verbose': 2,
}


class Verbosity:
"""
# Verbosity

Can be instantiated with and compared against a number or an enum.
"""

level_number: _Verbosity_Num
level_string: _Verbosity_Enum

def __init__(self, level: VerbosityLevel) -> None:
"""
Initializes the verbosity object.

Parameters
----------
:param level: The level of verbosity. Can be a number or an enum.

Raises
------
ValueError: If the level is not a valid number or enum.
ValueError: If the level is not of a valid type.
"""
if isinstance(level, str):
if level not in VerbosityMapping.keys():
raise ValueError('Invalid verbosity level')
self.level_string = level
self.level_number = VerbosityMapping[level]
elif isinstance(level, int):
if level not in VerbosityMapping.values():
raise ValueError('Invalid verbosity level')
self.level_number = level
self.level_string = list(VerbosityMapping.keys())[level]
else:
raise ValueError(f'{type(level)} is not a valid verbosity level')

def __str__(self) -> str:
return self.level_string

def __eq__(self, other: Any) -> bool:
try:
return self._compare(other, lambda a, b: a == b)
except ValueError:
return False

def __lt__(self, other: Any) -> bool:
return self._compare(other, lambda a, b: a < b)

def __le__(self, other: Any) -> bool:
return self._compare(other, lambda a, b: a <= b)

def __gt__(self, other: Any) -> bool:
return self._compare(other, lambda a, b: a > b)

def __ge__(self, other: Any) -> bool:
return self._compare(other, lambda a, b: a >= b)

def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)

def _compare(
self, other: Any, operator: Callable[[VerbosityLevel, Any], bool]
) -> bool:
if isinstance(other, Verbosity):
return operator(self.level_number, other.level_number)
if isinstance(other, int):
return operator(self.level_number, other)
if isinstance(other, str):
if Verbosity.is_valid_level(other):
return operator(self.level_number, VerbosityMapping[other])
return operator(self.level_string, other)
raise ValueError('Cannot compare Verbosity with other types')

@staticmethod
def is_valid_level(level: Any) -> bool:
"""
Determines whether the given level is a valid verbosity level.

Parameters
----------
:param level: The level to check.

Returns
-------
:returns: True if the level is a valid verbosity level, False otherwise.
"""
if isinstance(level, int):
return level in VerbosityMapping.values()
if isinstance(level, str):
return level in VerbosityMapping.keys()
return False


class Settings:
"""
# Settings
`Non Instantiable`
"""

# Verbosity
VerbosityLevel = VerbosityLevel
VERBOSITY: Verbosity = Verbosity(1)

# Graceful Exit
GRACEFUL_EXIT_ENABLED: bool = True

def __init__(self):
raise NotImplementedError('This class is not instantiable')

@staticmethod
def set_graceful_exit(enabled: bool = True):
def set_graceful_exit(enabled: bool = True) -> None:
"""
Enables/Disables graceful exiting.

Parameters
----------
:param enabled: True to enable graceful exit, False otherwise.

Returns
-------
:returns: None
"""
Settings.GRACEFUL_EXIT_ENABLED = enabled

@staticmethod
def set_verbosity(verbosity: VerbosityLevel = 'normal') -> None:
"""
Sets the verbosity level.

Parameters
----------
:param verbosity: The level of verbosity. Can be a number or an enum.

Returns
-------
:returns: None

Raises
------
ValueError: If the level is not a valid number or enum.
ValueError: If the level is not of a valid type.
"""
Settings.VERBOSITY = Verbosity(verbosity)
66 changes: 66 additions & 0 deletions tests/test_verbosity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest
from src.thread.utils.config import Verbosity


# >>>>>>>>>> General Use <<<<<<<<<< #
def test_eqTrue():
assert Verbosity(0) == 0
assert Verbosity(0) == 'quiet'
assert Verbosity(1) == 'normal'
assert Verbosity(1) == Verbosity(1)


def test_neTrue():
assert Verbosity(0) != 1
assert Verbosity(0) != 'normal'
assert Verbosity(1) != 'quiet'
assert Verbosity(1) != Verbosity(0)


def test_ltTrue():
assert Verbosity(0) < 1
assert Verbosity(0) < 'normal'
assert Verbosity(1) < 'verbose'
assert Verbosity(1) < Verbosity(2)


def test_leTrue():
assert Verbosity(0) <= 1
assert Verbosity(0) <= 'normal'
assert Verbosity(1) <= 'verbose'
assert Verbosity(1) <= Verbosity(2)


def test_gtTrue():
assert Verbosity(1) > 0
assert Verbosity(1) > 'quiet'
assert Verbosity(2) > 'normal'
assert Verbosity(2) > Verbosity(1)


def test_geTrue():
assert Verbosity(1) >= 0
assert Verbosity(1) >= 'quiet'
assert Verbosity(2) >= 'normal'
assert Verbosity(2) >= Verbosity(1)


# >>>>>>>>>> Raising <<<<<<<<<< #
def test_ltRaise():
with pytest.raises(ValueError):
Verbosity(0) < Exception()


def test_leRaise():
with pytest.raises(ValueError):
Verbosity(0) <= Exception()


def test_gtRaise():
with pytest.raises(ValueError):
Verbosity(1) > Exception()


def test_geRaise():
with pytest.raises(ValueError):
Verbosity(1) >= Exception()