def run(self):
        """
        On a timely fashion, execute every method found for this module.
        We will respect and set a cache timeout for each method if the user
        didn't already do so.
        We will execute the 'kill' method of the module when we terminate.
        """
        if self._py3_wrapper.running:
            cache_time = None
            # execute each method of this module
            for meth, my_method in self.methods.items():
                # always check py3status is running
                if not self._py3_wrapper.running:
                    break

                # respect the cache set for this method
                if time.monotonic() < my_method["cached_until"]:
                    if not cache_time or my_method["cached_until"] < cache_time:
                        cache_time = my_method["cached_until"]
                    continue

                try:
                    # execute method and get its output
                    method = getattr(self.module_class, meth)
                    if my_method["call_type"] == self.PARAMS_NEW:
                        # new style modules
                        response = method()
                    else:
                        # legacy modules had parameters passed
                        response = method(
                            self.i3status_thread.json_list,
                            self.config["py3_config"]["general"],
                        )

                    if isinstance(response, dict):
                        # this is a shiny new module giving a dict response
                        result = response
                    elif isinstance(response, tuple):
                        # this is an old school module reporting its position
                        position, result = response
                        if not isinstance(result, dict):
                            raise TypeError("response should be a dict")
                    else:
                        raise TypeError("response should be a dict")

                    if isinstance(response.get("full_text"), (list, Composite)):
                        response["composite"] = response["full_text"]
                        del response["full_text"]
                    if "composite" in response:
                        self.process_composite(response)
                    else:
                        # validate the response
                        if "full_text" not in result:
                            err = 'missing "full_text" key in response'
                            raise KeyError(err)
                        # Remove any none color from our output
                        if hasattr(result.get("color"), "none_setting"):
                            del result["color"]
                        # remove urgent if not allowed
                        if not self.allow_urgent and "urgent" in result:
                            del result["urgent"]
                        # set universal module options in result
                        result.update(self.i3bar_module_options)

                    result["instance"] = self.module_inst
                    result["name"] = self.module_name

                    # initialize method object
                    if my_method["name"] is None:
                        my_method["name"] = result["name"]
                        if "instance" in result:
                            my_method["instance"] = result["instance"]
                        else:
                            my_method["instance"] = result["name"]

                    # update method object cache
                    if "cached_until" in result:
                        cached_until = result["cached_until"]
                        # remove this so we can check later for output changes
                        del result["cached_until"]
                    else:
                        # get module default cached_until
                        cached_until = self.module_class.py3.time_in()
                    my_method["cached_until"] = cached_until
                    if not cache_time or cached_until < cache_time:
                        cache_time = cached_until

                    # update method object output
                    if "composite" in response:
                        my_method["last_output"] = result["composite"]
                    else:
                        my_method["last_output"] = result

                    # debug info
                    if self.config["debug"]:
                        self._py3_wrapper.log(f"method {meth} returned {result} ")
                    # module working correctly so ensure module works as
                    # expected
                    self.allow_config_clicks = True
                    self.error_messages = None
                    self.error_hide = False

                    # mark module as updated
                    self.set_updated()

                except ModuleErrorException as e:
                    # module has indicated that it has an error
                    self.runtime_error(e.msg, meth)
                    if e.timeout:
                        if e.timeout is Py3.CACHE_FOREVER:
                            cache_time = Py3.CACHE_FOREVER
                        else:
                            cache_time = time.monotonic() + e.timeout
                    else:
                        cache_time = time.monotonic() + getattr(
                            self.module_class,
                            "cache_timeout",
                            self.config["cache_timeout"],
                        )

                except Exception as e:
                    msg = "Instance `{}`, user method `{}` failed"
                    msg = msg.format(self.module_full_name, meth)
                    if not self.testing:
                        self._py3_wrapper.report_exception(msg, notify_user=False)
                    # added error
                    self.runtime_error(str(e) or e.__class__.__name__, meth)
                    cache_time = time.monotonic() + getattr(
                        self.module_class, "cache_timeout", self.config["cache_timeout"]
                    )

            if cache_time is None:
                cache_time = time.monotonic() + self.config["cache_timeout"]
            self.cache_time = cache_time
            # new style modules can signal they want to cache forever
            if cache_time == Py3.CACHE_FOREVER:
                self._py3_wrapper.clear_timeout_due(self)
                return
            # don't be hasty mate
            # set timeout to do update next time one is needed
            if not cache_time:
                cache_time = time.monotonic() + self.config["minimum_interval"]

            self._py3_wrapper.timeout_queue_add(self, cache_time)

        On a timely fashion, execute every method found for this module.
        We will respect and set a cache timeout for each method if the user
        didn't already do so.
        We will execute the 'kill' method of the module when we terminate.
        
py3status/module.py