def load_methods(self, module, user_modules):
        """
        Read the given user-written py3status class file and store its methods.
        Those methods will be executed, so we will deliberately ignore:
            - private methods starting with _
            - decorated methods such as @property or @staticmethod
            - 'on_click' methods as they'll be called upon a click_event
            - 'kill' methods as they'll be called upon this thread's exit
        """
        if not self.module_class:
            # user provided modules take precedence over py3status provided modules
            if self.module_name in user_modules:
                include_path, f_name = user_modules[self.module_name]
                module_path = Path(include_path) / f_name
                self._py3_wrapper.log(f'loading module "{module}" from {module_path}')
                self.module_class = self.load_from_file(module_path)
            # load from py3status provided modules
            else:
                self._py3_wrapper.log(
                    'loading module "{}" from py3status.modules.{}'.format(module, self.module_name)
                )
                self.module_class = self.load_from_namespace(self.module_name)

        class_inst = self.module_class
        if class_inst:
            try:
                # containers have items attribute set to a list of contained
                # module instance names.  If there are no contained items then
                # ensure that we have a empty list.
                if class_inst.Meta.container:
                    class_inst.items = []
            except AttributeError:
                pass

            # module configuration
            fn = self._py3_wrapper.get_config_attribute
            mod_config = self.config["py3_config"].get(module, {})

            # resources
            if self.config.get("resources"):
                module = self.module_full_name
                resources = fn(module, "resources")
                if not hasattr(resources, "none_setting"):
                    exception = True
                    if isinstance(resources, list):
                        exception = False
                        for resource in resources:
                            if not isinstance(resource, tuple) or len(resource) != 3:
                                exception = True
                                break
                    if exception:
                        err = "Invalid `resources` attribute, "
                        err += "should be a list of 3-tuples. "
                        raise TypeError(err)

                    from fnmatch import fnmatch

                    for resource in resources:
                        key, resource, value = resource
                        for setting in self.config["resources"]:
                            if fnmatch(setting, resource):
                                value = self.config["resources"][setting]
                                break
                        self.config["py3_config"][module][key] = value

            # process any deprecated configuration settings
            try:
                deprecated = class_inst.Meta.deprecated
            except AttributeError:
                deprecated = None

            if deprecated:

                def deprecation_log(item):
                    # log the deprecation
                    # currently this is just done to the log file as the user
                    # does not need to take any action.
                    if "msg" in item:
                        msg = item["msg"]
                        param = item.get("param")
                        if param:
                            msg = f"`{param}` {msg}"
                        msg = "DEPRECATION WARNING: {} {}".format(self.module_full_name, msg)
                        self._py3_wrapper.log(msg)

                if "rename" in deprecated:
                    # renamed parameters
                    for item in deprecated["rename"]:
                        param = item["param"]
                        new_name = item["new"]
                        if param in mod_config:
                            if new_name not in mod_config:
                                mod_config[new_name] = mod_config[param]
                                # remove from config
                                del mod_config[param]
                            deprecation_log(item)
                if "format_fix_unnamed_param" in deprecated:
                    # format update where {} was previously allowed
                    for item in deprecated["format_fix_unnamed_param"]:
                        param = item["param"]
                        placeholder = item["placeholder"]
                        if "{}" in mod_config.get(param, ""):
                            mod_config[param] = mod_config[param].replace(
                                "{}", f"{{{placeholder}}}"
                            )
                            deprecation_log(item)
                if "rename_placeholder" in deprecated:
                    # rename placeholders
                    placeholders = {}
                    for item in deprecated["rename_placeholder"]:
                        placeholders[item["placeholder"]] = item["new"]
                        format_strings = item["format_strings"]
                        for format_param in format_strings:
                            format_string = mod_config.get(format_param)
                            if not format_string:
                                continue
                            format = Formatter().update_placeholders(format_string, placeholders)
                            mod_config[format_param] = format

                if "update_placeholder_format" in deprecated:
                    # update formats for placeholders if a format is not set
                    for item in deprecated["update_placeholder_format"]:
                        placeholder_formats = item.get("placeholder_formats", {})
                        if "function" in item:
                            placeholder_formats.update(item["function"](mod_config))
                        format_strings = item["format_strings"]
                        for format_param in format_strings:
                            format_string = mod_config.get(format_param)
                            if not format_string:
                                continue
                            format = Formatter().update_placeholder_formats(
                                format_string, placeholder_formats
                            )
                            mod_config[format_param] = format
                if "substitute_by_value" in deprecated:
                    # one parameter sets the value of another
                    for item in deprecated["substitute_by_value"]:
                        param = item["param"]
                        value = item["value"]
                        substitute = item["substitute"]
                        substitute_param = substitute["param"]
                        substitute_value = substitute["value"]
                        if mod_config.get(param) == value and substitute_param not in mod_config:
                            mod_config[substitute_param] = substitute_value
                            deprecation_log(item)
                if "function" in deprecated:
                    # parameter set by function
                    for item in deprecated["function"]:
                        updates = item["function"](mod_config)
                        for name, value in updates.items():
                            if name not in mod_config:
                                mod_config[name] = value
                if "remove" in deprecated:
                    # obsolete parameters forcibly removed
                    for item in deprecated["remove"]:
                        param = item["param"]
                        if param in mod_config:
                            del mod_config[param]
                            deprecation_log(item)

            # apply module configuration
            for config, value in mod_config.items():
                # names starting with '.' are private
                if not config.startswith("."):
                    setattr(self.module_class, config, value)

            # process any update_config settings
            try:
                update_config = class_inst.Meta.update_config
            except AttributeError:
                update_config = None

            if update_config:
                if "update_placeholder_format" in update_config:
                    # update formats for placeholders if a format is not set
                    for item in update_config["update_placeholder_format"]:
                        placeholder_formats = item.get("placeholder_formats", {})
                        format_strings = item["format_strings"]
                        for format_param in format_strings:
                            format_string = getattr(class_inst, format_param, None)
                            if not format_string:
                                continue
                            format = Formatter().update_placeholder_formats(
                                format_string, placeholder_formats
                            )
                            setattr(class_inst, format_param, format)

            # Add the py3 module helper if modules self.py3 is not defined
            if not hasattr(self.module_class, "py3"):
                setattr(self.module_class, "py3", Py3(self))

            # Subscribe to udev events if on_udev_* dynamic variables are
            # configured on the module
            for param in dir(self.module_class):
                if param.startswith("on_udev_"):
                    trigger_action = getattr(self.module_class, param)
                    self.add_udev_trigger(trigger_action, param[8:])

            # allow_urgent
            param = fn(self.module_full_name, "allow_urgent")
            if hasattr(param, "none_setting"):
                param = True
            self.allow_urgent = param

            # on_error
            param = fn(self.module_full_name, "on_error")
            if hasattr(param, "none_setting"):
                param = "show"
            if param not in ON_ERROR_VALUES:
                err = "Invalid `on_error` attribute, should be one of "
                err += "{}. Got `{}`.".format(make_quotes(ON_ERROR_VALUES), param)
                raise ValueError(err)
            self.on_error = param

            # urgent background
            urgent_background = fn(self.module_full_name, "urgent_background")
            if not hasattr(urgent_background, "none_setting"):
                color = self.module_class.py3._get_color(urgent_background)
                if not color:
                    err = "Invalid `urgent_background` attribute, should be "
                    err += f"a color. Got `{urgent_background}`."
                    raise ValueError(err)
                self.i3bar_gaps_urgent_options["background"] = color

            # urgent foreground
            urgent_foreground = fn(self.module_full_name, "urgent_foreground")
            if not hasattr(urgent_foreground, "none_setting"):
                color = self.module_class.py3._get_color(urgent_foreground)
                if not color:
                    err = "Invalid `urgent_foreground` attribute, should be "
                    err += f"a color. Got `{urgent_foreground}`."
                    raise ValueError(err)
                self.i3bar_gaps_urgent_options["foreground"] = color

            # urgent urgent_borders
            urgent_border = fn(self.module_full_name, "urgent_border")
            if not hasattr(urgent_border, "none_setting"):
                color = self.module_class.py3._get_color(urgent_border)
                if not color:
                    err = "Invalid `urgent_border` attribute, should be a color. "
                    err += f"Got `{urgent_border}`."
                    raise ValueError(err)
                self.i3bar_gaps_urgent_options["border"] = color

                urgent_borders = ["top", "right", "bottom", "left"]
                for name in ["urgent_border_" + x for x in urgent_borders]:
                    param = fn(self.module_full_name, name)
                    if hasattr(param, "none_setting"):
                        param = 1
                    elif not isinstance(param, int):
                        err = f"Invalid `{name}` attribute, "
                        err += "should be an int. "
                        err += f"Got `{param}`."
                        raise TypeError(err)
                    self.i3bar_gaps_urgent_options[name[7:]] = param

            # get the available methods for execution
            for method in sorted(dir(class_inst)):
                if method.startswith("_"):
                    continue
                else:
                    m_attr = inspect.getattr_static(class_inst, method)
                    if isinstance(m_attr, FunctionType):
                        params_type = self._params_type(method, class_inst)
                        if method == "on_click":
                            self.click_events = params_type
                        elif method == "kill":
                            self.has_kill = params_type
                        elif method == "post_config_hook":
                            self.has_post_config_hook = True
                        else:
                            # the method_obj stores infos about each method
                            # of this module.
                            method_obj = {
                                "cached_until": time.monotonic(),
                                "call_type": params_type,
                                "instance": None,
                                "last_output": {"name": method, "full_text": ""},
                                "method": method,
                                "name": None,
                            }
                            self.methods[method] = method_obj

        # done, log some debug info
        if self.config["debug"]:
            self._py3_wrapper.log(
                'module "{}" click_events={} has_kill={} methods={}'.format(
                    module, self.click_events, self.has_kill, list(self.methods)
                )
            )

        Read the given user-written py3status class file and store its methods.
        Those methods will be executed, so we will deliberately ignore:
            - private methods starting with _
            - decorated methods such as @property or @staticmethod
            - 'on_click' methods as they'll be called upon a click_event
            - 'kill' methods as they'll be called upon this thread's exit
        
py3status/module.py