<commit_before>import os
import imp
import inspect

from collections import OrderedDict
from time import time

from py3status.composite import Composite
from py3status.py3 import Py3, PY3_CACHE_FOREVER, ModuleErrorException
from py3status.profiling import profile
from py3status.formatter import Formatter

# basestring does not exist in python3
try:
    basestring
except NameError:
    basestring = str


class Module:
    """
    This class represents a user module (imported file).
    It is responsible for executing it every given interval and
    caching its output based on user will.
    """

    PARAMS_NEW = 'new'
    PARAMS_LEGACY = 'legacy'

    def __init__(self, module, user_modules, py3_wrapper, instance=None):
        """
        We need quite some stuff to occupy ourselves don't we ?
        """
        self.allow_config_clicks = True
        self.allow_urgent = None
        self.cache_time = None
        self.click_events = False
        self.config = py3_wrapper.config
        self.disabled = False
        self.enabled = False
        self.error_messages = None
        self.error_hide = False
        self.has_post_config_hook = False
        self.has_kill = False
        self.i3status_thread = py3_wrapper.i3status_thread
        self.last_output = []
        self.methods = OrderedDict()
        self.module_class = instance
        self.module_full_name = module
        self.module_inst = ''.join(module.split(' ')[1:])
        self.module_name = module.split(' ')[0]
        self.new_update = False
        self.nagged = False
        self.prevent_refresh = False
        self.sleeping = False
        self.terminated = False
        self.testing = self.config.get('testing')
        self.urgent = False

        # create a nice name for the module that matches what the module is
        # called in the user config
        if self.module_inst.startswith('_anon_module_'):
            self.module_nice_name = self.module_name
        else:
            self.module_nice_name = self.module_full_name

        # py3wrapper this is private and any modules accessing their instance
        # should only use it on the understanding that it is not supported.
        self._py3_wrapper = py3_wrapper
        #
        self.set_module_options(module)

        try:
            self.load_methods(module, user_modules)
        except Exception as e:
            # Import failed notify user in module error output
            self.disabled = True
            self.methods['error'] = {}
            self.error_index = 0
            self.error_messages = [
                self.module_nice_name,
                u'{}: Import Error, {}'.format(self.module_nice_name, str(e)),
            ]
            self.error_output(self.error_messages[0])
            # log the error
            msg = 'Module `{}` could not be loaded'.format(
                self.module_full_name
            )
            if isinstance(e, SyntaxError):
                # provide full traceback
                self._py3_wrapper.report_exception(msg, notify_user=False)
            else:
                # module import error we can just report the module that cannot
                # be imported
                self._py3_wrapper.log(msg)
                self._py3_wrapper.log(str(e))

    def __repr__(self):
        return '<Module {}>'.format(self.module_full_name)

    @staticmethod
    def load_from_file(filepath):
        """
        Return user-written class object from given path.
        """
        class_inst = None
        expected_class = 'Py3status'
        module_name, file_ext = os.path.splitext(os.path.split(filepath)[-1])
        if file_ext.lower() == '.py':
            py_mod = imp.load_source(module_name, filepath)
            if hasattr(py_mod, expected_class):
                class_inst = py_mod.Py3status()
        return class_inst

    @staticmethod
    def load_from_namespace(module_name):
        """
        Load a py3status bundled module.
        """
        class_inst = None
        name = 'py3status.modules.{}'.format(module_name)
        py_mod = __import__(name)
        components = name.split('.')
        for comp in components[1:]:
            py_mod = getattr(py_mod, comp)
        class_inst = py_mod.Py3status()
        return class_inst

    def prepare_module(self):
        """
        Ready the module to get it ready to start.
        """
        # Modules can define a post_config_hook() method which will be run
        # after the module has had it config settings applied and before it has
        # its main method(s) called for the first time.  This allows modules to
        # perform any necessary setup.
        if self.has_post_config_hook:
            try:
                self.module_class.post_config_hook()
            except Exception as e:
                # An exception has been thrown in post_config_hook() disable
                # the module and show error in module output
                self.terminated = True
                self.error_index = 0

                self.error_messages = [
                    self.module_nice_name,
                    u'{}: {}'.format(self.module_nice_name, str(e) or e.__class__.__name__),
                ]
                self.error_output(self.error_messages[0])
                msg = 'Exception in `%s` post_config_hook()' % self.module_full_name
                self._py3_wrapper.report_exception(msg, notify_user=False)
                self._py3_wrapper.log('terminating module %s' % self.module_full_name)
        self.enabled = True

    def runtime_error(self, msg, method):
        """
        Show the error in the bar
        """
        if self.testing:
            self._py3_wrapper.report_exception(msg)
            raise KeyboardInterrupt

        if self.error_hide:
            self.hide_errors()
            return

        # only show first line of error
        msg = msg.splitlines()[0]

        errors = [
            self.module_nice_name,
            u'{}: {}'.format(self.module_nice_name, msg),
        ]

        # if we have shown this error then keep in the same state
        if self.error_messages != errors:
            self.error_messages = errors
            self.error_index = 0
        self.error_output(self.error_messages[self.error_index], method)

    def error_output(self, message, method_affected=None):
        """
        Something is wrong with the module so we want to output the error to
        the i3bar
        """
        color_fn = self._py3_wrapper.get_config_attribute
        color = color_fn(self.module_full_name, 'color_error')
        if hasattr(color, 'none_setting'):
            color = color_fn(self.module_full_name, 'color_bad')
        if hasattr(color, 'none_setting'):
            color = None

        error = {
            'full_text': message,
            'color': color,
            'instance': self.module_inst,
            'name': self.module_name,
        }
        for method in self.methods.values():
            if method_affected and method['method'] != method_affected:
                continue

            method['last_output'] = [error]

        self.allow_config_clicks = False
        self.set_updated()

    def hide_errors(self):
        """
        hide the module in the i3bar
        """
        for method in self.methods.values():
            method['last_output'] = {}

        self.allow_config_clicks = False
        self.error_hide = True
        self.set_updated()

    def start_module(self):
        """
        Start the module running.
        """
        self.prepare_module()
        if not (self.disabled or self.terminated):
            # Start the module and call its output method(s)
            self._py3_wrapper.log('starting module %s' % self.module_full_name)
            self._py3_wrapper.timeout_queue_add(self)

    def force_update(self):
        """
        Forces an update of the module.
        """
        if self.disabled or self.terminated or not self.enabled:
            return
        # clear cached_until for each method to allow update
        for meth in self.methods:
            self.methods[meth]['cached_until'] = time()
            if self.config['debug']:
                self._py3_wrapper.log('clearing cache for method {}'.format(meth))
        # set module to update
        self._py3_wrapper.timeout_queue_add(self)

    def sleep(self):
        self.sleeping = True

    def disable_module(self):
        # hide message
        self.disabled = True
        # purge from any container modules
        self._py3_wrapper.purge_module(self.module_full_name)
        self.error_output(u'')
        self._py3_wrapper.log('disabling module `%s`' % self.module_full_name)

    def wake(self):
        self.sleeping = False
        if self.disabled:
            # module is disabled so don't wake
            return
        if self.cache_time is None:
            return
        # new style modules can signal they want to cache forever
        if self.cache_time == PY3_CACHE_FOREVER:
            return
        # restart
        self._py3_wrapper.timeout_queue_add(self, self.cache_time)

    def set_updated(self):
        """
        Mark the module as updated.
        We check if the actual content has changed and if so we trigger an
        update in py3status.
        """
        # get latest output
        output = []
        for method in self.methods.values():
            data = method['last_output']
            if isinstance(data, list):
                if self.testing:
                    data[0]['cached_until'] = method.get('cached_until')
                output.extend(data)
            else:
                # if the output is not 'valid' then don't add it.
                if data.get('full_text') or 'separator' in data:
                    if self.testing:
                        data['cached_until'] = method.get('cached_until')
                    output.append(data)
        # if changed store and force display update.
        if output != self.last_output:
            # has the modules output become urgent?
            # we only care the update that this happens
            # not any after then.
            urgent = True in [x.get('urgent') for x in output]
            if urgent != self.urgent:
                self.urgent = urgent
            else:
                urgent = False
            self.last_output = output
            self._py3_wrapper.notify_update(self.module_full_name, urgent)

    def get_latest(self):
        """
        return latest output.
        """
        return self.last_output

    def set_module_options(self, module):
        """
        Set universal module options to be interpreted by i3bar
        https://i3wm.org/i3status/manpage.html#_universal_module_options
        """
        self.module_options = {}
        mod_config = self.config['py3_config'].get(module, {})

        if 'min_width' in mod_config:
            min_width = mod_config['min_width']
            if not isinstance(min_width, int):
                err = 'invalid "min_width" attribute should be an int'
                raise TypeError(err)

            self.module_options['min_width'] = min_width

        if 'separator' in mod_config:
            separator = mod_config['separator']
            if not isinstance(separator, bool):
                err = 'invalid "separator" attribute, should be a bool'
                raise TypeError(err)

            self.module_options['separator'] = separator

        if 'separator_block_width' in mod_config:
            sep_block_width = mod_config['separator_block_width']
            if not isinstance(sep_block_width, int):
                err = 'invalid "separator_block_width" attribute, '
                err += "should be an int"
                raise TypeError(err)

            self.module_options['separator_block_width'] = sep_block_width

        if 'align' in mod_config:
            align = mod_config['align']
            if not (isinstance(align, basestring) and
                    align.lower() in ("left", "center", "right")):
                err = 'invalid "align" attribute, valid values are:'
                err += ' left, center, right'
                raise ValueError(err)

            self.module_options['align'] = align

    def process_composite(self, response):
        """
        Process a composite response.
        composites do not have inter item separators as they appear joined.
        We need to respect the universal options too.
        """
        composite = response['composite']

        # if the composite is of not Composite make it one so we can simplify
        # it.
        if not isinstance(composite, Composite):
            composite = Composite(composite)

        # simplify and get underlying list.
        composite = composite.simplify().get_content()
        response['composite'] = composite

        if not isinstance(composite, list):
            raise Exception('expecting "composite" key in response')
        # if list is empty nothing to do
        if not len(composite):
            return
        if 'full_text' in response:
            err = 'conflicting "full_text" and "composite" in response'
            raise Exception(err)<commit_msg>module: nest align if statement inside min_width if statement<commit_after>import os
import imp
import inspect

from collections import OrderedDict
from time import time

from py3status.composite import Composite
from py3status.py3 import Py3, PY3_CACHE_FOREVER, ModuleErrorException
from py3status.profiling import profile
from py3status.formatter import Formatter

# basestring does not exist in python3
try:
    basestring
except NameError:
    basestring = str


class Module:
    """
    This class represents a user module (imported file).
    It is responsible for executing it every given interval and
    caching its output based on user will.
    """

    PARAMS_NEW = 'new'
    PARAMS_LEGACY = 'legacy'

    def __init__(self, module, user_modules, py3_wrapper, instance=None):
        """
        We need quite some stuff to occupy ourselves don't we ?
        """
        self.allow_config_clicks = True
        self.allow_urgent = None
        self.cache_time = None
        self.click_events = False
        self.config = py3_wrapper.config
        self.disabled = False
        self.enabled = False
        self.error_messages = None
        self.error_hide = False
        self.has_post_config_hook = False
        self.has_kill = False
        self.i3status_thread = py3_wrapper.i3status_thread
        self.last_output = []
        self.methods = OrderedDict()
        self.module_class = instance
        self.module_full_name = module
        self.module_inst = ''.join(module.split(' ')[1:])
        self.module_name = module.split(' ')[0]
        self.new_update = False
        self.nagged = False
        self.prevent_refresh = False
        self.sleeping = False
        self.terminated = False
        self.testing = self.config.get('testing')
        self.urgent = False

        # create a nice name for the module that matches what the module is
        # called in the user config
        if self.module_inst.startswith('_anon_module_'):
            self.module_nice_name = self.module_name
        else:
            self.module_nice_name = self.module_full_name

        # py3wrapper this is private and any modules accessing their instance
        # should only use it on the understanding that it is not supported.
        self._py3_wrapper = py3_wrapper
        #
        self.set_module_options(module)

        try:
            self.load_methods(module, user_modules)
        except Exception as e:
            # Import failed notify user in module error output
            self.disabled = True
            self.methods['error'] = {}
            self.error_index = 0
            self.error_messages = [
                self.module_nice_name,
                u'{}: Import Error, {}'.format(self.module_nice_name, str(e)),
            ]
            self.error_output(self.error_messages[0])
            # log the error
            msg = 'Module `{}` could not be loaded'.format(
                self.module_full_name
            )
            if isinstance(e, SyntaxError):
                # provide full traceback
                self._py3_wrapper.report_exception(msg, notify_user=False)
            else:
                # module import error we can just report the module that cannot
                # be imported
                self._py3_wrapper.log(msg)
                self._py3_wrapper.log(str(e))

    def __repr__(self):
        return '<Module {}>'.format(self.module_full_name)

    @staticmethod
    def load_from_file(filepath):
        """
        Return user-written class object from given path.
        """
        class_inst = None
        expected_class = 'Py3status'
        module_name, file_ext = os.path.splitext(os.path.split(filepath)[-1])
        if file_ext.lower() == '.py':
            py_mod = imp.load_source(module_name, filepath)
            if hasattr(py_mod, expected_class):
                class_inst = py_mod.Py3status()
        return class_inst

    @staticmethod
    def load_from_namespace(module_name):
        """
        Load a py3status bundled module.
        """
        class_inst = None
        name = 'py3status.modules.{}'.format(module_name)
        py_mod = __import__(name)
        components = name.split('.')
        for comp in components[1:]:
            py_mod = getattr(py_mod, comp)
        class_inst = py_mod.Py3status()
        return class_inst

    def prepare_module(self):
        """
        Ready the module to get it ready to start.
        """
        # Modules can define a post_config_hook() method which will be run
        # after the module has had it config settings applied and before it has
        # its main method(s) called for the first time.  This allows modules to
        # perform any necessary setup.
        if self.has_post_config_hook:
            try:
                self.module_class.post_config_hook()
            except Exception as e:
                # An exception has been thrown in post_config_hook() disable
                # the module and show error in module output
                self.terminated = True
                self.error_index = 0

                self.error_messages = [
                    self.module_nice_name,
                    u'{}: {}'.format(self.module_nice_name, str(e) or e.__class__.__name__),
                ]
                self.error_output(self.error_messages[0])
                msg = 'Exception in `%s` post_config_hook()' % self.module_full_name
                self._py3_wrapper.report_exception(msg, notify_user=False)
                self._py3_wrapper.log('terminating module %s' % self.module_full_name)
        self.enabled = True

    def runtime_error(self, msg, method):
        """
        Show the error in the bar
        """
        if self.testing:
            self._py3_wrapper.report_exception(msg)
            raise KeyboardInterrupt

        if self.error_hide:
            self.hide_errors()
            return

        # only show first line of error
        msg = msg.splitlines()[0]

        errors = [
            self.module_nice_name,
            u'{}: {}'.format(self.module_nice_name, msg),
        ]

        # if we have shown this error then keep in the same state
        if self.error_messages != errors:
            self.error_messages = errors
            self.error_index = 0
        self.error_output(self.error_messages[self.error_index], method)

    def error_output(self, message, method_affected=None):
        """
        Something is wrong with the module so we want to output the error to
        the i3bar
        """
        color_fn = self._py3_wrapper.get_config_attribute
        color = color_fn(self.module_full_name, 'color_error')
        if hasattr(color, 'none_setting'):
            color = color_fn(self.module_full_name, 'color_bad')
        if hasattr(color, 'none_setting'):
            color = None

        error = {
            'full_text': message,
            'color': color,
            'instance': self.module_inst,
            'name': self.module_name,
        }
        for method in self.methods.values():
            if method_affected and method['method'] != method_affected:
                continue

            method['last_output'] = [error]

        self.allow_config_clicks = False
        self.set_updated()

    def hide_errors(self):
        """
        hide the module in the i3bar
        """
        for method in self.methods.values():
            method['last_output'] = {}

        self.allow_config_clicks = False
        self.error_hide = True
        self.set_updated()

    def start_module(self):
        """
        Start the module running.
        """
        self.prepare_module()
        if not (self.disabled or self.terminated):
            # Start the module and call its output method(s)
            self._py3_wrapper.log('starting module %s' % self.module_full_name)
            self._py3_wrapper.timeout_queue_add(self)

    def force_update(self):
        """
        Forces an update of the module.
        """
        if self.disabled or self.terminated or not self.enabled:
            return
        # clear cached_until for each method to allow update
        for meth in self.methods:
            self.methods[meth]['cached_until'] = time()
            if self.config['debug']:
                self._py3_wrapper.log('clearing cache for method {}'.format(meth))
        # set module to update
        self._py3_wrapper.timeout_queue_add(self)

    def sleep(self):
        self.sleeping = True

    def disable_module(self):
        # hide message
        self.disabled = True
        # purge from any container modules
        self._py3_wrapper.purge_module(self.module_full_name)
        self.error_output(u'')
        self._py3_wrapper.log('disabling module `%s`' % self.module_full_name)

    def wake(self):
        self.sleeping = False
        if self.disabled:
            # module is disabled so don't wake
            return
        if self.cache_time is None:
            return
        # new style modules can signal they want to cache forever
        if self.cache_time == PY3_CACHE_FOREVER:
            return
        # restart
        self._py3_wrapper.timeout_queue_add(self, self.cache_time)

    def set_updated(self):
        """
        Mark the module as updated.
        We check if the actual content has changed and if so we trigger an
        update in py3status.
        """
        # get latest output
        output = []
        for method in self.methods.values():
            data = method['last_output']
            if isinstance(data, list):
                if self.testing:
                    data[0]['cached_until'] = method.get('cached_until')
                output.extend(data)
            else:
                # if the output is not 'valid' then don't add it.
                if data.get('full_text') or 'separator' in data:
                    if self.testing:
                        data['cached_until'] = method.get('cached_until')
                    output.append(data)
        # if changed store and force display update.
        if output != self.last_output:
            # has the modules output become urgent?
            # we only care the update that this happens
            # not any after then.
            urgent = True in [x.get('urgent') for x in output]
            if urgent != self.urgent:
                self.urgent = urgent
            else:
                urgent = False
            self.last_output = output
            self._py3_wrapper.notify_update(self.module_full_name, urgent)

    def get_latest(self):
        """
        return latest output.
        """
        return self.last_output

    def set_module_options(self, module):
        """
        Set universal module options to be interpreted by i3bar
        https://i3wm.org/i3status/manpage.html#_universal_module_options
        """
        self.module_options = {}
        mod_config = self.config['py3_config'].get(module, {})

        if 'min_width' in mod_config:
            min_width = mod_config['min_width']
            if not isinstance(min_width, int):
                err = 'invalid "min_width" attribute should be an int'
                raise TypeError(err)

            self.module_options['min_width'] = min_width

            if 'align' in mod_config:
                align = mod_config['align']
                if not (isinstance(align, basestring) and
                        align.lower() in ('left', 'center', 'right')):
                    err = 'invalid "align" attribute, valid values are:'
                    err += ' left, center, right'
                    raise ValueError(err)

                self.module_options['align'] = align

        if 'separator' in mod_config:
            separator = mod_config['separator']
            if not isinstance(separator, bool):
                err = 'invalid "separator" attribute, should be a bool'
                raise TypeError(err)

            self.module_options['separator'] = separator

        if 'separator_block_width' in mod_config:
            sep_block_width = mod_config['separator_block_width']
            if not isinstance(sep_block_width, int):
                err = 'invalid "separator_block_width" attribute, '
                err += "should be an int"
                raise TypeError(err)

            self.module_options['separator_block_width'] = sep_block_width

    def process_composite(self, response):
        """
        Process a composite response.
        composites do not have inter item separators as they appear joined.
        We need to respect the universal options too.
        """
        composite = response['composite']

        # if the composite is of not Composite make it one so we can simplify
        # it.
        if not isinstance(composite, Composite):
            composite = Composite(composite)

        # simplify and get underlying list.
        composite = composite.simplify().get_content()
        response['composite'] = composite

        if not isinstance(composite, list):
            raise Exception('expecting "composite" key in response')
        # if list is empty nothing to do
        if not len(composite):
            return
        if 'full_text' in response:
            err = 'conflicting "full_text" and "composite" in response'
            raise Exception(err)
{'fraction_non_alphanumeric': 0.08328315520096342, 'fraction_numerical': 0.004139695920517839, 'mean_word_length': 1.8993998908892527, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6676091', 'n_tokens_mistral': 7243, 'n_tokens_neox': 6996, 'n_words': 1999}