def process_config(config_path, py3_wrapper=None): """ Parse i3status.conf so we can adapt our code to the i3status config. """ def notify_user(error): if py3_wrapper: py3_wrapper.notify_user(error) else: print(error) def parse_config(config): """ Parse text or file as a py3status config file. """ if hasattr(config, "readlines"): config = "".join(config.readlines()) parser = ConfigParser(config, py3_wrapper) parser.parse() parsed = parser.config del parser return parsed def parse_config_error(e, config_path): # There was a problem use our special error config error = e.one_line(config_path) notify_user(error) # to display correctly in i3bar we need to do some substitutions for char in ['"', "{", "|"]: error = error.replace(char, "\\" + char) error_config = Template(ERROR_CONFIG).substitute(error=error) return parse_config(error_config) config = {} # get the file encoding this is important with multi-byte unicode chars try: encoding = check_output( ["file", "-b", "--mime-encoding", "--dereference", config_path]) encoding = encoding.strip().decode("utf-8") except CalledProcessError: # bsd does not have the --mime-encoding so assume utf-8 encoding = "utf-8" try: with codecs.open(config_path, "r", encoding) as f: try: config_info = parse_config(f) except ParseException as e: config_info = parse_config_error(e, config_path) except LookupError: with codecs.open(config_path) as f: try: config_info = parse_config(f) except ParseException as e: config_info = parse_config_error(e, config_path) # update general section with defaults general_defaults = GENERAL_DEFAULTS.copy() if "general" in config_info: general_defaults.update(config_info["general"]) config["general"] = general_defaults config["py3status"] = config_info.get("py3status", {}) modules = {} on_click = {} i3s_modules = [] py3_modules = [] module_groups = {} def process_onclick(key, value, group_name): """ Check on_click events are valid. Store if they are good """ button_error = False button = "" try: button = key.split()[1] if int(button) not in range(1, 20): button_error = True except (ValueError, IndexError): button_error = True if button_error: err = "Invalid on_click for `{}`. Number not in range 1-20: `{}`." notify_user(err.format(group_name, button)) return False clicks = on_click.setdefault(group_name, {}) clicks[button] = value return True def get_module_type(name): """ i3status or py3status? """ if name.split()[0] in I3S_MODULE_NAMES: return "i3status" return "py3status" def process_module(name, module, parent): if parent: modules[parent]["items"].append(name) mg = module_groups.setdefault(name, []) mg.append(parent) if get_module_type(name) == "py3status": module[".group"] = parent # check module content for k, v in list(module.items()): if k.startswith("on_click"): # on_click event process_onclick(k, v, name) # on_click should not be passed to the module via the config. del module[k] if isinstance(v, ModuleDefinition): # we are a container module["items"] = [] return module def get_modules(data, parent=None): for k, v in data.items(): if isinstance(v, ModuleDefinition): module = process_module(k, v, parent) modules[k] = module get_modules(v, parent=k) get_modules(config_info) config["order"] = [] def remove_any_contained_modules(module): """ takes a module definition and returns a dict without any modules that may be defined with it. """ fixed = {} for k, v in module.items(): if not isinstance(v, ModuleDefinition): fixed[k] = v return fixed def append_modules(item): module_type = get_module_type(item) if module_type == "i3status": if item not in i3s_modules: i3s_modules.append(item) else: if item not in py3_modules: py3_modules.append(item) def add_container_items(module_name): module = modules.get(module_name, {}) items = module.get("items", []) for item in items: if item in config: continue append_modules(item) module = modules.get(item, {}) config[item] = remove_any_contained_modules(module) # add any children add_container_items(item) # create config for modules in order for name in config_info.get("order", []): module = modules.get(name, {}) config["order"].append(name) add_container_items(name) append_modules(name) config[name] = remove_any_contained_modules(module) config["on_click"] = on_click config["i3s_modules"] = i3s_modules config["py3_modules"] = py3_modules config[".module_groups"] = module_groups # time and tztime modules need a format for correct processing for name in config: if name.split()[0] in TIME_MODULES and "format" not in config[name]: if name.split()[0] == "time": config[name]["format"] = TIME_FORMAT else: config[name]["format"] = TZTIME_FORMAT if not config["order"]: notify_user("Your configuration file does not list any module" ' to be loaded with the "order" directive.') return config
def process_config(config_path, py3_wrapper=None): """ Parse i3status.conf so we can adapt our code to the i3status config. """ def notify_user(error): if py3_wrapper: py3_wrapper.notify_user(error) else: print(error) def parse_config(config): ''' Parse text or file as a py3status config file. ''' if hasattr(config, 'readlines'): config = ''.join(config.readlines()) parser = ConfigParser(config) parser.parse() parsed = parser.config del parser return parsed config = {} # get the file encoding this is important with multi-byte unicode chars encoding = check_output( ['file', '-b', '--mime-encoding', '--dereference', config_path]) encoding = encoding.strip().decode('utf-8') with codecs.open(config_path, 'r', encoding) as f: try: config_info = parse_config(f) except ParseException as e: # There was a problem use our special error config error = e.one_line() notify_user(error) error_config = Template(ERROR_CONFIG).substitute( error=error.replace('"', '\\"')) config_info = parse_config(error_config) # update general section with defaults general_defaults = GENERAL_DEFAULTS.copy() if 'general' in config_info: general_defaults.update(config_info['general']) config['general'] = general_defaults config['py3status'] = config_info.get('py3status', {}) modules = {} on_click = {} i3s_modules = [] py3_modules = [] module_groups = {} def process_onclick(key, value, group_name): ''' Check on_click events are valid. Store if they are good ''' button_error = False button = '' try: button = key.split()[1] if int(button) not in range(1, 6): button_error = True except (ValueError, IndexError): button_error = True if button_error: err = 'Invalid on_click for `{}` should be 1, 2, 3, 4 or 5 saw `{}`' notify_user(err.format(group_name, button)) return False clicks = on_click.setdefault(group_name, {}) clicks[button] = value return True def get_module_type(name): ''' i3status or py3status? ''' if name.split()[0] in I3S_MODULE_NAMES: return 'i3status' return 'py3status' def process_module(name, module, parent): if parent: modules[parent]['items'].append(name) mg = module_groups.setdefault(name, []) mg.append(parent) if get_module_type(name) == 'py3status': module['.group'] = parent # check module content for k, v in list(module.items()): if k.startswith('on_click'): # on_click event process_onclick(k, v, name) # on_click should not be passed to the module via the config. del module[k] if isinstance(v, ModuleDefinition): # we are a container module['items'] = [] return module def get_modules(data, parent=None): for k, v in data.items(): if isinstance(v, ModuleDefinition): module = process_module(k, v, parent) modules[k] = module get_modules(v, parent=k) get_modules(config_info) config['order'] = [] def remove_any_contained_modules(module): ''' takes a module definition and returns a dict without any modules that may be defined with it. ''' fixed = {} for k, v in module.items(): if not isinstance(v, ModuleDefinition): fixed[k] = v return fixed def add_container_items(module_name): module = modules.get(module_name, {}) items = module.get('items', []) for item in items: if item in config: continue module_type = get_module_type(item) if module_type == 'i3status': if item not in i3s_modules: i3s_modules.append(item) else: if item not in py3_modules: py3_modules.append(item) module = modules.get(item, {}) config[item] = remove_any_contained_modules(module) # add any children add_container_items(item) # create config for modules in order for name in config_info.get('order', []): module = modules.get(name, {}) module_type = get_module_type(name) config['order'].append(name) add_container_items(name) if module_type == 'i3status': if name not in i3s_modules: i3s_modules.append(name) else: if name not in py3_modules: py3_modules.append(name) config[name] = remove_any_contained_modules(module) config['on_click'] = on_click config['i3s_modules'] = i3s_modules config['py3_modules'] = py3_modules config['.module_groups'] = module_groups # time and tztime modules need a format for correct processing for name in config: if name.split()[0] in TIME_MODULES and 'format' not in config[name]: if name.split()[0] == 'time': config[name]['format'] = TIME_FORMAT else: config[name]['format'] = TZTIME_FORMAT if not config['order']: notify_user('Your configuration file does not list any module' ' to be loaded with the "order" directive.') return config
def process_config(config_path, py3_wrapper=None): """ Parse i3status.conf so we can adapt our code to the i3status config. """ def notify_user(error): if py3_wrapper: py3_wrapper.notify_user(error) else: print(error) def parse_config(config): """ Parse text or file as a py3status config file. """ if hasattr(config, "readlines"): config = "".join(config.readlines()) parser = ConfigParser(config, py3_wrapper) parser.parse() parsed = parser.config del parser return parsed def parse_config_error(e, config_path): # There was a problem use our special error config error = e.one_line(config_path) notify_user(error) # to display correctly in i3bar we need to do some substitutions for char in ['"', "{", "|"]: error = error.replace(char, "\\" + char) error_config = Template(ERROR_CONFIG).substitute(error=error) return parse_config(error_config) config = {} # get the file encoding this is important with multi-byte unicode chars try: encoding = check_output( ["file", "-b", "--mime-encoding", "--dereference", config_path] ) encoding = encoding.strip().decode("utf-8") except CalledProcessError: # bsd does not have the --mime-encoding so assume utf-8 encoding = "utf-8" try: with codecs.open(config_path, "r", encoding) as f: try: config_info = parse_config(f) except ParseException as e: config_info = parse_config_error(e, config_path) except LookupError: with codecs.open(config_path) as f: try: config_info = parse_config(f) except ParseException as e: config_info = parse_config_error(e, config_path) # update general section with defaults general_defaults = GENERAL_DEFAULTS.copy() if "general" in config_info: general_defaults.update(config_info["general"]) config["general"] = general_defaults config["py3status"] = config_info.get("py3status", {}) modules = {} on_click = {} i3s_modules = [] py3_modules = [] module_groups = {} def process_onclick(key, value, group_name): """ Check on_click events are valid. Store if they are good """ button_error = False button = "" try: button = key.split()[1] if int(button) not in range(1, 20): button_error = True except (ValueError, IndexError): button_error = True if button_error: err = "Invalid on_click for `{}`. Number not in range 1-20: `{}`." notify_user(err.format(group_name, button)) return False clicks = on_click.setdefault(group_name, {}) clicks[button] = value return True def get_module_type(name): """ i3status or py3status? """ if name.split()[0] in I3S_MODULE_NAMES: return "i3status" return "py3status" def process_module(name, module, parent): if parent: modules[parent]["items"].append(name) mg = module_groups.setdefault(name, []) mg.append(parent) if get_module_type(name) == "py3status": module[".group"] = parent # check module content for k, v in list(module.items()): if k.startswith("on_click"): # on_click event process_onclick(k, v, name) # on_click should not be passed to the module via the config. del module[k] if isinstance(v, ModuleDefinition): # we are a container module["items"] = [] return module def get_modules(data, parent=None): for k, v in data.items(): if isinstance(v, ModuleDefinition): module = process_module(k, v, parent) modules[k] = module get_modules(v, parent=k) get_modules(config_info) config["order"] = [] def remove_any_contained_modules(module): """ takes a module definition and returns a dict without any modules that may be defined with it. """ fixed = {} for k, v in module.items(): if not isinstance(v, ModuleDefinition): fixed[k] = v return fixed def append_modules(item): module_type = get_module_type(item) if module_type == "i3status": if item not in i3s_modules: i3s_modules.append(item) else: if item not in py3_modules: py3_modules.append(item) def add_container_items(module_name): module = modules.get(module_name, {}) items = module.get("items", []) for item in items: if item in config: continue append_modules(item) module = modules.get(item, {}) config[item] = remove_any_contained_modules(module) # add any children add_container_items(item) # create config for modules in order for name in config_info.get("order", []): if name in module_groups: msg = "Module `{}` should not be listed in the 'order' directive, use" msg += " its parent group instead." notify_user(msg.format(name)) continue module_name = name.split(" ")[0] if module_name in RETIRED_MODULES: notify_user( "Module `{}` is no longer available".format(module_name) + ". Alternative modules are: {}.".format( ", ".join("`{}`".format(x) for x in RETIRED_MODULES[module_name]) ) ) continue module = modules.get(name, {}) config["order"].append(name) add_container_items(name) append_modules(name) config[name] = remove_any_contained_modules(module) config["on_click"] = on_click config["i3s_modules"] = i3s_modules config["py3_modules"] = py3_modules config[".module_groups"] = module_groups # time and tztime modules need a format for correct processing for name in config: if name.split()[0] in TIME_MODULES and "format" not in config[name]: if name.split()[0] == "time": config[name]["format"] = TIME_FORMAT else: config[name]["format"] = TZTIME_FORMAT if not config["order"]: notify_user( "Your configuration file does not list any module" ' to be loaded with the "order" directive.' ) return config
def process_config(config_path, py3_wrapper=None): """ Parse i3status.conf so we can adapt our code to the i3status config. """ def notify_user(error): if py3_wrapper: py3_wrapper.notify_user(error) else: print(error) def parse_config(config): ''' Parse text or file as a py3status config file. ''' if hasattr(config, 'readlines'): config = ''.join(config.readlines()) parser = ConfigParser(config) parser.parse() parsed = parser.config del parser return parsed config = {} # get the file encoding this is important with multi-byte unicode chars encoding = check_output(['file', '-b', '--mime-encoding', '--dereference', config_path]) encoding = encoding.strip().decode('utf-8') with codecs.open(config_path, 'r', encoding) as f: try: config_info = parse_config(f) except ParseException as e: # There was a problem use our special error config error = e.one_line() notify_user(error) error_config = Template(ERROR_CONFIG).substitute( error=error.replace('"', '\\"')) config_info = parse_config(error_config) # update general section with defaults general_defaults = GENERAL_DEFAULTS.copy() if 'general' in config_info: general_defaults.update(config_info['general']) config['general'] = general_defaults config['py3status'] = config_info.get('py3status', {}) modules = {} on_click = {} i3s_modules = [] py3_modules = [] module_groups = {} def process_onclick(key, value, group_name): ''' Check on_click events are valid. Store if they are good ''' button_error = False button = '' try: button = key.split()[1] if int(button) not in range(1, 6): button_error = True except (ValueError, IndexError): button_error = True if button_error: err = 'Invalid on_click for `{}` should be 1, 2, 3, 4 or 5 saw `{}`' notify_user(err.format(group_name, button)) return False clicks = on_click.setdefault(group_name, {}) clicks[button] = value return True def get_module_type(name): ''' i3status or py3status? ''' if name.split()[0] in I3S_MODULE_NAMES: return 'i3status' return 'py3status' def process_module(name, module, parent): if parent: modules[parent]['items'].append(name) mg = module_groups.setdefault(name, []) mg.append(parent) if get_module_type(name) == 'py3status': module['.group'] = parent # check module content for k, v in list(module.items()): if k.startswith('on_click'): # on_click event process_onclick(k, v, name) # on_click should not be passed to the module via the config. del module[k] if isinstance(v, ModuleDefinition): # we are a container module['items'] = [] return module def get_modules(data, parent=None): for k, v in data.items(): if isinstance(v, ModuleDefinition): module = process_module(k, v, parent) modules[k] = module get_modules(v, parent=k) get_modules(config_info) config['order'] = [] def remove_any_contained_modules(module): ''' takes a module definition and returns a dict without any modules that may be defined with it. ''' fixed = {} for k, v in module.items(): if not isinstance(v, ModuleDefinition): fixed[k] = v return fixed def add_container_items(module_name): module = modules.get(module_name, {}) items = module.get('items', []) for item in items: if item in config: continue module_type = get_module_type(item) if module_type == 'i3status': if item not in i3s_modules: i3s_modules.append(item) else: if item not in py3_modules: py3_modules.append(item) module = modules.get(item, {}) config[item] = remove_any_contained_modules(module) # add any children add_container_items(item) # create config for modules in order for name in config_info.get('order', []): module = modules.get(name, {}) module_type = get_module_type(name) config['order'].append(name) add_container_items(name) if module_type == 'i3status': if name not in i3s_modules: i3s_modules.append(name) else: if name not in py3_modules: py3_modules.append(name) config[name] = remove_any_contained_modules(module) config['on_click'] = on_click config['i3s_modules'] = i3s_modules config['py3_modules'] = py3_modules config['.module_groups'] = module_groups # time and tztime modules need a format for correct processing for name in config: if name.split()[0] in TIME_MODULES and 'format' not in config[name]: if name.split()[0] == 'time': config[name]['format'] = TIME_FORMAT else: config[name]['format'] = TZTIME_FORMAT if not config['order']: notify_user('Your configuration file does not list any module' ' to be loaded with the "order" directive.') return config