#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ ВОССТАНОВИТЬ certifi/core.py внутри gcloud SDK. Симптом: `gsutil` и `gcloud storage` падают с `ModuleNotFoundError: No module named 'certifi.core'`. Причина: в `/usr/lib/google-cloud-sdk/lib/third_party/certifi/` есть `__init__.py` (который делает `from .core import contents, where`) и `cacert.pem`, но **самого `core.py` нет**. Пакет обрезан. Через pip не чинится: pip сломан тем же самым (`pip._vendor.certifi.core`) и не может починить себя. Файл `core.py` в certifi крошечный — две функции. Восстанавливаем его, указывая на уже лежащий рядом `cacert.pem`. python3 certifi_fix.py сухой прогон python3 certifi_fix.py --apply восстановить """ import os, subprocess, sys DIRS = [ "/usr/lib/google-cloud-sdk/lib/third_party/certifi", "/usr/lib/google-cloud-sdk/lib/third_party/pip/_vendor/certifi", ] APPLY = "--apply" in sys.argv CORE = '''""" certifi.core — восстановлен вручную. Оригинал пропал из поставки SDK (остались __init__.py и cacert.pem, а core.py нет), из-за чего падали gsutil и gcloud storage. Через pip не чинилось: pip сломан тем же отсутствующим модулем и не может переустановить себя. Функции те же, что в оригинале: путь к связке корневых сертификатов и её содержимое. Связка берётся из cacert.pem, который лежит рядом и не пострадал. """ import os def where(): return os.path.join(os.path.dirname(__file__), "cacert.pem") def contents(): with open(where(), "r", encoding="ascii") as f: return f.read() ''' def sh(c): r = subprocess.run(c, shell=True, capture_output=True, text=True, timeout=90) return (r.stdout or r.stderr or "").strip() for d in DIRS: if not os.path.isdir(d): print(f" {d}\n каталога нет — пропуск") continue core = os.path.join(d, "core.py") pem = os.path.join(d, "cacert.pem") init = os.path.join(d, "__init__.py") print(f" {d}") print(f" __init__.py {'есть' if os.path.exists(init) else 'НЕТ'} · " f"cacert.pem {'есть' if os.path.exists(pem) else 'НЕТ'} · " f"core.py {'есть' if os.path.exists(core) else 'НЕТ'}") if os.path.exists(core): print(" уже на месте — не трогаю") continue if not os.path.exists(pem): print(" ⛔ нет cacert.pem — восстанавливать не из чего, пропуск") continue if not APPLY: print(" → был бы создан core.py") continue open(core, "w", encoding="utf-8").write(CORE) os.chmod(core, 0o644) # сбрасываем кэш байткода, иначе python может подсунуть старый pc = os.path.join(d, "__pycache__") if os.path.isdir(pc): sh(f"rm -rf '{pc}'") print(" ✅ core.py восстановлен, кэш байткода сброшен") if not APPLY: print("\nсухой прогон. Для восстановления добавьте --apply") else: print("\n--- проверка ---") print(" gcloud storage:", (sh("gcloud storage ls gs:// 2>&1 | tail -1") or "—")[:70]) print(" gsutil :", (sh("gsutil version 2>&1 | tail -1") or "—")[:70])