198 lines
6.7 KiB
Python
198 lines
6.7 KiB
Python
# 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(
|
|
"WARNING: the 'pygount' package is not available. Comment counts and some extended metrics may not be available.",
|
|
file=sys.stderr,
|
|
)
|
|
print(
|
|
"To fix: activate the virtualenv used to run the app and run 'pip install pygount'",
|
|
file=sys.stderr,
|
|
)
|
|
print(
|
|
"If you ship a PyInstaller executable, rebuild it including 'pygount' (add a hook or hiddenimports).",
|
|
file=sys.stderr,
|
|
)
|
|
except Exception:
|
|
# Non-critical: continue execution
|
|
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()
|