Source code for rgpycrumbs.cli

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

import click

# 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 CLI script names (without extension) in a folder. Excludes library modules (hdf5_io, plotting) and internal files (__init__, _*). Includes actual CLI entry point scripts. """ folder_path = PACKAGE_ROOT / folder_name if not folder_path.is_dir(): return [] # Library modules to exclude library_modules = {"hdf5_io", "plotting", "utils", "helpers"} scripts = [] for f in folder_path.glob("*.py"): if f.name.startswith("_"): continue stem = f.stem # Skip library modules and __init__ if stem in library_modules or stem == "__init__": continue # Strip 'cli_' prefix if present for cleaner command names if stem.startswith("cli_"): stem = stem[4:] scripts.append(stem) return sorted(scripts)
[docs] def _dispatch( group: str, script_name: str, script_args: tuple, is_dev: bool = False, is_verbose: bool = False, ): """ 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(): click.echo(f"Error: Script not found at '{script_path}'", err=True) sys.exit(1) if not is_dev: command = ["uv", "run", str(script_path), *script_args] else: command = [sys.executable, str(script_path), *script_args] if is_verbose: click.echo(f"VERBOSE: Resolved script path -> {script_path}", err=True) click.echo(f"VERBOSE: Constructed command -> {' '.join(command)}", err=True) # --- 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}" click.echo(f"--> Dispatching to: {' '.join(command)}") try: subprocess.run(command, check=True, env=env) # noqa: S603 except FileNotFoundError: click.echo("Error: 'uv' command not found. Is it installed?", err=True) sys.exit(1) except subprocess.CalledProcessError as e: sys.exit(e.returncode) except KeyboardInterrupt: sys.exit(130)
[docs] def _make_script_command(group_name: str, script_stem: str) -> click.Command: """Creates a click command that dispatches to a PEP 723 script. For full option help, run the script directly: python -m rgpycrumbs.<group>.<script> --help """ display_name = script_stem.replace("_", "-") @click.command( name=display_name, context_settings={"ignore_unknown_options": True, "allow_extra_args": True}, add_help_option=False, # Pass --help to underlying script ) @click.pass_context def cmd(ctx): # Retrieve the dev flag safely from the parent context is_dev = ctx.obj.get("is_dev", False) if ctx.obj else False is_verbose = ctx.obj.get("is_verbose", False) if ctx.obj else False # Pass through --help to underlying script if "--help" in ctx.args or "-h" in ctx.args: # Run script with --help to show actual options _dispatch( group_name, display_name, tuple(ctx.args), is_dev=is_dev, is_verbose=False, # Don't add verbose noise to help output ) return _dispatch( group_name, display_name, tuple(ctx.args), is_dev=is_dev, is_verbose=is_verbose, ) cmd.help = f"""Run the {display_name} script. For full option documentation, run: python -m rgpycrumbs.{group_name}.{display_name} --help Or use --help flag which will be passed to the script: rgpycrumbs {group_name} {display_name} --help """ return cmd
@click.group() @click.option( "--dev", is_flag=True, help="Run using sys.executable instead of 'uv run' for local development.", ) @click.option( "--verbose", "-v", is_flag=True, help="Print script paths and constructed commands before execution.", ) @click.version_option(package_name="rgpycrumbs") @click.pass_context
[docs] def main(ctx, dev, verbose): """A dispatcher that runs self-contained PEP 723 scripts using 'uv'.""" # Ensure ctx.obj is a dictionary so we can store state in it ctx.ensure_object(dict) ctx.obj["is_dev"] = dev ctx.obj["is_verbose"] = verbose
# --- DYNAMIC DISCOVERY --- # Scan the package directory for subfolders (groups) and register them
[docs] _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:
[docs] _file_stems = _get_scripts_in_folder(_group)
if not _file_stems: continue # Create a click group for this category _group_cmd = click.Group(name=_group, help=f"Tools in the '{_group}' category.") for _stem in _file_stems: _group_cmd.add_command(_make_script_command(_group, _stem)) main.add_command(_group_cmd) if __name__ == "__main__": main()