Metadata-Version: 2.4
Name: complexity-meter
Version: 0.0.3
Summary: Script to track python project complexity
Author-email: Oleg Strizhechenko <oleg.strizhechenko@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://codeberg.org/strizhechenko/complexity-meter
Keywords: python,complexity,analyzer,statistics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: toml; python_version < "3.11"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Provides-Extra: dev
Requires-Dist: pylint; extra == "dev"
Dynamic: license-file

# Анализ сложности проектов на python

Идея описана в статье моего блога - [Управление простотой](https://олег.кряхтит.онлайн/2025/06/14/simplicity-management.html).

# Кратко

Утилита для скоринга относительной сложности проекта. Сравнивать между собой проекты с её помощью не имеет большого смысла, идея в отслеживании динамики усложнения проекта в процессе его жизненного цикла. Комплексная метрика - попугаи.

## Формат вывода

Запуск на самого себя. Если null в тестах это проблема - надо писать тесты, а не ныть.

``` yaml
app:
  lines: 437
  files: 5
  dependencies: 0
  directories: 0
  max_directory_depth: 0
  web:
    html:
      lines: 150
      files: 3
    css:
      lines: 200
      files: 1
    js:
      lines: 156
      files: 3
  functions:
    count: 34
    size_stats:
      min: 1
      mean: 10
      p90: 21
      p95: 27
      max: 31
  classes:
    count: 10
    size_stats:
      min: 2
      mean: 40
      p90: 295
      p95: 295
      max: 295
  imports:
    count: 26
    size_stats:
      min: 2
      mean: 5
      p90: 11
      p95: 11
      max: 11
tests:
  lines: 0
  files: 0
  dependencies: 0
  directories: 0
  max_directory_depth: 0
  functions:
    count: 0
    size_stats: null
  classes:
    count: 0
    size_stats: null
  imports:
    count: 0
    size_stats: null
logical:
  api_endpoints: 0
  event_subscriptions: 0
  periodic_tasks: 0
  integrated_systems: 0
complexity_score: 172
```

# Длинно

Писалось по такому ТЗ (обвесил комментариями и отметил нереализованные пункты).

1. Анализ физической структуры проекта:
   - Подсчёт строк кода (LOC) раздельно для основного кода и тестов
   - Подсчёт количества файлов (.py) раздельно для app и tests
   - Подсчёт количества директорий раздельно для app и tests
   - Определение максимальной глубины вложенности директорий (кажется почти бесполезным).
   - Подсчёт строк кода и количества файлов для веб-ресурсов (HTML, CSS, JS, Jinja2) в секции app.web

2. Анализ кодовой базы:
   - Подсчёт количества классов раздельно для app и tests
   - Подсчёт количества функций раздельно для app и tests
   - Расчёт статистики размеров (min, mean, p90, p95, max) для классов и функций (p90, p95 и max совпадают в небольших проектах и не имеют значения).
   - Определение максимальной глубины наследования классов (кажется почти бесполезным).

3. Анализ зависимостей:
   - Определение зависимостей из pyproject.toml
     - PEP 621
     - Poetry
   - Анализ requirements.txt, requirements-dev.txt, requirements-test.txt
   - Разделение зависимостей на основные (app) и тестовые (tests)

4. Анализ логической структуры:
   - Обнаружение API эндпоинтов (FastAPI)
   - Подсчёт асинхронных событий (FastStream)
   - Выявление подписок на события (FastStream)
   - Обнаружение периодических задач (FastStream - оказалось ненужным).
   - Определение интеграций с внешними системами
     - [x] PostgreSQL, Redis и др. (кажется почти бесполезным)
     - [ ] HTTP-клиенты / SDK в микросервисной архитектуре

5. Расчёт комплексной метрики:
   - Вычисление единого коэффициента сложности проекта
   - Использование логарифмического масштабирования метрик
   - Жёстко заданные весовые коэффициенты для разных аспектов сложности, которые можно поправить "под себя", но я бы не стал. Всё равно это попугаи.
   - Учёт метрик веб-ресурсов (HTML, CSS, JS) при расчёте коэффициента сложности

6. Фильтрация и игнорирование:
   - [ ] Автоматическое использование .gitignore
   - Автоматическое разделение на основной код и тесты по шаблонам имён
   - Пропуск бинарных и не-Python/веб файлов

7. Обработка ошибок:
   - Устойчивость к синтаксическим ошибкам в коде
   - Обработка проблем с кодировкой файлов

8. Вывод результатов:
   - Машинно- и человеко-читаемый YAML-подобный вывод.
   - Разделение метрик на логические группы (app, tests, logical, complexity_score).
   - Включение всех рассчитанных метрик
   - Чёткое отделение финального коэффициента сложности
   - Отображение метрик веб-ресурсов только в секции app, не в tests

9. Производительность:
    - Никаких требований, но работает быстро.

10. Интерфейс:
    - Принимает только один обязательный аргумент: путь к директории проекта

11. Поддержка технологий:
    - Совместимость с FastAPI и FastStream
    - Поддержка Poetry и стандартных requirements.txt
    - Распознавание async/await функций
    - Поддержка HTML, CSS, JavaScript и Jinja2-шаблонов

12. Статистическая обработка:
    - Корректная обработка пустых выборок
    - Устойчивость к выбросам в данных

13. Архитектура:
    - Решил отклониться от "один python-скрипт без зависимостей"
    - "Плагинная" архитектура - достаточно докинуть новых файлов в plugins для поддержки других фреймворков, в том числе и самопальных/корпоративных.

14. Практическая полезность:
    - Выявление "раздутых" компонентов (большие классы/функции)
    - Возможность сравнения динамики усложнения проекта (см [/contrib/git-retrospective](/contrib/git-retrospective)).

Например мой [проект умного дома](https://codeberg.org/strizhechenko/mqtt_automator):

```markdown
$ git-retrospective ../mqtt-automator/
1749975046 234
1740035384 232
1739383604 232
1738518493 232
1736532815 230
1735987780 228
1735918929 227
1735265996 219
1735189505 219
1735149143 216
1726594246 214
1726510704 214
1722049469 214
1716868950 214
1716783122 213
1716610147 211
1716539087 204
1716310019 201
1716027458 200
1715889436 191
1715882189 191
```

Эти данные можно использовать для отгрузки в какой-нибудь InfluxDB + добавить такое сканирование при каждом коммите и потом любоваться на графике в Grafana тем, как сложность проекта неуклонно ползла вверх и никакие рефакторинги этому не помогали.
