def handle_cli_command(self, config): """Handle a command from the CLI. """ cmd = config["cli_command"] # aliases if cmd[0] in ["mod", "module", "modules"]: cmd[0] = "modules" # allowed cli commands if cmd[:2] in (["modules", "list"], ["modules", "details"]): docstrings.show_modules(config, cmd[1:]) # docstring formatting and checking elif cmd[:2] in (["docstring", "check"], ["docstring", "update"]): if cmd[1] == "check": show_diff = len(cmd) > 2 and cmd[2] == "diff" if show_diff: mods = cmd[3:] else: mods = cmd[2:] docstrings.check_docstrings(show_diff, config, mods) if cmd[1] == "update": if len(cmd) < 3: print_stderr("Error: you must specify what to update") sys.exit(1) if cmd[2] == "modules": docstrings.update_docstrings() else: docstrings.update_readme_for_modules(cmd[2:]) elif cmd[:2] in (["modules", "enable"], ["modules", "disable"]): # TODO: to be implemented pass else: print_stderr("Error: unknown command") sys.exit(1)
def handle_cli_command(self, config): """Handle a command from the CLI. """ cmd = config["cli_command"] # aliases if cmd[0] in ["mod", "module", "modules"]: cmd[0] = "modules" # allowed cli commands if cmd[:2] in (["modules", "list"], ["modules", "details"]): docstrings.show_modules(config, cmd[1:]) # docstring formatting and checking elif cmd[:2] in (["docstring", "check"], ["docstring", "update"]): if cmd[1] == "check": show_diff = len(cmd) > 2 and cmd[2] == "diff" docstrings.check_docstrings(show_diff, config) if cmd[1] == "update": if len(cmd) < 3: print_stderr("Error: you must specify what to update") sys.exit(1) if cmd[2] == "modules": docstrings.update_docstrings() else: docstrings.update_readme_for_modules(cmd[2:]) elif cmd[:2] in (["modules", "enable"], ["modules", "disable"]): # TODO: to be implemented pass else: print_stderr("Error: unknown command") sys.exit(1)
def handle_cli_command(self, config): """Handle a command from the CLI. """ cmd = config['cli_command'] # aliases if cmd[0] in ['mod', 'module', 'modules']: cmd[0] = 'modules' # allowed cli commands if cmd[:2] in (['modules', 'list'], ['modules', 'details']): docstrings.show_modules(config, cmd[1:]) # docstring formatting and checking elif cmd[:2] in (['docstring', 'check'], ['docstring', 'update']): if cmd[1] == 'check': show_diff = len(cmd) > 2 and cmd[2] == 'diff' if show_diff: mods = cmd[3:] else: mods = cmd[2:] docstrings.check_docstrings(show_diff, config, mods) if cmd[1] == 'update': if len(cmd) < 3: print_stderr('Error: you must specify what to update') sys.exit(1) if cmd[2] == 'modules': docstrings.update_docstrings() else: docstrings.update_readme_for_modules(cmd[2:]) elif cmd[:2] in (['modules', 'enable'], ['modules', 'disable']): # TODO: to be implemented pass else: print_stderr('Error: unknown command') sys.exit(1)
def update_docstrings(): ''' update the docstring of each module using info in the modules/README.md file ''' modules_dict = parse_readme() files = {} # update modules for mod in modules_dict: mod_file = os.path.join(modules_directory(), mod + '.py') with open(mod_file) as f: files[mod] = f.readlines() for mod in files: replaced = False done = False lines = False out = [] quotes = None for row in files[mod]: # deal with single or double quoted docstring if not quotes: if row.strip().startswith('"""'): quotes = '"""' if row.strip().startswith("'''"): quotes = "'''" if quotes and row.strip().startswith(quotes) and not done: out.append(row) if not replaced: out = out + [ ''.join(_to_docstring(modules_dict[mod])).strip() + '\n' ] replaced = True if lines: done = True if not done and not lines: lines = True continue if not lines or done: out.append(row) mod_file = os.path.join(modules_directory(), mod + '.py') with open(mod_file, 'w') as f: f.writelines(out) print_stderr('Modules updated from README.md')
def update_docstrings(): """ update the docstring of each module using info in the modules/README.md file """ modules_dict = parse_readme() files = {} # update modules for mod in modules_dict: mod_file = modules_directory() / f"{mod}.py" with mod_file.open() as f: files[mod] = f.readlines() for mod, rows in files.items(): replaced = False done = False lines = False out = [] quotes = None for row in rows: # deal with single or double quoted docstring if not quotes: if row.strip().startswith('"""'): quotes = '"""' if row.strip().startswith("'''"): quotes = "'''" if quotes and row.strip().startswith(quotes) and not done: out.append(row) if not replaced: out = out + [ "".join(_to_docstring(modules_dict[mod])).strip() + "\n" ] replaced = True if lines: done = True if not done and not lines: lines = True continue if not lines or done: out.append(row) mod_file = modules_directory() / f"{mod}.py" with mod_file.open("w") as f: f.writelines(out) print_stderr("Modules updated from README.md")
def update_readme_for_modules(modules): """ Update README.md updating the sections for the module names listed. """ readme = parse_readme() module_docstrings = core_module_docstrings() if modules == ["__all__"]: modules = core_module_docstrings().keys() for module in modules: if module in module_docstrings: print_stderr(f"Updating README.md for module {module}") readme[module] = module_docstrings[module] else: print_stderr(f"Module {module} not in core modules") # write the file readme_file = modules_directory() / "README.md" readme_file.write_text(create_readme(readme))
def update_readme_for_modules(modules): """ Update README.md updating the sections for the module names listed. """ readme = parse_readme() module_docstrings = core_module_docstrings() if modules == ["__all__"]: modules = core_module_docstrings().keys() for module in modules: if module in module_docstrings: print_stderr("Updating README.md for module {}".format(module)) readme[module] = module_docstrings[module] else: print_stderr("Module {} not in core modules".format(module)) # write the file readme_file = os.path.join(modules_directory(), "README.md") with open(readme_file, "w") as f: f.write(create_readme(readme))
def check_docstrings(show_diff=False, config=None, mods=None): """ Check docstrings in module match the README.md """ readme = parse_readme() modules_readme = core_module_docstrings(config=config) warned = False if create_readme(readme) != create_readme(modules_readme): for module in sorted(readme): if mods and module not in mods: continue err = None if module not in modules_readme: err = "Module {} in README but not in /modules".format(module) elif ( "".join(readme[module]).strip() != "".join(modules_readme[module]).strip() ): err = "Module {} docstring does not match README".format(module) if err: if not warned: print_stderr("Documentation does not match!\n") warned = True print_stderr(err) for module in modules_readme: if mods and module not in mods: continue if module not in readme: print_stderr("Module {} in /modules but not in README".format(module)) if show_diff: print_stderr( "\n".join( difflib.unified_diff( create_readme(readme).split("\n"), create_readme(modules_readme).split("\n"), ) ) ) else: if warned: print_stderr("\nUse `py3-cmd docstring --diff` to view diff.")
def update_readme_for_modules(modules): ''' Update README.md updating the sections for the module names listed. ''' readme = parse_readme() module_docstrings = core_module_docstrings() if modules == ['__all__']: modules = core_module_docstrings().keys() for module in modules: if module in module_docstrings: print_stderr('Updating README.md for module {}'.format(module)) readme[module] = module_docstrings[module] else: print_stderr('Module {} not in core modules'.format(module)) # write the file readme_file = os.path.join(modules_directory(), 'README.md') with open(readme_file, 'w') as f: f.write(create_readme(readme))
def check_docstrings(show_diff=False, config=None, mods=None): ''' Check docstrings in module match the README.md ''' readme = parse_readme() modules_readme = core_module_docstrings(config=config) warned = False if (create_readme(readme) != create_readme(modules_readme)): for module in sorted(readme): if mods and module not in mods: continue err = None if module not in modules_readme: err = '\tModule {} in README but not in /modules'.format( module ) elif ''.join(readme[module]).strip() != ''.join(modules_readme[ module]).strip(): err = '\tModule {} docstring does not match README'.format( module ) if err: if not warned: print_stderr('Documentation does not match!\n') warned = True print_stderr(err) for module in modules_readme: if mods and module not in mods: continue if module not in readme: print_stderr( '\tModule {} in /modules but not in README'.format(module)) if show_diff: print_stderr('\n'.join(difflib.unified_diff( create_readme(readme).split('\n'), create_readme( modules_readme).split('\n')))) else: if warned: print_stderr( '\nUse `py3status docstring check diff` to view diff.' )
def check_docstrings(show_diff=False, config=None, mods=None): """ Check docstrings in module match the README.md """ readme = parse_readme() modules_readme = core_module_docstrings(config=config) warned = False if create_readme(readme) != create_readme(modules_readme): for module in sorted(readme): if mods and module not in mods: continue err = None if module not in modules_readme: err = f"Module {module} in README but not in /modules" elif ( "".join(readme[module]).strip() != "".join(modules_readme[module]).strip() ): err = f"Module {module} docstring does not match README" if err: if not warned: print_stderr("Documentation does not match!\n") warned = True print_stderr(err) for module in modules_readme: if mods and module not in mods: continue if module not in readme: print_stderr(f"Module {module} in /modules but not in README") if show_diff: print_stderr( "\n".join( difflib.unified_diff( create_readme(readme).split("\n"), create_readme(modules_readme).split("\n"), ) ) ) else: if warned: print_stderr("\nUse `py3-cmd docstring --diff` to view diff.")
def report_exception(self, msg, notify_user=True, level="error", error_frame=None): """ Report details of an exception to the user. This should only be called within an except: block Details of the exception are reported eg filename, line number and exception type. Because stack trace information outside of py3status or it's modules is not helpful in actually finding and fixing the error, we try to locate the first place that the exception affected our code. Alternatively if the error occurs in a module via a Py3 call that catches and reports the error then we receive an error_frame and use that as the source of the error. NOTE: msg should not end in a '.' for consistency. """ # Get list of paths that our stack trace should be found in. py3_paths = [os.path.dirname(__file__)] + self.config["include_paths"] traceback = None try: # We need to make sure to delete tb even if things go wrong. exc_type, exc_obj, tb = sys.exc_info() stack = extract_tb(tb) error_str = "{}: {}\n".format(exc_type.__name__, exc_obj) traceback = [error_str] if error_frame: # The error occurred in a py3status module so the traceback # should be made to appear correct. We caught the exception # but make it look as though we did not. traceback += format_stack(error_frame, 1) + format_tb(tb) filename = os.path.basename(error_frame.f_code.co_filename) line_no = error_frame.f_lineno else: # This is a none module based error traceback += format_tb(tb) # Find first relevant trace in the stack. # it should be in py3status or one of it's modules. found = False for item in reversed(stack): filename = item[0] for path in py3_paths: if filename.startswith(path): # Found a good trace filename = os.path.basename(item[0]) line_no = item[1] found = True break if found: break # all done! create our message. msg = "{} ({}) {} line {}.".format(msg, exc_type.__name__, filename, line_no) except: # noqa e722 # something went wrong report what we can. msg = "{}.".format(msg) finally: # delete tb! del tb # log the exception and notify user self.py3_wrapper.log(msg, "warning") if traceback: # if debug is not in the config then we are at an early stage of # running py3status and logging is not yet available so output the # error to STDERR so it can be seen if "debug" not in self.config: print_stderr("\n".join(traceback)) elif self.config.get("log_file"): self.py3_wrapper.log("".join(["Traceback\n"] + traceback)) if notify_user: self.py3_wrapper.notify_user(msg, level=level)
def report_exception(self, msg, notify_user=True, level="error", error_frame=None): """ Report details of an exception to the user. This should only be called within an except: block Details of the exception are reported eg filename, line number and exception type. Because stack trace information outside of py3status or it's modules is not helpful in actually finding and fixing the error, we try to locate the first place that the exception affected our code. Alternatively if the error occurs in a module via a Py3 call that catches and reports the error then we receive an error_frame and use that as the source of the error. NOTE: msg should not end in a '.' for consistency. """ # Get list of paths that our stack trace should be found in. py3_paths = [os.path.dirname(__file__)] + self.config["include_paths"] traceback = None try: # We need to make sure to delete tb even if things go wrong. exc_type, exc_obj, tb = sys.exc_info() stack = extract_tb(tb) error_str = "{}: {}\n".format(exc_type.__name__, exc_obj) traceback = [error_str] if error_frame: # The error occurred in a py3status module so the traceback # should be made to appear correct. We caught the exception # but make it look as though we did not. traceback += format_stack(error_frame, 1) + format_tb(tb) filename = os.path.basename(error_frame.f_code.co_filename) line_no = error_frame.f_lineno else: # This is a none module based error traceback += format_tb(tb) # Find first relevant trace in the stack. # it should be in py3status or one of it's modules. found = False for item in reversed(stack): filename = item[0] for path in py3_paths: if filename.startswith(path): # Found a good trace filename = os.path.basename(item[0]) line_no = item[1] found = True break if found: break # all done! create our message. msg = "{} ({}) {} line {}.".format( msg, exc_type.__name__, filename, line_no ) except: # noqa e722 # something went wrong report what we can. msg = "{}.".format(msg) finally: # delete tb! del tb # log the exception and notify user self.py3_wrapper.log(msg, "warning") if traceback: # if debug is not in the config then we are at an early stage of # running py3status and logging is not yet available so output the # error to STDERR so it can be seen if "debug" not in self.config: print_stderr("\n".join(traceback)) elif self.config.get("log_file"): self.py3_wrapper.log("".join(["Traceback\n"] + traceback)) if notify_user: self.py3_wrapper.notify_user(msg, level=level)
def check_docstrings(show_diff=False, config=None): ''' Check docstrings in module match the README.md ''' readme = parse_readme() modules_readme = core_module_docstrings(config=config) if (create_readme(readme) != create_readme(modules_readme)): print_stderr('Documentation does not match!\n') for module in readme: if module not in modules_readme: print_stderr( '\tModule {} in README but not in /modules'.format(module)) elif ''.join(readme[module]).strip() != ''.join(modules_readme[ module]).strip(): print_stderr( '\tModule {} docstring does not match README'.format( module)) for module in modules_readme: if module not in readme: print_stderr( '\tModule {} in /modules but not in README'.format(module)) if show_diff: print_stderr('\n'.join(difflib.unified_diff( create_readme(readme).split('\n'), create_readme( modules_readme).split('\n')))) else: print_stderr() print_stderr('Use `py3status docstring check diff` to view diff.')
def show_modules(config, params): ''' List modules available optionally with details. ''' details = params[0] == 'details' if details: modules_list = params[1:] core_mods = True user_mods = True else: user_mods = True core_mods = True modules_list = [] if len(params) == 2: if params[1] == 'user': user_mods = True core_mods = False elif params[1] == 'core': user_mods = False core_mods = True if details: print_stderr('Module details:') else: print_stderr('Available modules:') modules = core_module_docstrings(include_core=core_mods, include_user=user_mods, config=config) for name in sorted(modules.keys()): if modules_list and name not in modules_list: continue module = _to_docstring(modules[name]) desc = module[0][:-1] if details: dash_len = len(name) print_stderr('=' * dash_len) print_stderr(name) print_stderr('=' * dash_len) for description in module: print_stderr(description[:-1]) else: print_stderr(' %-22s %s' % (name, desc))
def check_docstrings(show_diff=False, config=None): ''' Check docstrings in module match the README.md ''' readme = parse_readme() modules_readme = core_module_docstrings(config=config) if (create_readme(readme) != create_readme(modules_readme)): print_stderr('Documentation does not match!\n') for module in readme: if module not in modules_readme: print_stderr( '\tModule {} in README but not in /modules'.format(module)) elif ''.join(readme[module]).strip() != ''.join( modules_readme[module]).strip(): print_stderr( '\tModule {} docstring does not match README'.format( module)) for module in modules_readme: if module not in readme: print_stderr( '\tModule {} in /modules but not in README'.format(module)) if show_diff: print_stderr('\n'.join( difflib.unified_diff( create_readme(readme).split('\n'), create_readme(modules_readme).split('\n')))) else: print_stderr() print_stderr('Use `py3status docstring check diff` to view diff.')