Аудит дублирования (xhigh). Скелет тела get-команды тиражирован вручную по командным модулям; фабричный подход уже доказан в _lifecycle.py (17 файлов) и _batch.py.
Находки
5. Идентичный скелет get-команды в 26 файлах
Блок повторяется почти дословно:
if dry_run: format_output(body, "json", None); return
result = client.<service>().post(data=body)
if fetch_all:
items = []
for item in result().iter_items():
items.append(item)
format_output(items, output_format, output)
else:
format_output(result().extract(), output_format, output)
Верифицировано: if dry_run — 72 вхождения, fetch_all-цикл — в 26 файлах (commands/sitelinks.py:181, vcards.py, adextensions.py, advideos.py, turbopages.py, businesses.py, leads.py и т.д.).
→ make_get_command(group, service, default_fields, criteria_builder, limits) по образцу make_lifecycle_command. Сохранить per-module patchability (create_client передаётся аргументом, как в _lifecycle.py). Экономия ~180 строк + единое место для смены логики пагинации/dry-run.
11. Локальные lifecycle-обёртки
commands/dynamicads.py:146 (_dynamicad_lifecycle), audiencetargets.py:224 (_audiencetarget_lifecycle) — оборачивают make_lifecycle_command только чтобы повторить 3 одинаковых вызова delete/suspend/resume.
→ Цикл for method, help in [...]: make_lifecycle_command(...).
12. Почти идентичные set-bids команды
commands/dynamicads.py:157 ↔ audiencetargets.py:180 — ~45 строк: валидация селектор-полей против bid-полей, сборка Bids[], dry-run, post, format. Отличие только в наборе bid-полей.
→ make_set_bids_command(...) по образцу lifecycle-фабрики.
13. Boilerplate add-команды (~15 файлов)
body={method,params:{Type:[item]}} → dry-run → post → format. Меньший родственник #5; не покрывается _batch.py (тот — только multi-item).
→ execute_add(ctx, service, body, dry_run).
Не выношу (стиль/контракт, не дубль)
- parse_ids vs add_criteria_csv «непоследовательно» — стилистика.
- Разный контракт на пустой SelectionCriteria — про ошибки, не про дублирование.
Приёмка
Аудит дублирования (xhigh). Скелет тела
get-команды тиражирован вручную по командным модулям; фабричный подход уже доказан в_lifecycle.py(17 файлов) и_batch.py.Находки
5. Идентичный скелет
get-команды в 26 файлахБлок повторяется почти дословно:
Верифицировано:
if dry_run— 72 вхождения, fetch_all-цикл — в 26 файлах (commands/sitelinks.py:181,vcards.py,adextensions.py,advideos.py,turbopages.py,businesses.py,leads.pyи т.д.).→
make_get_command(group, service, default_fields, criteria_builder, limits)по образцуmake_lifecycle_command. Сохранить per-module patchability (create_clientпередаётся аргументом, как в_lifecycle.py). Экономия ~180 строк + единое место для смены логики пагинации/dry-run.11. Локальные lifecycle-обёртки
commands/dynamicads.py:146(_dynamicad_lifecycle),audiencetargets.py:224(_audiencetarget_lifecycle) — оборачиваютmake_lifecycle_commandтолько чтобы повторить 3 одинаковых вызова delete/suspend/resume.→ Цикл
for method, help in [...]: make_lifecycle_command(...).12. Почти идентичные
set-bidsкомандыcommands/dynamicads.py:157↔audiencetargets.py:180— ~45 строк: валидация селектор-полей против bid-полей, сборкаBids[], dry-run, post, format. Отличие только в наборе bid-полей.→
make_set_bids_command(...)по образцу lifecycle-фабрики.13. Boilerplate
add-команды (~15 файлов)body={method,params:{Type:[item]}}→ dry-run → post → format. Меньший родственник #5; не покрывается_batch.py(тот — только multi-item).→
execute_add(ctx, service, body, dry_run).Не выношу (стиль/контракт, не дубль)
Приёмка
make_get_commandпокрывает простые get-команды, CLI-поверхность байт-идентична (--help, опции, i18n-ключи).@patch("...<module>.create_client")сохранена.pytestзелёный (test_cli.py,test_comprehensive.py,test_dry_run.py).