Skip to main content

Другая версия: English

mutagen-cli

CI PyPI

Ваши тесты зелёные. Вот что они не проверяют.

mutagen-cli подсаживает в код правдоподобные баги — off-by-one, забытую инвалидацию кэша, перепутанные аргументы, инвертированные условия — и перезапускает ваш тестовый сьют. Любой баг, который выжил, — дыра в тестах; она репортится как конкретный сценарий отказа, с которым столкнётся пользователь.

В отличие от классического mutation testing, мутанты пишет LLM, которая прочитала и саму функцию, и покрывающие её тесты — она целится в слепые пятна, а не переставляет операторы наугад.

Сделано для ситуации, когда вы (или Claude Code, или Cursor) только что написали кучу кода и кучу тестов к нему, и хочется понять, значат ли эти тесты хоть что-нибудь.

Реальный отчёт — выдержка из прогона на стороннем репозитории (semantic-plagiarism-detector, 44 теста, все зелёные):

mutagen run: mutation score 21%, два выживших мутанта — кэш spaCy-пайплайна не различает язык, перепутанные местами пороги classify

Проверено на пяти других реальных проектах

mutagen-cli тестировался не только на фикстурах — на реальные приложения с уже написанными (и зелёными) тестами, без единой правки исходников под инструмент. Три — мои собственные проекты, два — независимые чужие библиотеки:

Проект Скоуп Score Стоимость
semantic-plagiarism-detector — детектор плагиата core/ (33 функции, 8 файлов) 21% (5 killed / 24 viable) $0.35
cityfeed — телеграм-бот с дайджестом новостей rank/ (ранжирование) 20% (5/25) $0.13
cityfeed — телеграм-бот с дайджестом новостей dedup/ (дедупликация) 24% (6/25) $0.14
CogniWeb_Agent — браузерный LLM-агент agent/, infrastructure/, utils/ (3 отдельных прогона) 12% / 5% / 4% $0.78
parse — обратная функция к str.format, 1.8k★ parse/__init__.py 68% (17/25) $0.13
parsy — парсер-комбинаторы, 451★ src/parsy/__init__.py 75% (18/24) $0.13
mutagen run: cityfeed dedup, mutation score 24%, два выживших мутанта — guard склейки событий можно обойти, off-by-one в n-граммах

Во всех четырёх собственных проектах — sub-25% score на коде, который прошёл человеческий ревью и зелёный CI. Типичные дыры: кэш, не различающий ключ (spaCy-пайплайн по языку в plagiarism-detector), перепутанные местами значения (пороги classify), boundary-условия на границах окна (cityfeed dedup), инвертированные проверки безопасности (капча и прокси в CogniWeb). Это не баги, специфичные для одного проекта или стиля кода — это форма слепого пятна, которую юнит-тесты на happy path систематически не видят.

Чтобы проверить это не только на своём коде, mutagen-cli прогонялся и на двух чужих открытых библиотеках — parse (обратная функция к str.format(), 1.8k★, MIT) и parsy (парсер-комбинаторы, 451★, MIT) — обе с уже зелёными pytest-сьютами, без единой правки исходников. Score там заметно выше: 68% и 75% против 4–24% на моих проектах — зрелый код с годами ревью и большим числом контрибьюторов действительно закрывает больше мутаций, и это ожидаемо: mutation score должен расти вместе с качеством покрытия, а не быть константой. Но и там находятся настоящие дыры — просто локализованные, а не размазанные по всему модулю: в parse все 7 живых мутантов сгруппированы вокруг FixedTzOffset (обработка часовых поясов системно недопокрыта) плюс один off-by-one для знаковых hex/octal/binary литералов; в parsy все 6 — в мета-логике отчётов об ошибках (ParseError/ Result: проглоченное исключение, неверная граница, потерянный furthest-index).

Воспроизвести на встроенном фикстур-проекте: python scripts/benchmark.py (офлайн, детерминированно, ноль обращений к сети — сеть трогается только с явным --live).

На том же проекте с мутантами, написанными моделью: 43 мутанта на 15 функциях, 8 killed, 35 survived, 0 неприменимых, из 35 выживших мусорных только 2 (5.7%). Они накрыли 18 из 22 задокументированных слепых пятен теста проекта — и ещё 7, которые не были описаны в его собственных заметках. Полные цифры и оговорки — BENCHMARKS.md.

Живой прогон через OpenRouter API (2026-08-13, --invent включён): anthropic/claude-sonnet-5 — 40 мутантов, 0% неприменимых, 12.9% мусорных выживших, $0.26; anthropic/claude-opus-5 — 0% неприменимых, 3.0% мусорных, 14/22 слепых пятен, $0.68. Подробности — BENCHMARKS.md, прогон D.

Быстрый старт

pip install mutagen-cli
export OPENROUTER_API_KEY=sk-or-...
mutagen run

Работает на изменённых функциях — на чистом дереве без диффа относительно main сравнивать не с чем. Для первого знакомства на чистом дереве:

mutagen run --all --max-mutants 5

(mutagen-cli — имя дистрибутива, mutagen сам по себе — библиотека для аудио-метаданных; устанавливаемая команда называется mutagen.) Чтобы разрабатывать сам mutagen-cli — git clone https://github.com/Ilyat9/mutagen-cli && cd mutagen-cli && pip install -e ".[dev]".

Готово. Никакого конфига. mutagen run сравнивает рабочее дерево с дефолтной веткой репозитория (автоопределяется через origin/HEAD, с откатом на локальные main/master), мутирует только изменённые функции и гоняет только те тесты, которые их реально покрывают. Если ваша дефолтная ветка называется иначе (develop, trunk, ...) и автоопределение не сработало — укажите её явно: mutagen run --base master (или develop, trunk — что у вас за ветка). Если хотите говорить напрямую с Anthropic — см. Провайдеры.

Провайдеры

mutagen-cli поддерживает два LLM-провайдера, переключается флагом --provider:

OpenRouter (по умолчанию). OpenAI-совместимый шлюз, отдающий те же модели Claude — полезно, потому что API Anthropic обслуживает не все регионы. OpenRouter работает из России без VPN.

  1. Создайте ключ на https://openrouter.ai/keys.
  2. export OPENROUTER_API_KEY=sk-or-..., либо положите {"openrouter_api_key": "sk-or-..."} в .mutagen/config.json.

Модель по умолчанию: anthropic/claude-sonnet-5 — лучшая точка цена/качество для генерации мутантов ($2/M input, $10/M output на 2026-08-13). Переопределяется --model, например --model anthropic/claude-opus-5.

Anthropic. Прямой доступ к API.

  1. export ANTHROPIC_API_KEY=sk-ant-..., либо положите {"anthropic_api_key": "sk-ant-..."} в .mutagen/config.json.
  2. Запуск с --provider anthropic. Модель по умолчанию: claude-opus-5.

Этот путь менее проверен вживую, чем OpenRouter (все наши живые прогоны — через OpenRouter); при проблемах — заводите issue.

Два нюанса, специфичных для провайдера:

  • Модели Claude 5 на OpenRouter по умолчанию гоняются с включённым reasoning, который засоряет JSON-ответ и раздувает стоимость. mutagen-cli явно шлёт reasoning: {"enabled": false} на каждый запрос. Чтобы включить обратно — {"openrouter_reasoning": true} в конфиге.
  • Параметры сэмплинга (temperature и другие) эти модели молча игнорируют, поэтому провайдер OpenRouter их вообще не отправляет.

Стоимость считается из полей usage в ответе API по встроенной таблице цен. Её можно переопределить или добавить цену для неизвестной модели через {"prices": {"model/id": [input_per_mtok, output_per_mtok]}} в конфиге; для модели без известной цены отчёт покажет «cost unavailable», а не $0.

Использование

mutagen run                          # только то, что изменилось относительно main
mutagen run --base develop           # ...относительно другой ветки
mutagen run --path src/billing.py    # конкретные файлы или директории
mutagen run --all                    # весь кодбейз
mutagen run --dry-run                # показать план и мэппинг тестов, не тратя денег

Превращаем выживших в тесты:

mutagen run --invent          # напечатать тест, который поймал бы каждого выжившего
mutagen run --invent-apply    # ...и сохранить проверенные в tests/mutagen_generated/

Каждый предложенный тест проверяется дважды, прежде чем вы его увидите: он обязан проходить на реальном коде и падать на мутанте. Тесты, не прошедшие хотя бы одну проверку, всё равно показываются, но с пометкой — фича не имеет права вам врать.

Для CI:

mutagen run --report-md report.md --report-json report.json --fail-under 70

GitHub Action

action.yml в этом репозитории — мутационный гейт для pull request'ов. Он мутирует только то, что изменил PR, и постит выживших комментарием, редактируя один и тот же комментарий на каждый push вместо того, чтобы плодить новые.

name: mutation
on: pull_request

jobs:
  mutagen:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -e .[dev]
      - uses: Ilyat9/mutagen-cli@v0
        with:
          provider: openrouter         # или anthropic
          openrouter-api-key: ${{ secrets.OPENROUTER_API_KEY }}
          # anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          fail-under: "70"
          invent: "true"

⚠️ Важно: pip install выполняет код из PR (setup.py, build-хуки) в основном процессе раннера до того, как mutagen вычистит секреты, поэтому для репозиториев, принимающих PR от форков, обязателен "Require approval for first-time contributors" в Settings → Actions → General.

Триггер — только pull_request, как в примере выше. Никогда не pull_request_target с чекаутом кода PR. pull_request_target выполняется с доступом к секретам base-репозитория, но чекаутит код, который вы не контролируете — это классический pwn request: PR из форка может как угодно поменять то, что запускает Action (включая сам тестовый сьют), и утащить OPENROUTER_API_KEY/ANTHROPIC_API_KEY/GITHUB_TOKEN наружу до того, как mutagen вообще успеет их вычистить. pull_request этой дыры не имеет: у него нет доступа к секретам репозитория при запуске на форк-PR.

Тесты из PR запускаются без секретов в окружении: mutagen вычищает OPENROUTER_API_KEY, ANTHROPIC_API_KEY и GITHUB_TOKEN из окружения каждого pytest-сабпроцесса, так что код из чужого PR не может их прочитать. Это защищает от вредоносного теста внутри сьюта, но не от pull_request_target — на fork-PR всё равно включайте required approval для CI (Settings > Actions > General > "Require approval for first-time contributors" или строже), как для любого CI, который гоняет чужой код.

Важные флаги

Флаг По умолчанию
--max-mutants N 25 Жёсткий потолок числа генерируемых мутантов.
--max-files N 20 Жёсткий потолок числа рассматриваемых файлов.
--timeout SECS 30 Бюджет времени на мутанта. Автоматически увеличивается, если сьют медленный.
--workers N CPUs/2 Мутанты гоняются параллельно, каждый — в своей копии репо.
--provider NAME openrouter openrouter или anthropic. См. Провайдеры.
--model ID дефолт провайдера anthropic/claude-sonnet-5 на OpenRouter, claude-opus-5 на Anthropic.
--effort LEVEL medium lowmax. Ниже — дешевле и быстрее. Только для Anthropic.
--no-cache выкл Игнорировать дисковый кэш в .mutagen/cache/.
--python PATH venv проекта Интерпретатор, которым гоняются тесты.

Ответы LLM кэшируются на диске отдельно на каждую функцию, так что повторный запуск после правки одной функции платит только за неё. Ключ кэша включает набор тестов, показанных модели, — так что изменение покрытия корректно промахивается мимо кэша.

Особенность, о которую легко споткнуться: --max-mutants — общий лимит на весь запуск, а не на файл. mutagen run --all --path a.py --path b.py --max-mutants 25 сгенерирует до 25 мутантов суммарно на оба файла — если функций в a.py достаточно, чтобы съесть весь лимит, b.py может не получить ни одного. Для гарантированного покрытия каждого файла — отдельные прогоны с --path по одному файлу за раз.

Что значат вердикты

Вердикт Значение
killed Тест упал. Хорошо — баг был бы пойман.
survived Все тесты прошли. Это дыра в сьюте.
timeout Мутант, вероятно, создал бесконечный цикл. Считается отдельно, не как kill.
survived (unreached) Выживший более сильного типа: ни один тест вообще не исполняет мутированные строки, так что упасть не могло ничего. Требует карты покрытия; засчитывается как survived.
unapplicable Правку не удалось применить к файлу, либо получившийся код не парсится. Полностью исключается из score.
error pytest не смог запуститься (ошибка сбора, нет тестов). Исключается из score.

Mutation score — это killed / (killed + survived). Timeout, error и unapplicable намеренно не входят ни в числитель, ни в знаменатель: засчитывать их как kill означало бы искусственно завышать score.

Важный нюанс про error. Сюда попадает в том числе мутация, которая ломает module-level код или декоратор целевой функции настолько, что pytest падает с ошибкой сбора (collection error, обычно exit code 2) ещё до запуска тестов. Это реальный, пойманный тестами баг — но раз он не попадает ни в killed, ни в survived, он исключён из знаменателя score и никак не влияет на итоговый процент. Логика сделана так намеренно (score должен отражать качество исполненных тестов, а не крах инфраструктуры сбора), но это означает, что такие мутанты стоит просматривать в отчёте отдельно, а не полагаться на то, что score их отразит.

Требования

  • Python 3.10+
  • pytest-cov в интерпретаторе, которым гоняются тесты — для мэппинга тестов по покрытию. Опционально: без него mutagen-cli откатывается на эвристику и прямо говорит об этом в отчёте.
  • Проходящий на момент запуска pytest-сьют. mutagen-cli проверяет это первым делом и отказывается работать на красном сьюте, потому что на нём любой мутант выглядел бы «убитым».
  • Ключ OpenRouter в OPENROUTER_API_KEY (получить — https://openrouter.ai/keys, работает из России без VPN), либо ключ Anthropic в ANTHROPIC_API_KEY с --provider anthropic. Ключи можно также держать в .mutagen/config.json.

macOS и Linux протестированы. Windows — нет.

Разработка самого mutagen-cli

pip install -e ".[dev]" && pytest && ruff check .

100+ офлайн-тестов, все бесплатные: тесты пайплайна гоняют настоящие подпроцессы pytest через replay-провайдер, а тесты CLI заранее засеивают дисковый кэш, так что API-ключ вообще не нужен.

Ограничения

  • Только Python и pytest. Другие языки и раннеры не поддерживаются.
  • Стоимость реальна. Один вызов LLM на изменённую функцию, плюс ещё один на каждого выжившего при --invent. Дисковый кэш делает повторные прогоны дешёвыми, но первый прогон на большом диффе бесплатным не будет. --dry-run покажет число вызовов заранее.
  • Эквивалентные мутанты всё равно проскакивают. Промпт активно запрещает мутации, не меняющие поведение, и большинство выживших — реальные баги, но не все. «Survivor» — это наводка для проверки, а не доказанная дыра.
  • Точный мэппинг тестов требует pytest-cov в интерпретаторе, которым гоняются тесты. С ним mutagen-cli измеряет, какие тесты исполняют какие строки. Без него — откат на эвристику по имени файла/символу, и отчёт прямо об этом говорит; эвристика, угадавшая не те файлы, репортит мутантов как выживших, хотя тест, который бы их убил, просто не запускался.
  • Каждый воркер копирует репозиторий во временную директорию. Большие репо с большими неотслеживаемыми директориями это почувствуют.
  • Рабочее дерево не трогается никогда — кроме --invent-apply, который пишет новые файлы в tests/mutagen_generated/ и больше никуда.
  • Это не инструмент покрытия. Высокий mutation score на изменённых вами функциях ничего не говорит о функциях, которые вы не трогали.
  • Тексты отчёта генерирует LLM по коду, включая недоверенный. Описание каждого мутанта и прочие тексты отчёта пишет модель на основе кода функции (и, при --invent, её тестов) — это может быть код чужого PR. Эти тексты постятся в markdown/JSON-отчёт и в комментарий к PR с санитизацией от prompt-injection векторов (image-beacon синтаксис, HTML-теги). Но комментарий или докстрока в PR, написанные так, чтобы влиять на модель, в принципе могут изменить формулировки в отчёте. Учитывайте это, когда Action гоняется на чужих PR: отчёт — это текст, сгенерированный по недоверенному вводу, а не утверждение от вашей CI-системы.
  • Проекты, чей пакет импортируется только из site-packages (например, с C-расширениями, собранными при установке), пока не подходят: mutagen-cli ставит исходники воркер-копии вперёд на PYTHONPATH, а собранных модулей в копии нет — baseline упадёт с ImportError.
  • Strip-лист секретов из окружения pytest-сабпроцессов фиксированныйOPENROUTER_API_KEY, ANTHROPIC_API_KEY, GITHUB_TOKEN. Другие токены (AWS_*, NPM_TOKEN и т.п.) вычищаются не автоматически. Не запускайте на недоверенном коде с такими переменными в окружении.

Как это работает

  1. git diff относительно merge base → изменённые диапазоны строк (закоммиченное и незакоммиченное, плюс untracked-файлы).
  2. ast сопоставляет эти строки с целыми функциями, так что модель видит законченные единицы кода.
  3. Ваш сьют один раз гоняется немутированным под coverage с контекстом на каждый тест. Этот единственный прогон делает две вещи: доказывает, что сьют зелёный, прежде чем тратятся деньги, и строит карту какие тесты исполняют какие строки. Без установленного pytest-cov mutagen-cli откатывается на эвристику по имени файла/символу и помечает отчёт mapping: heuristic.
  4. Каждая функция отправляется модели вместе с тестами, которые её реально покрывают, с инструкцией произвести баги, которые эти тесты с наименьшей вероятностью поймают. Ответ ограничен JSON-схемой.
  5. Мутации приходят как блоки SEARCH/REPLACE (не диффы — модели путаются в номерах строк). Они применяются сначала точно, затем с нормализацией пробелов и отступов, затем нечётко через difflib — и всегда строго в пределах диапазона строк целевой функции, так что блок, встречающийся и в соседней функции, не может незаметно мутировать её вместо нужной. Блоки, которые никуда не встали, или дающие код, который не парсится, помечаются unapplicable, а не подгоняются силой.
  6. Каждый мутант гоняется в приватной копии репозитория своего воркера, ровно против тех тестов, которые исполняют изменённые им строки, с таймаутом. Мутация на строках, которые не исполняет ни один тест, вообще не запускается — на неё ничто не могло бы полагаться — и репортится в отдельной секции unreached.

Сравнение с другими инструментами

mutmut cosmic-ray Mutahunter mutagen-cli
Подход к мутациям regex/AST-паттерны (фиксированный набор операторов) regex/AST-паттерны (фиксированный набор операторов) LLM, по всему проекту LLM, только по diff-скоупу
Двусторонняя верификация* нет нет нет да, режим --invent
Маппинг тестов на мутацию по coverage (pytest-cov) по coverage (pytest-cov) эвристика/полный сьют по coverage (pytest-cov), с откатом на эвристику по имени файла
Языки Python Python language-agnostic Python

* «Двусторонняя верификация» — это когда для выжившего мутанта не просто предлагается тест, а сгенерированный тест реально прогоняется дважды: должен пройти на исходном коде и упасть на мутанте. Само по себе автогенерирование тестов для повышения покрытия не уникально — это умеет и Mutahunter; уникальна именно проверка, что предложенный тест действительно ловит конкретный баг, а не просто выглядит правдоподобно.

История проекта

Мутационное тестирование — старая техника, которой почти никто не пользуется из-за тысяч тупых мутаций и часов прогона. LLM умеет генерировать осмысленные, а не случайные мутации — отсюда идея. Существующие аналоги на момент старта были либо академическими (LLMorpheus), либо закрытыми корпоративными (Meta ACH, Atlassian), либо решают ту же задачу другим способом (Mutahunter, ~300 звёзд, тоже LLM-based и тоже умеет генерировать тесты для непокрытых веток). Отличия от Mutahunter — не в самой идее LLM-мутаций или автогенерации тестов, а в скоупе (git diff вместо всего проекта), coverage-based маппинге тестов на мутацию вместо эвристики по имени файла, двусторонней верификации предложенных тестов через --invent (см. сравнение выше), плюс готовый PyPI-пакет и GitHub Action "из коробки".

Что построили

CLI-инструмент mutagen-cli: берёт Python-проект → LLM генерирует 10–30 семантических мутантов (off-by-one, потерянные проверки, перепутанные пороги — «типичные ошибки вайб-кодера») → применяет каждый к копии репо → прогоняет только релевантные тесты → отчёт: «ваши тесты зелёные, но вот конкретные баги, которые они не ловят». Плюс режим --invent: для выживших мутантов генерирует недостающий тест с двусторонней верификацией.

Ключевые компоненты: diff/AST-скоуп, SEARCH/REPLACE-патчи с четырёхъярусным fuzzy-apply, изолированные worker-копии с параллелизмом, coverage-based маппинг тестов, кэш LLM-ответов, два провайдера (OpenRouter по умолчанию — доступен из РФ, Anthropic опционально), GitHub Action, PyPI-пакет.

Эксперименты: прогнали на реальных проектах

Проект Mutation score Что нашлось
Полигон (валидация, ground truth) 43% метод работает
Детектор плагиата (ML) 21% пороги сохраняются перепутанными; язык определяется по первой букве
cityfeed (ML-лента) 20% / 24% guard склейки событий можно обойти; off-by-one в n-граммах
CogniWeb_Agent (LLM-агент) 7% все три главных мутанта — инверсии проверок безопасности: капча, прокси, невидимые элементы
parse — обратная к str.format 68% boundary-условия на часовых поясах (FixedTzOffset); off-by-one в hex/octal/binary литералах
parsy — парсер-комбинаторы 75% мета-логика ошибок (ParseError/Result): проглоченное исключение, неверная граница, потерянный index

Стоимость аудита модуля — $0.13–0.35.

Что узнали по дороге (главная ценность)

1. Инструмент дважды врал, и мы ловили его руками.

  • Editable install: мутация применялась в копии, а pytest импортировал оригинал → все «0 из N» по cityfeed были артефактом. Честные цифры после фикса — двузначный процент на каждом модуле.
  • Мутация могла попасть не в ту функцию (в alpha вместо beta при похожем коде) → ложные «выжившие». Фикс — привязка apply к строкам целевой функции.

2. Даже стандартные инструменты врут. Coverage на Python 3.12+ с дефолтным sysmon-ядром молча теряет контексты: в карту попадал только первый дошедший до строки тест — карта была бы «хуже, чем никакой». Поймали замером (1 vs 5 контекстов), форсировали ctrace.

3. Байткод-призрак. Мутация minmax не меняла размер файла, CPython переиспользовал старый .pyc — мутант не прогонялся и записывался «выжившим». Фикс: PYTHONDONTWRITEBYTECODE=1.

4. Недетерминизм — измерен, а не скрыт. 3 повторных прогона на одном файле: score плавает 24–32%, покрытие функций стабильно 9/9, конкретные мутанты совпадают лишь на 11–22%. Формулировка: «недетерминирован в том, как ломать; детерминирован в том, что тесты не проверяют».

5. Мусорные мутанты — тоже измерены. Junk rate: 12.9% у Sonnet 5, 3.0% у Opus 5. Эквивалентные мутанты не считаются в score (unapplicable/error/ timeout — отдельные вердикты).

Как преобразился проект

  • Из «обёртки над LLM» → в измерительный инструмент. Каждое утверждение подкреплено воспроизводимыми артефактами: BENCHMARKS.md с датированными прогонами (Run A–G), отчёты, регрессионные тесты на каждый найденный баг.
  • Из эвристики → в правильную архитектуру. Маппинг тестов по coverage с nodeid-селекцией вместо догадок по именам файлов; новый класс находок unreached («этот код вообще не выполняет ни один тест»).
  • Из «привязан к Anthropic» → в доступный из РФ. OpenRouter по умолчанию, с учётом ловушек новых моделей (reasoning по умолчанию, молчащие sampling-параметры).
  • Из «проверили один раз» → в самопроверяющуюся систему. Философия «survivor — повод посмотреть, а не доказанная дыра», честная секция Ограничения, двусторонняя верификация --invent.

Лицензия

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mutagen_cli-0.1.4.tar.gz (177.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mutagen_cli-0.1.4-py3-none-any.whl (55.1 kB view details)

Uploaded Python 3

File details

Details for the file mutagen_cli-0.1.4.tar.gz.

File metadata

  • Download URL: mutagen_cli-0.1.4.tar.gz
  • Upload date:
  • Size: 177.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for mutagen_cli-0.1.4.tar.gz
Algorithm Hash digest
SHA256 7aac2646f7e48f31c1e7b0e9935cfed2b1b04572d1e8cdcc87b90a2c4ed3a4d8
MD5 7e0a1c2359fc1f47ac5f9153315b3ae1
BLAKE2b-256 15e4ff69cc116c8d5a8b4d85d212994c090bd074c5b5ebf153cd5eed9562d38b

See more details on using hashes here.

File details

Details for the file mutagen_cli-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: mutagen_cli-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 55.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for mutagen_cli-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 a381df0284fcb746309b9b16f7824e910d24d32d239bbe04be11c91fd97bca9a
MD5 b03f688620aae6277aae670cac6f64a3
BLAKE2b-256 fdea774cb2c020e53579e6789616aa9dadbca3075985ce643f3b7c3edae71c35

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.6

2 files

0.1.5

2 files

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page