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.
Files changed (46) hide show
  1. codecaliper/__init__.py +64 -0
  2. codecaliper/_version.py +1 -0
  3. codecaliper/api.py +446 -0
  4. codecaliper/canonical.py +130 -0
  5. codecaliper/cli.py +231 -0
  6. codecaliper/errors.py +27 -0
  7. codecaliper/languages/__init__.py +44 -0
  8. codecaliper/languages/base.py +162 -0
  9. codecaliper/languages/java.py +192 -0
  10. codecaliper/languages/python.py +179 -0
  11. codecaliper/metrics/__init__.py +0 -0
  12. codecaliper/metrics/base.py +55 -0
  13. codecaliper/metrics/cognitive.py +111 -0
  14. codecaliper/metrics/cyclomatic.py +62 -0
  15. codecaliper/metrics/halstead.py +55 -0
  16. codecaliper/metrics/loc.py +62 -0
  17. codecaliper/metrics/mi.py +31 -0
  18. codecaliper/model.py +132 -0
  19. codecaliper/py.typed +0 -0
  20. codecaliper/readability/__init__.py +13 -0
  21. codecaliper/readability/base.py +11 -0
  22. codecaliper/readability/bw2010.py +156 -0
  23. codecaliper/readability/granularity.py +210 -0
  24. codecaliper/readability/retrain.py +67 -0
  25. codecaliper/spec/__init__.py +3 -0
  26. codecaliper/spec/registry.py +112 -0
  27. codecaliper/spec/rulings/bw.toml +156 -0
  28. codecaliper/spec/rulings/cognitive.toml +124 -0
  29. codecaliper/spec/rulings/core.toml +87 -0
  30. codecaliper/spec/rulings/cyclomatic.toml +172 -0
  31. codecaliper/spec/rulings/halstead.toml +20 -0
  32. codecaliper/spec/rulings/index.toml +54 -0
  33. codecaliper/spec/rulings/loc.toml +50 -0
  34. codecaliper/spec/rulings/mi.toml +15 -0
  35. codecaliper/spec/rulings/tokenization.toml +191 -0
  36. codecaliper/spec/validated_grammars.toml +6 -0
  37. codecaliper/syntax/__init__.py +0 -0
  38. codecaliper/syntax/_treesitter.py +179 -0
  39. codecaliper/syntax/grammars.py +45 -0
  40. codecaliper/syntax/tokens.py +165 -0
  41. codecaliper-0.1.0.dist-info/METADATA +180 -0
  42. codecaliper-0.1.0.dist-info/RECORD +46 -0
  43. codecaliper-0.1.0.dist-info/WHEEL +4 -0
  44. codecaliper-0.1.0.dist-info/entry_points.txt +2 -0
  45. codecaliper-0.1.0.dist-info/licenses/LICENSE +21 -0
  46. 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
+ [![ci](https://github.com/KurathSec/codecaliper/actions/workflows/ci.yml/badge.svg)](https://github.com/KurathSec/codecaliper/actions/workflows/ci.yml)
50
+ [![docs](https://github.com/KurathSec/codecaliper/actions/workflows/docs.yml/badge.svg)](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,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ codecaliper = codecaliper.cli:main
@@ -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).