edq.clilib.list
Show the CLI tools available in this package.
1""" 2Show the CLI tools available in this package. 3""" 4 5import argparse 6import inspect 7import os 8 9import edq.clilib.model 10import edq.util.dirent 11import edq.util.pyimport 12 13def auto_list( 14 recursive: bool = False, 15 skip_dirs: bool = False, 16 ) -> None: 17 """ 18 Print the caller's docstring and call _list_dir() on it, 19 but will figure out the package's docstring, base_dir, and command_prefix automatically. 20 This will use the inspect library, so only use in places that use code normally. 21 The first stack frame not in this file will be used. 22 """ 23 24 this_path = os.path.realpath(__file__) 25 26 caller_frame_info = None 27 for frame_info in inspect.stack(): 28 if (edq.util.dirent.same(this_path, frame_info.filename)): 29 # Ignore this file. 30 continue 31 32 caller_frame_info = frame_info 33 break 34 35 if (caller_frame_info is None): 36 raise ValueError("Unable to determine caller's stack frame.") 37 38 path = caller_frame_info.filename 39 base_dir = os.path.dirname(path) 40 41 try: 42 module = inspect.getmodule(caller_frame_info.frame) 43 if (module is None): 44 raise ValueError(f"Unable to get module for '{path}'.") 45 except Exception as ex: 46 raise ValueError("Unable to get caller information for listing CLI information.") from ex 47 48 if (module.__package__ is None): 49 raise ValueError(f"Caller module has no package information: '{path}'.") 50 51 package = edq.clilib.model.CLIPackage.from_path(base_dir, module.__package__) 52 if (package is None): 53 raise ValueError(f"Caller package is not a CLI package: '{base_dir}'.") 54 55 print(package.get_description()) 56 _list_dir(package, recursive, skip_dirs) 57 58def _list_dir(package: edq.clilib.model.CLIPackage, recursive: bool, skip_dirs: bool) -> None: 59 """ 60 List/descend the given dir. 61 Don't output information out this directory itself, just the entries. 62 """ 63 64 for dirent in package.dirents: 65 if (isinstance(dirent, edq.clilib.model.CLIModule)): 66 _handle_module(dirent) 67 elif (isinstance(dirent, edq.clilib.model.CLIPackage)): 68 if (not skip_dirs): 69 _handle_package(dirent) 70 71 if (recursive): 72 _list_dir(dirent, recursive, skip_dirs) 73 74def _handle_module(module: edq.clilib.model.CLIModule) -> None: 75 """ Process a module. """ 76 77 print() 78 print(module.qualified_name) 79 print(module.get_description()) 80 print(module.get_usage_text()) 81 82def _handle_package(package: edq.clilib.model.CLIPackage) -> None: 83 """ Process a package. """ 84 85 print() 86 print(package.qualified_name + '.*') 87 print(package.get_description()) 88 print(f"See `python3 -m {package.qualified_name}` for more information.") 89 90def _get_parser() -> argparse.ArgumentParser: 91 parser = argparse.ArgumentParser( 92 description = __doc__.strip(), 93 epilog = ("Note that you don't need to provide a package as an argument," 94 + " since you already called this on the target package.")) 95 96 parser.add_argument('-r', '--recursive', dest = 'recursive', 97 action = 'store_true', default = False, 98 help = 'Recur into each package to look for tools and subpackages (default: %(default)s).') 99 100 parser.add_argument('-s', '--skip-dirs', dest = 'skip_dirs', 101 action = 'store_true', default = False, 102 help = ('Do not output information about directories/packages,' 103 + ' only tools/files/modules (default: %(default)s).')) 104 105 return parser 106 107def run_cli(args: argparse.Namespace) -> int: 108 """ 109 List the caller's dir. 110 """ 111 112 auto_list(recursive = args.recursive, skip_dirs = args.skip_dirs) 113 114 return 0 115 116def main() -> int: 117 """ 118 Run as if this process has been called as a executable. 119 This will parse the command line and list the caller's dir. 120 """ 121 122 return run_cli(_get_parser().parse_args())
def
auto_list(recursive: bool = False, skip_dirs: bool = False) -> None:
14def auto_list( 15 recursive: bool = False, 16 skip_dirs: bool = False, 17 ) -> None: 18 """ 19 Print the caller's docstring and call _list_dir() on it, 20 but will figure out the package's docstring, base_dir, and command_prefix automatically. 21 This will use the inspect library, so only use in places that use code normally. 22 The first stack frame not in this file will be used. 23 """ 24 25 this_path = os.path.realpath(__file__) 26 27 caller_frame_info = None 28 for frame_info in inspect.stack(): 29 if (edq.util.dirent.same(this_path, frame_info.filename)): 30 # Ignore this file. 31 continue 32 33 caller_frame_info = frame_info 34 break 35 36 if (caller_frame_info is None): 37 raise ValueError("Unable to determine caller's stack frame.") 38 39 path = caller_frame_info.filename 40 base_dir = os.path.dirname(path) 41 42 try: 43 module = inspect.getmodule(caller_frame_info.frame) 44 if (module is None): 45 raise ValueError(f"Unable to get module for '{path}'.") 46 except Exception as ex: 47 raise ValueError("Unable to get caller information for listing CLI information.") from ex 48 49 if (module.__package__ is None): 50 raise ValueError(f"Caller module has no package information: '{path}'.") 51 52 package = edq.clilib.model.CLIPackage.from_path(base_dir, module.__package__) 53 if (package is None): 54 raise ValueError(f"Caller package is not a CLI package: '{base_dir}'.") 55 56 print(package.get_description()) 57 _list_dir(package, recursive, skip_dirs)
Print the caller's docstring and call _list_dir() on it, but will figure out the package's docstring, base_dir, and command_prefix automatically. This will use the inspect library, so only use in places that use code normally. The first stack frame not in this file will be used.
def
run_cli(args: argparse.Namespace) -> int:
108def run_cli(args: argparse.Namespace) -> int: 109 """ 110 List the caller's dir. 111 """ 112 113 auto_list(recursive = args.recursive, skip_dirs = args.skip_dirs) 114 115 return 0
List the caller's dir.
def
main() -> int:
117def main() -> int: 118 """ 119 Run as if this process has been called as a executable. 120 This will parse the command line and list the caller's dir. 121 """ 122 123 return run_cli(_get_parser().parse_args())
Run as if this process has been called as a executable. This will parse the command line and list the caller's dir.