Skip to content

feat(py): accept the CLI's metric names in metrics= #1614

Closed

Description

@dekobon

The Python bindings accept a narrower metrics= vocabulary than the CLI's --metrics. The same name works in one and raises in the other:

$ bca metrics --no-config --metrics dloc,sloc m.py     # ok
>>> big_code_analysis.analyze("m.py", metrics=["loc"])     # ok
>>> big_code_analysis.analyze("m.py", metrics=["dloc"])
ValueError: unknown metric: dloc; valid: abc, cognitive, cyclomatic, halstead, loc, mi, nargs, nexits, nom, npa, npm, tokens, wmc
>>> big_code_analysis.analyze("m.py", metrics=["sloc"])
ValueError: unknown metric: sloc; valid: ...
  • CLI. --metrics resolves names through metric_alias.rs: family names, the list-metrics bucket names (sloc, dloc, …) and dotted ids (cyclomatic.modified). Since PR feat(mi): stop charging documentation rows in MI #1609 it also takes the loc family name.
  • Python. metrics= takes only the 13 family names in METRIC_NAMES, which is how _native.pyi documents it.

This is not new. sloc already behaved this way on main, and PR #1609's dloc follows it. It is a parity gap rather than a bug.

Proposal. Resolve Python names with the same rules as the CLI, so a name copied from bca list-metrics, bca check --threshold or the CLI's --metrics works in Python. Keep METRIC_NAMES as the canonical family list and document the accepted aliases next to it. Then update:

  • _native.pyi and the stubtest allowlist, if needed;
  • the book's Python page.

Tests should cover a sub-metric, a dotted id, a family name and an unknown name, with its did-you-mean message.

Found during the whole-branch review of PR #1609 (#1597).


Resolution

Status: fixed (pending merge)
Commit: 02ac90b on fix/batch-2026-10-17 (rewritten onto the #1621 layer; the original 7630358 was superseded — see the "Resolution (rewritten)" comment)
Root cause: the CLI and the Python bindings each resolved metric names their own way. Both now go through the public metric_catalog::resolve_selector. The SARIF threshold-name gap moved to #1618.

Activity

  1. added
    enhancementNew feature or request
    pythonPython language support / metrics
    low-priorityLow-priority per issue-plan assessment
    on Oct 17, 2026
  2. dekobon commented on Oct 18, 2026

    @dekobon
    OwnerAuthor

    Fix summary

    Root cause: the CLI resolved --metrics names in its own metric_alias module (family names, list-metrics row names, dotted ids). The Python bindings parsed metrics= with Metric::from_str, which takes only the 13 family names. There was no shared resolver to keep the two in step.

    Changes:

    • Root crate: new public metric_catalog::resolve_selector, selector_names and UnknownSelectorError. Additive, and recorded in STABILITY.md. A selector maps to its Metric family, so "dloc" selects all of loc, exactly as --metrics dloc does. The did-you-mean algorithm moved from the CLI into a private src/suggestion.rs; the CLI reaches it through one #[doc(hidden)] re-export that STABILITY.md places outside the contract.
    • CLI: metric_for_name is a thin wrapper around resolve_selector. Its error text is byte-identical (16 cases compared before and after: --metrics, check --threshold, diff --metric, [thresholds.lang]).
    • Python: metrics= resolves through resolve_selector. The error keeps its unknown metric: <name> prefix, followed by the CLI's did-you-mean hint and name list.
    • Docs: _native.pyi, the book's Python metrics page and the bindings README. Also documented: cognitive, nargs and nexits pull in nom.

    Tests: root resolver tests for families, row names, dotted ids, a close miss, ties, no match and case; the CLI error text pinned; Python tests for sloc/dloc/loc.dloc, a dotted id, the suggestion message, and the nom dependency. Six production perturbations, each failing its tests. The review tightened one assertion: the known-names list is now checked by list membership, because a substring check passed with loc missing, as loc sits inside dloc and sloc. Patch coverage is 99.55 % (220/221) against 98.22 % project; the one missed region was already missed in the CLI's copy of the moved code.

    Premise check: all confirmed. The CLI computes whole families and never trims output to the named field, and Python now matches that.

    Out of scope, filed separately: to_sarif(thresholds=) needs a scalar resolver (mi.original must not collapse to Mi). That is #1618.

    Commit: 7630358 on fix/batch-2026-10-17.

  3. dekobon commented on Oct 18, 2026

    @dekobon
    OwnerAuthor

    Resolution (rewritten)

    The batch branch fix/batch-2026-10-17 was rewritten so these fixes sit on the shared value-semantics layer from #1621 (metric_catalog::Scale + Threshold). This issue's fix is now commit 02ac90ba on that branch; the earlier hash in the comments above is superseded and no longer on the branch (it survives on backup/batch-1017-before-rewrite locally). Behaviour, tests and reproducers are as described in the earlier resolution, with these deltas from the layer:

    • Replayed unchanged; git range-diff against the original shows only merge context in src/lib.rs / src/metric_catalog.rs and the regenerated .bca-baseline.toml.

    Gate: BCA_GATE: pass (gate=pre-commit) on the commit and on the series tip 1a8023dd.

  4. added 5 commits that reference this issue on Oct 19, 2026
    02ac90b
    1a8023d
    28a237d
    2055d06
    d150e7d
  5. dekobon commented on Oct 20, 2026

    @dekobon
    OwnerAuthor

    Pull request: #1650.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlow-priorityLow-priority per issue-plan assessmentpythonPython language support / metrics

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions