diff --git a/.gitignore b/.gitignore index 1d47b25..c9bf790 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ env*/* pyenv* *.loop *.spec +venv/ +.venv/ diff --git a/s_tui/about_menu.py b/s_tui/about_menu.py index 351dc36..df57504 100644 --- a/s_tui/about_menu.py +++ b/s_tui/about_menu.py @@ -43,34 +43,31 @@ \n\ """ -ABOUT_MESSAGE += "s-tui " + __version__ +\ - " Released under GNU GPLv2 " +ABOUT_MESSAGE += "s-tui " + __version__ + " Released under GNU GPLv2 " MESSAGE_LEN = 20 class AboutMenu: - """Displays the About message menu """ + """Displays the About message menu""" + MAX_TITLE_LEN = 50 def __init__(self, return_fn): - self.return_fn = return_fn self.about_message = ABOUT_MESSAGE self.time_out_ctrl = urwid.Text(self.about_message) - cancel_button = urwid.Button('Exit', on_press=self.on_cancel) - cancel_button._label.align = 'center' + cancel_button = urwid.Button("Exit", on_press=self.on_cancel) + cancel_button._label.align = "center" if_buttons = urwid.Columns([cancel_button]) - title = urwid.Text(('bold text', u" About Menu \n"), 'center') + title = urwid.Text(("bold text", " About Menu \n"), "center") - self.titles = [title, - self.time_out_ctrl, - if_buttons] + self.titles = [title, self.time_out_ctrl, if_buttons] self.main_window = urwid.LineBox(ViListBox(self.titles)) diff --git a/s_tui/help_menu.py b/s_tui/help_menu.py index db353e2..522ca33 100644 --- a/s_tui/help_menu.py +++ b/s_tui/help_menu.py @@ -52,34 +52,32 @@ class HelpMenu: - """ HelpMenu is a widget containing instructions on usage of s-tui""" + """HelpMenu is a widget containing instructions on usage of s-tui""" + MAX_TITLE_LEN = 90 def __init__(self, return_fn): - self.return_fn = return_fn self.help_message = HELP_MESSAGE self.time_out_ctrl = urwid.Text(self.help_message) - cancel_button = urwid.Button(('Exit'), on_press=self.on_cancel) - cancel_button._label.align = 'center' + cancel_button = urwid.Button(("Exit"), on_press=self.on_cancel) + cancel_button._label.align = "center" if_buttons = urwid.Columns([cancel_button]) - title = urwid.Text(('bold text', u" Help Menu \n"), 'center') + title = urwid.Text(("bold text", " Help Menu \n"), "center") - self.titles = [title, - self.time_out_ctrl, - if_buttons] + self.titles = [title, self.time_out_ctrl, if_buttons] self.main_window = urwid.LineBox(ViListBox(self.titles)) def get_size(self): - """ returns size of HelpMenu""" + """returns size of HelpMenu""" return MESSAGE_LEN + 3, self.MAX_TITLE_LEN def on_cancel(self, w): - """ Returns to original widget""" + """Returns to original widget""" self.return_fn() diff --git a/s_tui/helper_functions.py b/s_tui/helper_functions.py index 4ded596..d7c19af 100644 --- a/s_tui/helper_functions.py +++ b/s_tui/helper_functions.py @@ -30,7 +30,7 @@ from collections import OrderedDict -__version__ = "1.1.4" +__version__ = "1.1.5" _DEFAULT = object() PY3 = sys.version_info[0] == 3 @@ -46,24 +46,28 @@ def get_processor_name(): - """ Returns the processor name in the system """ + """Returns the processor name in the system""" if platform.system() == "Linux": with open("/proc/cpuinfo", "rb") as cpuinfo: all_info = cpuinfo.readlines() for line in all_info: - if b'model name' in line: - return re.sub(b'.*model name.*:', b'', line, 1) + if b"model name" in line: + return re.sub(b".*model name.*:", b"", line, 1) elif platform.system() == "FreeBSD": cmd = ["sysctl", "-n", "hw.model"] process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, ) str_value = process.stdout.read() return str_value elif platform.system() == "Darwin": - cmd = ['sysctl', '-n', 'machdep.cpu.brand_string'] + cmd = ["sysctl", "-n", "machdep.cpu.brand_string"] process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, ) str_value = process.stdout.read() return str_value @@ -72,25 +76,25 @@ def get_processor_name(): def kill_child_processes(parent_proc): - """ Kills a process and all its children """ + """Kills a process and all its children""" logging.debug("Killing stress process") try: for proc in parent_proc.children(recursive=True): - logging.debug('Killing %s', proc) + logging.debug("Killing %s", proc) proc.kill() parent_proc.kill() except AttributeError: - logging.debug('No such process') - logging.debug('Could not kill process') + logging.debug("No such process") + logging.debug("Could not kill process") def output_to_csv(sources, csv_writeable_file): """Print statistics to csv file""" file_exists = os.path.isfile(csv_writeable_file) - with open(csv_writeable_file, 'a') as csvfile: + with open(csv_writeable_file, "a") as csvfile: csv_dict = OrderedDict() - csv_dict.update({'Time': time.strftime("%Y-%m-%d_%H:%M:%S")}) + csv_dict.update({"Time": time.strftime("%Y-%m-%d_%H:%M:%S")}) summaries = [val for key, val in sources.items()] for summarie in summaries: update_dict = dict() @@ -139,11 +143,11 @@ def get_user_config_dir(): """ Return the path to the user s-tui config directory """ - user_home = os.getenv('XDG_CONFIG_HOME') + user_home = os.getenv("XDG_CONFIG_HOME") if user_home is None or not user_home: - config_path = os.path.expanduser(os.path.join('~', '.config', 's-tui')) + config_path = os.path.expanduser(os.path.join("~", ".config", "s-tui")) else: - config_path = os.path.join(user_home, 's-tui') + config_path = os.path.join(user_home, "s-tui") return config_path @@ -152,9 +156,9 @@ def get_config_dir(): """ Return the path to the user home config directory """ - user_home = os.getenv('XDG_CONFIG_HOME') + user_home = os.getenv("XDG_CONFIG_HOME") if user_home is None or not user_home: - config_path = os.path.expanduser(os.path.join('~', '.config')) + config_path = os.path.expanduser(os.path.join("~", ".config")) else: config_path = user_home @@ -165,12 +169,13 @@ def get_user_config_file(): """ Return the path to the user s-tui config directory """ - user_home = os.getenv('XDG_CONFIG_HOME') + user_home = os.getenv("XDG_CONFIG_HOME") if user_home is None or not user_home: - config_path = os.path.expanduser(os.path.join('~', '.config', - 's-tui', 's-tui.conf')) + config_path = os.path.expanduser( + os.path.join("~", ".config", "s-tui", "s-tui.conf") + ) else: - config_path = os.path.join(user_home, 's-tui', 's-tui.conf') + config_path = os.path.join(user_home, "s-tui", "s-tui.conf") return config_path @@ -212,7 +217,7 @@ def make_user_config_dir(): if not user_config_dir_exists(): try: os.mkdir(config_path) - os.mkdir(os.path.join(config_path, 'hooks.d')) + os.mkdir(os.path.join(config_path, "hooks.d")) except OSError: return None @@ -220,24 +225,25 @@ def make_user_config_dir(): def seconds_to_text(secs): - """ Converts seconds to a string of hours:minutes:seconds """ - hours = (secs)//3600 - minutes = (secs - hours*3600)//60 - seconds = secs - hours*3600 - minutes*60 + """Converts seconds to a string of hours:minutes:seconds""" + hours = (secs) // 3600 + minutes = (secs - hours * 3600) // 60 + seconds = secs - hours * 3600 - minutes * 60 return "%02d:%02d:%02d" % (hours, minutes, seconds) def str_to_bool(string): - """ Converts a string to a boolean """ - if string == 'True': + """Converts a string to a boolean""" + if string == "True": return True - if string == 'False': + if string == "False": return False raise ValueError def which(program): - """ Find the path of an executable """ + """Find the path of an executable""" + def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) @@ -264,8 +270,8 @@ def _open_text(fname, **kwargs): On Python 2 this is just an alias for open(name, 'rt'). """ if PY3: - kwargs.setdefault('encoding', ENCODING) - kwargs.setdefault('errors', ENCODING_ERRS) + kwargs.setdefault("encoding", ENCODING) + kwargs.setdefault("errors", ENCODING_ERRS) return open(fname, "rt", **kwargs) diff --git a/s_tui/s_tui.py b/s_tui/s_tui.py index 1c52382..76db817 100755 --- a/s_tui/s_tui.py +++ b/s_tui/s_tui.py @@ -49,6 +49,7 @@ from s_tui.help_menu import HELP_MESSAGE from s_tui.stress_menu import StressMenu from s_tui.sensors_menu import SensorsMenu + # Helpers from s_tui.helper_functions import __version__ from s_tui.helper_functions import get_processor_name @@ -64,6 +65,7 @@ from s_tui.helper_functions import seconds_to_text from s_tui.helper_functions import str_to_bool from s_tui.helper_functions import which + # Ui Elements from s_tui.sturwid.ui_elements import ViListBox from s_tui.sturwid.ui_elements import radio_button @@ -71,6 +73,7 @@ from s_tui.sturwid.ui_elements import DEFAULT_PALETTE from s_tui.sturwid.bar_graph_vector import BarGraphVector from s_tui.sturwid.summary_text_list import SummaryTextList + # Sources from s_tui.sources.util_source import UtilSource from s_tui.sources.freq_source import FreqSource @@ -81,17 +84,19 @@ UPDATE_INTERVAL = 1 HOOK_INTERVAL = 30 * 1000 -DEGREE_SIGN = u'\N{DEGREE SIGN}' +DEGREE_SIGN = "\N{DEGREE SIGN}" ZERO_TIME = seconds_to_text(0) DEFAULT_LOG_FILE = "_s-tui.log" DEFAULT_CSV_FILE = "s-tui_log_" + time.strftime("%Y-%m-%d_%H_%M_%S") + ".csv" -VERSION_MESSAGE = \ - "s-tui " + __version__ +\ - " - (C) 2017-2020 Alex Manuskin, Gil Tsuker\n\ +VERSION_MESSAGE = ( + "s-tui " + + __version__ + + " - (C) 2017-2020 Alex Manuskin, Gil Tsuker\n\ Released under GNU GPLv2" +) ERROR_MESSAGE = "\n\ Oops! s-tui has encountered a fatal error\n\ @@ -101,23 +106,23 @@ class MainLoop(urwid.MainLoop): - """ Inherit urwid Mainloop to catch special character inputs""" + """Inherit urwid Mainloop to catch special character inputs""" + def signal_handler(self, frame): """signal handler for properly exiting Ctrl+C""" graph_controller.stress_conroller.kill_stress_process() raise urwid.ExitMainLoop() def unhandled_input(self, uinput): - logging.debug('Caught %s', uinput) - if uinput == 'q': + logging.debug("Caught %s", uinput) + if uinput == "q": graph_controller.stress_conroller.kill_stress_process() raise urwid.ExitMainLoop() - if uinput == 'f1': - graph_controller.view.on_help_menu_open( - graph_controller.view.main_window_w) + if uinput == "f1": + graph_controller.view.on_help_menu_open(graph_controller.view.main_window_w) - if uinput == 'esc': + if uinput == "esc": graph_controller.view.on_menu_close() signal.signal(signal.SIGINT, signal_handler) @@ -130,39 +135,39 @@ class StressController: """ def __init__(self, stress_installed, firestarter_installed): - self.stress_modes = ['Monitor'] + self.stress_modes = ["Monitor"] if stress_installed: - self.stress_modes.append('Stress') + self.stress_modes.append("Stress") if firestarter_installed: - self.stress_modes.append('FIRESTARTER') + self.stress_modes.append("FIRESTARTER") self.current_mode = self.stress_modes[0] self.stress_process = None def get_modes(self): - """ Returns all possible stress_modes for stress operations """ + """Returns all possible stress_modes for stress operations""" return self.stress_modes def get_current_mode(self): - """ Returns the current stress test mode, monitor/stress/other """ + """Returns the current stress test mode, monitor/stress/other""" return self.current_mode def set_mode(self, mode): - """ Sets a stress test mode monitor/stress/other """ + """Sets a stress test mode monitor/stress/other""" self.current_mode = mode def get_stress_process(self): - """ Returns the current external stress process running """ + """Returns the current external stress process running""" return self.stress_process def set_stress_process(self, proc): - """ Sets the current stress process running """ + """Sets the current stress process running""" self.stress_process = proc def kill_stress_process(self): - """ Kills the current running stress process """ + """Kills the current running stress process""" try: kill_child_processes(self.stress_process) except psutil.NoSuchProcess: @@ -170,11 +175,12 @@ def kill_stress_process(self): self.stress_process = None def start_stress(self, stress_cmd): - """ Starts a new stress process with a given cmd """ - with open(os.devnull, 'w') as dev_null: + """Starts a new stress process with a given cmd""" + with open(os.devnull, "w") as dev_null: try: - stress_proc = subprocess.Popen(stress_cmd, stdout=dev_null, - stderr=dev_null) + stress_proc = subprocess.Popen( + stress_cmd, stdout=dev_null, stderr=dev_null + ) self.set_stress_process(psutil.Process(stress_proc.pid)) except OSError: logging.debug("Unable to start stress") @@ -187,6 +193,7 @@ class GraphView(urwid.WidgetPlaceholder): The GraphView can change the state of the graph, since it provides the UI The change is state should be reflected in the GraphController """ + def __init__(self, controller): # constants self.left_margin = 0 @@ -198,9 +205,10 @@ def __init__(self, controller): # general urwid items self.clock_view = urwid.Text(ZERO_TIME, align="center") - self.refresh_rate_ctrl = urwid.Edit((u'Refresh[s]:'), - self.controller.refresh_rate) - self.hline = urwid.AttrWrap(urwid.SolidFill(u' '), 'line') + self.refresh_rate_ctrl = urwid.Edit( + ("Refresh[s]:"), self.controller.refresh_rate + ) + self.hline = urwid.AttrWrap(urwid.SolidFill(" "), "line") self.mode_buttons = [] @@ -211,25 +219,27 @@ def __init__(self, controller): self.graph_place_holder = urwid.WidgetPlaceholder(urwid.Pile([])) # construct the various menus during init phase - self.stress_menu = StressMenu(self.on_menu_close, - self.controller.stress_exe) + self.stress_menu = StressMenu(self.on_menu_close, self.controller.stress_exe) self.help_menu = HelpMenu(self.on_menu_close) self.about_menu = AboutMenu(self.on_menu_close) - self.graphs_menu = SensorsMenu(self.on_graphs_menu_close, - self.controller.sources, - self.controller.graphs_default_conf) - self.summary_menu = SensorsMenu(self.on_summary_menu_close, - self.controller.sources, - self.controller.summary_default_conf) + self.graphs_menu = SensorsMenu( + self.on_graphs_menu_close, + self.controller.sources, + self.controller.graphs_default_conf, + ) + self.summary_menu = SensorsMenu( + self.on_summary_menu_close, + self.controller.sources, + self.controller.summary_default_conf, + ) # call super urwid.WidgetPlaceholder.__init__(self, self.main_window()) - urwid.connect_signal(self.refresh_rate_ctrl, 'change', - self.update_refresh_rate) + urwid.connect_signal(self.refresh_rate_ctrl, "change", self.update_refresh_rate) def update_refresh_rate(self, _, new_refresh_rate): # Special case of 'q' in refresh rate - if 'q' in new_refresh_rate: + if "q" in new_refresh_rate: self.on_exit_program() try: @@ -238,15 +248,16 @@ def update_refresh_rate(self, _, new_refresh_rate): else: self.controller.refresh_rate = new_refresh_rate except ValueError: - self.controller.refresh_rate = '2.0' + self.controller.refresh_rate = "2.0" def update_displayed_information(self): - """ Update all the graphs that are being displayed """ + """Update all the graphs that are being displayed""" for source in self.controller.sources: source_name = source.get_source_name() - if (any(self.graphs_menu.active_sensors[source_name]) or - any(self.summary_menu.active_sensors[source_name])): + if any(self.graphs_menu.active_sensors[source_name]) or any( + self.summary_menu.active_sensors[source_name] + ): source.update() for graph in self.visible_graphs.values(): @@ -265,9 +276,12 @@ def update_displayed_information(self): pass # Only update clock if not is stress mode - if self.controller.stress_conroller.get_current_mode() != 'Monitor': - self.clock_view.set_text(seconds_to_text( - (timeit.default_timer() - self.controller.stress_start_time))) + if self.controller.stress_conroller.get_current_mode() != "Monitor": + self.clock_view.set_text( + seconds_to_text( + (timeit.default_timer() - self.controller.stress_start_time) + ) + ) def on_reset_button(self, _): """Reset graph data and display empty graph""" @@ -292,8 +306,7 @@ def on_graphs_menu_close(self, update): are active in the view""" logging.info("closing sensor menu, update=%s", update) if update: - for sensor, visible_sensors in \ - self.graphs_menu.active_sensors.items(): + for sensor, visible_sensors in self.graphs_menu.active_sensors.items(): self.graphs[sensor].set_visible_graphs(visible_sensors) # If not sensor is selected, do not display the graph if sensor in self.visible_graphs and not any(visible_sensors): @@ -312,62 +325,69 @@ def on_summary_menu_close(self, update): are active in the view""" logging.info("closing summary_menu menu, update=%s", update) if update: - for sensor, visible_sensors in \ - self.summary_menu.active_sensors.items(): - self.visible_summaries[sensor].update_visibility( - visible_sensors) + for sensor, visible_sensors in self.summary_menu.active_sensors.items(): + self.visible_summaries[sensor].update_visibility(visible_sensors) - self.main_window_w.base_widget[0].body[self.summary_widget_index] =\ - self._generate_summaries() + self.main_window_w.base_widget[0].body[ + self.summary_widget_index + ] = self._generate_summaries() self.original_widget = self.main_window_w def on_stress_menu_open(self, widget): """Open stress options""" - self.original_widget = urwid.Overlay(self.stress_menu.main_window, - self.original_widget, - ('relative', self.left_margin), - self.stress_menu.get_size()[1], - ('relative', self.top_margin), - self.stress_menu.get_size()[0]) + self.original_widget = urwid.Overlay( + self.stress_menu.main_window, + self.original_widget, + ("fixed left", 1), + self.stress_menu.get_size()[1], + "top", + self.stress_menu.get_size()[0], + ) def on_help_menu_open(self, widget): """Open Help menu""" - self.original_widget = urwid.Overlay(self.help_menu.main_window, - self.original_widget, - ('relative', self.left_margin), - self.help_menu.get_size()[1], - ('relative', self.top_margin), - self.help_menu.get_size()[0]) + self.original_widget = urwid.Overlay( + self.help_menu.main_window, + self.original_widget, + ("fixed left", 1), + self.help_menu.get_size()[1], + "top", + self.help_menu.get_size()[0], + ) def on_about_menu_open(self, widget): """Open About menu""" - self.original_widget = urwid.Overlay(self.about_menu.main_window, - self.original_widget, - ('relative', self.left_margin), - self.about_menu.get_size()[1], - ('relative', self.top_margin), - self.about_menu.get_size()[0]) + self.original_widget = urwid.Overlay( + self.about_menu.main_window, + self.original_widget, + ("fixed left", 1), + self.about_menu.get_size()[1], + "top", + self.about_menu.get_size()[0], + ) def on_graphs_menu_open(self, widget): """Open Sensor menu on top of existing frame""" self.original_widget = urwid.Overlay( self.graphs_menu.main_window, self.original_widget, - ('relative', self.left_margin), + ("fixed left", 1), self.graphs_menu.get_size()[1], - ('relative', self.top_margin), - self.graphs_menu.get_size()[0]) + "top", + self.graphs_menu.get_size()[0], + ) def on_summary_menu_open(self, widget): """Open Sensor menu on top of existing frame""" self.original_widget = urwid.Overlay( self.summary_menu.main_window, self.original_widget, - ('relative', self.left_margin), + ("fixed left", 1), self.summary_menu.get_size()[1], - ('relative', self.top_margin), - self.summary_menu.get_size()[0]) + "top", + self.summary_menu.get_size()[0], + ) def on_mode_button(self, my_button, state): """Notify the controller of a new mode setting.""" @@ -383,9 +403,10 @@ def on_unicode_checkbox(self, w=None, state=False): self.controller.smooth_graph_mode = state if state: self.hline = urwid.AttrWrap( - urwid.SolidFill(u'\N{LOWER ONE QUARTER BLOCK}'), 'line') + urwid.SolidFill("\N{LOWER ONE QUARTER BLOCK}"), "line" + ) else: - self.hline = urwid.AttrWrap(urwid.SolidFill(u' '), 'line') + self.hline = urwid.AttrWrap(urwid.SolidFill(" "), "line") for graph in self.graphs.values(): graph.set_smooth_colors(state) @@ -393,104 +414,101 @@ def on_unicode_checkbox(self, w=None, state=False): self.show_graphs() def on_save_settings(self, w=None): - """ Calls controller save settings method """ + """Calls controller save settings method""" self.controller.save_settings() def on_exit_program(self, w=None): - """ Calls controller exit_program method """ + """Calls controller exit_program method""" self.controller.exit_program() def _generate_graph_controls(self): - """ Display sidebar controls. i.e. buttons, and controls""" + """Display sidebar controls. i.e. buttons, and controls""" # setup mode radio buttons stress_modes = self.controller.stress_conroller.get_modes() group = [] for mode in stress_modes: - self.mode_buttons.append(radio_button(group, mode, - self.on_mode_button)) + self.mode_buttons.append(radio_button(group, mode, self.on_mode_button)) # Set default radio button to "Monitor" mode self.mode_buttons[0].set_state(True, do_callback=False) # Create list of buttons control_options = list() - control_options.append(button('Graphs', - self.on_graphs_menu_open)) - control_options.append(button('Summaries', - self.on_summary_menu_open)) + control_options.append(button("Graphs", self.on_graphs_menu_open)) + control_options.append(button("Summaries", self.on_summary_menu_open)) if self.controller.stress_exe: - control_options.append(button('Stress Options', - self.on_stress_menu_open)) + control_options.append(button("Stress Options", self.on_stress_menu_open)) control_options.append(button("Reset", self.on_reset_button)) - control_options.append(button('Help', self.on_help_menu_open)) - control_options.append(button('About', self.on_about_menu_open)) - control_options.append(button("Save Settings", - self.on_save_settings)) + control_options.append(button("Help", self.on_help_menu_open)) + control_options.append(button("About", self.on_about_menu_open)) + control_options.append(button("Save Settings", self.on_save_settings)) control_options.append(button("Quit", self.on_exit_program)) # Create the menu - animate_controls = urwid.GridFlow(control_options, 18, 2, 0, 'center') + animate_controls = urwid.GridFlow(control_options, 18, 2, 0, "center") # Create smooth graph selection button default_smooth = self.controller.smooth_graph_mode if urwid.get_encoding_mode() == "utf8": unicode_checkbox = urwid.CheckBox( - "UTF-8", state=default_smooth, - on_state_change=self.on_unicode_checkbox) + "UTF-8", state=default_smooth, on_state_change=self.on_unicode_checkbox + ) # Init the state of the graph accoding to the selected mode self.on_unicode_checkbox(state=default_smooth) else: - unicode_checkbox = urwid.Text( - "[N/A] UTF-8") + unicode_checkbox = urwid.Text("[N/A] UTF-8") install_stress_message = urwid.Text("") if not self.controller.firestarter and not self.controller.stress_exe: install_stress_message = urwid.Text( - ('button normal', u"(N/A) install stress")) + ("button normal", "(N/A) install stress") + ) clock_widget = [] # if self.controller.stress_exe or self.controller.firestarter: if self.controller.stress_exe or self.controller.firestarter: clock_widget = [ - urwid.Text(('bold text', u"Stress Timer"), align="center"), - self.clock_view - ] + urwid.Text(("bold text", "Stress Timer"), align="center"), + self.clock_view, + ] - controls = [urwid.Text(('bold text', u"Modes"), align="center")] + controls = [urwid.Text(("bold text", "Modes"), align="center")] controls += self.mode_buttons controls += [install_stress_message] controls += clock_widget controls += [ urwid.Divider(), - urwid.Text(('bold text', u"Control Options"), align="center"), + urwid.Text(("bold text", "Control Options"), align="center"), animate_controls, urwid.Divider(), - urwid.Text(('bold text', u"Visual Options"), align="center"), + urwid.Text(("bold text", "Visual Options"), align="center"), unicode_checkbox, self.refresh_rate_ctrl, urwid.Divider(), - urwid.Text(('bold text', u"Summaries"), align="center"), + urwid.Text(("bold text", "Summaries"), align="center"), ] return controls @staticmethod def _generate_cpu_stats(): - """Read and display processor name """ + """Read and display processor name""" cpu_name = urwid.Text("CPU Name N/A", align="center") try: cpu_name = urwid.Text(get_processor_name().strip(), align="center") except OSError: logging.info("CPU name not available") - return [urwid.Text(('bold text', "CPU Detected"), - align="center"), cpu_name, urwid.Divider()] + return [ + urwid.Text(("bold text", "CPU Detected"), align="center"), + cpu_name, + urwid.Divider(), + ] def _generate_summaries(self): - fixed_stats = [] for summary in self.visible_summaries.values(): fixed_stats += summary.get_text_item_list() - fixed_stats += [urwid.Text('')] + fixed_stats += [urwid.Text("")] # return fixed_stats pile widget return urwid.Pile(fixed_stats) @@ -498,8 +516,8 @@ def _generate_summaries(self): def show_graphs(self): """Show a pile of the graph selected for dislpay""" elements = itertools.chain.from_iterable( - ([graph] - for graph in self.visible_graphs.values())) + ([graph] for graph in self.visible_graphs.values()) + ) self.graph_place_holder.original_widget = urwid.Pile(elements) def main_window(self): @@ -512,15 +530,17 @@ def main_window(self): color_pallet = source.get_pallet() alert_pallet = source.get_alert_pallet() self.graphs[source_name] = BarGraphVector( - source, color_pallet, + source, + color_pallet, len(source.get_sensor_list()), self.graphs_menu.active_sensors[source_name], - alert_colors=alert_pallet + alert_colors=alert_pallet, ) if self.controller.script_hooks_enabled: source.add_edge_hook( self.controller.script_loader.load_script( - source.__class__.__name__, HOOK_INTERVAL) + source.__class__.__name__, HOOK_INTERVAL + ) ) # Invoke threshold script every 30s self.summaries[source_name] = SummaryTextList( @@ -532,8 +552,8 @@ def main_window(self): # and summaries. # All available summaries are always visible self.visible_graphs = OrderedDict( - (key, val) for key, val in self.graphs.items() if - val.get_is_available()) + (key, val) for key, val in self.graphs.items() if val.get_is_available() + ) # Do not show the graph if there is no selected sensors for key in self.graphs.keys(): @@ -541,24 +561,29 @@ def main_window(self): del self.visible_graphs[key] self.visible_summaries = OrderedDict( - (key, val) for key, val in self.summaries.items() if - val.get_is_available()) + (key, val) for key, val in self.summaries.items() if val.get_is_available() + ) cpu_stats = self._generate_cpu_stats() graph_controls = self._generate_graph_controls() summaries = self._generate_summaries() - text_col = ViListBox(urwid.SimpleListWalker(cpu_stats + - graph_controls + - [summaries])) - - vline = urwid.AttrWrap(urwid.SolidFill(u'|'), 'line') - widget = urwid.Columns([('fixed', 20, text_col), - ('fixed', 1, vline), - ('weight', 2, self.graph_place_holder)], - dividechars=0, focus_column=0) - - widget = urwid.Padding(widget, ('fixed left', 1), ('fixed right', 1)) + text_col = ViListBox( + urwid.SimpleListWalker(cpu_stats + graph_controls + [summaries]) + ) + + vline = urwid.AttrWrap(urwid.SolidFill("|"), "line") + widget = urwid.Columns( + [ + ("fixed", 20, text_col), + ("fixed", 1, vline), + ("weight", 2, self.graph_place_holder), + ], + dividechars=0, + focus_column=0, + ) + + widget = urwid.Padding(widget, ("fixed left", 1), ("fixed right", 1)) self.main_window_w = widget base = self.main_window_w.base_widget[0].body @@ -598,8 +623,10 @@ def _load_config(self, t_thresh): user_config_dir = get_user_config_dir() if user_config_dir is None: - logging.warning("Failed to find or create scripts directory,\ - proceeding without scripting support") + logging.warning( + "Failed to find or create scripts directory,\ + proceeding without scripting support" + ) self.script_hooks_enabled = False else: self.script_loader = ScriptHookLoader(user_config_dir) @@ -614,85 +641,100 @@ def _load_config(self, t_thresh): # Load refresh refresh rate from config try: - self.refresh_rate = str(self.conf.getfloat( - 'GraphControll', 'refresh')) + self.refresh_rate = str(self.conf.getfloat("GraphControll", "refresh")) logging.debug("User refresh rate: %s", self.refresh_rate) - except (AttributeError, ValueError, configparser.NoOptionError, - configparser.NoSectionError): + except ( + AttributeError, + ValueError, + configparser.NoOptionError, + configparser.NoSectionError, + ): logging.debug("No refresh rate configed") # Change UTF8 setting from config try: - if self.conf.getboolean('GraphControll', 'UTF8'): + if self.conf.getboolean("GraphControll", "UTF8"): self.smooth_graph_mode = True else: - logging.debug("UTF8 selected as %s", - self.conf.get('GraphControll', 'UTF8')) - except (AttributeError, ValueError, configparser.NoOptionError, - configparser.NoSectionError): + logging.debug( + "UTF8 selected as %s", self.conf.get("GraphControll", "UTF8") + ) + except ( + AttributeError, + ValueError, + configparser.NoOptionError, + configparser.NoSectionError, + ): logging.debug("No user config for utf8") # Try to load high temperature threshold if configured if t_thresh is None: try: - self.temp_thresh = self.conf.get('GraphControll', 'TTHRESH') - logging.debug("Temperature threshold set to %s", - self.temp_thresh) - except (AttributeError, ValueError, configparser.NoOptionError, - configparser.NoSectionError): + self.temp_thresh = self.conf.get("GraphControll", "TTHRESH") + logging.debug("Temperature threshold set to %s", self.temp_thresh) + except ( + AttributeError, + ValueError, + configparser.NoOptionError, + configparser.NoSectionError, + ): logging.debug("No user config for temp threshold") else: self.temp_thresh = t_thresh # This should be the only place where sources are configured - possible_sources = [TempSource(self.temp_thresh), - FreqSource(), - UtilSource(), - RaplPowerSource(), - FanSource()] + possible_sources = [ + TempSource(self.temp_thresh), + FreqSource(), + UtilSource(), + RaplPowerSource(), + FanSource(), + ] # Load sensors config if available - sources = [x.get_source_name() for x in possible_sources - if x.get_is_available()] + sources = [ + x.get_source_name() for x in possible_sources if x.get_is_available() + ] for source in sources: try: options = list(self.conf.items(source + ",Graphs")) for option in options: # Returns tuples of values in order - self.graphs_default_conf[source].append( - str_to_bool(option[1])) + self.graphs_default_conf[source].append(str_to_bool(option[1])) options = list(self.conf.items(source + ",Summaries")) for option in options: # Returns tuples of values in order - self.summary_default_conf[source].append( - str_to_bool(option[1])) - except (AttributeError, ValueError, configparser.NoOptionError, - configparser.NoSectionError): + self.summary_default_conf[source].append(str_to_bool(option[1])) + except ( + AttributeError, + ValueError, + configparser.NoOptionError, + configparser.NoSectionError, + ): logging.debug("Error reading sensors config") return possible_sources def _config_stress(self): - """ Configures the possible stress processes and modes """ + """Configures the possible stress processes and modes""" # Configure stress_process self.stress_exe = None stress_installed = False - self.stress_exe = which('stress') + self.stress_exe = which("stress") if self.stress_exe: stress_installed = True else: - self.stress_exe = which('stress-ng') + self.stress_exe = which("stress-ng") if self.stress_exe: stress_installed = True self.firestarter = None firestarter_installed = False - if os.path.isfile('./FIRESTARTER/FIRESTARTER'): - self.firestarter = os.path.join(os.getcwd(), - 'FIRESTARTER', 'FIRESTARTER') + if os.path.isfile("./FIRESTARTER/FIRESTARTER"): + self.firestarter = os.path.join(os.getcwd(), "FIRESTARTER", "FIRESTARTER") firestarter_installed = True else: - firestarter_exe = which('FIRESTARTER') + firestarter_exe = which("FIRESTARTER") if firestarter_exe is not None: self.firestarter = firestarter_exe firestarter_installed = True @@ -747,28 +789,30 @@ def set_mode(self, mode): self.update_stress_mode() def main(self): - """ Starts the main loop and graph animation """ - loop = MainLoop(self.view, DEFAULT_PALETTE, - handle_mouse=self.handle_mouse, - # screen=urwid.curses_display.Screen() - ) + """Starts the main loop and graph animation""" + loop = MainLoop( + self.view, + DEFAULT_PALETTE, + handle_mouse=self.handle_mouse, + # screen=urwid.curses_display.Screen() + ) self.view.show_graphs() self.animate_graph(loop) try: loop.run() - except (ZeroDivisionError) as err: + except ZeroDivisionError as err: # In case of Zero division, we want an error to return, and # get a clue where this happens logging.debug("Some stat caused divide by zero exception. Exiting") logging.error(err, exc_info=True) print(ERROR_MESSAGE) - except (AttributeError) as err: + except AttributeError as err: # In this case we restart the loop, to address bug #50, where # urwid crashes on multiple presses on 'esc' logging.debug("Catch attribute Error in urwid and restart") logging.debug(err, exc_info=True) self.main() - except (psutil.NoSuchProcess) as err: + except psutil.NoSuchProcess as err: # This might happen if the stress process is not found, in this # case, we want to know why logging.error("No such process error") @@ -776,7 +820,7 @@ def main(self): print(ERROR_MESSAGE) def update_stress_mode(self): - """ Updates stress mode according to radio buttons state """ + """Updates stress mode according to radio buttons state""" self.stress_conroller.kill_stress_process() @@ -784,21 +828,22 @@ def update_stress_mode(self): self.view.clock_view.set_text(ZERO_TIME) self.stress_start_time = timeit.default_timer() - if self.stress_conroller.get_current_mode() == 'Stress': + if self.stress_conroller.get_current_mode() == "Stress": stress_cmd = self.view.stress_menu.get_stress_cmd() self.stress_conroller.start_stress(stress_cmd) - elif self.stress_conroller.get_current_mode() == 'FIRESTARTER': + elif self.stress_conroller.get_current_mode() == "FIRESTARTER": stress_cmd = [self.firestarter] self.stress_conroller.start_stress(stress_cmd) def save_settings(self): - """ Save the current configuration to a user config file """ + """Save the current configuration to a user config file""" + def _save_displayed_setting(conf, submenu): items = [] - if (submenu == "Graphs"): + if submenu == "Graphs": items = self.view.graphs_menu.active_sensors.items() - elif (submenu == "Summaries"): + elif submenu == "Summaries": items = self.view.summary_menu.active_sensors.items() for source, visible_sensors in items: @@ -809,13 +854,11 @@ def _save_displayed_setting(conf, submenu): logging.debug("Saving settings for %s", source) logging.debug("Visible sensors %s", visible_sensors) # TODO: consider changing sensors_list to dict - curr_sensor = [x for x in sources if - x.get_source_name() == source][0] + curr_sensor = [x for x in sources if x.get_source_name() == source][0] sensor_list = curr_sensor.get_sensor_list() for sensor_id, sensor in enumerate(sensor_list): try: - conf.set(section, sensor, str( - visible_sensors[sensor_id])) + conf.set(section, sensor, str(visible_sensors[sensor_id])) except IndexError: conf.set(section, sensor, str(True)) @@ -824,25 +867,22 @@ def _save_displayed_setting(conf, submenu): conf = configparser.ConfigParser() config_file = get_user_config_file() - with open(config_file, 'w') as cfgfile: - conf.add_section('GraphControll') + with open(config_file, "w") as cfgfile: + conf.add_section("GraphControll") # Save the configured refresh rete - conf.set('GraphControll', 'refresh', str( - self.refresh_rate)) + conf.set("GraphControll", "refresh", str(self.refresh_rate)) # Save the configured UTF8 setting - conf.set('GraphControll', 'UTF8', str( - self.smooth_graph_mode)) + conf.set("GraphControll", "UTF8", str(self.smooth_graph_mode)) # Save the configured t_thresh if self.temp_thresh: - conf.set('GraphControll', 'TTHRESH', str( - self.temp_thresh)) + conf.set("GraphControll", "TTHRESH", str(self.temp_thresh)) _save_displayed_setting(conf, "Graphs") _save_displayed_setting(conf, "Summaries") conf.write(cfgfile) def exit_program(self): - """ Kill all stress operations upon exit""" + """Kill all stress operations upon exit""" self.stress_conroller.kill_stress_process() raise urwid.ExitMainLoop() @@ -859,7 +899,8 @@ def animate_graph(self, loop, user_data=None): # Set next update self.animate_alarm = loop.set_alarm_in( - float(self.refresh_rate), self.animate_graph) + float(self.refresh_rate), self.animate_graph + ) if self.args.debug_run: # refresh rate is a string in float format @@ -884,7 +925,8 @@ def main(): if args.debug_file is not None: log_file = args.debug_file log_formatter = logging.Formatter( - "%(asctime)s [%(funcName)s()] [%(levelname)-5.5s] %(message)s") + "%(asctime)s [%(funcName)s()] [%(levelname)-5.5s] %(message)s" + ) root_logger = logging.getLogger() file_handler = logging.FileHandler(log_file) file_handler.setFormatter(log_formatter) @@ -893,16 +935,20 @@ def main(): else: level = logging.ERROR log_formatter = logging.Formatter( - "%(asctime)s [%(funcName)s()] [%(levelname)-5.5s] %(message)s") + "%(asctime)s [%(funcName)s()] [%(levelname)-5.5s] %(message)s" + ) root_logger = logging.getLogger() root_logger.setLevel(level) if args.terminal or args.json: logging.info("Printing single line to terminal") - sources = [FreqSource(), TempSource(), - UtilSource(), - RaplPowerSource(), - FanSource()] + sources = [ + FreqSource(), + TempSource(), + UtilSource(), + RaplPowerSource(), + FanSource(), + ] if args.terminal: output_to_terminal(sources) elif args.json: @@ -914,48 +960,78 @@ def main(): def get_args(): - parser = argparse.ArgumentParser( - description=HELP_MESSAGE, - formatter_class=argparse.RawTextHelpFormatter) - - parser.add_argument('-d', '--debug', - default=False, action='store_true', - help="Output debug log to _s-tui.log") - parser.add_argument('--debug-file', - default=None, - help="Use a custom debug file. Default: " + - "_s-tui.log") + description=HELP_MESSAGE, formatter_class=argparse.RawTextHelpFormatter + ) + + parser.add_argument( + "-d", + "--debug", + default=False, + action="store_true", + help="Output debug log to _s-tui.log", + ) + parser.add_argument( + "--debug-file", + default=None, + help="Use a custom debug file. Default: " + "_s-tui.log", + ) # This is mainly to be used for testing purposes - parser.add_argument('-dr', '--debug_run', - default=False, action='store_true', - help="Run for 5 seconds and quit") - parser.add_argument('-c', '--csv', action='store_true', - default=False, help="Save stats to csv file") - parser.add_argument('--csv-file', - default=None, - help="Use a custom CSV file. Default: " + - "s-tui_log_