logo

dotfiles

My dotfiles, one branch per machine
commit: 44cae17b221baf9b2ae7594a11fad0fe37c4aceb
parent: badee6a770e8d863aba7e6c4e8db7698e921512a
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sat, 15 Jun 2019 02:18:56 +0200

.weechat: Update autosort.py script

Diffstat:

M.weechat/autosort.conf2++
M.weechat/python/autosort.py149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 125 insertions(+), 26 deletions(-)

diff --git a/.weechat/autosort.conf b/.weechat/autosort.conf @@ -11,10 +11,12 @@ [sorting] case_sensitive = off +debug_log = off replacements = "" rules = "" signal_delay = 5 signals = "buffer_opened buffer_merged buffer_unmerged buffer_renamed" +sort_limit = 100 sort_on_config_change = on [v3] diff --git a/.weechat/python/autosort.py b/.weechat/python/autosort.py @@ -25,6 +25,11 @@ # # Changelog: +# 3.4: +# * Fix rate-limit of sorting to prevent high CPU load and lock-ups. +# * Fix bug in parsing empty arguments for info hooks. +# * Add debug_log option to aid with debugging. +# * Correct a few typos. # 3.3: # * Fix the /autosort debug command for unicode. # * Update the default rules to work better with Slack. @@ -71,14 +76,17 @@ import weechat SCRIPT_NAME = 'autosort' SCRIPT_AUTHOR = 'Maarten de Vries <maarten@de-vri.es>' -SCRIPT_VERSION = '3.3' +SCRIPT_VERSION = '3.4' SCRIPT_LICENSE = 'GPL3' SCRIPT_DESC = 'Flexible automatic (or manual) buffer sorting based on eval expressions.' -config = None -hooks = [] -timer = None +config = None +hooks = [] +signal_delay_timer = None +sort_limit_timer = None +sort_queued = False + # Make sure that unicode, bytes and str are always available in python2 and 3. # For python 2, str == bytes @@ -147,12 +155,12 @@ def decode_rules(blob): def decode_helpers(blob): parsed = json.loads(blob) if not isinstance(parsed, dict): - log('Malformed helpers, expected a JSON encoded dictonary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed))) + log('Malformed helpers, expected a JSON encoded dictionary but got a {0}. No helpers have been loaded. Please fix the setting manually.'.format(type(parsed))) return {} for key, value in parsed.items(): if not isinstance(value, (str, unicode)): - log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix seting manually.'.format(key, type(value))) + log('Helper "{0}" is not a string but a {1}. No helpers have been loaded. Please fix setting manually.'.format(key, type(value))) return {} return parsed @@ -180,6 +188,7 @@ class Config: }) default_signal_delay = 5 + default_sort_limit = 100 default_signals = 'buffer_opened buffer_merged buffer_unmerged buffer_renamed' @@ -196,14 +205,18 @@ class Config: self.helpers = {} self.signals = [] self.signal_delay = Config.default_signal_delay, + self.sort_limit = Config.default_sort_limit, self.sort_on_config = True + self.debug_log = False self.__case_sensitive = None self.__rules = None self.__helpers = None self.__signals = None self.__signal_delay = None + self.__sort_limit = None self.__sort_on_config = None + self.__debug_log = None if not self.config_file: log('Failed to initialize configuration file "{0}".'.format(self.filename)) @@ -273,6 +286,14 @@ class Config: '', '', '', '', '', '' ) + self.__sort_limit = weechat.config_new_option( + self.config_file, self.sorting_section, + 'sort_limit', 'integer', + 'Minimum delay in milliseconds to wait after sorting before signals can trigger a sort again. This is effectively a rate limit on sorting. Keeping signal_delay low while setting this higher can reduce excessive sorting without a long initial delay.', + '', 0, 1000, str(Config.default_sort_limit), str(Config.default_sort_limit), 0, + '', '', '', '', '', '' + ) + self.__sort_on_config = weechat.config_new_option( self.config_file, self.sorting_section, 'sort_on_config_change', 'boolean', @@ -281,6 +302,14 @@ class Config: '', '', '', '', '', '' ) + self.__debug_log = weechat.config_new_option( + self.config_file, self.sorting_section, + 'debug_log', 'boolean', + 'If enabled, print more debug messages. Not recommended for normal usage.', + '', 0, 0, 'off', 'off', 0, + '', '', '', '', '', '' + ) + if weechat.config_read(self.config_file) != weechat.WEECHAT_RC_OK: log('Failed to load configuration file.') @@ -302,7 +331,9 @@ class Config: self.helpers = decode_helpers(helpers_blob) self.signals = signals_blob.split() self.signal_delay = weechat.config_integer(self.__signal_delay) + self.sort_limit = weechat.config_integer(self.__sort_limit) self.sort_on_config = weechat.config_boolean(self.__sort_on_config) + self.debug_log = weechat.config_boolean(self.__debug_log) def save_rules(self, run_callback = True): ''' Save the current rules to the configuration. ''' @@ -317,10 +348,12 @@ def pad(sequence, length, padding = None): ''' Pad a list until is has a certain length. ''' return sequence + [padding] * max(0, (length - len(sequence))) - def log(message, buffer = 'NULL'): weechat.prnt(buffer, 'autosort: {0}'.format(message)) +def debug(message, buffer = 'NULL'): + if config.debug_log: + weechat.prnt(buffer, 'autosort: debug: {0}'.format(message)) def get_buffers(): ''' Get a list of all the buffers in weechat. ''' @@ -396,18 +429,23 @@ def split_args(args, expected, optional = 0): raise HumanReadableError('Expected at least {0} arguments, got {1}.'.format(expected, len(split))) return split[:-1] + pad(split[-1].split(' ', optional), optional + 1, '') -def do_sort(): +def do_sort(verbose = False): + start = perf_counter() + hdata, buffers = get_buffers() buffers = merge_buffer_list(buffers) buffers = sort_buffers(hdata, buffers, config.rules, config.helpers, config.case_sensitive) apply_buffer_order(buffers) + elapsed = perf_counter() - start + if verbose: + log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + else: + debug("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + def command_sort(buffer, command, args): ''' Sort the buffers and print a confirmation. ''' - start = perf_counter() - do_sort() - elapsed = perf_counter() - start - log("Finished sorting buffers in {0:.4f} seconds.".format(elapsed)) + do_sort(True) return weechat.WEECHAT_RC_OK def command_debug(buffer, command, args): @@ -429,7 +467,7 @@ def command_debug(buffer, command, args): fullname = ensure_str(fullname) result = [ensure_str(x) for x in result] log('{0}: {1}'.format(fullname, result)) - log('Computing evalutaion results took {0:.4f} seconds.'.format(elapsed)) + log('Computing evaluation results took {0:.4f} seconds.'.format(elapsed)) return weechat.WEECHAT_RC_OK @@ -574,7 +612,7 @@ def command_helper_swap(buffer, command, args): return weechat.WEECHAT_RC_OK def call_command(buffer, command, args, subcommands): - ''' Call a subccommand from a dictionary. ''' + ''' Call a subcommand from a dictionary. ''' subcommand, tail = pad(args.split(' ', 1), 2, '') subcommand = subcommand.strip() if (subcommand == ''): @@ -591,21 +629,78 @@ def call_command(buffer, command, args, subcommands): log('{0}: command not found'.format(' '.join(command))) return weechat.WEECHAT_RC_ERROR -def on_signal(*args, **kwargs): - global timer - ''' Called whenever the buffer list changes. ''' - if timer is not None: - weechat.unhook(timer) - timer = None - weechat.hook_timer(config.signal_delay, 0, 1, "on_timeout", "") +def on_signal(data, signal, signal_data): + global signal_delay_timer + global sort_queued + + # If the sort limit timeout is started, we're in the hold-off time after sorting, just queue a sort. + if sort_limit_timer is not None: + if sort_queued: + debug('Signal {0} ignored, sort limit timeout is active and sort is already queued.'.format(signal)) + else: + debug('Signal {0} received but sort limit timeout is active, sort is now queued.'.format(signal)) + sort_queued = True + return weechat.WEECHAT_RC_OK + + # If the signal delay timeout is started, a signal was recently received, so ignore this signal. + if signal_delay_timer is not None: + debug('Signal {0} ignored, signal delay timeout active.'.format(signal)) + return weechat.WEECHAT_RC_OK + + # Otherwise, start the signal delay timeout. + debug('Signal {0} received, starting signal delay timeout of {1} ms.'.format(signal, config.signal_delay)) + weechat.hook_timer(config.signal_delay, 0, 1, "on_signal_delay_timeout", "") return weechat.WEECHAT_RC_OK -def on_timeout(pointer, remaining_calls): - global timer - timer = None +def on_signal_delay_timeout(pointer, remaining_calls): + """ Called when the signal_delay_timer triggers. """ + global signal_delay_timer + global sort_limit_timer + global sort_queued + + signal_delay_timer = None + + # If the sort limit timeout was started, we're still in the no-sort period, so just queue a sort. + if sort_limit_timer is not None: + debug('Signal delay timeout expired, but sort limit timeout is active, sort is now queued.') + sort_queued = True + return weechat.WEECHAT_RC_OK + + # Time to sort! + debug('Signal delay timeout expired, starting sort.') do_sort() + + # Start the sort limit timeout if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + return weechat.WEECHAT_RC_OK +def on_sort_limit_timeout(pointer, remainin_calls): + """ Called when de sort_limit_timer triggers. """ + global sort_limit_timer + global sort_queued + + # If no signal was received during the timeout, we're done. + if not sort_queued: + debug('Sort limit timeout expired without receiving a signal.') + sort_limit_timer = None + return weechat.WEECHAT_RC_OK + + # Otherwise it's time to sort. + debug('Signal received during sort limit timeout, starting queued sort.') + do_sort() + sort_queued = False + + # Start the sort limit timeout again if not disabled. + if config.sort_limit > 0: + debug('Starting sort limit timeout of {0} ms.'.format(config.sort_limit)) + sort_limit_timer = weechat.hook_timer(config.sort_limit, 0, 1, "on_sort_limit_timeout", "") + + return weechat.WEECHAT_RC_OK + + def apply_config(): # Unhook all signals and hook the new ones. for hook in hooks: @@ -614,6 +709,7 @@ def apply_config(): hooks.append(weechat.hook_signal(signal, 'on_signal', '')) if config.sort_on_config: + debug('Sorting because configuration changed.') do_sort() def on_config_changed(*args, **kwargs): @@ -624,7 +720,7 @@ def on_config_changed(*args, **kwargs): return weechat.WEECHAT_RC_OK def parse_arg(args): - if not args: return None, None + if not args: return '', None result = '' escaped = False @@ -643,10 +739,11 @@ def parse_args(args, max = None): result = [] i = 0 while max is None or i < max: + i += 1 arg, args = parse_arg(args) if arg is None: break result.append(arg) - i += 1 + if args is None: break return result, args def on_info_replace(pointer, name, arguments):