trimmed spaces

This commit is contained in:
2026-09-13 23:13:48 +02:00
parent c33d115feb
commit f3125c3803
11 changed files with 73 additions and 73 deletions
+16 -16
View File
@@ -16,7 +16,7 @@ class TriggerTuple(NamedTuple):
interval_in_seconds: int
governor: str
custom_config: OrderedDict[str, Any]
def __repr__(self) -> str:
return f"name: {self.name}, loglevel: {self.loglevel}, interval: {self.interval_in_seconds}, governor: {self.governor}, custom_config: {self.custom_config}"
@@ -25,26 +25,26 @@ class Config:
def __init__(self, _basepath: Optional[Path] = None) -> None:
self._basepath = _basepath
self.config = ArgumentsParser().parser.config
@cached_property
def basepath(self) -> Path:
if self._basepath is None:
return Path.cwd()
return self._basepath
@cached_property
def root(self) -> etree._ElementTree:
return etree.parse(self.basepath / self.config)
@cached_property
def app_root(self) -> etree._Element:
_tree = self.root.xpath('/cpuGovernorAutoAdjust')
assert isinstance(_tree, list) and len(_tree) == 1, "main config section cpuGovernorAutoAdjust not found"
ret_val = _tree.pop()
assert isinstance(ret_val, etree._Element), "Unexpected error has occurred, cpuGovernorAutoAdjust is not of type etree._Element"
return ret_val
@cached_property
def loglevel(self) -> str:
_loglevel = self._get_single_text_value_from_xpath('logLevel')
@@ -56,11 +56,11 @@ class Config:
f"{_available_loglevels}"
)
return value
@cached_property
def log(self) -> logging.Logger:
return getLogger(self.__class__.__name__, loglevel=self.loglevel.upper())
@cached_property
def testmode(self) -> bool:
_testmode = self._get_single_text_value_from_xpath('testMode')
@@ -70,7 +70,7 @@ class Config:
if value not in ['true', 'false']:
raise ValueError("testMode can only be 'true' or 'false")
return True if value == 'true' else False
@cached_property
def statistics_interval_in_seconds(self) -> int:
_statistics_interval = self._get_single_text_value_from_xpath('statisticsIntervalInSeconds')
@@ -78,12 +78,12 @@ class Config:
if value < 1:
raise ValueError("statisticsIntervalInSeconds must be greater than 0")
return value
@cached_property
def default_governor(self) -> str:
_default_governor = self._get_single_text_value_from_xpath('defaultGovernor')
return _default_governor
@cached_property
def triggertuples(self) -> list[TriggerTuple]:
ret_val = []
@@ -108,9 +108,9 @@ class Config:
)
ret_val.append(_new_trigger)
self.log.info("Loaded trigger: %s", _new_trigger)
return ret_val
@lru_cache(maxsize=99)
def get_trigger_by_name(self, name: str) -> Optional[TriggerTuple]:
self.log.debug("getting trigger: %s", name)
@@ -119,13 +119,13 @@ class Config:
if ret_val is None:
self.log.error("trigger not found: %s", ret_val)
return ret_val
def _get_single_text_value_from_xpath(self, xpath_str: str, element: Optional[etree._Element] = None) -> str:
_elem = self._get_single_element_by_xpath(xpath_str, element)
value = _elem.text
assert isinstance(value, str)
return value
def _get_single_element_by_xpath(self, xpath_str: str, element: Optional[etree._Element] = None) -> etree._Element:
if element is None:
element = self.app_root
@@ -134,7 +134,7 @@ class Config:
_elem = _list_of_elements.pop()
assert isinstance(_elem, etree._Element)
return _elem
def _get_multiple_elements_by_xpath(self, xpath_str: str, element: Optional[etree._Element] = None) -> list[etree._Element]:
if element is None:
element = self.app_root
@@ -17,13 +17,14 @@ async def _main() -> None:
log = getLogger('main', loglevel=config.loglevel.upper())
if config.testmode:
log.warning("starting in testmode, cpu adjustments have been disabled")
scheduler = TriggerScheduler(config)
for triggertuple in config.triggertuples:
_trigger = trigger_mapping[triggertuple.name](config)
await scheduler.schedule_task(scheduler.start_trigger, _trigger)
await scheduler.run()
if __name__ == "__main__":
main()
+21 -21
View File
@@ -8,18 +8,18 @@ from time import monotonic_ns as monotonic
from cpu_governor_auto_adjust.helper import format_time_ns
import asyncio
class Governor(NamedTuple):
name: str
priority: int
_governor_list: tuple[Governor, Governor, Governor, Governor, Governor, Governor] = (
Governor("performance", 0),
Governor("schedutil", 1),
Governor("ondemand", 2),
Governor("conservative", 3),
Governor("userspace", 4),
Governor("performance", 0),
Governor("schedutil", 1),
Governor("ondemand", 2),
Governor("conservative", 3),
Governor("userspace", 4),
Governor("powersave", 5)
)
@@ -28,10 +28,10 @@ class DummyCpuFreq:
def __init__(self) -> None:
self.available_governors = [g.name for g in _governor_list]
self._governor = "performance"
def get_governors(self) -> dict[str, str]:
return {g.name: g.name for g in _governor_list}
def set_governors(self, governor_name: str) -> None:
self._governor = governor_name
@@ -44,14 +44,14 @@ class GovernorControl(AppClass):
self._timestamp_last_statistics = monotonic()
self._governor_statistics = self._statistics_dict()
self._last_statistic_update = monotonic()
def _statistics_dict(self) -> dict[str, int]:
ret_dict = {}
for gov in _governor_list:
if gov.name in self._cpufreq.available_governors:
ret_dict[gov.name] = 0
return ret_dict
def _update_statistics(self) -> None:
_now = monotonic()
self._governor_statistics[self.current_governor.name] += _now - self._last_statistic_update
@@ -72,7 +72,7 @@ class GovernorControl(AppClass):
except cpufreq.CPUFreqErrorInit:
self.log.warning("cpu architecture has no governor support")
return DummyCpuFreq()
@property
def current_governor(self) -> Governor:
if self._config.testmode:
@@ -89,26 +89,26 @@ class GovernorControl(AppClass):
if t_gov.priority < _governor_to_return.priority:
_governor_to_return == t_gov
return _governor_to_return
def _establish_governor(self, governor_name: str) -> Governor:
governor = next((g for g in _governor_list if g.name == governor_name), None)
if governor is None:
raise RuntimeError("could not establish governor")
return governor
def _set_governor(self, governor_name: str) -> None:
self.log.info("setting cpu governor to %s", governor_name)
if self._config.testmode:
self.log.warning("application is running in testmode, cpu governor not set")
return None
if governor_name not in self._cpufreq.available_governors:
self.log.error("governor %s not supported by cpu", governor_name)
return None
self._cpufreq.set_governors(governor_name)
async def run(self) -> None:
self.log.debug("running governor control")
while True:
@@ -117,9 +117,9 @@ class GovernorControl(AppClass):
if self._timestamp_last_statistics + (int(self._config.statistics_interval_in_seconds) * 1_000_000_000) < start:
self.log_statistics()
self._timestamp_last_statistics = start
if self.current_governor != self.governor:
self.log.info("governor has changed from %s to %s", self.current_governor, self.governor)
self._set_governor(self.governor.name)
await asyncio.sleep(1)
+1 -1
View File
@@ -15,4 +15,4 @@ def format_time_ns(elapsed_ns):
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
seconds = total_seconds % 60
return f"{hours:02}:{minutes:02}:{seconds:02}"
return f"{hours:02}:{minutes:02}:{seconds:02}"
+3 -3
View File
@@ -11,15 +11,15 @@ class LoggerFormat(NamedTuple):
message: str = '%(message)s'
filename: str = '%(filename)s'
lineno: str = '%(lineno)d'
def _base(self, class_name: Optional[str] = None) -> str:
if class_name is not None:
return f"{self.time} {self.level} [{class_name}] {self.message} "
return f"{self.time} {self.level} {self.message}"
def info(self, class_name: Optional[str] = None) -> str:
return f"{self._base(class_name)}"
def debug(self, class_name: Optional[str] = None) -> str:
return f"{self._base(class_name)} ({self.filename}:{self.lineno})"
+5 -5
View File
@@ -1,10 +1,10 @@
from types import MappingProxyType
from cpu_governor_auto_adjust.triggers import (
TestTrigger1,
TestTrigger2,
RoonTrigger,
WakeupTrigger,
CpuLoadTrigger,
TestTrigger1,
TestTrigger2,
RoonTrigger,
WakeupTrigger,
CpuLoadTrigger,
RoonArcTrigger,
RoonClientTrigger,
)
+7 -7
View File
@@ -16,11 +16,11 @@ class TriggerScheduler(AppClass):
super().__init__(_config)
self.running_triggers: list[Trigger] = []
self.loop = asyncio.get_event_loop()
@cached_property
def governor_control(self) -> GovernorControl:
return GovernorControl(self._config)
def establish_preferred_governor(self) -> Governor:
self.log.debug('establishing preferred governor, default governor: %s', self._config.default_governor)
preferred_governor: Governor = [gov for gov in _governor_list if gov.name == self._config.default_governor].pop()
@@ -31,7 +31,7 @@ class TriggerScheduler(AppClass):
preferred_governor = trigger.governor
self.log.debug('preferred governor: %s', preferred_governor)
return preferred_governor
async def start_trigger(self, _trigger: Trigger) -> None:
"""Start a new trigger."""
self.loop.create_task(_trigger.run())
@@ -45,21 +45,21 @@ class TriggerScheduler(AppClass):
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
self.loop.stop()
async def schedule_task(self, task: Callable, *args: Any) -> None:
self.log.debug("scheduling task: %s", task.__name__)
start = monotonic()
await self.loop.create_task(task(*args))
self.log.debug("task %s finished in %s", task.__name__, format_time_ns(monotonic() - start))
self.log.debug("task %s finished in %s", task.__name__, format_time_ns(monotonic() - start))
async def run(self) -> None:
"""Run the scheduler and keep it alive until stopped."""
def signal_handler(sig):
self.log.info("received signal: %s", sig)
self.log.info("Exiting, stopping all triggers")
self.loop.create_task(self.stop_triggers())
for sig in [signal.SIGINT, signal.SIGTERM]:
self.loop.add_signal_handler(sig, partial(signal_handler, sig=signal.SIGINT))
+13 -14
View File
@@ -19,10 +19,10 @@ class Trigger(AppClass):
self._minimum_consumed_time = 10000000000
self._maximum_consumed_time = 0
self.runs = 0
def __hash__(self):
return super().__hash__(hash(self.name))
@property
def consumed_time(self) -> dict[str, str]:
_dict = {
@@ -35,15 +35,15 @@ class Trigger(AppClass):
return _dict
_dict["average"] = format_time_ns(self._total_consumed_time / self.runs)
return _dict
def log_statistics(self):
class_vars = {}
start = monotonic()
for base in reversed(self.__class__.__mro__[:-1]):
for base in reversed(self.__class__.__mro__[:-1]):
if hasattr(base, '__dict__'):
for key, value in base.__dict__.items():
# Ignore methods and private attributes
# Ignore methods and private attributes
if not key.startswith("__") and not key.startswith("_") and not callable(value):
if isinstance(value, property):
# If it's a property, call the getter method
@@ -59,13 +59,13 @@ class Trigger(AppClass):
if not isinstance(value, (int, float, Governor)):
value = str(value)
class_vars[key] = value
self.log.info("info and statistics [time taken: %s]: %s", format_time_ns(monotonic() - start), class_vars)
@property
def trigger_state(self) -> str:
return "active" if self._active else "not active"
@property
def name(self) -> str:
if self.__module__ == 'trigger':
@@ -74,24 +74,24 @@ class Trigger(AppClass):
)
_name = self.__module__.split('.').pop()
return _name
@property
def config(self) -> TriggerTuple:
_config = self._config.get_trigger_by_name(self.name)
if _config is None:
raise MissingConfig(f"Trigger {self.name} hasn't been properly configured")
return _config
@cached_property
def governor(self) -> Governor:
_governor = next((gov for gov in _governor_list if self.config.governor == gov.name), None)
if _governor is None:
raise ValueError("unknown governor: %s", _governor)
return _governor
def trigger_code(self) -> None:
# this is the main function of the trigger class and can be used to execute a callback function
raise TriggerImportError(
"the Trigger class can't used directly, but must be inherited in a trigger specific class"
)
@@ -111,4 +111,3 @@ class Trigger(AppClass):
self.runs += 1
self.log.debug("trigger state: %s, finished in %s", self.trigger_state, format_time_ns(end - start))
await asyncio.sleep(self.config.interval_in_seconds)
@@ -15,4 +15,4 @@ StandardOutput=append:/var/log/cpu_governor_auto_adjust.log
StandardError=append:/var/log/cpu_governor_auto_adjust.log
[Install]
WantedBy=multi-user.target
WantedBy=multi-user.target
+1 -1
View File
@@ -3,4 +3,4 @@ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export TZ='Europe/Amsterdam'
source /opt/cpu_governor_auto_adjust/.venv/bin/activate
pip install -U cpu-governor-auto-adjust
exec cpu_governor_auto_adjust -c /opt/cpu_governor_auto_adjust/cpu_governor_auto_adjust.xml
exec cpu_governor_auto_adjust -c /opt/cpu_governor_auto_adjust/cpu_governor_auto_adjust.xml
+2 -2
View File
@@ -18,8 +18,8 @@ dependencies = [
[project.optional-dependencies]
dev = [
"pytest",
"black",
"pytest",
"black",
"mypy"
]