From cfa2c8f0455495c5a234c32bf0e367cba1ef2107 Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Wed, 6 Mar 2024 16:23:32 -0600 Subject: [PATCH 01/16] add config.py and config_event_handler.py also introduces the utils folder --- auto_cpufreq/utils/__init__.py | 0 auto_cpufreq/utils/config.py | 37 ++++++++++++++++++++++ auto_cpufreq/utils/config_event_handler.py | 10 ++++++ 3 files changed, 47 insertions(+) create mode 100644 auto_cpufreq/utils/__init__.py create mode 100644 auto_cpufreq/utils/config.py create mode 100644 auto_cpufreq/utils/config_event_handler.py diff --git a/auto_cpufreq/utils/__init__.py b/auto_cpufreq/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py new file mode 100644 index 00000000..799b70f4 --- /dev/null +++ b/auto_cpufreq/utils/config.py @@ -0,0 +1,37 @@ +from configparser import ConfigParser +from auto_cpufreq.utils.config_event_handler import ConfigEventHandler +import pyinotify +import os + +class _Config: + def __init__(self) -> None: + self.path: str = "" + self._config: ConfigParser = ConfigParser() + self.watch_manager: pyinotify.WatchManager = pyinotify.WatchManager() + self.config_handler = ConfigEventHandler(self) + self.notifier: pyinotify.Notifier = pyinotify.Notifier( + self.watch_manager, self.config_handler) + + def set_path(self, path: str) -> None: + if os.path.isfile(path): + self.path = path; + self.update_config() + self.watch_manager.add_watch(os.path.dirname(path), pyinotify.ALL_EVENTS) + + + def has_config(self) -> bool: + return os.path.isfile(self.path) + + def get_config(self) -> ConfigParser: + return self._config + + def update_config(self) -> None: + self._config.read(self.path) + + def check_for_changes(self) -> None: + self.notifier.process_events() + if self.notifier.check_events(): + self.notifier.read_events() + + +config = _Config() \ No newline at end of file diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py new file mode 100644 index 00000000..32bd2cd6 --- /dev/null +++ b/auto_cpufreq/utils/config_event_handler.py @@ -0,0 +1,10 @@ +import os +import pyinotify + +class ConfigEventHandler(pyinotify.ProcessEvent): + def __init__(self, config) -> None: + self.config = config + + def process_IN_MODIFY(self, event: pyinotify.Event) -> None: + if event.pathname.rstrip("~") == self.config.path: + self.config.update_config() \ No newline at end of file From 17522bfae7764fa70d088dd80b174ca04b1f5ace Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Wed, 6 Mar 2024 16:24:04 -0600 Subject: [PATCH 02/16] update config imports and variables --- auto_cpufreq/battery_scripts/battery.py | 11 ++++++----- auto_cpufreq/bin/auto_cpufreq.py | 8 +++++++- auto_cpufreq/core.py | 18 ++++-------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/auto_cpufreq/battery_scripts/battery.py b/auto_cpufreq/battery_scripts/battery.py index 0cc6fe5d..58825073 100644 --- a/auto_cpufreq/battery_scripts/battery.py +++ b/auto_cpufreq/battery_scripts/battery.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 import subprocess -from auto_cpufreq.core import get_config, root_check +from auto_cpufreq.core import root_check from auto_cpufreq.battery_scripts.thinkpad import * from auto_cpufreq.battery_scripts.ideapad import * +from auto_cpufreq.utils.config import config def lsmod(module): @@ -16,7 +17,7 @@ def lsmod(module): def battery_start_threshold(): - conf = get_config() + conf = config.config() if conf.has_option("battery", "start_threshold"): start_threshold = conf["battery"]["start_threshold"] return int(start_threshold) @@ -25,7 +26,7 @@ def battery_start_threshold(): def battery_stop_threshold(): - conf = get_config() + conf = config.get_config() if conf.has_option("battery", "stop_threshold"): stop_threshold = conf["battery"]["stop_threshold"] return int(stop_threshold) @@ -35,7 +36,7 @@ def battery_stop_threshold(): def battery_setup(): root_check() - conf = get_config() + conf = config.get_config() if conf.has_option("battery", "enable_thresholds"): if conf["battery"]["enable_thresholds"] == "true": if lsmod("thinkpad_acpi"): @@ -53,7 +54,7 @@ def battery_setup(): def battery_get_thresholds(): - conf = get_config() + conf = config.get_config() if conf.has_option("battery", "enable_thresholds"): if conf["battery"]["enable_thresholds"] == "true": print("-" * 30) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index 39fb0944..d8b73293 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -14,6 +14,7 @@ from auto_cpufreq.core import * from auto_cpufreq.power_helper import * from auto_cpufreq.battery_scripts.battery import * +from auto_cpufreq.utils.config import config as conf # cli @click.command() @click.option("--monitor", is_flag=True, help="Monitor and see suggestions for CPU optimizations") @@ -40,8 +41,9 @@ def main(config, daemon, debug, update, install, remove, live, log, monitor, stats, version, donate, force, get_state, completions): # display info if config file is used + conf.set_path(config) def config_info_dialog(): - if get_config(config) and hasattr(get_config, "using_cfg_file"): + if conf.has_config(): print("\nUsing settings defined in " + config + " file") # set governor override unless None or invalid @@ -69,6 +71,7 @@ def config_info_dialog(): tlp_service_detect_snap() battery_setup() while True: + conf.check_for_changes() footer() gov_check() cpufreqctl() @@ -81,6 +84,7 @@ def config_info_dialog(): tlp_service_detect() battery_setup() while True: + conf.check_for_changes() footer() gov_check() cpufreqctl() @@ -106,6 +110,7 @@ def config_info_dialog(): tlp_service_detect() while True: time.sleep(1) + conf.check_for_changes() running_daemon_check() footer() gov_check() @@ -130,6 +135,7 @@ def config_info_dialog(): tlp_service_detect() while True: try: + conf.check_for_changes() running_daemon_check() footer() gov_check() diff --git a/auto_cpufreq/core.py b/auto_cpufreq/core.py index 66e4978a..719fc8ff 100755 --- a/auto_cpufreq/core.py +++ b/auto_cpufreq/core.py @@ -12,7 +12,6 @@ import click import pickle import warnings -import configparser # import pkg_resources import importlib.metadata from math import isclose @@ -27,6 +26,7 @@ sys.path.append("../") from auto_cpufreq.power_helper import * +from auto_cpufreq.utils.config import config warnings.filterwarnings("ignore") @@ -84,16 +84,6 @@ def file_stats(): auto_cpufreq_stats_file = open(auto_cpufreq_stats_path, "w") sys.stdout = auto_cpufreq_stats_file -def get_config(config_file=""): - if not hasattr(get_config, "config"): - get_config.config = configparser.ConfigParser() - - if os.path.isfile(config_file): - get_config.config.read(config_file) - get_config.using_cfg_file = True - - return get_config.config - def get_override(): if os.path.isfile(governor_override_state): with open(governor_override_state, "rb") as store: @@ -638,7 +628,7 @@ def set_frequencies(): if not hasattr(set_frequencies, "min_limit"): set_frequencies.min_limit = int(getoutput(f"cpufreqctl.auto-cpufreq --frequency-min-limit")) - conf = get_config() + conf = config.get_config() for freq_type in frequency.keys(): value = None @@ -679,7 +669,7 @@ def set_frequencies(): # set powersave and enable turbo def set_powersave(): - conf = get_config() + conf = config.get_config() if conf.has_option("battery", "governor"): gov = conf["battery"]["governor"] else: @@ -902,7 +892,7 @@ def mon_powersave(): # set performance and enable turbo def set_performance(): - conf = get_config() + conf = config.get_config() if conf.has_option("charger", "governor"): gov = conf["charger"]["governor"] else: From eb31ce37d3c7f7ff3e33638c0afa501fcfaa626a Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Wed, 6 Mar 2024 16:24:49 -0600 Subject: [PATCH 03/16] add 'pyinotify' dependency --- nix/default.nix | 2 +- poetry.lock | 14 ++++++++++++-- pyproject.toml | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index df95ebda..5234003a 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -15,7 +15,7 @@ python310Packages.buildPythonPackage { buildInputs = with pkgs; [gtk3 python310Packages.poetry-core]; - propagatedBuildInputs = with python310Packages; [requests pygobject3 click distro psutil setuptools poetry-dynamic-versioning]; + propagatedBuildInputs = with python310Packages; [requests pygobject3 click distro psutil setuptools poetry-dynamic-versioning pyinotify]; doCheck = false; pythonImportsCheck = ["auto_cpufreq"]; diff --git a/poetry.lock b/poetry.lock index 88629f3f..00143666 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] name = "attrs" @@ -879,6 +879,16 @@ files = [ [package.dependencies] pycairo = ">=1.16,<2.0" +[[package]] +name = "pyinotify" +version = "0.9.6" +description = "Linux filesystem events monitoring" +optional = false +python-versions = "*" +files = [ + {file = "pyinotify-0.9.6.tar.gz", hash = "sha256:9c998a5d7606ca835065cdabc013ae6c66eb9ea76a00a1e3bc6e0cfe2b4f71f4"}, +] + [[package]] name = "pyproject-hooks" version = "1.0.0" @@ -1293,4 +1303,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "88a745c11c7890867d0cf645825882d41ba49f1819d490faf3e370590c7cf78a" +content-hash = "4fec0dc15d4799f23a684d36edb45a0a9259aedb443d246b9c763c490447d717" diff --git a/pyproject.toml b/pyproject.toml index 4800d55a..62570bde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ click = "^8.1.0" distro = "^1.8.0" requests = "^2.31.0" PyGObject = "^3.46.0" +pyinotify = "^0.9.6" [tool.poetry.group.dev.dependencies] poetry = "^1.6.1" From 84a98558c5b3eed4691dffb2c17a6bcf80da550a Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Thu, 7 Mar 2024 14:45:47 -0600 Subject: [PATCH 04/16] config: check for changes using threading --- auto_cpufreq/bin/auto_cpufreq.py | 4 ---- auto_cpufreq/utils/config.py | 11 ++++------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index d8b73293..f7b803cf 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -71,7 +71,6 @@ def config_info_dialog(): tlp_service_detect_snap() battery_setup() while True: - conf.check_for_changes() footer() gov_check() cpufreqctl() @@ -84,7 +83,6 @@ def config_info_dialog(): tlp_service_detect() battery_setup() while True: - conf.check_for_changes() footer() gov_check() cpufreqctl() @@ -110,7 +108,6 @@ def config_info_dialog(): tlp_service_detect() while True: time.sleep(1) - conf.check_for_changes() running_daemon_check() footer() gov_check() @@ -135,7 +132,6 @@ def config_info_dialog(): tlp_service_detect() while True: try: - conf.check_for_changes() running_daemon_check() footer() gov_check() diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index 799b70f4..6bad7a7d 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -9,8 +9,11 @@ def __init__(self) -> None: self._config: ConfigParser = ConfigParser() self.watch_manager: pyinotify.WatchManager = pyinotify.WatchManager() self.config_handler = ConfigEventHandler(self) - self.notifier: pyinotify.Notifier = pyinotify.Notifier( + + # check for file changes using threading + notifier: pyinotify.ThreadedNotifierNotifier = pyinotify.ThreadedNotifier( self.watch_manager, self.config_handler) + notifier.start() def set_path(self, path: str) -> None: if os.path.isfile(path): @@ -28,10 +31,4 @@ def get_config(self) -> ConfigParser: def update_config(self) -> None: self._config.read(self.path) - def check_for_changes(self) -> None: - self.notifier.process_events() - if self.notifier.check_events(): - self.notifier.read_events() - - config = _Config() \ No newline at end of file From d9453a0eee15e14f3c7a6a55e51cb10fbf6217aa Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Thu, 7 Mar 2024 14:48:40 -0600 Subject: [PATCH 05/16] config: handle errors and new eventsx --- auto_cpufreq/utils/config.py | 10 +++++++--- auto_cpufreq/utils/config_event_handler.py | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index 6bad7a7d..81f3bb4b 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -1,4 +1,4 @@ -from configparser import ConfigParser +from configparser import ConfigParser, ParsingError from auto_cpufreq.utils.config_event_handler import ConfigEventHandler import pyinotify import os @@ -17,9 +17,10 @@ def __init__(self) -> None: def set_path(self, path: str) -> None: if os.path.isfile(path): + mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY self.path = path; self.update_config() - self.watch_manager.add_watch(os.path.dirname(path), pyinotify.ALL_EVENTS) + self.watch_manager.add_watch(os.path.dirname(path), mask=mask) def has_config(self) -> bool: @@ -29,6 +30,9 @@ def get_config(self) -> ConfigParser: return self._config def update_config(self) -> None: - self._config.read(self.path) + try: + self._config.read(self.path) + except ParsingError as e: + print(f"The following error occured while parsing the config file: \n{e}") config = _Config() \ No newline at end of file diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py index 32bd2cd6..07fe7e03 100644 --- a/auto_cpufreq/utils/config_event_handler.py +++ b/auto_cpufreq/utils/config_event_handler.py @@ -1,10 +1,23 @@ -import os import pyinotify class ConfigEventHandler(pyinotify.ProcessEvent): def __init__(self, config) -> None: self.config = config + # activates when file is modified def process_IN_MODIFY(self, event: pyinotify.Event) -> None: if event.pathname.rstrip("~") == self.config.path: - self.config.update_config() \ No newline at end of file + self.config.update_config() + print("file modified") + + # activates when file is deleted + def process_IN_DELETE(self, event: pyinotify.Event) -> None: + if event.pathname.rstrip("~") == self.config.path: + self.config.update_config() + print("File deleted") + + # activates when file is created + def process_IN_CREATE(self, event: pyinotify.Event) -> None: + if event.pathname.rstrip("~") == self.config.path: + self.config.update_config() + print("File created") \ No newline at end of file From 47711ff685b66b350951f1696b61b415a14ea446 Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Thu, 7 Mar 2024 15:18:32 -0600 Subject: [PATCH 06/16] config: set_path even if file doesn't exist and make new ConfigParser on every update --- auto_cpufreq/utils/config.py | 8 +++++--- auto_cpufreq/utils/config_event_handler.py | 5 +---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index 81f3bb4b..1e0ec39c 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -16,11 +16,11 @@ def __init__(self) -> None: notifier.start() def set_path(self, path: str) -> None: + self.path = path; + mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY + self.watch_manager.add_watch(os.path.dirname(path), mask=mask) if os.path.isfile(path): - mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY - self.path = path; self.update_config() - self.watch_manager.add_watch(os.path.dirname(path), mask=mask) def has_config(self) -> bool: @@ -30,6 +30,8 @@ def get_config(self) -> ConfigParser: return self._config def update_config(self) -> None: + # create new ConfigParser to prevent old data from remaining + self._config = ConfigParser() try: self._config.read(self.path) except ParsingError as e: diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py index 07fe7e03..4a6148c0 100644 --- a/auto_cpufreq/utils/config_event_handler.py +++ b/auto_cpufreq/utils/config_event_handler.py @@ -8,16 +8,13 @@ def __init__(self, config) -> None: def process_IN_MODIFY(self, event: pyinotify.Event) -> None: if event.pathname.rstrip("~") == self.config.path: self.config.update_config() - print("file modified") # activates when file is deleted def process_IN_DELETE(self, event: pyinotify.Event) -> None: if event.pathname.rstrip("~") == self.config.path: self.config.update_config() - print("File deleted") # activates when file is created def process_IN_CREATE(self, event: pyinotify.Event) -> None: if event.pathname.rstrip("~") == self.config.path: - self.config.update_config() - print("File created") \ No newline at end of file + self.config.update_config() \ No newline at end of file From 6e269d9caeec4bc93016a343cd64a85143c1bdab Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Thu, 7 Mar 2024 15:25:30 -0600 Subject: [PATCH 07/16] fix get_config call --- auto_cpufreq/battery_scripts/battery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_cpufreq/battery_scripts/battery.py b/auto_cpufreq/battery_scripts/battery.py index 58825073..f4d91dc9 100644 --- a/auto_cpufreq/battery_scripts/battery.py +++ b/auto_cpufreq/battery_scripts/battery.py @@ -17,7 +17,7 @@ def lsmod(module): def battery_start_threshold(): - conf = config.config() + conf = config.get_config() if conf.has_option("battery", "start_threshold"): start_threshold = conf["battery"]["start_threshold"] return int(start_threshold) From 6cf4a66f3c5a2c2b6c26b6f1aa4cba83acafc199 Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Mon, 18 Mar 2024 19:23:04 -0500 Subject: [PATCH 08/16] config: check for changes on moved file --- auto_cpufreq/utils/config.py | 3 ++- auto_cpufreq/utils/config_event_handler.py | 23 ++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index 1e0ec39c..ae68b82f 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -17,7 +17,8 @@ def __init__(self) -> None: def set_path(self, path: str) -> None: self.path = path; - mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY + mask = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY \ + | pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO self.watch_manager.add_watch(os.path.dirname(path), mask=mask) if os.path.isfile(path): self.update_config() diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py index 4a6148c0..332cdd6d 100644 --- a/auto_cpufreq/utils/config_event_handler.py +++ b/auto_cpufreq/utils/config_event_handler.py @@ -4,17 +4,24 @@ class ConfigEventHandler(pyinotify.ProcessEvent): def __init__(self, config) -> None: self.config = config - # activates when file is modified - def process_IN_MODIFY(self, event: pyinotify.Event) -> None: + def _process_update(self, event: pyinotify.Event): if event.pathname.rstrip("~") == self.config.path: self.config.update_config() + # activates when file is modified + def process_IN_MODIFY(self, event: pyinotify.Event) -> None: + self._process_update(event) + # activates when file is deleted - def process_IN_DELETE(self, event: pyinotify.Event) -> None: - if event.pathname.rstrip("~") == self.config.path: - self.config.update_config() - + def process_IN_DELETE_SELF(self, event: pyinotify.Event) -> None: + self._process_update(event) + # activates when file is created def process_IN_CREATE(self, event: pyinotify.Event) -> None: - if event.pathname.rstrip("~") == self.config.path: - self.config.update_config() \ No newline at end of file + self._process_update(event) + + def process_IN_MOVED_FROM(self, event: pyinotify.Event) -> None: + self._process_update(event) + + def process_IN_MOVED_TO(self, event: pyinotify.Event) -> None: + self._process_update(event) \ No newline at end of file From 81536c82c7b0f6a30191cb1dfe6a808a4529a45f Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Mon, 18 Mar 2024 20:01:02 -0500 Subject: [PATCH 09/16] call notifier.start() manually to prevent hanging --- auto_cpufreq/bin/auto_cpufreq.py | 34 +++++++++++--------------------- auto_cpufreq/utils/config.py | 3 +-- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index f7b803cf..7c6ff2e7 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -69,37 +69,26 @@ def config_info_dialog(): if os.getenv("PKG_MARKER") == "SNAP" and dcheck == "enabled": gnome_power_detect_snap() tlp_service_detect_snap() - battery_setup() - while True: - footer() - gov_check() - cpufreqctl() - distro_info() - sysinfo() - set_autofreq() - countdown(2) elif os.getenv("PKG_MARKER") != "SNAP": gnome_power_detect() tlp_service_detect() - battery_setup() - while True: - footer() - gov_check() - cpufreqctl() - distro_info() - sysinfo() - set_autofreq() - countdown(2) - else: - pass - #"daemon_not_found" is not defined - #daemon_not_found() + battery_setup() + conf.notifier.start() + while True: + footer() + gov_check() + cpufreqctl() + distro_info() + sysinfo() + set_autofreq() + countdown(2) elif monitor: config_info_dialog() root_check() print('\nNote: You can quit monitor mode by pressing "ctrl+c"') battery_setup() battery_get_thresholds() + conf.notifier.start() if os.getenv("PKG_MARKER") == "SNAP": gnome_power_detect_snap() tlp_service_detect_snap() @@ -123,6 +112,7 @@ def config_info_dialog(): time.sleep(1) battery_setup() battery_get_thresholds() + conf.notifier.start() if os.getenv("PKG_MARKER") == "SNAP": gnome_power_detect_snap() tlp_service_detect_snap() diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index ae68b82f..1ae6d4a5 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -11,9 +11,8 @@ def __init__(self) -> None: self.config_handler = ConfigEventHandler(self) # check for file changes using threading - notifier: pyinotify.ThreadedNotifierNotifier = pyinotify.ThreadedNotifier( + self.notifier: pyinotify.ThreadedNotifier = pyinotify.ThreadedNotifier( self.watch_manager, self.config_handler) - notifier.start() def set_path(self, path: str) -> None: self.path = path; From 00c963080e61d5bd3e25114609f51014854f31ea Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Mon, 18 Mar 2024 20:05:59 -0500 Subject: [PATCH 10/16] config: update comments --- auto_cpufreq/utils/config_event_handler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py index 332cdd6d..460ab603 100644 --- a/auto_cpufreq/utils/config_event_handler.py +++ b/auto_cpufreq/utils/config_event_handler.py @@ -8,20 +8,22 @@ def _process_update(self, event: pyinotify.Event): if event.pathname.rstrip("~") == self.config.path: self.config.update_config() - # activates when file is modified + # activates when auto-cpufreq config file is modified def process_IN_MODIFY(self, event: pyinotify.Event) -> None: self._process_update(event) - # activates when file is deleted + # activates when auto-cpufreq config file is deleted def process_IN_DELETE_SELF(self, event: pyinotify.Event) -> None: self._process_update(event) - # activates when file is created + # activates when auto-cpufreq config file is created def process_IN_CREATE(self, event: pyinotify.Event) -> None: self._process_update(event) + # activates when auto-cpufreq config file is moved from watched directory def process_IN_MOVED_FROM(self, event: pyinotify.Event) -> None: self._process_update(event) + # activates when auto-cpufreq config file is moved into the watched directory def process_IN_MOVED_TO(self, event: pyinotify.Event) -> None: self._process_update(event) \ No newline at end of file From 9056d7defc252e25fdf35578dfd3ca1e914c1e1e Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Mon, 1 Apr 2024 11:41:52 -0500 Subject: [PATCH 11/16] battery: fix config imports --- auto_cpufreq/battery_scripts/ideapad_acpi.py | 14 ++++++------- .../battery_scripts/ideapad_laptop.py | 20 +++++++++---------- auto_cpufreq/battery_scripts/thinkpad.py | 14 ++++++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/auto_cpufreq/battery_scripts/ideapad_acpi.py b/auto_cpufreq/battery_scripts/ideapad_acpi.py index ca5f9288..ee69d1ac 100644 --- a/auto_cpufreq/battery_scripts/ideapad_acpi.py +++ b/auto_cpufreq/battery_scripts/ideapad_acpi.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os import subprocess -from auto_cpufreq.core import get_config +from auto_cpufreq.utils.config import config def set_battery(value, mode, bat): @@ -14,9 +14,9 @@ def set_battery(value, mode, bat): def get_threshold_value(mode): - config = get_config() - if config.has_option("battery", f"{mode}_threshold"): - return config["battery"][f"{mode}_threshold"] + conf = config.get_config() + if conf.has_option("battery", f"{mode}_threshold"): + return conf["battery"][f"{mode}_threshold"] else: if mode == "start": @@ -26,11 +26,11 @@ def get_threshold_value(mode): def ideapad_acpi_setup(): - config = get_config() + conf = config.get_config() - if not config.has_option("battery", "enable_thresholds"): + if not conf.has_option("battery", "enable_thresholds"): return - if not config["battery"]["enable_thresholds"] == "true": + if not conf["battery"]["enable_thresholds"] == "true": return battery_count = len([name for name in os.listdir( diff --git a/auto_cpufreq/battery_scripts/ideapad_laptop.py b/auto_cpufreq/battery_scripts/ideapad_laptop.py index f424a832..77304be4 100644 --- a/auto_cpufreq/battery_scripts/ideapad_laptop.py +++ b/auto_cpufreq/battery_scripts/ideapad_laptop.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os import subprocess -from auto_cpufreq.core import get_config +from auto_cpufreq.utils.config import config def set_battery(value, mode, bat): @@ -14,9 +14,9 @@ def set_battery(value, mode, bat): def get_threshold_value(mode): - config = get_config() - if config.has_option("battery", f"{mode}_threshold"): - return config["battery"][f"{mode}_threshold"] + conf = config.get_config() + if conf.has_option("battery", f"{mode}_threshold"): + return conf["battery"][f"{mode}_threshold"] else: if mode == "start": return 0 @@ -52,21 +52,21 @@ def check_conservation_mode(): def ideapad_laptop_setup(): - config = get_config() + conf = config.get_config() - if not config.has_option("battery", "enable_thresholds"): + if not conf.has_option("battery", "enable_thresholds"): return - if not config["battery"]["enable_thresholds"] == "true": + if not conf["battery"]["enable_thresholds"] == "true": return battery_count = len([name for name in os.listdir( "/sys/class/power_supply/") if name.startswith('BAT')]) - if config.has_option("battery", "ideapad_laptop_conservation_mode"): - if config["battery"]["ideapad_laptop_conservation_mode"] == "true": + if conf.has_option("battery", "ideapad_laptop_conservation_mode"): + if conf["battery"]["ideapad_laptop_conservation_mode"] == "true": conservation_mode(1) return - if config["battery"]["ideapad_laptop_conservation_mode"] == "false": + if conf["battery"]["ideapad_laptop_conservation_mode"] == "false": conservation_mode(0) if check_conservation_mode() is False: diff --git a/auto_cpufreq/battery_scripts/thinkpad.py b/auto_cpufreq/battery_scripts/thinkpad.py index 214ed402..cab5133c 100644 --- a/auto_cpufreq/battery_scripts/thinkpad.py +++ b/auto_cpufreq/battery_scripts/thinkpad.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os import subprocess -from auto_cpufreq.core import get_config +from auto_cpufreq.utils.config import config def set_battery(value, mode, bat): @@ -14,9 +14,9 @@ def set_battery(value, mode, bat): def get_threshold_value(mode): - config = get_config() - if config.has_option("battery", f"{mode}_threshold"): - return config["battery"][f"{mode}_threshold"] + conf = config.get_config() + if conf.has_option("battery", f"{mode}_threshold"): + return conf["battery"][f"{mode}_threshold"] else: if mode == "start": @@ -26,11 +26,11 @@ def get_threshold_value(mode): def thinkpad_setup(): - config = get_config() + conf = config.get_config() - if not config.has_option("battery", "enable_thresholds"): + if not conf.has_option("battery", "enable_thresholds"): return - if not config["battery"]["enable_thresholds"] == "true": + if not conf["battery"]["enable_thresholds"] == "true": return battery_count = len([name for name in os.listdir( From 5397ca53613a9391828026e9b9b4f890674bbebe Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Mon, 1 Apr 2024 11:42:09 -0500 Subject: [PATCH 12/16] config: fix config deletion detection --- auto_cpufreq/utils/config_event_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_cpufreq/utils/config_event_handler.py b/auto_cpufreq/utils/config_event_handler.py index 460ab603..7dc79bc0 100644 --- a/auto_cpufreq/utils/config_event_handler.py +++ b/auto_cpufreq/utils/config_event_handler.py @@ -13,7 +13,7 @@ def process_IN_MODIFY(self, event: pyinotify.Event) -> None: self._process_update(event) # activates when auto-cpufreq config file is deleted - def process_IN_DELETE_SELF(self, event: pyinotify.Event) -> None: + def process_IN_DELETE(self, event: pyinotify.Event) -> None: self._process_update(event) # activates when auto-cpufreq config file is created From 4cdcf74acde3bb562f33cee9cd039acb805b5902 Mon Sep 17 00:00:00 2001 From: Steven Braun Date: Wed, 3 Apr 2024 17:40:30 +0200 Subject: [PATCH 13/16] Add load from user config in XDG_CONFIG_HOME if available (#672) * Add load from user config from in XDG_CONFIG_HOME if available This update introduces the flexibility to load the configuration file from multiple locations, prioritizing user preferences and system standards. Previously, the configuration was strictly read from a hardcoded system path (`/etc/auto-cpufreq.conf`). Now, the application first checks if the user has specified a configuration file path via command line arguments. If not, it looks for a configuration file in the user's config directory (`$XDG_CONFIG_HOME/auto-cpufreq/auto-cpufreq.conf`). If neither is found, it defaults to the original system-wide configuration file. This allows users to add their auto-cpufreq configuration to their dotfiles. * If --config is set but invalid, exit with error * Remove redundant empty string check on config file path * Remove duplicate isfile check for config path See also: https://github.com/AdnanHodzic/auto-cpufreq/pull/672#discussion_r1548003119 * Update configuration options in README See also: #672 --- README.md | 6 +++++- auto_cpufreq/bin/auto_cpufreq.py | 8 +++++--- auto_cpufreq/core.py | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5d690c1e..abc1fa04 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,11 @@ See [`--force` flag](#overriding-governor) for more info. You can configure separate profiles for the battery and power supply. These profiles will let you pick which governor to use, as well as how and when turbo boost is enabled. The possible values for turbo boost behavior are `always`, `auto`, and `never`. The default behavior is `auto`, which only activates turbo during high load. -By default, auto-cpufreq does not use the config file! If you wish to use it, the location where it needs to be placed to be read automatically is: `/etc/auto-cpufreq.conf` +By default, auto-cpufreq does not use a config file. If you wish to configure auto-cpufreq statically, we look for a configuration file in the following order: + +1. Commandline argument: `--config ` if passed as commandline argument to `auto-cpufreq` +2. User-specific configuration: `$XDG_CONFIG_HOME/auto-cpufreq/auto-cpufreq.conf` +3. System-wide configuration: `/etc/auto-cpufreq.conf` #### Example config file contents ```python diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index 39fb0944..b592b27b 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -28,7 +28,7 @@ @click.option( "--config", is_flag=False, - default="/etc/auto-cpufreq.conf", + required=False, help="Use config file at defined path", ) @click.option("--debug", is_flag=True, help="Show debug info (include when submitting bugs)") @@ -41,8 +41,10 @@ def main(config, daemon, debug, update, install, remove, live, log, monitor, sta # display info if config file is used def config_info_dialog(): - if get_config(config) and hasattr(get_config, "using_cfg_file"): - print("\nUsing settings defined in " + config + " file") + + config_file = find_config_file(config) + if get_config(config_file) and hasattr(get_config, "using_cfg_file"): + print("\nUsing settings defined in " + config_file + " file") # set governor override unless None or invalid if force is not None: diff --git a/auto_cpufreq/core.py b/auto_cpufreq/core.py index 66e4978a..8ba2b845 100755 --- a/auto_cpufreq/core.py +++ b/auto_cpufreq/core.py @@ -84,6 +84,40 @@ def file_stats(): auto_cpufreq_stats_file = open(auto_cpufreq_stats_path, "w") sys.stdout = auto_cpufreq_stats_file + +def find_config_file(args_config_file): + """ + Find the config file to use. + + Look for a config file in the following priorization order: + 1. Command line argument + 2. User config file + 3. System config file + + :param args_config_file: Path to the config file provided as a command line argument + :return: The path to the config file to use + """ + + # Prepare paths + home = os.getenv("HOME") + user_config_dir = os.getenv("XDG_CONFIG_HOME", default=os.path.join(home, ".config")) + user_config_file = os.path.join(user_config_dir, "auto-cpufreq/auto-cpufreq.conf") + system_config_file = "/etc/auto-cpufreq.conf" + + if args_config_file is not None: # (1) Command line argument was specified + # Check if the config file path points to a valid file + if os.path.isfile(args_config_file): + return args_config_file + else: + # Not a valid file + print(f"Config file specified with '--config {args_config_file}' not found.") + sys.exit(1) + elif os.path.isfile(os.path.join(user_config_file)): # (2) User config file + return user_config_file + else: # (3) System config file (default if nothing else is found) + return system_config_file + + def get_config(config_file=""): if not hasattr(get_config, "config"): get_config.config = configparser.ConfigParser() From bc99a285e061c4cf3db2b918895c3e5780a40ddb Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Wed, 3 Apr 2024 11:51:08 -0500 Subject: [PATCH 14/16] config: move find_config_file function and fix finding home directory --- auto_cpufreq/bin/auto_cpufreq.py | 7 +++--- auto_cpufreq/core.py | 33 -------------------------- auto_cpufreq/utils/config.py | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index 28be6c2b..e01bf1fc 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -14,7 +14,7 @@ from auto_cpufreq.core import * from auto_cpufreq.power_helper import * from auto_cpufreq.battery_scripts.battery import * -from auto_cpufreq.utils.config import config as conf +from auto_cpufreq.utils.config import config as conf, find_config_file # cli @click.command() @click.option("--monitor", is_flag=True, help="Monitor and see suggestions for CPU optimizations") @@ -41,10 +41,11 @@ def main(config, daemon, debug, update, install, remove, live, log, monitor, stats, version, donate, force, get_state, completions): # display info if config file is used - conf.set_path(config) + config_path = find_config_file(config) + conf.set_path(config_path) def config_info_dialog(): if conf.has_config(): - print("\nUsing settings defined in " + config + " file") + print("\nUsing settings defined in " + config_path + " file") # set governor override unless None or invalid if force is not None: diff --git a/auto_cpufreq/core.py b/auto_cpufreq/core.py index 1fd351c5..05790bfd 100755 --- a/auto_cpufreq/core.py +++ b/auto_cpufreq/core.py @@ -85,39 +85,6 @@ def file_stats(): sys.stdout = auto_cpufreq_stats_file -def find_config_file(args_config_file): - """ - Find the config file to use. - - Look for a config file in the following priorization order: - 1. Command line argument - 2. User config file - 3. System config file - - :param args_config_file: Path to the config file provided as a command line argument - :return: The path to the config file to use - """ - - # Prepare paths - home = os.getenv("HOME") - user_config_dir = os.getenv("XDG_CONFIG_HOME", default=os.path.join(home, ".config")) - user_config_file = os.path.join(user_config_dir, "auto-cpufreq/auto-cpufreq.conf") - system_config_file = "/etc/auto-cpufreq.conf" - - if args_config_file is not None: # (1) Command line argument was specified - # Check if the config file path points to a valid file - if os.path.isfile(args_config_file): - return args_config_file - else: - # Not a valid file - print(f"Config file specified with '--config {args_config_file}' not found.") - sys.exit(1) - elif os.path.isfile(os.path.join(user_config_file)): # (2) User config file - return user_config_file - else: # (3) System config file (default if nothing else is found) - return system_config_file - - def get_override(): if os.path.isfile(governor_override_state): with open(governor_override_state, "rb") as store: diff --git a/auto_cpufreq/utils/config.py b/auto_cpufreq/utils/config.py index 1ae6d4a5..650049a8 100644 --- a/auto_cpufreq/utils/config.py +++ b/auto_cpufreq/utils/config.py @@ -1,7 +1,47 @@ from configparser import ConfigParser, ParsingError from auto_cpufreq.utils.config_event_handler import ConfigEventHandler import pyinotify +from subprocess import run, PIPE import os +import sys + +def find_config_file(args_config_file: str | None) -> str: + """ + Find the config file to use. + + Look for a config file in the following priorization order: + 1. Command line argument + 2. User config file + 3. System config file + + :param args_config_file: Path to the config file provided as a command line argument + :return: The path to the config file to use + """ + + # Prepare paths + + # use $SUDO_USER or $USER to get home dir since sudo can't access + # user env vars + home = run(["getent passwd ${SUDO_USER:-$USER} | cut -d: -f6"], + shell=True, + stdout=PIPE, + universal_newlines=True).stdout.rstrip() + user_config_dir = os.getenv("XDG_CONFIG_HOME", default=os.path.join(home, ".config")) + user_config_file = os.path.join(user_config_dir, "auto-cpufreq/auto-cpufreq.conf") + system_config_file = "/etc/auto-cpufreq.conf" + + if args_config_file is not None: # (1) Command line argument was specified + # Check if the config file path points to a valid file + if os.path.isfile(args_config_file): + return args_config_file + else: + # Not a valid file + print(f"Config file specified with '--config {args_config_file}' not found.") + sys.exit(1) + elif os.path.isfile(user_config_file): # (2) User config file + return user_config_file + else: # (3) System config file (default if nothing else is found) + return system_config_file class _Config: def __init__(self) -> None: From 00463b39d19e308ea16215a229eb2e26b526a17d Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Tue, 23 Apr 2024 21:48:47 -0500 Subject: [PATCH 15/16] auto_cpufreq: fix hanging on --daemon, --live, and --monitor --- auto_cpufreq/bin/auto_cpufreq.py | 43 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/auto_cpufreq/bin/auto_cpufreq.py b/auto_cpufreq/bin/auto_cpufreq.py index e01bf1fc..26cef3f3 100755 --- a/auto_cpufreq/bin/auto_cpufreq.py +++ b/auto_cpufreq/bin/auto_cpufreq.py @@ -76,13 +76,17 @@ def config_info_dialog(): battery_setup() conf.notifier.start() while True: - footer() - gov_check() - cpufreqctl() - distro_info() - sysinfo() - set_autofreq() - countdown(2) + try: + footer() + gov_check() + cpufreqctl() + distro_info() + sysinfo() + set_autofreq() + countdown(2) + except KeyboardInterrupt: + break; + conf.notifier.stop() elif monitor: config_info_dialog() root_check() @@ -97,15 +101,19 @@ def config_info_dialog(): gnome_power_detect() tlp_service_detect() while True: - time.sleep(1) - running_daemon_check() - footer() - gov_check() - cpufreqctl() - distro_info() - sysinfo() - mon_autofreq() - countdown(2) + try: + time.sleep(1) + running_daemon_check() + footer() + gov_check() + cpufreqctl() + distro_info() + sysinfo() + mon_autofreq() + countdown(2) + except KeyboardInterrupt: + break + conf.notifier.stop() elif live: root_check() config_info_dialog() @@ -134,7 +142,8 @@ def config_info_dialog(): except KeyboardInterrupt: gnome_power_start_live() print("") - sys.exit() + break + conf.notifier.stop() elif stats: not_running_daemon_check() config_info_dialog() From e3678c1767d2791a16077b2dbdae0f20214d2141 Mon Sep 17 00:00:00 2001 From: shadeyg56 Date: Sun, 28 Apr 2024 22:35:24 -0500 Subject: [PATCH 16/16] swap pyinotify for patched version --- nix/default.nix | 21 ++++++++++++++++----- poetry.lock | 13 +++++++++---- pyproject.toml | 2 +- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/nix/default.nix b/nix/default.nix index ea57104f..e0536b73 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,11 +1,12 @@ { lib, - python310Packages, + python3Packages, pkgs, fetchFromGitHub, }: let - psutilGit = python310Packages.psutil.overrideAttrs (oldAttrs: { + + psutil = python3Packages.psutil.overrideAttrs (oldAttrs: { src = fetchFromGitHub { owner = "giampaolo"; repo = "psutil"; @@ -13,8 +14,18 @@ let sha256 = "61JwXP/cZrXqdBnb2J0kdDJoKpltO62KcpM0sYX6g1A="; }; }); + + pyinotify = python3Packages.pyinotify.overrideAttrs (oldAttrs: { + src = fetchFromGitHub { + owner = "shadeyg56"; + repo = "pyinotify-3.12"; + rev = "923cebec3a2a84c7e38c9e68171eb93f5d07ce5d"; + hash = "sha256-714CximEK4YhIqDmvqJYOUGs39gvDkWGrkNrXwxT8iM="; + }; + }); + in -python310Packages.buildPythonPackage { +python3Packages.buildPythonPackage { # use pyproject.toml instead of setup.py format = "pyproject"; @@ -24,9 +35,9 @@ python310Packages.buildPythonPackage { nativeBuildInputs = with pkgs; [wrapGAppsHook gobject-introspection]; - buildInputs = with pkgs; [gtk3 python310Packages.poetry-core]; + buildInputs = with pkgs; [gtk3 python3Packages.poetry-core]; - propagatedBuildInputs = with python310Packages; [requests pygobject3 click distro psutilGit setuptools poetry-dynamic-versioning pyinotify]; + propagatedBuildInputs = with python3Packages; [requests pygobject3 click distro psutil setuptools poetry-dynamic-versioning pyinotify]; doCheck = false; pythonImportsCheck = ["auto_cpufreq"]; diff --git a/poetry.lock b/poetry.lock index 85e51f52..265794ed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -877,9 +877,14 @@ version = "0.9.6" description = "Linux filesystem events monitoring" optional = false python-versions = "*" -files = [ - {file = "pyinotify-0.9.6.tar.gz", hash = "sha256:9c998a5d7606ca835065cdabc013ae6c66eb9ea76a00a1e3bc6e0cfe2b4f71f4"}, -] +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/shadeyg56/pyinotify-3.12" +reference = "HEAD" +resolved_reference = "923cebec3a2a84c7e38c9e68171eb93f5d07ce5d" [[package]] name = "pyproject-hooks" @@ -1295,4 +1300,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "e3f4ec63d598a563c21fca6d7c885183baf257663537ca010dec33674163b175" +content-hash = "ee73b2db6a43cac87120f38c93d0a8a297bec52f1346b55bc0ca2992aa464482" diff --git a/pyproject.toml b/pyproject.toml index 4bdfc1cc..47fcc502 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ click = "^8.1.0" distro = "^1.8.0" requests = "^2.31.0" PyGObject = "^3.46.0" -pyinotify = "^0.9.6" +pyinotify = {git = "https://github.com/shadeyg56/pyinotify-3.12"} [tool.poetry.group.dev.dependencies] poetry = "^1.6.1"