Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SARIF 出力

bca.to_sarif(result, *, thresholds=None) renders an analysis result (or an iterable of them) into a SARIF 2.1.0 JSON document, ready for upload to GitHub Code Scanning or any other SARIF consumer. The output is produced by the same Rust writer that backs bca check --report-format sarif, so the schema URL, tool driver name / version, and rule descriptions match the CLI byte-for-byte.

Findings match in order as well as in content. Both surfaces sort their findings by path, then start line, then metric name (the order bca check applies after its walk), and findings tying on all three keep the depth-first source order of the space tree. For the same files and thresholds the two results arrays therefore line up entry for entry against bca check --no-suppress. to_sarif compares raw metric values, so it applies none of the in-source suppression markers bca check honours by default (each marked space keeps its suppressed key, for a caller that wants to filter), no baseline, and no [check] exclude globs.

“The same files” means a unique file set. The CLI folds repeated path seeds together, so bca check -p a.py -p a.py analyses a.py once and emits one finding per breach. analyze_batch instead returns one result per input, and to_sarif renders every result it is handed, so to_sarif(analyze_batch([a, a]), ...) emits each finding twice. Deduplicating in the binding would be wrong, because two distinct results may legitimately share a name (analyze_source takes the caller’s). Hand to_sarif a unique file set when comparing the two documents positionally.

Examples on this page import the package as bca (import big_code_analysis as bca). A bare bca in a shell command is the CLI binary.

def run(
    paths: Iterable[Path],
    sarif_path: Path,
    thresholds: Mapping[str, float],
) -> str:
    """``paths`` を分析し、SARIF ドキュメントを ``sarif_path`` に書き込む。

    レンダリング済みの SARIF JSON を返すため、呼び出し側(やテスト)は
    ファイルを読み直さずに内容を検査できる。
    """
    batch = bca.analyze_batch(paths)
    sarif = bca.to_sarif(batch, thresholds=dict(thresholds))

    sarif_path.parent.mkdir(parents=True, exist_ok=True)
    sarif_path.write_text(sarif, encoding="utf-8")
    print(f"wrote {sarif_path} ({len(sarif.encode('utf-8'))} bytes)")
    return sarif

to_sarif は次を受け付けます。

  • bca.analyze または bca.analyze_source が返す単一の dict。
  • Any iterable yielding such dicts, bca.AnalysisFailure instances, or None (the natural shape of bca.analyze_batch’s return value). AnalysisFailure and None entries are skipped silently; they represent files for which no record was emitted, not findings.
  • A scalar None, the documented return of bca.analyze for a skipped file; it yields an empty SARIF run.

しきい値

Accepted threshold names are the library’s metric_catalog::METRICS table in src/metric_catalog.rs, the same table the CLI’s --threshold reads:

  • cognitive, cyclomatic, cyclomatic.modified
  • halstead.volume、halstead.difficulty、halstead.effort、halstead.time、halstead.bugs
  • loc.sloc, loc.ploc, loc.lloc, loc.cloc, loc.blank, loc.dloc
  • nom, tokens, nexits, nargs
  • mi.original、mi.sei、mi.visual_studio
  • abc, wmc, npm, npa

Names resolve by the rule bca check --threshold applies, through the library’s metric_catalog::resolve_threshold_metric, so a name copied from a CLI invocation works unchanged. A bare loc sub-metric name (sloc, ploc, lloc, cloc, blank, dloc, the spelling bca diff --metric buckets by) stands for its dotted id, so {"sloc": 1200} and {"loc.sloc": 1200} render the same document. A family head with no scalar of its own (mi, halstead, loc) raises ValueError naming the ids to choose from:

ambiguous metric "mi": this family has no single threshold scalar; did you mean one of: mi.original, mi.sei, mi.visual_studio?

One id under both spellings ({"sloc": 1200, "loc.sloc": 1500}) is also a ValueError, as the same pair is in a bca.toml table: it is one metric, so set it once. Any other name raises ValueError listing the accepted set, with a “did you mean” hint when an id is close, so a typo fails fast instead of silently producing an empty SARIF run.

Limits are validated by the same rules bca check applies, so a dict that the binding accepts is one the CLI accepts too. Most metrics are counts: the limit is a ceiling, a value strictly above it is a finding, and the limit must be a finite, non-negative number. The mi.* Maintainability Index family is the other way round. Its limit is a floor, and a value strictly below it is the finding. mi.original and mi.sei have no true zero and go negative on large functions, so {"mi.original": -50} is a legal floor. A limit that parses but can never do what you meant raises a UserWarning instead of a ValueError. mi.visual_studio is clamped to [0, 100], so {"mi.visual_studio": 0} warns limit mi.visual_studio=0 cannot fire: 0 is the metric's floor; gate mi.original instead. A NaN or infinite limit raises ValueError, and a bool limit raises TypeError rather than reading False as 0. The CLI side of these rules is described under bca check.

thresholds=None (the default) and thresholds={} both produce a well-formed SARIF document with empty results and rules arrays. This matches bca check, which applies no implicit limits: every run supplies its own, from --threshold or a bca.toml (which bca init scaffolds with a starting table).

GitHub Code Scanning へのアップロード

# .github/workflows/code-scanning.yml(抜粋)
- name: Compute metric SARIF
  run: |
    python - <<'PY'
    import big_code_analysis as bca
    with open("paths.txt", encoding="utf-8") as paths_fh:
        results = bca.analyze_batch(paths_fh.read().splitlines())
    with open("metrics.sarif", "w", encoding="utf-8") as fh:
        fh.write(bca.to_sarif(results, thresholds={"cyclomatic": 15}))
    PY
- name: Upload to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: metrics.sarif

アップロード用アクションのドキュメントは github/codeql-action/upload-sarif にあります。バインディングは呼び出しごとに 1 つの SARIF run を生成し、リポジトリの Code Scanning アラートへのアップロードはこのアクションが担います。

Which spaces produce findings

to_sarif emits a finding when a space’s own value breaches a limit and the metric applies to that space, exactly matching bca check --report-format sarif. LoC limits apply to file units; nom, wmc, npm, and npa apply to containers; the remaining limits apply to functions. For most metrics the JSON headline at a space already is that space’s own value. The five subtree-aggregate metrics (cyclomatic, cyclomatic.modified, cognitive, abc and nargs) also expose a sum / magnitude / total rolled up across child spaces; the binding reads their per-space value field instead, so it reports an interior breach (for example a function whose own complexity breaches even though a nested closure’s does not) without being fooled by the larger aggregate. For nargs that means a function is gated on its own parameter list, exactly as bca check has been since #1196; a closure with its own space produces its own finding rather than inflating the enclosing function’s.

Unit findings carry logicalLocations: [{"fullyQualifiedName": "<file>"}]. Every other space carries its qualified symbol. Within that symbol, a closure/lambda (the <anonymous> name every grammar emits) and the None-name parse-failure case both collapse to <anon@L{start_line}>, matching the CLI’s space_segment.

関連項目

  • Batch processing: the natural source of input iterables for to_sarif; AnalysisFailure entries are skipped silently.
  • Metric selection: threshold names are a closed set independent of metrics=. Metrics omitted by metrics= produce no findings; other selected metrics can still produce findings for their supplied thresholds.
  • Error handling: the typed exceptions to_sarif raises for bad caller input (TypeError / ValueError).