SXXXXXXX_PyUCC/doc/Sviluppo.md
2025-11-12 13:39:56 +01:00

27 KiB
Raw Blame History

Documento tecnico — Pipeline Python per replicare e migliorare UCC v.2018.07

Scopo: descrivere in modo preciso ed esaustivo come progettare e realizzare uno script / pacchetto Python che automatizzi tutte le funzionalità chiave di UCC v.2018.07 (conteggio fisico/logico, differencing, cyclomatic complexity, maintainability index, function-level differencing, estensioni personalizzate, reportistica), ottenendo risultati comparabili o migliori rispetto alloriginale. Le scelte tecniche privilegiano leggibilità, estendibilità, integrazione CI e riproducibilità. Il documento descrive requisiti, architettura, algoritmi, formati di output, gestione del differencing, prestazioni, test di validazione e piano di integrazione.

Nota: molte funzionalità, opzioni e file di output di riferimento sono prese dalla documentazione UCC v.2018.07 (manuale e release notes).


1. Requisiti funzionali (sintesi)

  1. Counting: contare per file/language:

    • righe totali (physical SLOC)
    • righe codice (executables / logical SLOC)
    • righe commento (linee commento intere + commenti embedded)
    • righe vuote (blank)
    • keyword counts (quando definito)
  2. Differencing: confronto tra due baselines A e B per:

    • added / deleted / modified / unmodified logical SLOC
    • function-level differencing (quando possibile)
    • threshold di modifica (percentuale match tra linee, come -t in UCC)
  3. Metriche:

    • Cyclomatic Complexity (per funzione / file / linguaggio)
    • Maintainability Index (per file / lingua dove applicabile)
  4. Duplicate detection: identificare file duplicati (opzionale, disattivabile per performance)

  5. Estensioni e mapping: file di mapping estensioni → linguaggio (equivalente -extfile)

  6. Output/report: generare file CSV/JSON/HTML con lo stesso set informativo di UCC (vedi tabella output in manuale).

  7. CLI: interfaccia compatibile con flusso UCC (opzioni -dir, -d, -threads, -t, -funcDiff, -outdir, -nodup, -nocomplex, ecc.).

  8. Performance: supporto multithreading / multiprocessing e gestione memorizzazione parziale per grandi codebase.

  9. Tracciabilità: hash dei file, timestamp dei run, lockfile delle dipendenze (per certificazione).


2. Requisiti non funzionali

  • Portabilità: Linux / macOS / Windows (Python 3.9+).
  • Facilità di integrazione in CI (exit codes, artefatti JSON/CSV).
  • Estendibilità: regole per aggiungere nuovi linguaggi senza cambiare core.
  • Licenza: preferibilmente MIT/BSD (compatibile con uso aziendale).

3. Panoramica architetturale

ucc_py/                      # package principale
├── __main__.py              # CLI entry point (typer/argparse)
├── core/
│   ├── scanner.py           # discovery files, apply filespecs, extfile mapping
│   ├── counter.py           # wrapper pygount / parser custom
│   ├── metrics.py           # lizard wrapper + MI calculation
│   ├── differ.py            # differencing logic (file pairing, line compare, func-level)
│   ├── dupdetect.py         # duplicate detection
│   ├── executor.py         # worker pool orchestration (threads/processes)
│   └── outputs.py           # csv/json/html writer, unified reports
├── rules/
│   └── default_extfile.toml # mapping default extensions <-> language + comment delimiters
├── tests/
│   └── fixtures/...
├── docs/
└── requirements.txt

Componenti chiave esterni:

  • pygount (conteggio righe/commenti per centinaia di linguaggi)
  • lizard (cyclomatic complexity + function parsing)
  • gitpython / difflib (differencing quando necessario)
  • pandas (composizione report), jinja2 (HTML templates)
  • concurrent.futures / multiprocessing (parallelismo)

4. Strategie tecniche e algoritmi

4.1 Identificazione linguaggio e mapping

  • Caricare rules/default_extfile.toml che mappa estensioni → linguaggio e definisce i delimitatori di commento (line/block).
  • Scanner ricorsivo con pathlib che seleziona file secondo filespecs o filelist.
  • Supportare estensioni multiple e "no extension" (come UCC) tramite extfile.

4.2 Conteggio SLOC (physical/logical/comment/blank)

Approccio consigliato:

  1. Primary: usare pygount per generare per-file:

    • raw_total_lines, code_lines, comment_lines, blank_lines, language.
    • pygount usa Pygments tokenization, quindi buona copertura di linguaggi.
  2. Fallback/custom: per linguaggi non ben gestiti da pygount o per regole UCC molto specifiche, implementare un parser di fallback basato su:

    • regole per delimitatori linea/block,
    • riconoscimento stringhe/literal per evitare conteggio di keyword dentro stringhe,
    • conteggio keyword (regex + parsing rudimentale).
  3. Logical SLOC: definire mapping di regole per riconoscere logical SLOC (es. end-of-statement per linguaggi C-like, contatori per dichiarazioni in Fortran, ecc.). Per la migliore compatibilità con UCC utilizzare le definizioni presenti nei "counting standards" forniti da UCC e tradurle in regole/AST parziali.

4.3 Cyclomatic Complexity e Maintainability Index

  • Usare lizard che fornisce CC per funzione e alcune metriche.

  • Maintainability Index (MI) può essere calcolato con formula standard (es. Coleman, Oman & Hagemeister o variante) usando:

    • LOC, CC (per file), Halstead Volume (opzionale).
  • Per linguaggi non supportati si può escludere MI o fornire n/a come UCC fa per alcuni linguaggi. Documentare le differenze.

4.4 Differencing (baseline A vs B)

Obiettivi: determinare added/modified/deleted/unmodified logical SLOC e, quando richiesto, function-level diff.

Flusso:

  1. File pairing:

    • Default: matchare file per percorso relativo nome.
    • Supportare matching avanzato: heuristica per file spostati/rename (fuzzy match based on content hash or similarity).
  2. Line matching:

    • Per ogni coppia fileA/fileB, estrarre le logical SLOC (seguendo la definizione adottata) e confrontare usando algoritmo LCS (Longest Common Subsequence) o difflib.SequenceMatcher.

    • Implementare la soglia t (0100). Come in UCC: se la percentuale di caratteri in comune tra due linee supera la soglia, considerarle modified piuttosto che deleted + added. Procedura:

      • Per ogni candidato match, compute ratio = common_chars / max(len(a), len(b)).
      • modified se ratio >= t/100.
  3. Function-level differencing:

    • Ottenere dalle metriche (lizard) la lista di funzioni con loro start/end lines.
    • Applicare differencing dentro ciascuna funzione. Se le funzioni si spostano/rename, usare heuristics: match per signature + similitudine.
  4. Visual diff:

    • Generare highlighted_diff.html come UCC: colorare added/modified/deleted con snippet contesto.

Performance:

  • Differencing è costoso; implementare streaming e chunking per file grandi. Offrire flag --nodup per disabilitare duplicate detection, --nocomplex per saltare complexity e velocizzare.

4.5 Duplicate detection

  • Hashing first (sha1) per file e poi confronto logico (rimozione commenti/blank) per identificare duplicati con soglia tdup.
  • Opzionale: memorizzare hash nel DB temporaneo per job multipli.

5. Formati di output (mappatura verso UCC)

Produzione di file equivalenti a UCC, con nomi compatibili, esempio:

  • <LANG>_outfile.csv → per-language counts (physical/logical/comment/blank/keywords).
  • outfile_summary.csv → aggregato globale.
  • outfile_cplx.csv → complexity per file.
  • outfile_cyclomatic_cplx.csv → CC dettagliato.
  • outfile_diff_results.csv → differencing summary (added/modified/deleted/unmodified per language & per file).
  • outfile_func_diff_results.csv → differencing a livello di funzione.
  • Highlighted_diff.html → visual diff.
  • UCC_Performance_<timestamp>.txt → performance metrics (runtime, files/sec).
  • error_log_<timestamp>.txt → log errori/warnings.

Schema JSON proposto per un file entry:

{
  "file": "src/main.cpp",
  "language": "C++",
  "physical_lines": 500,
  "logical_lines": 420,
  "comment_lines": 50,
  "blank_lines": 30,
  "keywords": {"if": 10, "for": 5},
  "cyclomatic_complexity": 22,
  "maintainability_index": 72.5,
  "sha1": "abc123...",
  "diff": {"added": 10, "modified": 5, "deleted": 2}
}

6. Interfaccia CLI (proposta, compatibile con UCC)

Esempio di comando:

python -m ucc_py \
  --dir ./baselineA ./baselineB \
  --filespecs "*.c" "*.cpp" \
  --threads 4 \
  --t 60 \
  --func-diff \
  --nodup \
  --nocomplex \
  --outdir ./ucc_output \
  --report json,csv,html

Parametri supportati (mappa UCC → ucc_py):

  • --dir A [B] (come -dir)
  • --diff / --d (come -d)
  • --filespecs (wildcards)
  • --threads (numero worker)
  • --t (threshold percentage)
  • --func-diff (function-level differencing)
  • --nodup, --nocomplex
  • --extfile (custom extension mapping file)
  • --outdir
  • --report (csv,json,html)
  • --verbose / --no-warnings ecc.

7. Implementazione - dettagli tecnici e snippet

7.1 Esecuzione parallela

Usare concurrent.futures.ProcessPoolExecutor per processare file in parallelo (IO + CPU bound: counter + metrics). Esempio orchestration:

  • Step 1: scanner costruisce file list.

  • Step 2: submit tasks analyze_file(path) che esegue:

    • pygount invocation
    • lizard parsing for CC
    • compute sha1
    • store per-file JSON partial in temp dir
  • Step 3: aggregator unisce i partial JSON e genera report.

7.2 Pygount wrapper (esempio)

from pygount import analysis

def analyze_with_pygount(path):
    # returns dict with code, comment, blank, language
    result = analysis.start([path], options=...)
    # map result into our schema
    return mapped_dict

7.3 Lizard wrapper (CC)

import lizard

def analyze_cc(path):
    res = lizard.analyze_file(path)
    return {
       "functions": [{ "name": f.name, "start": f.start_line, "end": f.end_line, "ccn": f.cyclomatic_complexity } for f in res.function_list],
       "avg_cc": res.average_cyclomatic_complexity
    }

7.4 Differencing algorithm (line-level)

  • Extract logical SLOC sequences A_seq, B_seq
  • Use difflib.SequenceMatcher(None, A_seq, B_seq) to find matches
  • For each unmatched pair check common_chars_ratio and apply threshold t
  • Classify lines accordingly (added/modified/deleted/unmodified)

7.5 Function-level differencing

  • Use function boundaries from lizard
  • For each function pair (matched by name/signature or nearest position) run line-level differencing on their bodies.

8. Validazione e compatibilità con UCC (test di confronto)

Per dimostrare pari-compatibilità o miglioramento:

  1. Dataset di test: scegliere progetti di riferimento (kernel Linux small, repository C/C++/Java/Python).

  2. Esecuzione UCC: eseguire UCC v.2018.07 (build) su dataset — produrre output.

  3. Esecuzione ucc_py: eseguire lo script e generare output identici in schema.

  4. Confronto:

    • confrontare outfile_summary.csv per language-level totals (physical, logical, comments).
    • controllare outfile_diff_results.csv per added/modified/deleted.
    • raggruppare differenze, investigare cause: regole counting, string literal handling, differenze nei delim commento.
  5. Toleranza: documentare le differenze previste (es. UCC seguito standard CSSE; pygount/tokenizer può differire su casi limite). Se richiesto, implementare patch rules per replicare esattamente le regole UCC (usando gli standard UCC inclusi nei rules/).

Importante: UCC implementa counting standards dettagliati; per replicare esattamente alcuni casi limite potrebbe servire codifica di regole specifiche tratte dalla documentazione UCC.


9. Prestazioni e limiti

  • Memory: differencing e duplicate detection sono le parti più esigenti. Soluzioni:

    • chunking, work partitions, temp file partial results;
    • opzione --ramlimit per stimare consumo e suggerire azioni;
    • --nodup per disabilitare ricerca duplicati costosa.
  • Parallelismo: ProcessPoolExecutor con N worker; bilanciamento tra IO e CPU.

  • I/O: scrittura streaming JSON/CSV invece di mantenere tutto in memoria per codebase grandi.


10. Logging, error handling e robustezza

  • Central logging (logging), livelli INFO/DEBUG/WARNING/ERROR.
  • Per ogni file problematico: salvare entry in outfile_uncounted_files.csv con motivo (parse error, memory).
  • Handle SIGINT/SIGTERM e dump parziale dei risultati (utile per job lunghi).
  • Unit tests + integration tests con fixture (piccoli repo) per garantire regressioni.

11. Pipeline CI / distribuzione

  • Container Docker con Python 3.11, pygount, lizard e dipendenze.

  • GitHub Actions workflow:

    • Checkout repo
    • Run python -m ucc_py --path . --outdir report
    • Upload report artifacts
    • Fail build se metrics peggiorano rispetto baseline (opzionale)
  • Artefatti: JSON/CSV/HTML e log.


12. Roadmap di sviluppo e stima (proposta)

  1. MVP (12 settimane)

    • scanner + pygount wrapper + output JSON/CSV + CLI minimal
  2. Core (23 settimane)

    • integra lizard, per-file CC, basic differencing con difflib, reports estesi
  3. Refinement (2 settimane)

    • function-level differencing, extfile mapping, duplicate detection
  4. Performance & robustness (12 settimane)

    • parallelizzazione, ramlimit, streaming outputs
  5. Validazione vs UCC & tuning (12 settimane)

    • test di confronto, patch rules per aderire allo standard UCC, documentazione
  6. CI, Docker, packaging (1 settimana)

Totale stimato: 812 settimane per una versione completa e robusta (timeline modulare).


13. Testing / Validazione (procedura)

  • Unit tests per ogni modulo (scanner, counter, metrics, differ).
  • Integration tests: esecuzione su set di repository di test, comparazione con output UCC.
  • Regression tests: confronti automatizzati con golden files.
  • Performance tests: usa dataset grandi (es. Linux kernel subset) e misura runtime/peak RAM; documentare rallentamenti (specialmente function-diff e cc4enable-like features).

14. Estensioni avanzate (post-MVP)

  • Supporto per Halstead metrics.
  • Visual dashboard web (Streamlit) per esplorare report.
  • Export compatibile con tool di stima COCOMO.
  • Firma digitale dei report (per certificazione).
  • Integrazione con SonarQube o strumenti di vulnerability scanning.

15. Conclusioni e raccomandazioni

  • È praticamente fattibile ottenere tramite Python una pipeline che replica e spesso migliora UCC v.2018.07, sfruttando pygount (conteggio), lizard (complexity), difflib/git (differencing).
  • Per ottenere compatibilità assoluta con UCC in tutti i casi limite, è necessario tradurre alcune regole dettagliate dal Counting Standards di UCC (inclusi nei documenti) in routine specifiche — raccomando di usare i manuali UCC come reference per i casi limite.
  • Implementare il progetto in step incrementali: MVP → integrazione differencing → function-level diff → ottimizzazioni/performance.

16. Allegati utili (da consultare)

  • UCC User Manual v.2018.07 (per comandi, output e counting standards).
  • UCC Release Notes v.2018.07 (per limiti, note performance, feature list).

Vuoi che proceda subito con:

  • A) la generazione del codice skeleton del pacchetto ucc_py/ (con __main__.py e moduli vuoti + requirements.txt), oppure
  • B) un script prototipo monolitico (ucc_pipeline.py) che esegue unanalisi rapida (pygount + lizard + differ) su una cartella di esempio?

Scegli A o B e te lo preparo completo e pronto per esecuzione.

Ottimo — avendo il codice sorgente C/C++ di UCC v.2018.07 a disposizione possiamo sfruttarlo in tre modi complementari e pratici per accelerare lo sviluppo della pipeline Python e garantire parità con UCC:

  1. Usare UCC compilato come “oracle” (rapido, affidabile) — eseguire il binario UCC sui progetti di test, leggere i CSV prodotti e usarli come ground-truth per tarare e validare la pipeline Python.
  2. Wrappare il motore UCC come libreria o binding (pybind11 / C-extension) — riutilizzare il parsing/algoritmi originali C++ per ottenere esattamente gli stessi risultati, esponendo funzioni utilizzabili da Python (più lavoro di integrazione ma massima compatibilità).
  3. Riscrittura/parziale rewriting in Python + validazione contro UCC — implementare il nuovo tool Python (pygount + lizard + differ) e confrontarlo automaticamente contro UCC per identificare i casi limite e applicare patch/regole per convergere ai risultati UCC quando necessario.

Qui sotto trovi un piano pratico, operativo e completo con script e snippet pronti da eseguire. Segui i passi nellordine: prima ottieni loracolo compilato, poi costruisci integrazione e tool Python prototipo, poi confronti e affini.


1) Compilare UCC (ottenere il binario oracle)

(estratto e adattato dal manuale UCC v.2018.07).

Esempio semplice (Linux / macOS con g++):

# nella root dove hai la cartella src/ del sorgente UCC
g++ ./src/*.cpp -o ucc -DUNIX -std=c++0x -O3 -pthread

Note pratiche:

  • Se vuoi multithreading efficiente potresti dover collegare Boost (opzionale); la GUI Qt non è necessaria per l'oracolo.
  • Se usi Windows, usa Visual Studio o MinGW con le opzioni indicate nel manuale.

Salva il binario come ./tools/ucc.


2) Script: eseguire UCC e raccogliere i report (wrapper shell)

Salva come tools/run_ucc.sh:

#!/usr/bin/env bash
set -euo pipefail

UCC_BIN="${1:-./tools/ucc}"
INPUT_DIR="${2:-.}"
OUTDIR="${3:-./ucc_output}"
THREADS="${4:-4}"

mkdir -p "$OUTDIR"
# esempio: conta tutto nella cartella
"$UCC_BIN" -dir "$INPUT_DIR" -threads "$THREADS" -outdir "$OUTDIR" -unified
echo "UCC finished. Reports in $OUTDIR"

Esecuzione:

chmod +x tools/run_ucc.sh
./tools/run_ucc.sh ./tools/ucc /path/to/project ./ucc_out 4

I CSV generati (es. outfile_summary.csv, <LANG>_outfile.csv, outfile_diff_results.csv) saranno il gold standard per confronto.


3) Wrapper Python per leggere i report UCC e usarli come baseline

tools/ucc_report_parser.py — converte i CSV UCC in JSON standard per confronto.

#!/usr/bin/env python3
import csv, json, pathlib, sys

def parse_csv(path):
    rows = []
    with open(path, newline='', encoding='utf-8') as f:
        r = csv.DictReader(f)
        for row in r:
            rows.append(row)
    return rows

def main(ucc_outdir, out_json):
    p = pathlib.Path(ucc_outdir)
    summary = {}
    for f in p.glob("*_outfile.csv"):
        lang = f.name.split("_outfile.csv")[0]
        summary[lang] = parse_csv(f)
    # outfile_summary.csv
    if (p / "outfile_summary.csv").exists():
        summary["TOTAL"] = parse_csv(p / "outfile_summary.csv")
    with open(out_json, "w", encoding="utf-8") as j:
        json.dump(summary, j, indent=2)
    print("Wrote", out_json)

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: ucc_report_parser.py <ucc_outdir> <out.json>")
        raise SystemExit(1)
    main(sys.argv[1], sys.argv[2])

Questo JSON è quello contro cui confronterai la pipeline Python.


4) Prototipo Python che replica le funzionalità principali (pygount + lizard + differ)

Ti do uno script prototipo monolitico ucc_py_prototype.py che:

  • scandisce la directory,
  • usa pygount per conteggi SLOC/commenti,
  • usa lizard per CC e funzioni,
  • salva JSON con schema compatibile.

Installa dipendenze:

pip install pygount lizard

Script (ucc_py_prototype.py):

#!/usr/bin/env python3
import json, pathlib, hashlib, sys
from pygount import analysis
import lizard

def sha1_of_file(path):
    import hashlib
    h = hashlib.sha1()
    with open(path, "rb") as f:
        while True:
            chunk = f.read(65536)
            if not chunk:
                break
            h.update(chunk)
    return h.hexdigest()

def run_pygount_for_files(paths):
    # pygount analysis accepts list of files; returns AnalysisResults
    results = analysis.start(paths, options={'format': 'summary', 'encoding': 'utf-8'})
    # results is an iterator of file summaries
    out = {}
    for item in results:
        # item has attributes: filename, language, code, comments, blank, total
        out[item.filename] = {
            "language": item.language,
            "physical_lines": item.total,
            "code_lines": item.code,
            "comment_lines": item.comments,
            "blank_lines": item.blank,
        }
    return out

def run_lizard_on_file(path):
    res = lizard.analyze_file(path)
    funcs = []
    for f in res.function_list:
        funcs.append({"name": f.name, "start": f.start_line, "end": f.end_line, "ccn": f.cyclomatic_complexity})
    return {"functions": funcs, "avg_cc": res.average_cyclomatic_complexity}

def main(root):
    p = pathlib.Path(root)
    # build file list by extension (simple approach: common source file extensions)
    exts = {".c",".cpp",".h",".py",".java",".js",".rb",".php",".cs",".m",".f",".f90",".vhd",".v"}
    files = [str(f) for f in p.rglob("*") if f.suffix in exts]
    print(f"Found {len(files)} candidate files")
    pyg = run_pygount_for_files(files)
    out = {}
    for f in files:
        sha1 = sha1_of_file(f)
        lz = run_lizard_on_file(f)
        base = pyg.get(f, {})
        base.update({"sha1": sha1, "lizard": lz})
        out[f] = base
    with open("ucc_py_output.json", "w", encoding="utf-8") as j:
        json.dump(out, j, indent=2)
    print("Wrote ucc_py_output.json")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: ucc_py_prototype.py <path>")
        raise SystemExit(1)
    main(sys.argv[1])

Questo è un prototipo rapido: non ancora differencing, non ancora function-level diff, ma produce la maggior parte dei dati necessari per confrontare i conteggi e le metriche.


5) Script di confronto automatico (UCC vs ucc_py)

tools/compare_ucc_vs_py.py — confronto a livello language totals e segnala scostamenti.

#!/usr/bin/env python3
import json, sys
from collections import defaultdict

def load_json(p): return json.load(open(p, encoding="utf-8"))

def aggregate_py(py_json):
    agg = defaultdict(lambda: {"physical":0,"code":0,"comment":0,"blank":0})
    for f,v in py_json.items():
        lang = v.get("language","UNKNOWN")
        agg[lang]["physical"] += v.get("physical_lines",0)
        agg[lang]["code"] += v.get("code_lines",0)
        agg[lang]["comment"] += v.get("comment_lines",0)
        agg[lang]["blank"] += v.get("blank_lines",0)
    return agg

def compare(ucc_json, py_json):
    # ucc_json: dict of languages -> rows
    py_agg = aggregate_py(py_json)
    for lang, rows in ucc_json.items():
        if lang == "TOTAL": continue
        # ucc rows list may contain one or many rows; sum totals
        ucc_tot = {"physical":0,"code":0,"comment":0,"blank":0}
        for r in rows:
            ucc_tot["physical"] += int(r.get("Total Physical SLOC", r.get("physical",0)) or 0)
            ucc_tot["code"] += int(r.get("Total Logical SLOC", r.get("code",0)) or 0)
            ucc_tot["comment"] += int(r.get("Total Comments", r.get("comments",0)) or 0)
            ucc_tot["blank"] += int(r.get("Total blank", r.get("blank",0)) or 0)
        py_vals = py_agg.get(lang, {})
        print(f"Lang {lang}: UCC phys={ucc_tot['physical']} py={py_vals.get('physical',0)} | code ucc={ucc_tot['code']} py={py_vals.get('code',0)}")
        # highlight differences
        for k in ("physical","code","comment","blank"):
            u = ucc_tot[k]; p = py_vals.get(k,0)
            if u != p:
                print(f"  DIFF {k}: UCC={u} PY={p} Δ={p-u}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: compare_ucc_vs_py.py <ucc_json> <py_json>")
        raise SystemExit(1)
    ucc = json.load(open(sys.argv[1], encoding="utf-8"))
    py = json.load(open(sys.argv[2], encoding="utf-8"))
    compare(ucc, py)

Questo confronto evidenzia i casi limite e ti permette di decidere dove adattare le regole Python per raggiungere la parità.


6) Strategie avanzate — riuso del codice C++ UCC (se vuoi massima compatibilità)

Hai tre opzioni (ordinate per sforzo → compatibilità):

A) Eseguire UCC come processo e parsare i CSV (vedi sopra) — minimo sforzo, risultati identici. B) Creare binding Python su UCC (pybind11) — sposti alcune funzioni del src/ in libreria .so/.dll e poi le esponi a Python; ottieni stessa logica, meno overhead I/O. Richiede:

  • refactoring del codice C++ per separare CLI dalla logica (esporre API),
  • compilazione con pybind11 wrapper. C) Incorporare parti del parsing C++ direttamente tramite CFFI / ctypes — fattibile ma complesso e fragile.

Se lobiettivo è compatibilità certificabile, ti consiglio B: sviluppare binding pybind11 che esportino funzioni come count_files(list_of_paths, extfile, options) e diff_baselines(listA, listB, options). Una volta esposte, il frontend Python può orchestrare, aggiungere report moderni, CI, ecc.


7) Piano operativo raccomandato (pratico, step by step)

  1. Compila UCC (step 1) e verifica esecuzione su un repo di test. Ottieni CSV.

  2. Esegui il prototipo Python su stesso repo e genera JSON.

  3. Esegui lo script di confronto per identificare scostamenti.

  4. Per ogni scostamento significativo, decidere:

    • se adattare il parser Python (regole o fallback custom) oppure
    • se riutilizzare la logica C++ per quel caso via binding (pybind11).
  5. Itera fino a convergenza sui dataset di test.

  6. Implementa differencing e function-level diff nel prototipo; ripeti confronto.

  7. Aggiungi ottimizzazioni (parallelismo, streaming) e packaging (CLI, Docker, pip).


8) Cosa posso fare subito per te (scegli e parto)

    1. Generare il pacchetto skeleton ucc_py/ (struttura completa + setup.py + __main__) pronto per essere popolato (approccio A dalla tua richiesta precedente).
    1. Produrre il prototipo monolitico completo (B) che include anche il differencing di base e loutput HTML minimal.
    1. Scrivere il wrapper pybind11 minimale che estrae la funzione di counting dal sorgente UCC e la espone a Python (richiede accesso al codice sorgente già presente) — ti preparo i file C++/CMake e esempio Python che la usa.
    1. Eseguire per te la validazione (run + confronto) su un repo desempio (se mi fornisci il path o un piccolo archive di test).

Dimmi quale delle 4 preferisci (1/2/3/4) e parto subito a generare i file necessari — pronto ad esportare codice e script eseguibili.

CONCLUSIONE E COSA VOGLIO FARE io sarei per sviluppare da capo una versione python, usare ucc come paragone per lo sviluppo e solo se alcune funzioni di ucc non riesco a replicarle, allora posso prendere il codice di ucc, analizzare la funzione che fa quella cosa e rifarla in python. Vorrei slegarmi dal lanciare ucc perchè voglio avere il pieno controllo di tutte le funzionalità ed avere la possibilità di ampliare le funzionalità se necessario senza essere bloccato su