Source code for rgpycrumbs.cli

import argparse
import logging
import os
import site
import subprocess
import sys
from pathlib import Path

# Configure logging to output to stderr
logging.basicConfig(level=logging.INFO, format="%(message)s")

# The directory where cli.py is located
[docs] PACKAGE_ROOT = Path(__file__).parent.resolve()
[docs] def _get_scripts_in_folder(folder_name: str) -> list[str]: """Returns a sorted list of script names (without extension) in a folder.""" folder_path = PACKAGE_ROOT / folder_name if not folder_path.is_dir(): return [] return sorted( f.stem for f in folder_path.glob("*.py") if not f.name.startswith("_") )
[docs] def _dispatch(group: str, script_name: str, script_args: list): """ Sets up the environment and runs the target script via 'uv run'. """ # Convert script-name to filename (e.g., plt-neb -> plt_neb.py) filename = f"{script_name.replace('-', '_')}.py" script_path = PACKAGE_ROOT / group / filename if not script_path.is_file(): rerr = f"Error: Script not found at '{script_path}'" logging.error(rerr) sys.exit(1) command = ["uv", "run", str(script_path), *script_args] # --- SETUP ENVIRONMENT --- env = os.environ.copy() # Fallback imports try: site_packages = [*site.getsitepackages(), *site.getusersitepackages()] env["RGPYCRUMBS_PARENT_SITE_PACKAGES"] = os.pathsep.join(site_packages) except (AttributeError, ImportError): pass # Add parent dir to PYTHONPATH for internal imports (e.g. rgpycrumbs._aux) project_root = str(PACKAGE_ROOT.parent) current_pythonpath = env.get("PYTHONPATH", "") env["PYTHONPATH"] = f"{project_root}{os.pathsep}{current_pythonpath}" logging.info(f"--> Dispatching to: {' '.join(command)}") try: # Use subprocess.run to block until completion subprocess.run(command, check=True, env=env) # noqa: S603 except FileNotFoundError: logging.error("Error: 'uv' command not found. Is it installed?") sys.exit(1) except subprocess.CalledProcessError as e: sys.exit(e.returncode) except KeyboardInterrupt: sys.exit(130)
[docs] def main(): parser = argparse.ArgumentParser( prog="rgpycrumbs", description="A dispatcher that runs self-contained scripts using 'uv'.", ) # Create subparsers for the groups (e.g., 'eon', 'prefix') subparsers = parser.add_subparsers( title="Command Groups", dest="group", required=True, metavar="GROUP" ) # --- DYNAMIC DISCOVERY --- # Scan the package directory for subfolders (groups) valid_groups = sorted( d.name for d in PACKAGE_ROOT.iterdir() if d.is_dir() and not d.name.startswith(("_", ".")) ) for group in valid_groups: file_stems = _get_scripts_in_folder(group) if not file_stems: continue # Create a display list with hyphens (e.g. plt-neb) display_scripts = [s.replace("_", "-") for s in file_stems] # Build a set of all valid inputs (both _ and - forms) for manual validation valid_inputs = set(file_stems) | set(display_scripts) # Create a subparser for this group group_parser = subparsers.add_parser( group, help=f"Tools in the '{group}' category.", description=f"Available scripts in '{group}':\n" f" {', '.join(display_scripts)}", formatter_class=argparse.RawDescriptionHelpFormatter, ) # The script name is a positional argument. # We REMOVE 'choices' to hide the ugly list from the usage string. # We manually validate later. group_parser.add_argument( "script", help="The specific script to run (e.g., 'plt-neb')." " Accepts names with '_' or '-'.", metavar="SCRIPT", ) # REMAINDER captures everything after the script name (flags, args, etc.) group_parser.add_argument( "script_args", nargs=argparse.REMAINDER, help="Arguments passed to the script.", ) # Attach the valid inputs to the parser instance so we can check later # (This is a bit hacky but keeps the main() logic clean) group_parser.set_defaults(valid_inputs=valid_inputs) # Parse if len(sys.argv) == 1: parser.print_help(sys.stderr) sys.exit(1) args = parser.parse_args() # Manual Validation of the script name if args.script not in args.valid_inputs: logging.error( f"Error: Invalid script '{args.script}' for group '{args.group}'." ) # Re-generate the "nice" list for the error message file_stems = _get_scripts_in_folder(args.group) display_scripts = [s.replace("_", "-") for s in file_stems] logging.error(f"Available scripts: {', '.join(display_scripts)}") sys.exit(1) # Dispatch _dispatch(args.group, args.script, args.script_args)
if __name__ == "__main__": main()