Копипаста в Python редко выглядит как копипаста
В Python-проектах дублирование кода почти никогда не выглядит как «один файл скопировали в другой». Чаще это повторяющиеся структуры, контрольные потоки и оркестрационная логика, которые со временем начинают незаметно расползаться по коду.
Формально всё выглядит по-разному: другие имена, другие константы, чуть иной порядок.
Но архитектурно — это одно и то же решение, просто размноженное.
Я хочу рассказать про CodeClone — инструмент, который я написал для поиска именно такого дублирования. Он не сравнивает строки и токены, а работает на уровне **нормализованного Python AST и графов управления потоком (CFG).
Почему текстовые clone-detectors не работают
Большинство инструментов ищут дублирование через строки, токены или поверхностное сравнение AST. Это отлично ловит copy-paste, но почти бесполезно, когда код:
переименован,
отформатирован по-другому,
слегка отрефакторен,
но реализует один и тот же сценарий.
В реальных проектах это часто:
одинаковые цепочки валидации,
повторяющиеся request/handler пайплайны,
скопированная оркестрационная логика,
похожие
try/exceptилиmatch/caseконструкции.
Идея: сравнивать структуру, а не текст
В CodeClone я пошёл другим путём:
Код парсится в Python AST.
AST нормализуется (имена, константы, аннотации убираются).
Для каждой функции строится Control Flow Graph.
Сравнивается структура CFG, а не исходный код.
Важно: CFG здесь — структурная абстракция, а не модель выполнения. Цель — найти повторяющиеся архитектурные решения, а не доказать семантическую эквивалентность.
Что именно ищется
Функциональные клоны (Type-2)
Функции и методы с одинаковой структурой управления:
if/else, циклы,try/except,with,match/case(Python 3.10+).Инструмент устойчив к переименованию, форматированию и type hints.
Блочные клоны (Type-3-lite)
Повторяющиеся блоки внутри функций: guard-clauses, проверки, orchestration-фрагменты. Используется скользящее окно по CFG-нормализованным инструкциям с жёсткими фильтрами, чтобы снизить шум.
Почему инструмент намеренно консервативный
Один из принципов проекта:
Лучше пропустить клон, чем показать ложный.
CodeClone не использует ML, вероятностные коэффициенты или эвристические скоринги.
Если клон найден — его можно объяснить и воспроизвести. Это важно при использовании в CI.
Baseline и CI
В живых проектах дубликаты уже есть, поэтому CodeClone работает в baseline-режиме:
codeclone . --update-baseline
Baseline коммитится в репозиторий, а в CI используется:
codeclone . --fail-on-new
Существующие дубликаты допускаются, новые — запрещены.
Это работает как архитектурный регресс-чек.
Про Python-версии
AST в Python не полностью стабилен между версиями интерпретатора. Поэтому версия Python фиксируется в baseline и должна совпадать при проверке. Это сделано ради детерминизма и честности результатов.
Итог
CodeClone не заменяет линтеры или type-checkers. Он полезен, если проект живёт долго, код растёт, и хочется вовремя замечать архитектурное дублирование, а не разбираться с его последствиями позже.
Исходники
GitHub: https://github.com/orenlab/codeclone
PyPI: https://pypi.org/project/codeclone/











