# ucc_py/__main__.py import argparse import json import sys from pathlib import Path from pyucc.core.differ import BaselineManager, Differ from pyucc.core import duplicates as duplicates_mod def main(): """ Main entry point for the ucc-py CLI application. """ raw = sys.argv[1:] # If the user invoked the dedicated 'duplicates' or 'differ' subcommand, build a parser for it. if raw and raw[0] == "duplicates": parser = argparse.ArgumentParser(description="duplicates utilities") parser.add_argument( "path", type=Path, help="Path to scan for duplicates (project root)" ) parser.add_argument( "--threshold", dest="threshold", type=float, default=5.0, help="Maximum percent of changed lines to consider files duplicates (default: 5.0)", ) parser.add_argument( "--ext", dest="extensions", nargs="*", default=None, help="Optional list of extensions to include (e.g. .py .c .cpp)", ) args = parser.parse_args() path = str(args.path) exts = args.extensions if exts: # normalize to lowercase and ensure leading dot exts = [e if e.startswith(".") else f".{e}" for e in exts] exts = [e.lower() for e in exts] res = duplicates_mod.find_duplicates_in_dir( path, extensions=exts, dup_threshold=args.threshold ) print(json.dumps(res, indent=2)) return if raw and raw[0] == "differ": parser = argparse.ArgumentParser(description="differ utilities") subparsers = parser.add_subparsers(dest="differ_cmd") create_sp = subparsers.add_parser( "create", help="Create a baseline from a directory or git commit" ) create_sp.add_argument( "path", type=Path, help="Path to project directory or git repo" ) create_sp.add_argument( "--git-ref", dest="git_ref", default=None, help="If set, export given git ref as baseline", ) create_sp.add_argument( "--snapshot", action="store_true", default=True, help="Store snapshot of files (default: True)", ) create_sp.add_argument( "--ignore", dest="ignore", nargs="*", default=None, help="Ignore patterns to exclude from baseline (e.g. *.pyc)", ) diff_sp = subparsers.add_parser( "diff", help="Run diff against an existing baseline" ) diff_sp.add_argument("baseline_id", help="Baseline id to compare against") diff_sp.add_argument("path", type=Path, help="Current project path to compare") args = parser.parse_args() if args.differ_cmd == "create": path = str(args.path) bm = BaselineManager(path) ignore_patterns = args.ignore if getattr(args, "ignore", None) else None if args.git_ref: baseline_id = bm.create_baseline_from_git( path, commit_ref=args.git_ref, baseline_id=None, snapshot=args.snapshot, ignore_patterns=ignore_patterns, ) else: baseline_id = bm.create_baseline_from_dir( path, baseline_id=None, snapshot=args.snapshot, ignore_patterns=ignore_patterns, ) print(f"Baseline created: {baseline_id}") return elif args.differ_cmd == "diff": baseline_id = args.baseline_id path = str(args.path) bm = BaselineManager(path) meta = bm.load_metadata(baseline_id) baseline_files_dir = bm.get_baseline_files_dir(baseline_id) d = Differ(meta, path, baseline_files_dir=baseline_files_dir) result = d.diff() print(json.dumps(result, indent=2)) return else: parser.print_help() return # Legacy CLI mode (keep backward-compatible behavior) parser = argparse.ArgumentParser( description="A Python tool to replicate and enhance UCC functionalities." ) parser.add_argument( "--gui", action="store_true", help="Launch the graphical user interface (Tkinter).", ) parser.add_argument( "baseline_dirs", metavar="DIR", nargs="*", type=Path, help="One (for analysis) or two (for diff) directories to process.", ) parser.add_argument( "--outdir", type=Path, default=Path("./ucc_py_output"), help="Directory to store output reports.", ) args = parser.parse_args() # Inform the user early if pygount is not available (affects comment counting) try: from pyucc.core import countings_impl if not getattr(countings_impl, "_HAS_PYGOUNT", False): print( "ATTENZIONE: il pacchetto 'pygount' non รจ disponibile. I conteggi dei commenti e le metriche estese potrebbero non essere disponibili.", file=sys.stderr, ) print( "Per risolvere: attiva la virtualenv usata per eseguire l'app e esegui 'pip install pygount'", file=sys.stderr, ) print( "Se distribuisci con PyInstaller, ricostruisci l'eseguibile includendo 'pygount' (aggiungi un hook o hiddenimports).", file=sys.stderr, ) except Exception: # Non critico: continuiamo senza interrompere l'esecuzione pass # If user asked for GUI, or no positional directories were provided, launch GUI and exit if args.gui or len(args.baseline_dirs or []) == 0: try: from pyucc.gui.gui import run_app except Exception as e: print(f"Error launching GUI: {e}", file=sys.stderr) sys.exit(1) run_app() return # --- Argument Validation for legacy CLI mode --- if len(args.baseline_dirs) > 2: print( "Error: Maximum of two baseline directories are supported.", file=sys.stderr ) sys.exit(1) for directory in args.baseline_dirs: if not directory.is_dir(): print( f"Error: Path '{directory}' is not a valid directory.", file=sys.stderr ) sys.exit(1) print(f"Starting analysis for: {[str(d) for d in args.baseline_dirs]}") print(f"Output will be saved to: {args.outdir}") args.outdir.mkdir(parents=True, exist_ok=True) if __name__ == "__main__": main()