codecaliper 0.1.0__py3-none-any.whl
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.
- codecaliper/__init__.py +64 -0
- codecaliper/_version.py +1 -0
- codecaliper/api.py +446 -0
- codecaliper/canonical.py +130 -0
- codecaliper/cli.py +231 -0
- codecaliper/errors.py +27 -0
- codecaliper/languages/__init__.py +44 -0
- codecaliper/languages/base.py +162 -0
- codecaliper/languages/java.py +192 -0
- codecaliper/languages/python.py +179 -0
- codecaliper/metrics/__init__.py +0 -0
- codecaliper/metrics/base.py +55 -0
- codecaliper/metrics/cognitive.py +111 -0
- codecaliper/metrics/cyclomatic.py +62 -0
- codecaliper/metrics/halstead.py +55 -0
- codecaliper/metrics/loc.py +62 -0
- codecaliper/metrics/mi.py +31 -0
- codecaliper/model.py +132 -0
- codecaliper/py.typed +0 -0
- codecaliper/readability/__init__.py +13 -0
- codecaliper/readability/base.py +11 -0
- codecaliper/readability/bw2010.py +156 -0
- codecaliper/readability/granularity.py +210 -0
- codecaliper/readability/retrain.py +67 -0
- codecaliper/spec/__init__.py +3 -0
- codecaliper/spec/registry.py +112 -0
- codecaliper/spec/rulings/bw.toml +156 -0
- codecaliper/spec/rulings/cognitive.toml +124 -0
- codecaliper/spec/rulings/core.toml +87 -0
- codecaliper/spec/rulings/cyclomatic.toml +172 -0
- codecaliper/spec/rulings/halstead.toml +20 -0
- codecaliper/spec/rulings/index.toml +54 -0
- codecaliper/spec/rulings/loc.toml +50 -0
- codecaliper/spec/rulings/mi.toml +15 -0
- codecaliper/spec/rulings/tokenization.toml +191 -0
- codecaliper/spec/validated_grammars.toml +6 -0
- codecaliper/syntax/__init__.py +0 -0
- codecaliper/syntax/_treesitter.py +179 -0
- codecaliper/syntax/grammars.py +45 -0
- codecaliper/syntax/tokens.py +165 -0
- codecaliper-0.1.0.dist-info/METADATA +180 -0
- codecaliper-0.1.0.dist-info/RECORD +46 -0
- codecaliper-0.1.0.dist-info/WHEEL +4 -0
- codecaliper-0.1.0.dist-info/entry_points.txt +2 -0
- codecaliper-0.1.0.dist-info/licenses/LICENSE +21 -0
- codecaliper-0.1.0.dist-info/licenses/NOTICE +29 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""The unified lexical token stream (TOK-* rulings).
|
|
2
|
+
|
|
3
|
+
Derived from the tree-sitter leaf walk (with atomic-token subtrees) and
|
|
4
|
+
classified by the adapter's token tables. This one stream feeds the BW
|
|
5
|
+
readability features, lexical Halstead, and the LOC coverage counts — one
|
|
6
|
+
tokenizer, three consumers, zero drift.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
from codecaliper.model import Diagnostic
|
|
17
|
+
from codecaliper.spec import iter_rulings, require
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
20
|
+
from codecaliper.languages.base import LanguageAdapter
|
|
21
|
+
from codecaliper.syntax._treesitter import Tree
|
|
22
|
+
|
|
23
|
+
R_ANON_WORD = require("TOK-ALL-0007") # anonymous word tokens are identifiers
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TokenKind(Enum):
|
|
27
|
+
IDENTIFIER = "identifier"
|
|
28
|
+
KEYWORD = "keyword"
|
|
29
|
+
NUMBER = "number"
|
|
30
|
+
STRING = "string"
|
|
31
|
+
COMMENT = "comment"
|
|
32
|
+
OPERATOR = "operator"
|
|
33
|
+
PUNCT = "punct"
|
|
34
|
+
OTHER = "other"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class LexicalToken:
|
|
39
|
+
kind: TokenKind
|
|
40
|
+
text: str
|
|
41
|
+
line: int # 1-based physical line of the token start (TOK-ALL-0005)
|
|
42
|
+
end_line: int # 1-based line of the token end (span coverage for sloc/cloc)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_PUNCT = frozenset({"(", ")", "[", "]", "{", "}", ",", ".", ";", ":"})
|
|
46
|
+
|
|
47
|
+
# Word-shaped anonymous tokens (TOK-ALL-0007): identifier-shaped, with internal
|
|
48
|
+
# hyphens allowed for Java's hyphenated contextual keyword (`non-sealed`).
|
|
49
|
+
_WORD_RE = re.compile(r"[^\W\d]\w*(?:-\w+)*", re.UNICODE)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def normalize(source: str | bytes) -> tuple[str, tuple[Diagnostic, ...]]:
|
|
53
|
+
"""Input normalization per TOK-ALL-0001..0003."""
|
|
54
|
+
diags: list[Diagnostic] = []
|
|
55
|
+
if isinstance(source, bytes):
|
|
56
|
+
# the diagnostic fires only when replacement actually happened — valid
|
|
57
|
+
# UTF-8 that legitimately contains U+FFFD is not flagged
|
|
58
|
+
try:
|
|
59
|
+
text = source.decode("utf-8")
|
|
60
|
+
except UnicodeDecodeError:
|
|
61
|
+
text = source.decode("utf-8", errors="replace")
|
|
62
|
+
diags.append(
|
|
63
|
+
Diagnostic("warning", "encoding-replaced",
|
|
64
|
+
"undecodable bytes replaced with U+FFFD", ruling="TOK-ALL-0001")
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
text = source
|
|
68
|
+
if text.startswith(""):
|
|
69
|
+
text = text[1:]
|
|
70
|
+
diags.append(
|
|
71
|
+
Diagnostic("info", "bom-stripped", "leading UTF-8 BOM stripped", ruling="TOK-ALL-0002")
|
|
72
|
+
)
|
|
73
|
+
text = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
74
|
+
return text, tuple(diags)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def source_lines(text: str) -> list[str]:
|
|
78
|
+
"""Physical lines; a trailing final newline does not create an extra empty
|
|
79
|
+
line (LOC-ALL-0001, and the BW reference's convention)."""
|
|
80
|
+
lines = text.split("\n")
|
|
81
|
+
if lines and lines[-1] == "":
|
|
82
|
+
lines = lines[:-1]
|
|
83
|
+
return lines
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def lang_tokenization_rulings(adapter: LanguageAdapter) -> tuple[str, ...]:
|
|
87
|
+
"""The language's STATIC tokenization rulings: the atomic-string rules
|
|
88
|
+
(TOK-PY-0002 / TOK-JAVA-0001) that unconditionally govern how the stream is
|
|
89
|
+
cut, so every token-derived value cites them. Construct-specific
|
|
90
|
+
classification rulings (``adapter.conditional_token_rulings``, e.g.
|
|
91
|
+
TOK-JAVA-0002) are excluded — they are cited per-occurrence, like the
|
|
92
|
+
language-neutral TOK-ALL-0007, not statically."""
|
|
93
|
+
conditional = set(adapter.conditional_token_rulings.values())
|
|
94
|
+
return tuple(sorted(
|
|
95
|
+
r.id
|
|
96
|
+
for r in iter_rulings(metric="tokenization", language=adapter.name)
|
|
97
|
+
if r.language == adapter.name and r.status == "active"
|
|
98
|
+
and r.id not in conditional
|
|
99
|
+
))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def lex(
|
|
103
|
+
tree: Tree,
|
|
104
|
+
source_bytes: bytes,
|
|
105
|
+
adapter: LanguageAdapter,
|
|
106
|
+
*,
|
|
107
|
+
include_error_tokens: bool = False,
|
|
108
|
+
cond_lines: dict[str, set[int]] | None = None,
|
|
109
|
+
) -> list[LexicalToken]:
|
|
110
|
+
"""Classify the leaf stream via the adapter's token tables.
|
|
111
|
+
|
|
112
|
+
``include_error_tokens=True`` descends into ERROR subtrees (BW-ALL-0007:
|
|
113
|
+
the BW construct is lexical); the default stream is error-opaque
|
|
114
|
+
(CORE-ALL-0002) and feeds every metric. ``cond_lines``, when given, records
|
|
115
|
+
for each conditional tokenization ruling the physical lines where it
|
|
116
|
+
engaged (TOK-ALL-0007 anonymous words; the adapter's
|
|
117
|
+
``conditional_token_rulings``, e.g. TOK-JAVA-0002 `_`), so provenance can
|
|
118
|
+
cite it per-occurrence and per-unit rather than file-globally.
|
|
119
|
+
"""
|
|
120
|
+
from codecaliper.syntax._treesitter import leaves, leaves_lexical, node_text
|
|
121
|
+
|
|
122
|
+
iterator = leaves_lexical if include_error_tokens else leaves
|
|
123
|
+
out: list[LexicalToken] = []
|
|
124
|
+
for node in iterator(tree, adapter.atomic_types):
|
|
125
|
+
ttype = node.type
|
|
126
|
+
if ttype in ("ERROR",) or node.is_missing:
|
|
127
|
+
continue
|
|
128
|
+
text = node_text(node, source_bytes)
|
|
129
|
+
if not text:
|
|
130
|
+
continue
|
|
131
|
+
line = node.start_point[0] + 1
|
|
132
|
+
end_line = node.end_point[0] + 1
|
|
133
|
+
if ttype in adapter.comment_types:
|
|
134
|
+
kind = TokenKind.COMMENT
|
|
135
|
+
elif ttype in adapter.string_types:
|
|
136
|
+
kind = TokenKind.STRING
|
|
137
|
+
elif ttype in adapter.number_types:
|
|
138
|
+
kind = TokenKind.NUMBER
|
|
139
|
+
elif ttype in adapter.keyword_leaf_types or text in adapter.keywords:
|
|
140
|
+
kind = TokenKind.KEYWORD
|
|
141
|
+
elif ttype in adapter.identifier_types:
|
|
142
|
+
kind = TokenKind.IDENTIFIER
|
|
143
|
+
elif ttype in adapter.operator_leaf_types:
|
|
144
|
+
# named leaves that are operator tokens (Python `...`, TOK-PY-0003),
|
|
145
|
+
# so they are not swallowed by the is_named -> OTHER fallthrough
|
|
146
|
+
kind = TokenKind.OPERATOR
|
|
147
|
+
elif not node.is_named and text in adapter.soft_keywords:
|
|
148
|
+
kind = TokenKind.IDENTIFIER # soft keywords are identifiers (BW-PY-0001)
|
|
149
|
+
elif not node.is_named and _WORD_RE.fullmatch(text) is not None:
|
|
150
|
+
# TOK-ALL-0007: anonymous word tokens outside the keyword table are
|
|
151
|
+
# identifiers (Python `__future__`, Java contextual keywords) —
|
|
152
|
+
# matching the reference tokenizers, which yield NAME for them.
|
|
153
|
+
kind = TokenKind.IDENTIFIER
|
|
154
|
+
if cond_lines is not None:
|
|
155
|
+
cond_lines.setdefault(R_ANON_WORD, set()).add(line)
|
|
156
|
+
elif node.is_named:
|
|
157
|
+
kind = TokenKind.OTHER
|
|
158
|
+
elif text in _PUNCT:
|
|
159
|
+
kind = TokenKind.PUNCT
|
|
160
|
+
else:
|
|
161
|
+
kind = TokenKind.OPERATOR
|
|
162
|
+
if cond_lines is not None and ttype in adapter.conditional_token_rulings:
|
|
163
|
+
cond_lines.setdefault(adapter.conditional_token_rulings[ttype], set()).add(line)
|
|
164
|
+
out.append(LexicalToken(kind, text, line, end_line))
|
|
165
|
+
return out
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codecaliper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A cross-language code readability + complexity measurement instrument with a versioned metric-to-syntax specification
|
|
5
|
+
Project-URL: Repository, https://github.com/KurathSec/codecaliper
|
|
6
|
+
Project-URL: Documentation, https://kurathsec.github.io/codecaliper/
|
|
7
|
+
Project-URL: Issues, https://github.com/KurathSec/codecaliper/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/KurathSec/codecaliper/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Yuxiang Ji <kurathandrew@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: NOTICE
|
|
13
|
+
Keywords: buse-weimer,code metrics,cognitive complexity,cyclomatic complexity,halstead,maintainability index,measurement instrument,mining software repositories,readability,tree-sitter
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
28
|
+
Requires-Dist: tree-sitter-java<0.24,>=0.23.5
|
|
29
|
+
Requires-Dist: tree-sitter-python<0.26,>=0.25
|
|
30
|
+
Requires-Dist: tree-sitter<0.27,>=0.25
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: mkdocs-material>=9.6; extra == 'docs'
|
|
37
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
38
|
+
Requires-Dist: mkdocstrings[python]>=1.0; extra == 'docs'
|
|
39
|
+
Provides-Extra: oracles
|
|
40
|
+
Requires-Dist: cognitive-complexity>=1.3; extra == 'oracles'
|
|
41
|
+
Requires-Dist: lizard>=1.17; extra == 'oracles'
|
|
42
|
+
Requires-Dist: radon>=6; extra == 'oracles'
|
|
43
|
+
Provides-Extra: retrain
|
|
44
|
+
Requires-Dist: scikit-learn>=1.4; extra == 'retrain'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# codecaliper
|
|
48
|
+
|
|
49
|
+
[](https://github.com/KurathSec/codecaliper/actions/workflows/ci.yml)
|
|
50
|
+
[](https://kurathsec.github.io/codecaliper/)
|
|
51
|
+
|
|
52
|
+
> A cross-language (Python + Java, more staged) **code-readability + complexity
|
|
53
|
+
> measurement instrument** — not another metrics scoreboard.
|
|
54
|
+
> Documentation: <https://kurathsec.github.io/codecaliper/>
|
|
55
|
+
|
|
56
|
+
codecaliper is built as an *instrument*: every number it emits is **traceable**
|
|
57
|
+
— to a versioned metric-to-syntax specification, to the exact machine-readable
|
|
58
|
+
rulings that fired, to the exact tree-sitter grammar that parsed the source —
|
|
59
|
+
and **reproducible** — clock-free, hash-seed-free, order-stable. Where a
|
|
60
|
+
scoreboard says "CC = 7", codecaliper says "CC = 7 *under spec 1.1.0, ruling
|
|
61
|
+
CC-PY-0003, tree-sitter-python 0.25.0*".
|
|
62
|
+
|
|
63
|
+
## What it measures
|
|
64
|
+
|
|
65
|
+
- **Readability (the core):** the first open, tested, cross-language
|
|
66
|
+
implementation of the **Buse–Weimer (2010) 25-feature readability set**,
|
|
67
|
+
computed on tree-sitter (comments and positions preserved). Output is always
|
|
68
|
+
the **raw feature vector** — there is deliberately no "readability score" —
|
|
69
|
+
plus a retraining scaffold. Scalabrino/Dorn feature sets are staged for 1.x.
|
|
70
|
+
- **Complexity (a convenience, not the selling point):** cyclomatic,
|
|
71
|
+
**dual-mode cognitive complexity** (whitepaper-faithful by default,
|
|
72
|
+
`--sonar-compat` opt-in, every divergence between the modes is an enumerable
|
|
73
|
+
spec ruling), Halstead (a declared lexical approximation), maintainability
|
|
74
|
+
index (typed as *derived from* CC — never an independent signal), and the
|
|
75
|
+
LOC family.
|
|
76
|
+
|
|
77
|
+
## Why an instrument
|
|
78
|
+
|
|
79
|
+
Different tools disagree on the same file because each is a different
|
|
80
|
+
*operationalization* of the same construct (radon vs lizard cyclomatic; the
|
|
81
|
+
per-language cognitive-complexity ports; every hand-rolled Buse–Weimer
|
|
82
|
+
extractor in the literature). codecaliper's answer:
|
|
83
|
+
|
|
84
|
+
1. a **versioned mapping specification** — machine-readable rulings with
|
|
85
|
+
immutable IDs (`CC-PY-0003`), shipped as package data, stamped into every
|
|
86
|
+
result;
|
|
87
|
+
2. a **hand-computed consistency corpus** — every active ruling is exercised by
|
|
88
|
+
a case with human-verified expected values;
|
|
89
|
+
3. **differential tests** against radon / lizard / cognitive_complexity with
|
|
90
|
+
a **published known-divergence list** — every disagreement is classified
|
|
91
|
+
against a ruling, in both directions (an unclassified divergence fails CI,
|
|
92
|
+
and so does a stale entry); PMD and rust-code-analysis are staged as
|
|
93
|
+
additional oracles;
|
|
94
|
+
4. a **faithfulness reproduction** of the original Buse–Weimer study (100 Java
|
|
95
|
+
snippets, 120 annotators) using this extractor and a retrained model.
|
|
96
|
+
|
|
97
|
+
## Honesty boundaries
|
|
98
|
+
|
|
99
|
+
codecaliper guarantees **procedural consistency** (identical rules over
|
|
100
|
+
isomorphic syntax across languages), *not* cross-language numerical
|
|
101
|
+
comparability. MI contains CC. Halstead absolute values are
|
|
102
|
+
implementation-defined. BW output is a feature set, not a score, calibrated on
|
|
103
|
+
4–11-line snippets — function/file vectors are labelled extrapolation. Metrics
|
|
104
|
+
operate on pre-preprocessing source text. Every one of these is enforced in the
|
|
105
|
+
result types, not just stated here.
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install codecaliper # (not yet on PyPI — install from source for now)
|
|
111
|
+
pip install -e . # from a checkout
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Use
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
codecaliper myfile.py --json
|
|
118
|
+
codecaliper src/**/*.java --csv -o metrics.csv
|
|
119
|
+
codecaliper myfile.py --sonar-compat --explain
|
|
120
|
+
codecaliper spec show CC-PY-0003 # read the ruling behind a number
|
|
121
|
+
codecaliper env # the calibration plate for bug reports/papers
|
|
122
|
+
codecaliper cite # methods-section template
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from codecaliper import measure
|
|
127
|
+
|
|
128
|
+
report = measure(source_code, language="python")
|
|
129
|
+
cc = next(m for m in report.file_metrics if m.metric == "cyclomatic")
|
|
130
|
+
print(cc.value, cc.rulings, report.provenance.spec_version)
|
|
131
|
+
|
|
132
|
+
vec = report.readability[0] # raw BW 25-feature vector — never a score
|
|
133
|
+
print(dict(zip(vec.names, vec.values)), vec.extrapolated)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Status
|
|
137
|
+
|
|
138
|
+
Spec **v1.1.0** (package 0.1.0.dev0 — the spec and package are versioned
|
|
139
|
+
independently): Python + Java wired end-to-end; every active ruling is
|
|
140
|
+
exercised by a hand-computed corpus case; mypy --strict and the differential
|
|
141
|
+
oracle lane (radon/lizard/cognitive_complexity, classified divergence list)
|
|
142
|
+
are hard CI gates. The BW faithfulness reproduction ran on the original
|
|
143
|
+
100-snippet dataset: 10-fold logistic accuracy 0.820 (bootstrap 95% CI
|
|
144
|
+
[0.770, 0.870], overlapping the paper's ~0.80), AUC 0.828, Fig. 9 sign
|
|
145
|
+
agreement 21/24 — after a pre-registered arbitration experiment resolved two
|
|
146
|
+
feature-definition ambiguities (indentation tab width; lexical fallback on
|
|
147
|
+
parse errors) as versioned ruling supersessions (see
|
|
148
|
+
`validation/bw_faithfulness/derived/`). The
|
|
149
|
+
[docs site](https://kurathsec.github.io/codecaliper/) and the tag-triggered
|
|
150
|
+
release pipeline (PyPI trusted publishing + Zenodo archival, `RELEASING.md`)
|
|
151
|
+
are in place; next: the first tagged release (v0.1.0 + DOI) and the MSR
|
|
152
|
+
Data & Tool Showcase paper (`ARCHITECTURE.md` §16, W8).
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install -e ".[dev]" -c constraints/ci.txt
|
|
158
|
+
pytest
|
|
159
|
+
ruff check src tests tools
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
See `ARCHITECTURE.md` for the full design (data model, spec mechanism,
|
|
163
|
+
validation architecture, grammar-evolution policy) and `CONTRIBUTING.md` for
|
|
164
|
+
how rulings, corpus cases, and adapters are added.
|
|
165
|
+
|
|
166
|
+
## Citing
|
|
167
|
+
|
|
168
|
+
Citation metadata lives in
|
|
169
|
+
[`CITATION.cff`](https://github.com/KurathSec/codecaliper/blob/main/CITATION.cff)
|
|
170
|
+
(GitHub's "Cite this repository" box reads it; Zenodo archives releases from
|
|
171
|
+
it).
|
|
172
|
+
`codecaliper cite` prints a methods-section template stating the package,
|
|
173
|
+
spec, and grammar versions your numbers were produced under — report all
|
|
174
|
+
three.
|
|
175
|
+
|
|
176
|
+
## License & provenance
|
|
177
|
+
|
|
178
|
+
MIT. Some measurement primitives descend from
|
|
179
|
+
[Spaghetti Architect](https://doi.org/10.5281/zenodo.21033174) (same author) —
|
|
180
|
+
see `NOTICE`.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
codecaliper/__init__.py,sha256=wT2JL3MXQEbZEeComy9hNlTfc2I85ikyrp7iE0FCBhw,1690
|
|
2
|
+
codecaliper/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
3
|
+
codecaliper/api.py,sha256=6M4eb4Tm7QRlQ-qnrC3euBqtVO3HM1DXoSowsGo5fLQ,18384
|
|
4
|
+
codecaliper/canonical.py,sha256=-IzyNckvvl_a91T3LXqtaftBh-KjS1-FfqWBYzPQjXo,4766
|
|
5
|
+
codecaliper/cli.py,sha256=ru4PiJ-7FKXrWsHz3KYkPrFJEZ3qKeTMLdT4Frw5y_8,8986
|
|
6
|
+
codecaliper/errors.py,sha256=TAg0RWSlmUYBB7oJZ0jIrVQn9xJVBhf3Wg7bMj7iICQ,811
|
|
7
|
+
codecaliper/model.py,sha256=TadZRdwNhQL_HTu80STGxEkvuGnCwgqXC_07vxBhtx8,4392
|
|
8
|
+
codecaliper/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
codecaliper/languages/__init__.py,sha256=CdgZmsjGtyEDbEEc1sKGFNd9U9KBW7YKTxXz9XKCUDQ,1387
|
|
10
|
+
codecaliper/languages/base.py,sha256=vbYEonzJFEzTiNEmRYYf19hIAKWX4gqcYhY8Sh6Quio,6087
|
|
11
|
+
codecaliper/languages/java.py,sha256=yb53hFc0lbwlgN2_gra8wYesTyWmPerviFG5i3w9VpQ,9853
|
|
12
|
+
codecaliper/languages/python.py,sha256=1U3HmIkrU9Z5nwHJCVimP987AYRbhxYgxXWxRSdil5E,8758
|
|
13
|
+
codecaliper/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
codecaliper/metrics/base.py,sha256=Ttsz4mNvjUFsswTf4WjQ11PG9iOhrmqC5v-sxfIvdI4,2297
|
|
15
|
+
codecaliper/metrics/cognitive.py,sha256=FY2yU9GlEpT78SiU9KgxZ2IWjJg66itQQVOugKYTdLU,4880
|
|
16
|
+
codecaliper/metrics/cyclomatic.py,sha256=161PYwmleKReNG9f1kYNHPOTR8aBQ0IlCS9hr1EUmuU,2086
|
|
17
|
+
codecaliper/metrics/halstead.py,sha256=4GHBI36ijMlqnPuKI2ZuzhDRvTfRFhLme9764wj_0xE,2045
|
|
18
|
+
codecaliper/metrics/loc.py,sha256=yEMfPgjuopWPIOEQASnc6SwKANIARYli4GmCTDiQERA,1986
|
|
19
|
+
codecaliper/metrics/mi.py,sha256=Vst5XEwNn_e7LPkONpm-E5HT3l-DuBv1SUVwRU3GYBI,950
|
|
20
|
+
codecaliper/readability/__init__.py,sha256=q2tRja8vtzEiuhJrQT6lzqgNoCpBwSKfU9GhNJ8N29A,292
|
|
21
|
+
codecaliper/readability/base.py,sha256=lZHQfXxtHmLviTaDoXBrYL3bR9VVU7X63_HfjH83H-g,399
|
|
22
|
+
codecaliper/readability/bw2010.py,sha256=JpEn8jNs9lysAS4BfcVa-3BQ7OFLDQJR2OGoc_8jvbY,6188
|
|
23
|
+
codecaliper/readability/granularity.py,sha256=amjGIzsWrWK8JPoKc55jnDB5fmKo1McCBFqrDw9r4hw,8097
|
|
24
|
+
codecaliper/readability/retrain.py,sha256=w1WM5kw1W5uuw6jcWhLWdR-yM0E5HHdKt4vZ0CDFya8,2517
|
|
25
|
+
codecaliper/spec/__init__.py,sha256=fWqCw2gEnyN9UUA4mqobtqUlsyZvgZ0oA7Pop9f5T2k,165
|
|
26
|
+
codecaliper/spec/registry.py,sha256=7l1AAoWfDfOnup7ZKx5X41r-wrIsRmuPGNFeaaTjIjU,3452
|
|
27
|
+
codecaliper/spec/validated_grammars.toml,sha256=0EqnAsjw9w-FDaVUwKdEc3uVL11bRP9XslnJSpBh5vo,269
|
|
28
|
+
codecaliper/spec/rulings/bw.toml,sha256=Q5mDKF2SiuGPYjzTji8jQPnNNwunlqmTrwWoF56g51E,5921
|
|
29
|
+
codecaliper/spec/rulings/cognitive.toml,sha256=cozChgXyWyVCZXaryd8papMskC_6NkHZtjXGGngGzss,4978
|
|
30
|
+
codecaliper/spec/rulings/core.toml,sha256=ZCOG-3k0Lue1rR5Up7YUArdnsZ80KecHUPccwzplTZM,3492
|
|
31
|
+
codecaliper/spec/rulings/cyclomatic.toml,sha256=Sb-zwMNw9IbhV_0rD8o7xaXLNC6zkFOBhJUI50mDcu8,5630
|
|
32
|
+
codecaliper/spec/rulings/halstead.toml,sha256=loFpLbmvn18fJCsS1CbOP2STJdNR6FGk3AKrL4GWgNc,993
|
|
33
|
+
codecaliper/spec/rulings/index.toml,sha256=67A_CUJA7S8De9AsEOoDzwU4_v29iE3UeS9FD7XgJWA,3028
|
|
34
|
+
codecaliper/spec/rulings/loc.toml,sha256=uCLriHAfx6g1w-7p7wU5gZVMfT0Th10TuL6mGmeJ08E,1630
|
|
35
|
+
codecaliper/spec/rulings/mi.toml,sha256=k-29C1KY0K-GZusrAmNHWW4hMNRSrVY-Js6tmTPmptU,565
|
|
36
|
+
codecaliper/spec/rulings/tokenization.toml,sha256=TLMd4jcyWLrK_2S4CxjMHq43XgWsSL4lw-6Y5mHFojw,7716
|
|
37
|
+
codecaliper/syntax/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
codecaliper/syntax/_treesitter.py,sha256=Yy-c5sMjgxUOYwqg-99FIW3CqPzNw_8Q2-_Og2kKqTQ,6613
|
|
39
|
+
codecaliper/syntax/grammars.py,sha256=SQ0XZwElLRZrFPVFLkDQgoD5kN49HakkR3VnBy5cFnM,1405
|
|
40
|
+
codecaliper/syntax/tokens.py,sha256=aYMF6MWAnYkkHL4rx-BJzgOqLNru44rUhaxM831RkxQ,6601
|
|
41
|
+
codecaliper-0.1.0.dist-info/METADATA,sha256=e7olri0fdImNG1l1oPLYI6PnhAIL-kE44ZuVafHi5gQ,8261
|
|
42
|
+
codecaliper-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
43
|
+
codecaliper-0.1.0.dist-info/entry_points.txt,sha256=c5CNZiaFhMsPWo4gyhL7yj_iEtphBUzKzM4yn6YuowQ,53
|
|
44
|
+
codecaliper-0.1.0.dist-info/licenses/LICENSE,sha256=JgGBN2SG1F4x-r8tKpJ3u8rU4eST_VHZu_GpESSjKvI,1067
|
|
45
|
+
codecaliper-0.1.0.dist-info/licenses/NOTICE,sha256=szeLMKAvHrFkogcNPh0kFPulUOKrmL5wHtmtpDSC0Lg,1329
|
|
46
|
+
codecaliper-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuxiang Ji
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
codecaliper
|
|
2
|
+
Copyright (c) 2026 Yuxiang Ji
|
|
3
|
+
Licensed under the MIT License (see LICENSE).
|
|
4
|
+
|
|
5
|
+
Provenance of measurement primitives
|
|
6
|
+
------------------------------------
|
|
7
|
+
|
|
8
|
+
Several measurement primitives in this repository descend from Spaghetti
|
|
9
|
+
Architect (MIT, same author) and are reused here with attribution:
|
|
10
|
+
|
|
11
|
+
- The Buse-Weimer (2010) 25-feature reference extractor
|
|
12
|
+
(tests/_reference/bw_stdlib.py, and the canonical feature order in
|
|
13
|
+
src/codecaliper/readability/bw2010.py) descends from bench/anchor.py.
|
|
14
|
+
- The Python-AST metric lane used as a test-only differential oracle
|
|
15
|
+
(tests/_reference/py_ast_lane.py) descends from eval/metrics.py.
|
|
16
|
+
- The "optional oracle probe with honest SKIP" differential-testing pattern
|
|
17
|
+
descends from bench/anchor.py.
|
|
18
|
+
- The pure-stdlib statistics helpers (spearman, mean, bootstrap CI) used by the
|
|
19
|
+
BW faithfulness pipeline descend from bench/grade.py.
|
|
20
|
+
|
|
21
|
+
Please cite Spaghetti Architect via its concept DOI:
|
|
22
|
+
|
|
23
|
+
Ji, Yuxiang. Spaghetti Architect: A Contamination-Resistant,
|
|
24
|
+
By-Construction-Labelled, Multi-Language Code Dataset Generator.
|
|
25
|
+
https://doi.org/10.5281/zenodo.21033174
|
|
26
|
+
|
|
27
|
+
codecaliper is a general-purpose measurement instrument for arbitrary user
|
|
28
|
+
source code; it does not ingest Spaghetti Architect's IR, dataset, or
|
|
29
|
+
anti-pattern machinery (anti-salami boundary, see ARCHITECTURE.md §12).
|