codemetrics-ai 0.4.0 → 0.6.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -5
- package/dist/analyzer.d.ts +1 -0
- package/dist/analyzer.js +11 -5
- package/dist/cli.js +7 -3
- package/dist/dependency-evidence.d.ts +5 -0
- package/dist/dependency-evidence.js +30 -0
- package/dist/dependency-inventory.d.ts +67 -0
- package/dist/dependency-inventory.js +203 -0
- package/dist/dependency-metadata.d.ts +65 -0
- package/dist/dependency-metadata.js +125 -0
- package/dist/dependency-pnpm.d.ts +4 -0
- package/dist/dependency-pnpm.js +237 -0
- package/dist/dependency-spec.d.ts +13 -0
- package/dist/dependency-spec.js +30 -0
- package/dist/error-handling-scoring.d.ts +1 -0
- package/dist/error-handling-scoring.js +15 -0
- package/dist/error-handling.d.ts +4 -1
- package/dist/error-handling.js +50 -17
- package/dist/evidence-reader.js +3 -1
- package/dist/evidence-tools.js +4 -0
- package/dist/failure-contracts.d.ts +19 -0
- package/dist/failure-contracts.js +261 -0
- package/dist/function-scoring.d.ts +2 -1
- package/dist/function-scoring.js +13 -24
- package/dist/handler-disposition.d.ts +9 -0
- package/dist/handler-disposition.js +156 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/python/ASYNC_ADAPTERS.md +38 -0
- package/dist/python/ASYNC_CONTEXT.md +58 -0
- package/dist/python/ASYNC_SCORING.md +174 -0
- package/dist/python/ASYNC_USAGE.md +62 -0
- package/dist/python/BEHAVIOR_REVIEW.md +84 -0
- package/dist/python/COVERAGE.md +96 -0
- package/dist/python/DECOMPOSITION.md +46 -0
- package/dist/python/DUPLICATION.md +36 -0
- package/dist/python/ERROR_HANDLING.md +45 -0
- package/dist/python/ERROR_HANDLING_CONTEXT.md +61 -0
- package/dist/python/ERROR_HANDLING_OPERATIONS.md +54 -0
- package/dist/python/ERROR_HANDLING_TRANSFERS.md +64 -0
- package/dist/python/METHOD_COMPLEXITY.md +46 -0
- package/dist/python/README.md +162 -0
- package/dist/python/analysis.py +54 -0
- package/dist/python/async_adapters.py +243 -0
- package/dist/python/async_bindings.py +180 -0
- package/dist/python/async_context.py +143 -0
- package/dist/python/async_helper_paths.py +186 -0
- package/dist/python/async_provenance.py +129 -0
- package/dist/python/async_scoring.py +203 -0
- package/dist/python/async_usage.py +244 -0
- package/dist/python/coverage_collect.py +121 -0
- package/dist/python/coverage_provenance.py +77 -0
- package/dist/python/coverage_review.py +273 -0
- package/dist/python/coverage_worker.py +46 -0
- package/dist/python/decomposition.py +199 -0
- package/dist/python/duplication.py +177 -0
- package/dist/python/error_handling.py +249 -0
- package/dist/python/error_handling_context.py +294 -0
- package/dist/python/error_handling_operations.py +228 -0
- package/dist/python/error_handling_transfers.py +211 -0
- package/dist/python/review_cli.py +36 -0
- package/dist/python/source_facts.py +293 -0
- package/dist/python/source_selection.py +157 -0
- package/dist/python-analyzer.d.ts +21 -0
- package/dist/python-analyzer.js +123 -0
- package/dist/python-async-adapters.d.ts +67 -0
- package/dist/python-async-adapters.js +197 -0
- package/dist/python-async-context.d.ts +73 -0
- package/dist/python-async-context.js +181 -0
- package/dist/python-async-scoring.d.ts +89 -0
- package/dist/python-async-scoring.js +249 -0
- package/dist/python-async-usage.d.ts +56 -0
- package/dist/python-async-usage.js +143 -0
- package/dist/python-cli.d.ts +2 -0
- package/dist/python-cli.js +53 -0
- package/dist/python-collect-cli.d.ts +2 -0
- package/dist/python-collect-cli.js +28 -0
- package/dist/python-complexity.d.ts +18 -0
- package/dist/python-complexity.js +36 -0
- package/dist/python-coverage.d.ts +63 -0
- package/dist/python-coverage.js +173 -0
- package/dist/python-decomposition.d.ts +56 -0
- package/dist/python-decomposition.js +102 -0
- package/dist/python-duplication.d.ts +65 -0
- package/dist/python-duplication.js +89 -0
- package/dist/python-error-context.d.ts +84 -0
- package/dist/python-error-context.js +165 -0
- package/dist/python-error-handling.d.ts +56 -0
- package/dist/python-error-handling.js +120 -0
- package/dist/python-error-operations.d.ts +44 -0
- package/dist/python-error-operations.js +155 -0
- package/dist/python-error-transfers.d.ts +38 -0
- package/dist/python-error-transfers.js +125 -0
- package/dist/python-evidence.d.ts +23 -0
- package/dist/python-evidence.js +255 -0
- package/dist/score-arithmetic.d.ts +1 -12
- package/dist/score-arithmetic.js +1 -40
- package/dist/shared-source-facts/arithmetic.d.mts +9 -0
- package/dist/shared-source-facts/arithmetic.mjs +32 -0
- package/dist/shared-source-facts/contract.d.mts +35 -0
- package/dist/shared-source-facts/contract.mjs +95 -0
- package/dist/shared-source-facts/population.d.mts +12 -0
- package/dist/shared-source-facts/population.mjs +41 -0
- package/dist/source-facts.d.ts +4 -0
- package/dist/source-facts.js +16 -0
- package/package.json +15 -6
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Experimental Python exception-handler evidence
|
|
2
|
+
|
|
3
|
+
This source-only reviewer records exception-handler syntax and local documentation without importing or executing target code. It assigns **no score, deduction, gate, defect classification, or handling-success rate**. Python's packaged Error Handling dimension remains unavailable; Method Complexity remains its only scored dimension.
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
py -3.12 analyzers/python/error_handling.py E:/repos/my-project --include 'src/**' --output TestResults/handlers.json
|
|
7
|
+
py -3.12 analyzers/python/error_handling_corpus.py --repos E:/repos --output TestResults/python-handlers-new
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.12 is required. The CLI accepts a directory or Python source file, with the same selection/population flags as `source_facts.py`. Pass a manifest's containing directory rather than the manifest itself. Output must be a new `.json` file with an existing parent; omission writes to stdout. Caller-supplied UUID `--run-id` and `--audit-id` are preserved, otherwise fresh IDs are generated. Exit 0 means complete, including an empty selection; exit 2 means incomplete analysis or invalid invocation. The development `codemetrics-python` adapter **0.11.0** embeds this report at `dimensions.errorHandling.review` under ruleset `python-2026-10-15-exception-operations-review-v1`. The npm archive includes the producer and this policy document. The companion skill reports the evidence without assigning an Error Handling score. Use an explicit local archive until publication; published npm 0.5.0 lacks this workflow.
|
|
11
|
+
|
|
12
|
+
The packaged path analyzes handlers automatically from the same source facts and run/audit IDs as the other measurements. The validator checks matching provenance, source digests, owner identities, unique handler IDs, annotation/classification consistency, owned-transfer fields, and recomputed category/population summaries. It does not independently reparse source or prove that an externally edited report retained every handler. Malformed, missing, or incomplete handler evidence fails the combined run and withholds its Method Complexity score and all findings. Empty complete reports contain zero handlers without implying excellent handling. Historical 0.7.0 evidence remains valid with its original score and no invented handler observations; 0.5/0.6 previews retain their original contracts. Cross-version comparison is not enabled.
|
|
13
|
+
|
|
14
|
+
## Policy: `python/exception-handler-observations-v1`
|
|
15
|
+
|
|
16
|
+
Each selected `except` or `except*` suite receives one observation. Module and class-body handlers are included as well as handlers in selected functions. File populations remain explicit; tests, support, generated files, and declaration files follow the shared source-selection policy. The handler population is not the function population and must not be used as its denominator.
|
|
17
|
+
|
|
18
|
+
| Classification | Syntactic condition |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `documented-no-op` | Only `pass`, bare ellipsis, or string-literal statements, with local documentation as defined below |
|
|
21
|
+
| `undocumented-no-op` | The same inert statements, without qualifying local documentation |
|
|
22
|
+
| `terminal-raise` | Final direct statement is `raise` |
|
|
23
|
+
| `terminal-return` | Final direct statement is `return` |
|
|
24
|
+
| `terminal-loop-control` | Final direct statement is `break` or `continue` |
|
|
25
|
+
| `other-body` | Any other body, including logging, assignments, conditional transfers, or nested recovery logic |
|
|
26
|
+
|
|
27
|
+
These categories describe syntax. A final direct `raise` is not a guarantee that all paths propagate: earlier branches may return, enclosing handlers may intercept it, or `finally` may override it. Explicit fallbacks and loop transfers are not defects by default. `other-body` establishes neither adequate nor inadequate handling.
|
|
28
|
+
|
|
29
|
+
Owned body observations record raises (bare or with an explicit cause), returns, loop transfers, and call counts. Nested function/class bodies and nested handlers cannot credit the enclosing handler; nested handlers receive their own rows. Class bodies retain lexically enclosing `finally` context because they execute at definition time; function bodies start a separate context. `enclosingFinallyLines` records containing try-statement locations with a final suite, not proof of runtime behavior or a call graph.
|
|
30
|
+
|
|
31
|
+
## Documentation presence
|
|
32
|
+
|
|
33
|
+
For inert suites, preserve comments after the handler-header colon, including inline comments and trailing indented comment-only lines. Preserve nonempty string-literal statements too. Require at least one alphanumeric character. Punctuation-only comments and recognized tool directives (`noqa`, `type: ignore`, `pragma: no cover`, `fmt:`, `pylint:`, `ruff:`) do not qualify; directive comments are retained separately. Comments within the exception-type expression, before the handler, or after a dedent are outside this association rule.
|
|
34
|
+
|
|
35
|
+
`documented-no-op` means **local prose is present**, not that the analyzer has judged its adequacy. Short notes such as `# Nope` and TODO prose are preserved without guessing the author's intent. Conversely, `undocumented-no-op` does not mean the surrounding function lacks an explanation: a preceding comment, docstring, or later fallback may explain it. No minimum word count or comment-to-code ratio is used. Neither category warrants an automatic penalty. Comments on non-inert bodies are not collected by this first policy.
|
|
36
|
+
|
|
37
|
+
## Provenance and limits
|
|
38
|
+
|
|
39
|
+
The report records run/audit IDs, source-facts producer and policy, parser version, selection inventory/fingerprint, and its own configuration fingerprint. Each row records a stable source-position ID, source hash, location, population, owner, exception-type syntax, exception-group flag, and `review-only` disposition. Summary counts are separated by classification, owner kind, and file population. Zero handlers is an observation, not a perfect score.
|
|
40
|
+
|
|
41
|
+
Source reads verify the selected hashes and existing path/size boundaries. Repeated or changed function identities and populations exceeding 100,000 handlers fail closed. Any source-facts or review failure leaves all handler rows withheld and the summary null. Complete facts are not mutated. Integrity checks bind the review to its supplied facts; they do not authenticate externally edited artifacts.
|
|
42
|
+
|
|
43
|
+
Exception types and calls are not resolved. A spelling such as `Exception` does not prove the built-in type is referenced. Context-manager suppression, `__exit__`/`__aexit__`, API contracts, logging effectiveness, retries, cancellation policy, and business correctness are outside scope. No .NET `throw ex` rule is transplanted to Python's `raise exc`.
|
|
44
|
+
|
|
45
|
+
The [first pinned corpus review](../../shared/scorecard-schema/calibration-runs/python-error-handling-2026-10-13.md) demonstrates why no-op counts require context before any future calibration. The [packaged validation record](../../shared/scorecard-schema/calibration-runs/python-error-handling-package-2026-10-15.md) records installed-package and skill verification.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Experimental Python exception context
|
|
2
|
+
|
|
3
|
+
This reviewer adds bounded lexical context and conservative syntactic patterns to the existing [handler observations](ERROR_HANDLING.md). **Error Handling stays unscored.** Recognized patterns are not proof of correct handling, and unresolved handlers are not defects. Method Complexity is unchanged; historical 0.8.0 reports retain their original contract.
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
py -3.12 analyzers/python/error_handling_context.py E:/repos/my-project --include 'src/**' --output TestResults/handler-context.json
|
|
7
|
+
py -3.12 analyzers/python/error_handling_context_corpus.py --repos E:/repos --output TestResults/handler-context-corpus-new
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.12 and its standard library are sufficient; target code is never imported or executed. The CLI accepts a source file or directory and the existing selection flags. Pass a manifest's directory rather than the manifest itself. Output must be a new `.json` file with an existing parent; omission writes to stdout. Supplied UUID run/audit IDs are preserved, otherwise generated. Complete, including empty, returns 0; incomplete analysis or invalid invocation returns 2. The development adapter **0.9.0** embeds the report at `dimensions.errorHandling.contextReview` under ruleset `python-2026-10-15-exception-context-review-v1`. The local npm archive includes this producer and guide, and the companion skill presents the evidence. Published npm 0.5.0 lacks this workflow; supply an explicit local archive.
|
|
11
|
+
|
|
12
|
+
## Policy: `python/exception-context-patterns-v1`
|
|
13
|
+
|
|
14
|
+
Each context row joins one existing handler by ID, source hash, location, population, and owner. It records:
|
|
15
|
+
|
|
16
|
+
- The containing try statement, its protected body, `else`, `finally`, and sibling handler lines.
|
|
17
|
+
- The handler's direct statement kinds/locations, including literal return kinds and simple literal-assignment targets.
|
|
18
|
+
- Up to three following statements **in the same lexical suite**, without skipping active statements or crossing out of a branch. These are nearby syntax, not a promise they execute next.
|
|
19
|
+
- Lexically enclosing constructs, stopping at the deferred function boundary. Class bodies retain their surrounding execution context.
|
|
20
|
+
- Contiguous same-indentation comments immediately before the try, plus the nearest function/class docstring. These are surrounding prose, not an automatic explanation of each handler. Existing local documentation classification is preserved.
|
|
21
|
+
|
|
22
|
+
Direct suites show at most 32 statements, with full direct-statement counts and truncation flags. Surrounding documentation is capped at 2,000 characters per context and marked when truncated. Literal observations record value kinds, not string contents. Complex expressions and nested suites remain inspectable through source locations and the original owned-body observations, not full source dumps.
|
|
23
|
+
|
|
24
|
+
## Pattern precedence and boundaries
|
|
25
|
+
|
|
26
|
+
Exactly one primary pattern is recorded per handler; unmatched cases have `status: unresolved`, `pattern: null`, and explicit reasons. Pattern priority is a reporting convention, not a scoring ladder.
|
|
27
|
+
|
|
28
|
+
| Pattern | Required local syntax |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `locally-documented-no-op` | Inert suite with local documentation under the existing policy |
|
|
31
|
+
| `direct-literal-fallback-return` | Handler contains exactly one return of a scalar literal, signed numeric literal, or implicit `None` |
|
|
32
|
+
| `direct-bare-rethrow` | Handler contains exactly one bare `raise` |
|
|
33
|
+
| `guarded-import-shape` | Protected body is exactly one import statement; narrow import-exception name syntax; handler is inert |
|
|
34
|
+
| `guarded-import-literal-default` | Same protected import and exception constraints; handler is exactly one scalar-literal assignment to names bound by that import |
|
|
35
|
+
| `no-op-then-literal-return` | Inert handler followed immediately by a scalar-literal return in the containing try's own suite |
|
|
36
|
+
|
|
37
|
+
Local documentation presence is recorded even with unresolved execution context. Its precedence means a documented import guard remains primarily documented. It does not establish rationale adequacy or successful handling. TODO prose and terse comments are preserved without judging business decisions. Nearby prose does not upgrade an undocumented local suite.
|
|
38
|
+
|
|
39
|
+
For the other patterns, exception groups, lexically enclosing finalizers, context managers, and nested handler execution are conservative blockers. An enclosing class executes immediately; entering a method/function resets deferred execution context. The finalizer flags are from the original handler evidence. An enclosing try without a finalizer can still intercept a rethrow; a recognized rethrow is only a local shape, never a propagation guarantee.
|
|
40
|
+
|
|
41
|
+
The import patterns accept `ImportError`, `ModuleNotFoundError`, or a tuple containing only those names. Any syntactic binding/deletion of a candidate exception name anywhere in the file, a star import, or a direct `exec`/`eval`/`globals`/`locals` call vetoes recognition. This deliberately over-conservative check is not complete symbol resolution. Indirect namespace mutation, externally supplied bindings, and cross-module semantics remain unresolved. Qualified exception names and dynamic `import_module` calls are outside this first pattern. Even a recognized guard does not prove that a dependency is optional: imports can execute module code and fail for other reasons.
|
|
42
|
+
|
|
43
|
+
Returns of calls, variables, attributes, containers, and computed expressions remain unresolved. Logging followed by a rethrow, conditional recovery, compatibility-class implementations, imported fallback implementations, and additional protected work remain unresolved too. Explicit scalar fallbacks describe syntax, not API-contract correctness. Neither an exception name nor a function name establishes business intent.
|
|
44
|
+
|
|
45
|
+
## Provenance and failure behavior
|
|
46
|
+
|
|
47
|
+
The context report embeds the unchanged original handler review, shares its run/audit IDs, and records its handler-row digest, context policy/configuration fingerprint, and producer. Source files are hash-verified and reparsed. Original handler rows and summary must regenerate exactly, selected function identities must still match, and the shared handler budget applies. Context analysis does not mutate source facts or handler observations.
|
|
48
|
+
|
|
49
|
+
Source drift, malformed source, missing/changed owners, changed handler observations, or a budget failure withholds all context rows and the summary. The embedded base review is diagnostic context on a failed run, not permission to claim fresh usable results. These checks are source-consistency checks rather than authentication of externally edited artifacts or immutable-filesystem guarantees.
|
|
50
|
+
|
|
51
|
+
Summary counts separate recognized shapes, unresolved handlers, patterns, and populations. Reason counts can overlap; they are not a partition. No handled/unhandled rate, deduction, defect grade, or automatic refactoring recommendation is derived. The [pinned corpus study](../../shared/scorecard-schema/calibration-runs/python-error-context-2026-10-15.md) records observed patterns, conservative exclusions, and source-reviewed examples.
|
|
52
|
+
|
|
53
|
+
The packaged [raise/chaining extension](ERROR_HANDLING_TRANSFERS.md) adds owned raise/cause metadata and conservative patterns at `dimensions.errorHandling.transferReview` in adapter 0.10.0. It embeds this v1 report unchanged; it does not retroactively reclassify historical 0.9.0 evidence.
|
|
54
|
+
|
|
55
|
+
## Packaged validation and historical reports
|
|
56
|
+
|
|
57
|
+
The packaged analyzer measures the selected source population once and passes its handler report to this context pass. The shared validator checks matching invocation IDs, the embedded base report, handler-row digest, configuration, one-to-one handler identities, source locations, bounded-suite accounting, pattern constraints, and summary counts. This verifies internal consistency, not authenticity or independent AST remeasurement. In particular, the artifact does not carry full import binding analysis; that check belongs to the source producer.
|
|
58
|
+
|
|
59
|
+
A missing, inconsistent, or incomplete required context report makes the entire 0.9.0 run incomplete, with no Method Complexity score or findings available. Complete context adds no findings, score, deduction, or gate. Error Handling remains `skipped` without a score; an empty handler population remains unscored. Only Method Complexity may contribute to a partial assessment of 1/9 dimensions.
|
|
60
|
+
|
|
61
|
+
Saved 0.8.0 reports remain valid without context evidence. Absence in older reports means unavailable context, not zero patterns. Do not append synthetic context or change their provenance. Comparisons require matching supported versions and rulesets; a context packaging upgrade does not establish a code improvement.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Experimental Python handler calls and returns
|
|
2
|
+
|
|
3
|
+
This reviewer inventories the handlers left unresolved by the [raise/chaining reviewer](ERROR_HANDLING_TRANSFERS.md), records owned call and return-expression syntax, and recognizes three additional local shapes. **Error Handling remains unscored.** There are no findings, deductions, gates, handling-success rates, or automatic refactoring recommendations. Method Complexity and historical 0.10.0 reports are unchanged.
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
py -3.12 analyzers/python/error_handling_operations.py E:/repos/my-project --include 'src/**' --output TestResults/handler-operations.json
|
|
7
|
+
py -3.12 analyzers/python/error_handling_operations_corpus.py --repos E:/repos --output TestResults/handler-operations-corpus-new
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.12 and its standard library are sufficient. Sources are parsed, never imported or executed. The target is a Python file or directory. Existing selection switches and optional UUID run/audit IDs are supported. Output must be a new `.json` file with an existing parent; omit it for stdout. Complete results, including empty populations, return 0. Incomplete analysis or invalid invocation returns 2.
|
|
11
|
+
|
|
12
|
+
## Policy: `python/exception-call-return-v1`
|
|
13
|
+
|
|
14
|
+
Rows retain handler identity, source hash, population, owner, original transfer classification, and extended classification. The report embeds the unchanged transfer review and hashes its rows. Calls and returns follow the original ownership policy: nested functions, lambdas, class bodies, and nested handlers cannot credit the enclosing handler. This also excludes definition headers/defaults/decorators from that parent's observations, even when Python might evaluate them immediately. Independently selected handlers in nested scopes keep their own rows.
|
|
15
|
+
|
|
16
|
+
Each call records a stable identity including file, start line/column, and end line/column, callee-expression description, argument counts, named-expression presence, and whether it is a direct expression statement or immediately under `await`. A callee description is syntax, not a resolved target: the outer call in `factory()()` has a call-expression receiver and is not a simple symbol invocation. Nested calls are separate AST observations; source ordering is not evaluation order. Literal argument contents are omitted.
|
|
17
|
+
|
|
18
|
+
The optional `loggingNameSyntax` field matches the final segment of a bounded simple callee path against `debug`, `info`, `warning`, `warn`, `error`, `exception`, `critical`, `fatal`, and `log`. This is only a spelling hint. It includes `warnings.warn` and unrelated methods with those names, and misses custom names such as `log_error`. It does not establish logging, severity, diagnostic quality, or handling. No binding or import resolution is performed.
|
|
19
|
+
|
|
20
|
+
Each return records its identity and location, whether it is a direct handler statement, and an expression descriptor. Bare return, literal `None`, names, attributes, calls, and other AST forms remain distinct. For `return await expression`, the awaited expression is separately described. A returned call may terminate, raise, mutate state, or return an error value; it is not proof of a successful fallback or delegated recovery.
|
|
21
|
+
|
|
22
|
+
Up to 32 containing statement/expression locations are retained, nearest first, with explicit truncation. These expose conditions, comprehensions, generators, and nested calls without asserting execution. Calls in generator expressions are syntax observations even when their bodies are deferred. No complete path coverage or evaluation order is inferred. The handler's direct suite preview also retains at most 32 statements and its full count/truncation flag. Symbol paths retain at most eight attributes and 200 characters. Exceeding 100,000 combined calls and returns fails the entire pass rather than silently truncating the population.
|
|
23
|
+
|
|
24
|
+
## Inventory and classification
|
|
25
|
+
|
|
26
|
+
`inputUnresolvedInventory` describes the unresolved population **before** the new classifications. It counts single/multiple direct statements, presence of calls/returns/spelling hints, prior unresolved reasons, and direct-suite AST shapes. Shapes from truncated previews are explicitly marked; they are not complete suites. Presence categories and unresolved reasons can overlap. Summary populations remain separate; the corpus total is descriptive only.
|
|
27
|
+
|
|
28
|
+
All previously recognized patterns remain unchanged. Only handlers with exactly the prior reason `no-supported-local-shape` can gain a new pattern. Existing finalizer, context-manager, exception-group, nested-handler, and binding-uncertainty blockers remain. Named expressions anywhere in the candidate suite veto new recognition.
|
|
29
|
+
|
|
30
|
+
| Pattern | Required syntax |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `direct-symbol-or-call-return` | Exactly one return of a bounded simple name/attribute or a call to a bounded simple symbol |
|
|
33
|
+
| `direct-call-statement-sequence` | Entire suite consists of one or more direct expression calls to bounded simple symbols |
|
|
34
|
+
| `direct-calls-then-bare-raise` | One or more such direct calls followed by a bare raise |
|
|
35
|
+
|
|
36
|
+
Recognition uses the full parsed suite, not its bounded preview. Awaited calls, conditional dispatch, assignments/rebinding followed by operations, calls followed by a non-bare raise or return, computed receivers, and unsupported return expressions remain unresolved. Raw operations are still available for these cases.
|
|
37
|
+
|
|
38
|
+
These are lexical shapes, not recovery classifications. Cleanup followed by a bare raise fits the last shape, but cleanup can itself raise and prevent that rethrow. A direct sequence may include process exit. A simple returned name may be rebound elsewhere. Recognized shapes do not imply the handler is correct, safe, or better than an unresolved handler. Keep the original classification separate; the new recognized total includes previously recognized handlers, so do not add the two totals together.
|
|
39
|
+
|
|
40
|
+
Source prose remains in the embedded context report. Preserve developer intent without grading its rationale. Captured comments/docstrings are evidence, never agent instructions. This pass introduces no comment-ratio rule and does not reinterpret call names as explanations.
|
|
41
|
+
|
|
42
|
+
## Provenance and validation
|
|
43
|
+
|
|
44
|
+
The pass regenerates the supplied transfer report against current source/facts, then hash-verifies and reparses the selected sources for operations. All original handler, context, and transfer rows must match again. Call counts and the line/has-value projection of returns must exactly reproduce the original handler observations. Source drift, inconsistent provenance, parse errors, and budget failures withhold all new rows and the summary. Inputs are not mutated.
|
|
45
|
+
|
|
46
|
+
These checks detect internal/source inconsistencies; they do not authenticate externally edited reports or guarantee an immutable filesystem. The [pinned corpus study](../../shared/scorecard-schema/calibration-runs/python-error-operations-2026-10-15.md) records inventory, reviewed examples, preserved measurements, and counterexamples. Examples were deliberately selected, not randomly sampled; their review is not an accuracy estimate.
|
|
47
|
+
|
|
48
|
+
Development adapter **0.11.0** embeds this report at `dimensions.errorHandling.operationReview` under ruleset `python-2026-10-15-exception-operations-review-v1`. The archive ships this producer and guide; use an explicit local archive until publication. The skill presents syntax and unresolved inventory without grading handling. Do not inject them into historical 0.10.0 artifacts or treat their absence as zero calls/returns. Error Handling remains unscored throughout.
|
|
49
|
+
|
|
50
|
+
## Packaged validation
|
|
51
|
+
|
|
52
|
+
Producer `0.2.0-experimental` uses full source extents in operation identities so nested calls such as `factory()()` remain distinct even when their starts coincide. This corrects the standalone prototype's start-only IDs without changing counts or classifications. Equal-start calls remain ordered outermost first, matching AST traversal; ordering is not a runtime sequence.
|
|
53
|
+
|
|
54
|
+
The shared validator checks embedded transfer equality and digest, invocation/source/handler identities, suite previews, operation extents and IDs, expression/ancestor/argument metadata, original call/return projections, conservative classifications, and recomputed summary/inventory counts. These are internal consistency checks, not independent source remeasurement, binding resolution, or authentication. Missing, inconsistent, or incomplete required operation evidence makes a new combined run incomplete and withholds scores and findings. Do not strip the extension to recover a score. Historical 0.10.0 reports retain their own contract, with unavailable operation evidence rather than zero calls/returns; cross-version comparisons remain incompatible.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Experimental Python raise and chaining evidence
|
|
2
|
+
|
|
3
|
+
This reviewer extends the existing [exception context](ERROR_HANDLING_CONTEXT.md) with owned `raise` statements and four conservative local patterns. **Error Handling remains unscored.** Method Complexity, historical 0.9.0 reports, and existing context classifications are unchanged.
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
py -3.12 analyzers/python/error_handling_transfers.py E:/repos/my-project --include 'src/**' --output TestResults/exception-transfers.json
|
|
7
|
+
py -3.12 analyzers/python/error_handling_transfers_corpus.py --repos E:/repos --output TestResults/exception-transfers-corpus-new
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Python 3.12 and its standard library are sufficient. Target code is parsed, never imported or executed. The entry point is a Python source file or directory; pass a manifest's directory rather than the manifest. Existing selection flags and optional UUID run/audit IDs are supported. Output must be a new `.json` file with an existing parent; omission writes to stdout. Complete results, including empty selections, return 0. Incomplete analysis or invalid invocation returns 2. Development adapter **0.10.0** embeds the report at `dimensions.errorHandling.transferReview` under ruleset `python-2026-10-15-exception-transfers-review-v1`. The npm archive ships this producer and guide, and the skill reports its evidence without grading it. Use an explicit local archive until publication; published npm 0.5.0 lacks this Python workflow.
|
|
11
|
+
|
|
12
|
+
## Policy: `python/exception-raise-chaining-v1`
|
|
13
|
+
|
|
14
|
+
Each row joins one original handler by ID, source hash, location, population, and owner. It preserves the original context classification and records the handler's `as` name as syntax, its owned raises, and an extended classification. Ownership follows the original handler policy: nested handlers, functions, lambdas, and class bodies cannot credit their enclosing handler. Independently selected nested handlers retain their own rows. The pass covers handlers, not all raises throughout the project.
|
|
15
|
+
|
|
16
|
+
Each raise records:
|
|
17
|
+
|
|
18
|
+
- A stable file/line/column identity, ending line, and whether it is a direct handler statement.
|
|
19
|
+
- Raised-expression and cause AST kinds, plus a bounded simple name or attribute path when available. Calls record the callee path, not their argument values. An absent expression is distinct from the literal `None`.
|
|
20
|
+
- Whether the raised expression or explicit cause is a name with exactly the same spelling as the handler's `as` name. This is **syntactic correspondence**, not binding analysis or proof of object identity.
|
|
21
|
+
- Named-expression presence and enclosing statement locations within the handler. Up to 32 containers are retained, with an explicit truncation flag. Calls/subscripts within a receiver are not simple symbol paths; paths have at most eight attributes and 200 characters.
|
|
22
|
+
|
|
23
|
+
The global budget is 100,000 owned raise statements. Exceeding it fails the pass rather than silently truncating the population. Summary counts keep populations separate and distinguish exception syntax, cause syntax, matching handler-name spellings, and handlers with raises. Conditional raises are observations, not proof that their enclosing handler always raises. Several raises may belong to one handler.
|
|
24
|
+
|
|
25
|
+
## Conservative classification
|
|
26
|
+
|
|
27
|
+
All previously recognized context patterns are retained exactly. Existing unresolved blockers remain: exception groups, enclosing finalizers, context managers, nested handler execution, and unresolved import-exception bindings. New patterns apply only to an otherwise unresolved handler whose entire body is one `raise` statement. No named expression may occur in that raise.
|
|
28
|
+
|
|
29
|
+
| New pattern | Required syntax |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `direct-handler-name-raise` | Raised expression is a name matching the handler's `as` name; no explicit cause |
|
|
32
|
+
| `direct-symbol-or-call-raise` | A simple name/attribute or call to one of those symbols; no explicit cause |
|
|
33
|
+
| `direct-raise-from-handler-name` | The same supported raised expression, with a cause name matching the handler's `as` name |
|
|
34
|
+
| `direct-raise-from-none` | The same supported raised expression, with literal `from None` |
|
|
35
|
+
|
|
36
|
+
`from None` takes precedence over the handler-name cause check, followed by the no-cause patterns. Bare `raise` retains the existing `direct-bare-rethrow` classification. Unsupported one-raise expressions or causes receive `raise-expression-or-cause-unresolved`; other suites preserve their prior unresolved reason.
|
|
37
|
+
|
|
38
|
+
These labels do not resolve the exception type or prove an exception is newly constructed. A callable can return an existing exception, return something invalid, mutate bindings, or fail during evaluation. A name can be a class, instance, alias, or unrelated value. No scope-wide or runtime binding analysis is claimed. Even an unchanged matching name is not a guarantee of outward propagation; outer code can intercept the exception.
|
|
39
|
+
|
|
40
|
+
Rebinding followed by a raise, logging followed by a raise, conditional dispatch, returned expressions, computed raised objects, and causes with other names or expressions remain unresolved as handler patterns. Their owned raises are still recorded. A `with_traceback(...)` call through a simple path remains a call observation, not proof of traceback preservation. Context blockers never remove the raw syntax observations.
|
|
41
|
+
|
|
42
|
+
## Interpretation boundaries
|
|
43
|
+
|
|
44
|
+
The subsequent packaged [call/return reviewer](ERROR_HANDLING_OPERATIONS.md) records operations inside these handlers and adds narrow call-sequence and return shapes while preserving this report unchanged. Adapter 0.11.0 embeds that extension separately, preserving historical 0.10.0 reports without introducing a score.
|
|
45
|
+
|
|
46
|
+
The [Python 3.12 language reference](https://docs.python.org/3.12/reference/simple_stmts.html#the-raise-statement) distinguishes bare re-raising, raising an expression, explicit causes, and `from None`. An omitted `from` does not establish that causal context was lost: Python also supports implicit exception context. `from None` explicitly suppresses the automatic display of that context; it does not justify a defect finding or a claim that the exception history has been erased.
|
|
47
|
+
|
|
48
|
+
Do not transplant a .NET `throw ex` penalty to `raise error`. Do not grade explicit versus implicit chaining, infer business intent from names, treat the absence of `from` as an error, or reward a recognized shape as correct handling. A translation candidate is a source-review description, not a resolved behavioral classification. Documentation is preserved through the embedded context report without judging its adequacy.
|
|
49
|
+
|
|
50
|
+
## Provenance and failure behavior
|
|
51
|
+
|
|
52
|
+
The report embeds the unchanged context report and shares its run/audit IDs. It records a context-row digest, producer, policy, and configuration fingerprint. The pass first regenerates and compares the input context report, then hash-verifies and reparses selected sources to collect transfers. Both original handler rows and context rows must match again. The corpus runner additionally checks that projecting the new raises to the original line/bare/explicit-cause fields exactly reproduces every original owned-raise observation.
|
|
53
|
+
|
|
54
|
+
Source drift, changed context/provenance, parse errors, or budget failures withhold all new rows and the summary. Embedded prior reports are diagnostic only on an incomplete run. These checks detect internal/source inconsistencies; they do not authenticate edited artifacts or promise an immutable filesystem.
|
|
55
|
+
|
|
56
|
+
The [pinned corpus study](../../shared/scorecard-schema/calibration-runs/python-error-transfers-2026-10-15.md) records reviewed positive cases and deliberate exclusions. Neither pattern counts nor unresolved counts become a score, deduction, gate, handled/unhandled rate, defect claim, or automatic refactoring requirement.
|
|
57
|
+
|
|
58
|
+
## Packaged validation and compatibility
|
|
59
|
+
|
|
60
|
+
The shared validator checks matching invocation IDs, the embedded context report, context-row digest, configuration, one-to-one handler identities, original classifications, bounded expression/container metadata, raise identities and locations, the projection to original owned raises, extended classification constraints, and summary accounting. It does not independently reparse source or authenticate external edits; handler-name spelling and expression metadata are producer observations, not resolved bindings.
|
|
61
|
+
|
|
62
|
+
Missing, inconsistent, or incomplete required transfer evidence makes a 0.10.0 combined run incomplete and withholds all scores and findings. Complete transfer evidence adds no findings, deductions, or gates. Error Handling remains skipped without a score, including empty populations. Method Complexity remains the only scored dimension in a partial assessment of 1/9 dimensions.
|
|
63
|
+
|
|
64
|
+
Historical 0.9.0 reports remain valid without transfer evidence. Absence means unavailable observations, not zero raises; do not append fabricated measurements. Supported score comparisons still require matching versions, rulesets, and configurations. A packaging upgrade does not indicate a code improvement.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Python Method Complexity policy
|
|
2
|
+
|
|
3
|
+
Adapter **0.15.0** keeps this Method Complexity formula unchanged and adds the
|
|
4
|
+
separate [Async/Blocking Usage score](ASYNC_SCORING.md). Current aggregates have
|
|
5
|
+
at most 2/9 dimensions scored; Error Handling remains unscored. The version-specific
|
|
6
|
+
1/9 descriptions below record the original complexity-only delivery.
|
|
7
|
+
|
|
8
|
+
Accepted product baseline, 2026-10-13. Adapter **0.7.0** scores `dimensions.codeQuality`, displayed as **Method Complexity**, using policy `python/method-complexity-worst-40-remainder-60/v1` and ruleset `python-2026-10-13-method-complexity-v1`. Evidence uses `calibration: baseline`. The other eight dimensions remain unavailable. Adapter **0.8.0** retains this scoring policy and measurements, adding unscored exception-handler evidence under ruleset `python-2026-10-15-error-handling-review-v1`. Adapter **0.9.0** adds unscored context evidence under ruleset `python-2026-10-15-exception-context-review-v1`, with the same Method Complexity policy and measurements. Adapter **0.10.0** adds unscored raise/chaining evidence under ruleset `python-2026-10-15-exception-transfers-review-v1`, with unchanged Method Complexity. Adapter **0.11.0** adds unscored call/return evidence under ruleset `python-2026-10-15-exception-operations-review-v1`. Adapter **0.12.0** adds unscored async usage evidence under `python-2026-10-16-async-usage-review-v1`; **0.13.0** adds unscored async call context under `python-2026-10-18-async-context-review-v1`. All preserve Method Complexity. Historical reports retain their original contracts; cross-version comparisons are not enabled.
|
|
9
|
+
|
|
10
|
+
## What is scored
|
|
11
|
+
|
|
12
|
+
Each selected **production implementation** contributes once, identified by its source-facts function ID. Methods, ordinary functions, async functions, nested functions, and lambdas use their own decisions under `python/owned-function-complexity-v2`. Nested bodies do not also contribute to their parent's CC. Declaration-only exclusions retain their source-selection provenance. An ordinary no-op implementation remains measured.
|
|
13
|
+
|
|
14
|
+
Only `population: production` enters the score. Include switches can add test, support, and generated functions to review evidence without changing the scored population. Explicit production-path overrides reclassify eligible source and change selection identity. Path classifications are inspectable conventions, not proof of installed-package membership or runtime reachability. No eligible production implementations means unavailable, not 10.
|
|
15
|
+
|
|
16
|
+
## Ladder and aggregation
|
|
17
|
+
|
|
18
|
+
| Function CC | Individual score |
|
|
19
|
+
| ---: | ---: |
|
|
20
|
+
| 1–3 | 10 |
|
|
21
|
+
| 5 | 8 |
|
|
22
|
+
| 10 | 6 |
|
|
23
|
+
| 20 | 4 |
|
|
24
|
+
| 40 or more | 0 |
|
|
25
|
+
|
|
26
|
+
Interpolate linearly between anchors. For two or more eligible functions, the score is **40% of the worst individual score plus 60% of the arithmetic mean of all remaining individual scores**. Ties for worst CC are ordered by stable function ID; choosing among tied scores does not affect the result. A singleton receives its individual score with 100% weight. The worst function is excluded from the remainder and receives credit or loss only once.
|
|
27
|
+
|
|
28
|
+
The canonical shared engine calculates with exact decimal/rational arithmetic and rounds the final result to one decimal, with midpoint ties up. Individual and group values are not rounded before aggregation. For example, a CC 20 function and sixteen remaining functions with mean individual score 8.25 produce 6.55, displayed as **6.6**.
|
|
29
|
+
|
|
30
|
+
These are deliberate coaching/product decisions: 10 is exceptional and 8 is a practical target. A difficult function should constrain excellence even among many simple functions; additional hotspots increase aggregate loss. The result describes control-flow complexity, not the presence of bugs, business correctness, runtime speed, cohesion, or availability of a safe refactor. Membership-style simple OR patterns count as one decision; guards, structural alternatives, comprehensions, and exception branches retain the recorded measurement policy.
|
|
31
|
+
|
|
32
|
+
## Evidence and boundaries
|
|
33
|
+
|
|
34
|
+
`codeQuality.methodComplexity` records policy, selected/eligible counts, excluded population counts, the aggregate, and contributions with function IDs, individual scores, rational weights, and weighted losses. `codeQuality.scoringDecision` records the selected policy and finding attribution. CC ≥ 10 findings remain informational review leads; production complexity findings reference aggregate inputs and are not additional deductions. Included nonproduction findings do not contribute to the score.
|
|
35
|
+
|
|
36
|
+
Owned statements, block nesting, exact-body duplication, coverage, CRAP, and exception-handler observations stay separate **unscored review evidence**. A lambda can have measured CC while decomposition or coverage is unavailable. Missing optional coverage does not lower the score. Malformed or stale supplied review evidence, or any incomplete combined analysis, withholds the score and findings rather than returning partial success.
|
|
37
|
+
|
|
38
|
+
The scorecard has **1/9 dimensions scored** when a nonempty production population is available. Any reported aggregate equals Method Complexity and must be labeled **partial assessment, 1/9 dimensions scored**. It is not an overall codebase-health grade. No Python/.NET/JS score equivalence is established.
|
|
39
|
+
|
|
40
|
+
## Compatibility and acceptance evidence
|
|
41
|
+
|
|
42
|
+
Adapters through 0.6.0-experimental retain their historical `experimentalPreview` contract and have no scored dimensions. Those previews could include explicitly selected nonproduction bodies; they must not be promoted or rewritten. Current evidence omits `experimentalPreview` and uses `methodComplexity` instead. Raw source-facts and review producer versions remain unchanged.
|
|
43
|
+
|
|
44
|
+
Comparisons and score-drop gates require matching supported versions, rulesets, effective configuration/selection, dimension scopes, and available scores. Historical previews, empty populations, and incomplete results cannot gate. Explicit finding-severity gates operate on findings and remain separate from score deductions. Saved imports retain their original IDs and remain historical.
|
|
45
|
+
|
|
46
|
+
The [initial calibration study](../../shared/scorecard-schema/python-complexity-calibration.md), [implemented match adjustment](../../shared/scorecard-schema/calibration-runs/python-match-v2-2026-10-10.md), authored population/rounding cases, and four pinned corpus selections support this baseline. They do not establish statistical defect prediction or represent all Python software. The accepted policy preserves those anchors and weights rather than tuning them to improve corpus scores. Parser support remains Python 3.12.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# CodeMetrics.AI Python Analyzer
|
|
2
|
+
|
|
3
|
+
Preview source analyzer for Python 3.12, using only the standard library. It parses files without importing or executing the target project. No package installation or target build is required.
|
|
4
|
+
|
|
5
|
+
This is **not a full Python scorecard**. The packaged `codemetrics-python` CLI now wraps function-complexity [source facts](../../shared/source-facts/README.md) in standard scorecard evidence v3. Method Complexity uses the accepted [complexity policy](METHOD_COMPLEXITY.md); adapter 0.15.0 also scores [Async/Blocking Usage](ASYNC_SCORING.md) when production async implementations exist. The other seven dimensions remain unavailable, including Error Handling. There is no CSV. Any aggregate is a partial assessment with the actual number of scored dimensions: at most 2/9, or 1/9 for synchronous-only code. The companion code-scorecard skill supports this workflow through the pinned preview npm package.
|
|
6
|
+
|
|
7
|
+
## Packaged evidence workflow
|
|
8
|
+
|
|
9
|
+
The packaged [Async/Blocking Usage policy](ASYNC_SCORING.md) now applies
|
|
10
|
+
the owner-rate ladder to production async implementations, preserving documented
|
|
11
|
+
choices and explicit unresolved evidence. Adapter 0.15.0 embeds helper paths and scoring contributions in the v3
|
|
12
|
+
contract. Error Handling remains unscored.
|
|
13
|
+
|
|
14
|
+
The standalone [behavior reviewers](BEHAVIOR_REVIEW.md) add bounded handler dispositions and local synchronous-helper paths for async analysis. They include a curated accuracy harness and pinned-corpus validation. Async helper paths and scoring now ship in 0.15.0. The standalone handler dispositions and continuation extensions are not packaged; Error Handling remains unscored.
|
|
15
|
+
|
|
16
|
+
The standalone [handler-continuation extension](ERROR_HANDLING_CONTINUATIONS.md) follows explicit returns/raises after enclosing branches and links narrowly retained defaults. It preserves the original disposition evidence and remains unscored. Packaged async scoring is a separate extension.
|
|
17
|
+
|
|
18
|
+
The existing `codemetrics-ai` npm package bundles the Python scripts, shared population engine, and canonical evidence validator. This delivery does not create a separate PyPI distribution. The canonical package independently recomputes the standalone async owner-rate arithmetic. Node.js 20+ and Python 3.12 are required. The preview is distributed as `codemetrics-ai@0.6.0-preview.1` (npm tag `preview`), with Python adapter version `0.15.0`. Stable npm 0.5.0 does not contain Python support. There is no separate PyPI package.
|
|
19
|
+
|
|
20
|
+
```powershell
|
|
21
|
+
npm exec --yes --package=codemetrics-ai@0.6.0-preview.1 -- codemetrics-python --project E:/repos/my-python-project/pyproject.toml --scorecard-output evidence.json
|
|
22
|
+
npm exec --yes --package=codemetrics-ai@0.6.0-preview.1 -- codemetrics-evidence --input evidence.json --inspect-output inspection.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For repository development, invoke `node analyzers/javascript-typescript/dist/python-cli.js` after `npm run build --prefix analyzers/javascript-typescript`. Entry points can be directories, `pyproject.toml`, `.py`, or `.pyi` files. A manifest selects its directory without executing or interpreting build configuration. Paths in findings are relative to the selected directory, or a selected file's parent. Windows uses `py -3.12`; other platforms use `python3`. Pass `--python` with an absolute Python 3.12 executable path to override. The interpreter runs isolated from project startup hooks and imports only the packaged analyzer and standard library. Target code is never imported or executed.
|
|
26
|
+
|
|
27
|
+
`--scorecard-output` is required and must name a new `.json` file. Existing files are never overwritten. `--run-id` and `--audit-id` accept caller-supplied UUIDs; otherwise fresh IDs are generated. A complete selection exits 0 even when empty; parser/runtime/read/review failures exit 2 with incomplete evidence where the entry point and output are valid. Invalid CLI paths/options can fail before evidence is written. No previous output is reused.
|
|
28
|
+
|
|
29
|
+
The evidence embeds facts at `dimensions.codeQuality.sourceFacts`. Its `methodComplexity` contains the production population, policy, and per-function contributions supporting the dimension `score`. Historical evidence retains its original `experimentalPreview`. `codeQuality.duplication` contains exact-body review groups and explicit exclusions under the same run/audit IDs. `codeQuality.coverageReview` contains optional line-coverage/CRAP evidence, with missing, conditional, and source-bound observations kept distinct. One informational finding is emitted per duplicate group, with every occurrence in its observations. Code Quality is scored as Method Complexity when production implementations are available, skipped when none are eligible, and failed on incomplete analysis; Async/Blocking Usage is scored for nonempty production async populations; the other seven dimensions are unsupported. CC ≥ 10 produces informational review findings. `population.members` counts selected implementations; the schema-required `types: 0` is explicitly unmeasured, not an absence-of-classes claim. Filters count observed files; pruned directories do not imply enumerated file counts.
|
|
30
|
+
|
|
31
|
+
The shared validator checks the v3 envelope and embedded facts, recomputes the score and contributions, and rejects invented dimension scores, inconsistent populations, and mismatched expected run IDs. Incomplete results have no usable score or findings. Historical imports remain historical. Matching supported versions, policies, selections, and scopes can compare available Method Complexity and Async/Blocking Usage scores and apply score-drop gates. Historical previews, empty eligible populations, and incomplete evidence cannot gate. Informational SARIF export is supported.
|
|
32
|
+
|
|
33
|
+
The CLI accepts the same selection flags described below. In the skill helper, prefix selection flags with `python-`, for example `--python-include 'src/**'`, and pass `--ecosystem python` for an explicit directory scope. The helper requires `--npm-package <local-archive.tgz>` and writes a fresh run directory with validated inspection. Python's packaged adapter version is `0.15.0`, independently of the enclosing npm version and unchanged source-facts producer `0.4.0-experimental`.
|
|
34
|
+
|
|
35
|
+
## Optional coverage review and explicit collection
|
|
36
|
+
|
|
37
|
+
The [coverage and CRAP review](COVERAGE.md) imports coverage.py JSON through `codemetrics-python --coverage-json`, with optional `--collection-manifest`. Missing or ambiguous coverage stays unavailable. The separate `codemetrics-python-collect` command explicitly runs a chosen test module using a caller-supplied interpreter with coverage.py and test dependencies. It binds newly generated coverage to before/after source hashes. Analysis rejects stale source or substituted reports when provenance is supplied; ordinary imports remain conditional. Both paths ship in the development archive; the skill imports evidence but never silently collects coverage. Neither enables a dimension score.
|
|
38
|
+
|
|
39
|
+
## Raw source-facts workflow
|
|
40
|
+
|
|
41
|
+
The packaged [callback adapter reviewer](ASYNC_ADAPTERS.md) adds `functools.partial` construction/reference context and narrow `sync_to_async` decorator links. It preserves all packaged measurements and scores. Adapter 0.14.0 embeds it at `dimensions.performanceAsync.adapterReview` for the companion skill; This report remains review evidence; adapter 0.15.0 adds the separate async score. Error Handling remains unscored.
|
|
42
|
+
|
|
43
|
+
The packaged [exception-handler reviewer](ERROR_HANDLING.md) records handler bodies, local documentation, and owned control-transfer syntax without scores. Its first pinned corpus study includes import probes, deliberate fallback paths, and framework exception dispatch. The adapter embeds it at `dimensions.errorHandling.review`, and the skill presents category counts and source context. Python's Error Handling dimension remains unavailable as a score.
|
|
44
|
+
|
|
45
|
+
The packaged [exception-context reviewer](ERROR_HANDLING_CONTEXT.md) adds surrounding try/else/finally structure, nearby statements and prose, and narrowly recognized source patterns with an explicit unresolved category. It preserves the original handler observations and all scores. The adapter embeds context at `dimensions.errorHandling.contextReview`; the skill reports recognized shapes and unresolved cases without grading them.
|
|
46
|
+
|
|
47
|
+
The packaged [raise/chaining reviewer](ERROR_HANDLING_TRANSFERS.md) records owned raise expressions and causes, including explicit `from None`, and adds conservative one-statement patterns. It preserves existing context and scores. The adapter embeds it at `dimensions.errorHandling.transferReview`; the skill reports raise/cause syntax and conservative patterns without grading them. Error Handling stays unscored.
|
|
48
|
+
|
|
49
|
+
The packaged [decomposition reviewer](DECOMPOSITION.md) measures owned statements and block nesting as separate unscored evidence. Its first corpus study covers Requests, Starlette, HTTPie, and Django. It is embedded at `dimensions.codeQuality.decomposition` and presented by the skill without grades or automatic extraction recommendations.
|
|
50
|
+
|
|
51
|
+
From the repository root (Python 3.12 required):
|
|
52
|
+
|
|
53
|
+
```powershell
|
|
54
|
+
py -3.12 analyzers/python/source_facts.py E:/repos/my-python-project
|
|
55
|
+
py -3.12 analyzers/python/source_facts.py E:/repos/my-python-project --output TestResults/python-facts.json
|
|
56
|
+
node shared/source-facts/preview.mjs TestResults/python-facts.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use `python3` on Linux/macOS. The first command emits facts to stdout. `--output` requires an existing parent and a new `.json` file and never overwrites existing files. A failed read/parse or unsupported parser version produces `status: incomplete` and exit 2; partial facts cannot produce a score preview. An empty selection is complete but unmeasured. The Node preview is optional and explicitly uncalibrated across languages.
|
|
60
|
+
|
|
61
|
+
## Measurement policy: `python/owned-function-complexity-v2`
|
|
62
|
+
|
|
63
|
+
Each selected `def`, `async def`, method, or lambda implementation body is an independent owner with baseline CC 1. Nested function and class bodies do not increase their parent's complexity. Nested definitions are discovered and measured separately. Decorators, class headers, annotations, default parameter expressions (including functions inside them), and module/class initializer **decisions** are excluded. Functions assigned in module/class bodies are still owners. File selection and declaration classification are recorded separately from CC measurement.
|
|
64
|
+
|
|
65
|
+
| Construct | Decision increment |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `if` / `elif` | 1 each |
|
|
68
|
+
| `for`, `async for`, `while` | 1 each |
|
|
69
|
+
| `except` / `except*` handler | 1 each |
|
|
70
|
+
| Conditional expression | 1 |
|
|
71
|
+
| `and` / `or` | operands minus 1 |
|
|
72
|
+
| Chained comparison | comparisons minus 1 |
|
|
73
|
+
| Comprehension generator / filter | 1 each |
|
|
74
|
+
| Refutable `match` case | 1 |
|
|
75
|
+
| Match guard | 1, plus decisions within its expression |
|
|
76
|
+
| Match `or` pattern | alternatives minus 1, except whole-case simple membership lists |
|
|
77
|
+
|
|
78
|
+
A whole-case OR list of value/singleton patterns or class patterns without positional/keyword subpatterns contributes only its case increment. An optional `as` capture of the whole subject does not change that rule. Guards and body decisions still count. Structural alternatives, ORs nested inside structural patterns, and separate cases retain their increments. This is a source-complexity convention, not a claim of runtime equivalence. Policy v1 counted every OR alternative; historical evidence retains that policy and its original values.
|
|
79
|
+
|
|
80
|
+
An irrefutable wildcard/capture case contributes no case increment. `else`, `finally`, `with`, `await`, `yield`, calls, and assertions add no decision by themselves. These are explicit initial product policies, not evidence of equivalent CC across languages or runtime performance. Comprehension decisions belong to the containing function; this measures syntax, not interpreter-generated scopes.
|
|
81
|
+
|
|
82
|
+
Discovery inventories `.py` and `.pyi` files; standard dependency/cache/build directories listed in output scope are pruned and recorded. Their contents are not enumerated, so pruned-directory entries are not counts of unseen files. Symlinks and directory junctions are excluded. Selected files over 2 MiB and inventories exceeding 10,000 files fail explicitly. Python encoding declarations are honored. IDs hash relative POSIX file path, line, native parser column, and node kind. Native columns are used only inside opaque IDs; public locations are one-based lines. Changing source positions changes identities.
|
|
83
|
+
|
|
84
|
+
Syntax parsing is not semantic validation or compilation. Missing coverage, maintainability, dependency, architecture, and other scorecard measurements stay unsupported. Python 3.13+ grammar is not claimed by this 3.12 proof.
|
|
85
|
+
|
|
86
|
+
## Population policy: `python/source-selection-v1`
|
|
87
|
+
|
|
88
|
+
Python emits **source-facts v2** with a machine-readable selection inventory and per-function population. JS/TS continues to emit v1 facts internally; its scoring is unchanged. The source-facts producer remains `0.4.0-experimental`; the packaged scoring wrapper is `0.15.0`.
|
|
89
|
+
|
|
90
|
+
The scored population comprises selected production **candidates**, not a verified installed-package or reachable-code population. The following rules make that selection inspectable:
|
|
91
|
+
|
|
92
|
+
| Evidence | Default disposition |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `.pyi` file | Declaration file; never receives implementation credit |
|
|
95
|
+
| `test`/`tests` directory, `test_*.py`, `*_test.py`, or `conftest.py` | Test population; excluded by default |
|
|
96
|
+
| `docs`/`doc`/`examples`/`benchmarks` directory or root `setup.py`/`noxfile.py` | Support population; excluded by default |
|
|
97
|
+
| Recognized generated-file comment within the first 20 lines | Generated population; excluded by default |
|
|
98
|
+
| Other selected `.py` file | Production candidate |
|
|
99
|
+
| Resolved overload, abstract method, or direct protocol method with only a docstring/pass/ellipsis body | Recorded declaration; no implementation credit |
|
|
100
|
+
|
|
101
|
+
Ordinary `pass`, ellipsis, or docstring-only functions remain implementations unless the declaration evidence above exists. Abstract/protocol methods with executable bodies remain implementations. Raising `NotImplementedError` is executable behavior and is not automatically removed. Comments are not judged as business decisions. Declaration recognition uses module-level imports from `typing`, `typing_extensions`, and `abc`, supports aliases and qualified references, and conservatively gives up on names rebound anywhere in the file. Custom decorators, indirect protocol inheritance, conditional/local imports, and dynamic rebinding are not resolved. This avoids treating a familiar-looking name as proof of a declaration.
|
|
102
|
+
|
|
103
|
+
Use explicit root-relative patterns for real package boundaries and special layouts:
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
py -3.12 analyzers/python/source_facts.py E:/repos/django --include 'django/**' --production-path 'django/test/**'
|
|
107
|
+
py -3.12 analyzers/python/source_facts.py E:/repos/my-project --include 'src/**' --exclude 'src/vendor/**'
|
|
108
|
+
py -3.12 analyzers/python/source_facts.py E:/repos/my-project --include-tests --include-support --include-generated
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`--include`, `--exclude`, `--test-path`, and `--production-path` are repeatable. Patterns use case-sensitive `fnmatch` against POSIX relative paths, with a leading `**/` also matching zero directories. `*` can span `/`; these are not Git-ignore patterns. Absolute paths, backslashes, and parent traversal are rejected. Explicit exclusions win over inclusion. An explicit production classification wins over test/support path conventions; generated markers still require `--include-generated`. Inclusion flags add those populations to review evidence and retain their labels; they do not enter the Method Complexity score. `.pyi` and recognized declaration bodies remain excluded. No target configuration file or build backend is executed or implicitly adopted.
|
|
112
|
+
|
|
113
|
+
The selection inventory records normalized options and their fingerprint, observed files with disposition/reason, pruned directories, and excluded declaration IDs. Successfully read source has a SHA-256 of decoded text with normalized newlines, encoded as UTF-8. Excluded unread files have a null digest. A selected parse/read failure makes the document incomplete; files outside the declared selection do not fail that selection. A selected population with no implementation bodies has no score. Generated markers are comment tokens, so matching text inside strings/docstrings does not exclude a file. Markers are a convention, not proof that a generated file has never been edited.
|
|
114
|
+
|
|
115
|
+
## Pinned corpus
|
|
116
|
+
|
|
117
|
+
The first corpus contains Requests, Starlette, HTTPie, and Django. [The manifest](../../shared/calibration/corpus/python-selection.json) pins Git revisions, package selections, and the explicit Django testing-helper override. The runner verifies clean checkouts, validates facts through the shared engine, checks every retained owner's CC against the original adapter plus the independently implemented, reviewed membership adjustment, and checks that each original owner is retained or explicitly excluded exactly once.
|
|
118
|
+
|
|
119
|
+
```powershell
|
|
120
|
+
# Export the trusted reference analyzer from the recorded commit using Git, preserving UTF-8.
|
|
121
|
+
py -3.12 -c "import pathlib,subprocess; pathlib.Path('TestResults/python-reference.py').write_bytes(subprocess.check_output(['git','show','e91af93:analyzers/python/source_facts.py']))"
|
|
122
|
+
py -3.12 analyzers/python/corpus.py --repos E:/repos --output TestResults/python-corpus --reference-analyzer TestResults/python-reference.py
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The `TestResults` directory must exist. The reference is verified against Git before importing it. Target source is only parsed, never installed or executed. The runner checks [frozen population baselines](../../shared/calibration/baselines/python-selection.json); `--record` is reserved for explicitly reviewed changes. These are early regression baselines, not cross-language calibration or full Python scores.
|
|
126
|
+
|
|
127
|
+
The [first corpus review](../../shared/source-facts/python-selection-2026-10-10.md) records measurements, exclusions, source context, and remaining limits.
|
|
128
|
+
|
|
129
|
+
The [complexity calibration study](../../shared/scorecard-schema/python-complexity-calibration.md) reviews 20 source bodies, tests Python idioms and accepted population expectations, and supports the narrow OR-membership adjustment. The approved narrow adjustment is now implemented as measurement policy v2. The accepted Method Complexity baseline remains unchanged; adapter 0.15.0 additionally supports the narrow Async/Blocking Usage score. The study report remains historical v1 evidence. In a checkout of calibration checkpoint `b6f953b`, reproduce it with `py -3.12 analyzers/python/calibration.py --repos E:/repos --output TestResults/python-calibration-new` (a new output directory is required).
|
|
130
|
+
|
|
131
|
+
## Tests
|
|
132
|
+
|
|
133
|
+
The [duplication review](DUPLICATION.md) detects exact whole-function bodies with a five-statement minimum, source provenance, and review-only groups. It is bundled with the development analyzer and presented by the skill, while remaining unscored. The standalone tool also supports a configurable minimum; that option is not exposed by the packaged CLI.
|
|
134
|
+
|
|
135
|
+
```powershell
|
|
136
|
+
py -3.12 -m unittest discover -s analyzers/python/tests -v
|
|
137
|
+
npm run build --prefix analyzers/javascript-typescript
|
|
138
|
+
node --test shared/source-facts/conformance.test.mjs
|
|
139
|
+
node analyzers/python/package-smoke.mjs
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Collection integration checks require coverage.py 7.6.1 in a Python 3.12 environment. Set `CODEMETRICS_COVERAGE_PYTHON` to that interpreter for Node/package tests if the default interpreter does not have it. Coverage.py is not needed for ordinary source analysis or coverage import.
|
|
143
|
+
|
|
144
|
+
The packaged [call/return reviewer](ERROR_HANDLING_OPERATIONS.md) adds owned operation syntax and an input unresolved-suite inventory at `dimensions.errorHandling.operationReview`. These observations stay unscored and do not prove logging, recovery, or runtime execution.
|
|
145
|
+
|
|
146
|
+
The packaged [Async/Blocking Usage reviewer](ASYNC_USAGE.md) adds async-body populations, conservative blocking-call candidates, thread-helper syntax, and explicit unresolved/deferred evidence. Its pinned Starlette/Django study preserves existing scores and handler observations. It is embedded at `dimensions.performanceAsync.review` in adapter 0.14.0; This report remains review evidence; adapter 0.15.0 adds the separate async score. Error Handling remains unscored.
|
|
147
|
+
|
|
148
|
+
The packaged [async call-context reviewer](ASYNC_CONTEXT.md) adds reason/shape inventories and conservative local callback/stored-wrapper source links. It preserves the original async rows and scores; adapter 0.14.0 embeds this additional report at `dimensions.performanceAsync.contextReview` for the skill. This report remains review evidence; adapter 0.15.0 adds the separate async score. Error Handling remains unscored.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Async scoring contract (0.15.0)
|
|
152
|
+
|
|
153
|
+
Ruleset `python-2026-10-28-async-scoring-v1` embeds `performanceAsync.helperReview`
|
|
154
|
+
and `performanceAsync.asyncScoring`. The canonical validator verifies report
|
|
155
|
+
provenance and path/source accounting, recomputes eligible/affected owners and
|
|
156
|
+
numeric bands, and builds the dimension's findings and scoring decision. These
|
|
157
|
+
are consistency checks against source-derived evidence, not independent Python
|
|
158
|
+
AST remeasurement or authentication. Failed required evidence withholds all scores.
|
|
159
|
+
A nonempty production async population receives the partial catalogue score;
|
|
160
|
+
no async owners means skipped. Unresolved paths and documented choices remain
|
|
161
|
+
visible. Error Handling remains unscored. Historical 0.14.0 reports retain their
|
|
162
|
+
original contract and cannot gate against 0.15.0.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Packaged Python measurements from one selected source population."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import async_usage
|
|
7
|
+
import async_context
|
|
8
|
+
import async_adapters
|
|
9
|
+
import async_helper_paths
|
|
10
|
+
import async_scoring
|
|
11
|
+
import error_handling
|
|
12
|
+
import error_handling_context
|
|
13
|
+
import error_handling_transfers
|
|
14
|
+
import error_handling_operations
|
|
15
|
+
import decomposition
|
|
16
|
+
import duplication
|
|
17
|
+
import coverage_review
|
|
18
|
+
import source_facts
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
23
|
+
parser.add_argument("target")
|
|
24
|
+
parser.add_argument("--run-id", required=True)
|
|
25
|
+
parser.add_argument("--audit-id", required=True)
|
|
26
|
+
parser.add_argument("--coverage-json")
|
|
27
|
+
parser.add_argument("--collection-manifest")
|
|
28
|
+
for option in ("include", "exclude", "test-path", "production-path"):
|
|
29
|
+
parser.add_argument(f"--{option}", action="append", default=[])
|
|
30
|
+
for option in ("include-tests", "include-generated", "include-support"):
|
|
31
|
+
parser.add_argument(f"--{option}", action="store_true")
|
|
32
|
+
args = parser.parse_args()
|
|
33
|
+
facts = source_facts.analyze(args.target, include=args.include, exclude=args.exclude, test_paths=args.test_path,
|
|
34
|
+
production_paths=args.production_path, include_tests=args.include_tests,
|
|
35
|
+
include_generated=args.include_generated, include_support=args.include_support)
|
|
36
|
+
duplicates = duplication.review(args.target, facts, run_id=args.run_id, audit_id=args.audit_id)
|
|
37
|
+
coverage = coverage_review.review(args.target, facts, coverage_json=args.coverage_json, collection_manifest=args.collection_manifest,
|
|
38
|
+
run_id=args.run_id, audit_id=args.audit_id)
|
|
39
|
+
bodies = decomposition.review(args.target, facts, run_id=args.run_id, audit_id=args.audit_id)
|
|
40
|
+
handlers = error_handling.review(args.target, facts, run_id=args.run_id, audit_id=args.audit_id)
|
|
41
|
+
context = error_handling_context.review(args.target, facts, handlers)
|
|
42
|
+
transfers = error_handling_transfers.review(args.target, facts, context)
|
|
43
|
+
operations = error_handling_operations.review(args.target, facts, transfers)
|
|
44
|
+
async_review = async_usage.review(args.target, facts, run_id=args.run_id, audit_id=args.audit_id)
|
|
45
|
+
async_context_review = async_context.review(args.target, facts, async_review)
|
|
46
|
+
async_adapter_review = async_adapters.review(args.target, facts, async_review)
|
|
47
|
+
async_helpers = async_helper_paths.review(args.target, facts, async_review)
|
|
48
|
+
async_score = async_scoring.review(args.target, facts, async_helpers)
|
|
49
|
+
print(json.dumps({"sourceFacts": facts, "duplication": duplicates, "coverageReview": coverage, "decomposition": bodies, "errorHandlingReview": handlers, "errorHandlingContext": context, "errorHandlingTransfers": transfers, "errorHandlingOperations": operations, "asyncUsageReview": async_review, "asyncContextReview": async_context_review, "asyncAdapterReview": async_adapter_review, "asyncHelperReview": async_helpers, "asyncScoring": async_score}, ensure_ascii=True))
|
|
50
|
+
return 0 if facts["status"] == duplicates["status"] == coverage["status"] == bodies["status"] == handlers["status"] == context["status"] == transfers["status"] == operations["status"] == async_review["status"] == async_context_review["status"] == async_adapter_review["status"] == async_helpers["status"] == async_score["status"] == "complete" else 2
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
raise SystemExit(main())
|