codeclone 1.4.3__tar.gz → 2.0.0b1__tar.gz
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.
- {codeclone-1.4.3 → codeclone-2.0.0b1}/LICENSE +3 -1
- codeclone-2.0.0b1/PKG-INFO +386 -0
- codeclone-2.0.0b1/README.md +344 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/__init__.py +2 -7
- codeclone-2.0.0b1/codeclone/_cli_args.py +382 -0
- codeclone-2.0.0b1/codeclone/_cli_baselines.py +389 -0
- codeclone-2.0.0b1/codeclone/_cli_config.py +266 -0
- codeclone-2.0.0b1/codeclone/_cli_gating.py +136 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/_cli_meta.py +56 -12
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/_cli_paths.py +9 -13
- codeclone-2.0.0b1/codeclone/_cli_reports.py +147 -0
- codeclone-2.0.0b1/codeclone/_cli_rich.py +128 -0
- codeclone-2.0.0b1/codeclone/_cli_runtime.py +189 -0
- codeclone-2.0.0b1/codeclone/_cli_summary.py +151 -0
- codeclone-2.0.0b1/codeclone/_coerce.py +50 -0
- codeclone-2.0.0b1/codeclone/_html_badges.py +172 -0
- codeclone-2.0.0b1/codeclone/_html_css.py +1103 -0
- codeclone-2.0.0b1/codeclone/_html_data_attrs.py +27 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/_html_escape.py +2 -7
- codeclone-2.0.0b1/codeclone/_html_filters.py +56 -0
- codeclone-2.0.0b1/codeclone/_html_js.py +598 -0
- codeclone-2.0.0b1/codeclone/_html_report/__init__.py +10 -0
- codeclone-2.0.0b1/codeclone/_html_report/_assemble.py +361 -0
- codeclone-2.0.0b1/codeclone/_html_report/_components.py +128 -0
- codeclone-2.0.0b1/codeclone/_html_report/_context.py +292 -0
- codeclone-2.0.0b1/codeclone/_html_report/_glossary.py +56 -0
- codeclone-2.0.0b1/codeclone/_html_report/_icons.py +76 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/__init__.py +2 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_clones.py +645 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_coupling.py +159 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_dead_code.py +112 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_dependencies.py +473 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_meta.py +383 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_overview.py +644 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_structural.py +29 -0
- codeclone-2.0.0b1/codeclone/_html_report/_sections/_suggestions.py +275 -0
- codeclone-2.0.0b1/codeclone/_html_report/_tables.py +121 -0
- codeclone-2.0.0b1/codeclone/_html_report/_tabs.py +57 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/_html_snippets.py +54 -65
- codeclone-2.0.0b1/codeclone/_schema_validation.py +41 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/baseline.py +127 -34
- codeclone-2.0.0b1/codeclone/blockhash.py +29 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/blocks.py +18 -38
- codeclone-2.0.0b1/codeclone/cache.py +2377 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/cfg.py +82 -51
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/cfg_model.py +6 -8
- codeclone-2.0.0b1/codeclone/cli.py +1265 -0
- codeclone-2.0.0b1/codeclone/contracts.py +66 -0
- codeclone-2.0.0b1/codeclone/domain/__init__.py +132 -0
- codeclone-2.0.0b1/codeclone/domain/findings.py +74 -0
- codeclone-2.0.0b1/codeclone/domain/quality.py +77 -0
- codeclone-2.0.0b1/codeclone/domain/source_scope.py +44 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/errors.py +2 -7
- codeclone-2.0.0b1/codeclone/extractor.py +1152 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/fingerprint.py +2 -7
- codeclone-2.0.0b1/codeclone/grouping.py +67 -0
- codeclone-2.0.0b1/codeclone/html_report.py +26 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/meta_markers.py +2 -7
- codeclone-2.0.0b1/codeclone/metrics/__init__.py +36 -0
- codeclone-2.0.0b1/codeclone/metrics/cohesion.py +88 -0
- codeclone-2.0.0b1/codeclone/metrics/complexity.py +93 -0
- codeclone-2.0.0b1/codeclone/metrics/coupling.py +93 -0
- codeclone-2.0.0b1/codeclone/metrics/dead_code.py +120 -0
- codeclone-2.0.0b1/codeclone/metrics/dependencies.py +198 -0
- codeclone-2.0.0b1/codeclone/metrics/health.py +125 -0
- codeclone-2.0.0b1/codeclone/metrics_baseline.py +788 -0
- codeclone-2.0.0b1/codeclone/models.py +298 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/normalize.py +18 -26
- codeclone-2.0.0b1/codeclone/paths.py +16 -0
- codeclone-2.0.0b1/codeclone/pipeline.py +1703 -0
- codeclone-2.0.0b1/codeclone/report/__init__.py +74 -0
- codeclone-2.0.0b1/codeclone/report/_formatting.py +12 -0
- codeclone-2.0.0b1/codeclone/report/_source_kinds.py +45 -0
- codeclone-2.0.0b1/codeclone/report/blocks.py +43 -0
- codeclone-2.0.0b1/codeclone/report/derived.py +227 -0
- codeclone-1.4.3/codeclone/_report_explain.py → codeclone-2.0.0b1/codeclone/report/explain.py +152 -72
- codeclone-1.4.3/codeclone/_report_explain_contract.py → codeclone-2.0.0b1/codeclone/report/explain_contract.py +10 -16
- codeclone-2.0.0b1/codeclone/report/findings.py +556 -0
- codeclone-2.0.0b1/codeclone/report/json_contract.py +2270 -0
- codeclone-2.0.0b1/codeclone/report/markdown.py +524 -0
- codeclone-2.0.0b1/codeclone/report/merge.py +78 -0
- codeclone-2.0.0b1/codeclone/report/overview.py +497 -0
- codeclone-2.0.0b1/codeclone/report/sarif.py +944 -0
- codeclone-2.0.0b1/codeclone/report/segments.py +209 -0
- codeclone-2.0.0b1/codeclone/report/serialize.py +717 -0
- codeclone-2.0.0b1/codeclone/report/suggestions.py +731 -0
- codeclone-2.0.0b1/codeclone/report/types.py +26 -0
- codeclone-2.0.0b1/codeclone/scanner.py +171 -0
- codeclone-2.0.0b1/codeclone/structural_findings.py +1047 -0
- codeclone-2.0.0b1/codeclone/suppressions.py +263 -0
- codeclone-2.0.0b1/codeclone/templates.py +38 -0
- codeclone-2.0.0b1/codeclone/ui_messages.py +569 -0
- codeclone-2.0.0b1/codeclone.egg-info/PKG-INFO +386 -0
- codeclone-2.0.0b1/codeclone.egg-info/SOURCES.txt +138 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone.egg-info/requires.txt +6 -3
- {codeclone-1.4.3 → codeclone-2.0.0b1}/pyproject.toml +29 -12
- codeclone-2.0.0b1/tests/test_architecture.py +149 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_baseline.py +298 -26
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_blockhash.py +2 -2
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cache.py +766 -35
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cfg.py +22 -41
- codeclone-2.0.0b1/tests/test_cli_config.py +266 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cli_inprocess.py +1370 -946
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cli_smoke.py +3 -3
- codeclone-2.0.0b1/tests/test_cli_unit.py +1030 -0
- codeclone-2.0.0b1/tests/test_coerce.py +53 -0
- codeclone-2.0.0b1/tests/test_core_branch_coverage.py +969 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_detector_golden.py +12 -12
- codeclone-2.0.0b1/tests/test_extractor.py +1614 -0
- codeclone-2.0.0b1/tests/test_golden_v2.py +337 -0
- codeclone-2.0.0b1/tests/test_html_report.py +2032 -0
- codeclone-2.0.0b1/tests/test_html_report_helpers.py +95 -0
- codeclone-2.0.0b1/tests/test_metrics_baseline.py +543 -0
- codeclone-2.0.0b1/tests/test_metrics_modules.py +601 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_normalize.py +130 -126
- codeclone-2.0.0b1/tests/test_pipeline_metrics.py +347 -0
- codeclone-2.0.0b1/tests/test_pipeline_process.py +456 -0
- codeclone-2.0.0b1/tests/test_python_syntax_compat.py +19 -0
- codeclone-2.0.0b1/tests/test_report.py +2940 -0
- codeclone-2.0.0b1/tests/test_report_branch_invariants.py +365 -0
- codeclone-2.0.0b1/tests/test_report_contract_coverage.py +1665 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_report_explain.py +113 -61
- codeclone-2.0.0b1/tests/test_report_source_kinds.py +29 -0
- codeclone-2.0.0b1/tests/test_report_suggestions.py +368 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_scanner_extra.py +101 -6
- codeclone-2.0.0b1/tests/test_structural_findings.py +824 -0
- codeclone-2.0.0b1/tests/test_suppressions.py +430 -0
- codeclone-1.4.3/PKG-INFO +0 -389
- codeclone-1.4.3/README.md +0 -348
- codeclone-1.4.3/codeclone/_cli_args.py +0 -180
- codeclone-1.4.3/codeclone/_cli_summary.py +0 -129
- codeclone-1.4.3/codeclone/_report_blocks.py +0 -94
- codeclone-1.4.3/codeclone/_report_grouping.py +0 -64
- codeclone-1.4.3/codeclone/_report_segments.py +0 -247
- codeclone-1.4.3/codeclone/_report_serialize.py +0 -418
- codeclone-1.4.3/codeclone/_report_types.py +0 -18
- codeclone-1.4.3/codeclone/blockhash.py +0 -21
- codeclone-1.4.3/codeclone/cache.py +0 -895
- codeclone-1.4.3/codeclone/cli.py +0 -906
- codeclone-1.4.3/codeclone/contracts.py +0 -64
- codeclone-1.4.3/codeclone/extractor.py +0 -292
- codeclone-1.4.3/codeclone/html_report.py +0 -789
- codeclone-1.4.3/codeclone/report.py +0 -61
- codeclone-1.4.3/codeclone/scanner.py +0 -115
- codeclone-1.4.3/codeclone/templates.py +0 -3103
- codeclone-1.4.3/codeclone/ui_messages.py +0 -323
- codeclone-1.4.3/codeclone.egg-info/PKG-INFO +0 -389
- codeclone-1.4.3/codeclone.egg-info/SOURCES.txt +0 -64
- codeclone-1.4.3/tests/test_cli_unit.py +0 -313
- codeclone-1.4.3/tests/test_extractor.py +0 -542
- codeclone-1.4.3/tests/test_html_report.py +0 -981
- codeclone-1.4.3/tests/test_report.py +0 -1302
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone/py.typed +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone.egg-info/dependency_links.txt +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone.egg-info/entry_points.txt +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/codeclone.egg-info/top_level.txt +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/setup.cfg +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_blocks.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cfg_model.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cli_main_guard.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_cli_main_guard_runpy.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_fingerprint.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_init.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_security.py +0 -0
- {codeclone-1.4.3 → codeclone-2.0.0b1}/tests/test_segments.py +0 -0
|
@@ -2,6 +2,8 @@ MIT License
|
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2024 Denis Rozhnovskiy
|
|
4
4
|
|
|
5
|
+
The name “CodeClone” refers to the official project distribution.
|
|
6
|
+
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
8
|
of this software and associated documentation files (the "Software"), to deal
|
|
7
9
|
in the Software without restriction, including without limitation the rights
|
|
@@ -18,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
20
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
21
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
22
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeclone
|
|
3
|
+
Version: 2.0.0b1
|
|
4
|
+
Summary: Structural code quality analysis for Python
|
|
5
|
+
Author-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
6
|
+
Maintainer-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/orenlab/codeclone
|
|
9
|
+
Project-URL: Repository, https://github.com/orenlab/codeclone
|
|
10
|
+
Project-URL: Issues, https://github.com/orenlab/codeclone/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/orenlab/codeclone/releases
|
|
12
|
+
Project-URL: Documentation, https://orenlab.github.io/codeclone/
|
|
13
|
+
Keywords: python,ast,cfg,code-clone,duplication,static-analysis,architecture,control-flow,ci
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
17
|
+
Classifier: Topic :: Software Development :: Testing
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: pygments>=2.19.2
|
|
31
|
+
Requires-Dist: rich>=14.3.2
|
|
32
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=9.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: build>=1.4.1; extra == "dev"
|
|
37
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy>=1.19.1; extra == "dev"
|
|
39
|
+
Requires-Dist: ruff>=0.15.7; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit>=4.5.1; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<img src="https://orenlab.github.io/codeclone/assets/codeclone-wordmark.svg" alt="CodeClone" height="60">
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<strong>Structural code quality analysis for Python</strong>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<a href="https://pypi.org/project/codeclone/"><img src="https://img.shields.io/pypi/v/codeclone.svg?style=flat-square" alt="PyPI"></a>
|
|
53
|
+
<a href="https://pypi.org/project/codeclone/"><img src="https://img.shields.io/pypi/dm/codeclone.svg?style=flat-square" alt="Downloads"></a>
|
|
54
|
+
<a href="https://github.com/orenlab/codeclone/actions/workflows/tests.yml"><img src="https://github.com/orenlab/codeclone/actions/workflows/tests.yml/badge.svg?branch=main&style=flat-square" alt="Tests"></a>
|
|
55
|
+
<a href="https://github.com/orenlab/codeclone/actions/workflows/benchmark.yml"><img src="https://github.com/orenlab/codeclone/actions/workflows/benchmark.yml/badge.svg?style=flat-square" alt="Benchmark"></a>
|
|
56
|
+
<a href="https://pypi.org/project/codeclone/"><img src="https://img.shields.io/pypi/pyversions/codeclone.svg?style=flat-square" alt="Python"></a>
|
|
57
|
+
<a href="https://github.com/orenlab/codeclone"><img src="https://img.shields.io/badge/codeclone-81%20(B)-green" alt="codeclone 81 (B)"></a>
|
|
58
|
+
<a href="https://github.com/orenlab/codeclone/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/codeclone.svg?style=flat-square" alt="License"></a>
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
CodeClone provides comprehensive structural code quality analysis for Python. It detects architectural
|
|
64
|
+
duplication via normalized AST and Control Flow Graphs, computes quality metrics, and enforces CI gates —
|
|
65
|
+
all with baseline-aware governance that separates **known** technical debt from **new** regressions.
|
|
66
|
+
|
|
67
|
+
Docs: [orenlab.github.io/codeclone](https://orenlab.github.io/codeclone/) ·
|
|
68
|
+
Live sample report:
|
|
69
|
+
[orenlab.github.io/codeclone/examples/report/](https://orenlab.github.io/codeclone/examples/report/)
|
|
70
|
+
|
|
71
|
+
## Features
|
|
72
|
+
|
|
73
|
+
- **Clone detection** — function (CFG fingerprint), block (statement windows), and segment (report-only) clones
|
|
74
|
+
- **Structural findings** — duplicated branch families, clone guard/exit divergence and clone-cohort drift (report-only)
|
|
75
|
+
- **Quality metrics** — cyclomatic complexity, coupling (CBO), cohesion (LCOM4), dependency cycles, dead code, health
|
|
76
|
+
score
|
|
77
|
+
- **Baseline governance** — known debt stays accepted; CI blocks only new clones and metric regressions
|
|
78
|
+
- **Reports** — interactive HTML, deterministic JSON/TXT plus Markdown and SARIF projections from one canonical report
|
|
79
|
+
- **CI-first** — deterministic output, stable ordering, exit code contract, pre-commit support
|
|
80
|
+
- **Fast*** — incremental caching, parallel processing, warm-run optimization, and reproducible benchmark coverage
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install codeclone # or: uv tool install codeclone
|
|
86
|
+
|
|
87
|
+
codeclone . # analyze current directory
|
|
88
|
+
codeclone . --html # generate HTML report
|
|
89
|
+
codeclone . --html --open-html-report # generate and open HTML report
|
|
90
|
+
codeclone . --json --md --sarif --text # generate machine-readable reports
|
|
91
|
+
codeclone . --html --json --timestamped-report-paths # keep timestamped report snapshots
|
|
92
|
+
codeclone . --ci # CI mode (--fail-on-new --no-color --quiet)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary>Run without install</summary>
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uvx codeclone@latest .
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
</details>
|
|
103
|
+
|
|
104
|
+
## CI Integration
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 1. Generate baseline (commit to repo)
|
|
108
|
+
codeclone . --update-baseline
|
|
109
|
+
|
|
110
|
+
# 2. Add to CI pipeline
|
|
111
|
+
codeclone . --ci
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `--ci` preset equals `--fail-on-new --no-color --quiet`.
|
|
115
|
+
When a trusted metrics baseline is loaded, CI mode also enables
|
|
116
|
+
`--fail-on-new-metrics`.
|
|
117
|
+
|
|
118
|
+
### Quality Gates
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Metrics thresholds
|
|
122
|
+
codeclone . --fail-complexity 20 --fail-coupling 10 --fail-cohesion 4 --fail-health 60
|
|
123
|
+
|
|
124
|
+
# Structural policies
|
|
125
|
+
codeclone . --fail-cycles --fail-dead-code
|
|
126
|
+
|
|
127
|
+
# Regression detection vs baseline
|
|
128
|
+
codeclone . --fail-on-new-metrics
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Pre-commit
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
repos:
|
|
135
|
+
- repo: local
|
|
136
|
+
hooks:
|
|
137
|
+
- id: codeclone
|
|
138
|
+
name: CodeClone
|
|
139
|
+
entry: codeclone
|
|
140
|
+
language: system
|
|
141
|
+
pass_filenames: false
|
|
142
|
+
args: [ ".", "--ci" ]
|
|
143
|
+
types: [ python ]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
CodeClone can load project-level configuration from `pyproject.toml`:
|
|
149
|
+
|
|
150
|
+
```toml
|
|
151
|
+
[tool.codeclone]
|
|
152
|
+
min_loc = 10
|
|
153
|
+
min_stmt = 6
|
|
154
|
+
baseline = "codeclone.baseline.json"
|
|
155
|
+
skip_metrics = false
|
|
156
|
+
quiet = false
|
|
157
|
+
html_out = ".cache/codeclone/report.html"
|
|
158
|
+
json_out = ".cache/codeclone/report.json"
|
|
159
|
+
md_out = ".cache/codeclone/report.md"
|
|
160
|
+
sarif_out = ".cache/codeclone/report.sarif"
|
|
161
|
+
text_out = ".cache/codeclone/report.txt"
|
|
162
|
+
block_min_loc = 20
|
|
163
|
+
block_min_stmt = 8
|
|
164
|
+
segment_min_loc = 20
|
|
165
|
+
segment_min_stmt = 10
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Precedence: CLI flags > `pyproject.toml` > built-in defaults.
|
|
169
|
+
|
|
170
|
+
## Baseline Workflow
|
|
171
|
+
|
|
172
|
+
Baselines capture the current duplication state. Once committed, they become the CI reference point.
|
|
173
|
+
|
|
174
|
+
- Clones are classified as **NEW** (not in baseline) or **KNOWN** (accepted debt)
|
|
175
|
+
- `--update-baseline` writes both clone and metrics snapshots
|
|
176
|
+
- Trust is verified via `generator`, `fingerprint_version`, and `payload_sha256`
|
|
177
|
+
- In `--ci` mode, an untrusted baseline is a contract error (exit 2)
|
|
178
|
+
|
|
179
|
+
Full contract: [Baseline contract](https://orenlab.github.io/codeclone/book/06-baseline/)
|
|
180
|
+
|
|
181
|
+
## Exit Codes
|
|
182
|
+
|
|
183
|
+
| Code | Meaning |
|
|
184
|
+
|------|-------------------------------------------------------------------------------|
|
|
185
|
+
| `0` | Success |
|
|
186
|
+
| `2` | Contract error — untrusted baseline, invalid config, unreadable sources in CI |
|
|
187
|
+
| `3` | Gating failure — new clones or metric threshold exceeded |
|
|
188
|
+
| `5` | Internal error |
|
|
189
|
+
|
|
190
|
+
Contract errors (`2`) take precedence over gating failures (`3`).
|
|
191
|
+
|
|
192
|
+
## Reports
|
|
193
|
+
|
|
194
|
+
| Format | Flag | Default path |
|
|
195
|
+
|----------|-----------|---------------------------------|
|
|
196
|
+
| HTML | `--html` | `.cache/codeclone/report.html` |
|
|
197
|
+
| JSON | `--json` | `.cache/codeclone/report.json` |
|
|
198
|
+
| Markdown | `--md` | `.cache/codeclone/report.md` |
|
|
199
|
+
| SARIF | `--sarif` | `.cache/codeclone/report.sarif` |
|
|
200
|
+
| Text | `--text` | `.cache/codeclone/report.txt` |
|
|
201
|
+
|
|
202
|
+
All report formats are rendered from one canonical JSON report document.
|
|
203
|
+
|
|
204
|
+
- `--open-html-report` opens the generated HTML report in the default browser and requires `--html`.
|
|
205
|
+
- `--timestamped-report-paths` appends a UTC timestamp to default report filenames for bare report flags such as
|
|
206
|
+
`--html` or `--json`. Explicit report paths are not rewritten.
|
|
207
|
+
|
|
208
|
+
The published docs site also includes a live example HTML/JSON/SARIF report
|
|
209
|
+
generated from the current `codeclone` repository during the docs build.
|
|
210
|
+
|
|
211
|
+
Structural findings include:
|
|
212
|
+
|
|
213
|
+
- `duplicated_branches`
|
|
214
|
+
- `clone_guard_exit_divergence`
|
|
215
|
+
- `clone_cohort_drift`
|
|
216
|
+
|
|
217
|
+
### Inline Suppressions
|
|
218
|
+
|
|
219
|
+
CodeClone keeps dead-code detection deterministic and static by default. When a symbol is intentionally
|
|
220
|
+
invoked through runtime dynamics (for example framework callbacks, plugin loading, or reflection), suppress
|
|
221
|
+
the known false positive explicitly at the declaration site:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
# codeclone: ignore[dead-code]
|
|
225
|
+
def handle_exception(exc: Exception) -> None:
|
|
226
|
+
...
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class Middleware: # codeclone: ignore[dead-code]
|
|
230
|
+
...
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Dynamic/runtime false positives are resolved via explicit inline suppressions, not via broad heuristics.
|
|
234
|
+
|
|
235
|
+
<details>
|
|
236
|
+
<summary>JSON report shape (v2.1)</summary>
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"report_schema_version": "2.1",
|
|
241
|
+
"meta": {
|
|
242
|
+
"codeclone_version": "2.0.0b1",
|
|
243
|
+
"project_name": "...",
|
|
244
|
+
"scan_root": ".",
|
|
245
|
+
"report_mode": "full",
|
|
246
|
+
"baseline": {
|
|
247
|
+
"...": "..."
|
|
248
|
+
},
|
|
249
|
+
"cache": {
|
|
250
|
+
"...": "..."
|
|
251
|
+
},
|
|
252
|
+
"metrics_baseline": {
|
|
253
|
+
"...": "..."
|
|
254
|
+
},
|
|
255
|
+
"runtime": {
|
|
256
|
+
"report_generated_at_utc": "..."
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
"inventory": {
|
|
260
|
+
"files": {
|
|
261
|
+
"...": "..."
|
|
262
|
+
},
|
|
263
|
+
"code": {
|
|
264
|
+
"...": "..."
|
|
265
|
+
},
|
|
266
|
+
"file_registry": {
|
|
267
|
+
"encoding": "relative_path",
|
|
268
|
+
"items": []
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"findings": {
|
|
272
|
+
"summary": {
|
|
273
|
+
"...": "..."
|
|
274
|
+
},
|
|
275
|
+
"groups": {
|
|
276
|
+
"clones": {
|
|
277
|
+
"functions": [],
|
|
278
|
+
"blocks": [],
|
|
279
|
+
"segments": []
|
|
280
|
+
},
|
|
281
|
+
"structural": {
|
|
282
|
+
"groups": []
|
|
283
|
+
},
|
|
284
|
+
"dead_code": {
|
|
285
|
+
"groups": []
|
|
286
|
+
},
|
|
287
|
+
"design": {
|
|
288
|
+
"groups": []
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
"metrics": {
|
|
293
|
+
"summary": {},
|
|
294
|
+
"families": {}
|
|
295
|
+
},
|
|
296
|
+
"derived": {
|
|
297
|
+
"suggestions": [],
|
|
298
|
+
"overview": {
|
|
299
|
+
"families": {},
|
|
300
|
+
"top_risks": [],
|
|
301
|
+
"source_scope_breakdown": {},
|
|
302
|
+
"health_snapshot": {}
|
|
303
|
+
},
|
|
304
|
+
"hotlists": {
|
|
305
|
+
"most_actionable_ids": [],
|
|
306
|
+
"highest_spread_ids": [],
|
|
307
|
+
"production_hotspot_ids": [],
|
|
308
|
+
"test_fixture_hotspot_ids": []
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
"integrity": {
|
|
312
|
+
"canonicalization": {
|
|
313
|
+
"version": "1",
|
|
314
|
+
"scope": "canonical_only"
|
|
315
|
+
},
|
|
316
|
+
"digest": {
|
|
317
|
+
"algorithm": "sha256",
|
|
318
|
+
"verified": true,
|
|
319
|
+
"value": "..."
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Canonical contract: [Report contract](https://orenlab.github.io/codeclone/book/08-report/) and
|
|
326
|
+
[Dead-code contract](https://orenlab.github.io/codeclone/book/16-dead-code-contract/)
|
|
327
|
+
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
## How It Works
|
|
331
|
+
|
|
332
|
+
1. **Parse** — Python source to AST
|
|
333
|
+
2. **Normalize** — canonical structure (robust to renaming, formatting)
|
|
334
|
+
3. **CFG** — per-function control flow graph
|
|
335
|
+
4. **Fingerprint** — stable hash computation
|
|
336
|
+
5. **Group** — function, block, and segment clone groups
|
|
337
|
+
6. **Metrics** — complexity, coupling, cohesion, dependencies, dead code, health
|
|
338
|
+
7. **Gate** — baseline comparison, threshold checks
|
|
339
|
+
|
|
340
|
+
Architecture: [Architecture narrative](https://orenlab.github.io/codeclone/architecture/) ·
|
|
341
|
+
CFG semantics: [CFG semantics](https://orenlab.github.io/codeclone/cfg/)
|
|
342
|
+
|
|
343
|
+
## Documentation
|
|
344
|
+
|
|
345
|
+
| Topic | Link |
|
|
346
|
+
|----------------------------|----------------------------------------------------------------------------------------------------|
|
|
347
|
+
| Contract book (start here) | [Contracts and guarantees](https://orenlab.github.io/codeclone/book/00-intro/) |
|
|
348
|
+
| Exit codes | [Exit codes and failure policy](https://orenlab.github.io/codeclone/book/03-contracts-exit-codes/) |
|
|
349
|
+
| Configuration | [Config and defaults](https://orenlab.github.io/codeclone/book/04-config-and-defaults/) |
|
|
350
|
+
| Baseline contract | [Baseline contract](https://orenlab.github.io/codeclone/book/06-baseline/) |
|
|
351
|
+
| Cache contract | [Cache contract](https://orenlab.github.io/codeclone/book/07-cache/) |
|
|
352
|
+
| Report contract | [Report contract](https://orenlab.github.io/codeclone/book/08-report/) |
|
|
353
|
+
| Metrics & quality gates | [Metrics and quality gates](https://orenlab.github.io/codeclone/book/15-metrics-and-quality-gates/) |
|
|
354
|
+
| Dead code | [Dead-code contract](https://orenlab.github.io/codeclone/book/16-dead-code-contract/) |
|
|
355
|
+
| Docker benchmark contract | [Benchmarking contract](https://orenlab.github.io/codeclone/book/18-benchmarking/) |
|
|
356
|
+
| Determinism | [Determinism policy](https://orenlab.github.io/codeclone/book/12-determinism/) |
|
|
357
|
+
|
|
358
|
+
## * Benchmarking
|
|
359
|
+
|
|
360
|
+
<details>
|
|
361
|
+
<summary>Reproducible Docker Benchmark</summary>
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
./benchmarks/run_docker_benchmark.sh
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The wrapper builds `benchmarks/Dockerfile`, runs isolated container benchmarks, and writes results to
|
|
368
|
+
`.cache/benchmarks/codeclone-benchmark.json`.
|
|
369
|
+
|
|
370
|
+
Use environment overrides to pin the benchmark envelope:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
CPUSET=0 CPUS=1.0 MEMORY=2g RUNS=16 WARMUPS=4 \
|
|
374
|
+
./benchmarks/run_docker_benchmark.sh
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Performance claims are backed by the reproducible benchmark workflow documented
|
|
378
|
+
in [Benchmarking contract](https://orenlab.github.io/codeclone/book/18-benchmarking/)
|
|
379
|
+
|
|
380
|
+
</details>
|
|
381
|
+
|
|
382
|
+
## Links
|
|
383
|
+
|
|
384
|
+
- **Issues:** <https://github.com/orenlab/codeclone/issues>
|
|
385
|
+
- **PyPI:** <https://pypi.org/project/codeclone/>
|
|
386
|
+
- **License:** MIT
|