Руководство по миграции: с версии v2 на v3
Это руководство содержит описание ключевых ломающих изменений (breaking changes) при переходе с версии 2.x на версию
3.0.0 библиотеки chutils, а также инструкции по обновлению вашего кода.
1. Повышение требований к версии Python и типизации
Минимальная поддерживаемая версия Python повышена:
- Было:
Python >= 3.9 - Стало:
Python >= 3.10
Если ваш проект использует Python 3.9, вам необходимо обновить среду выполнения до версии 3.10 или выше. В кодовой
базе библиотеки теперь активно используются новые синтаксические возможности Python 3.10 (например, объединенные типы
int | str вместо Union[int, str]).
Отказ от typing_extensions:
- Пакет
typing-extensionsполностью удален из зависимостейchutils. - Все импорты из
typing_extensions(например,TypeAlias,ParamSpecи др.) в клиентском коде следует заменить на стандартные импорты из модуляtypingстандартной библиотеки Python.
2. Унификация ошибок отсутствия зависимостей
При обращении к модулям, требующим неустановленные опциональные зависимости (extra-пакеты), тип выбрасываемого исключения был изменен для повышения единообразия обработки ошибок.
- Было: Выбрасывалось стандартное исключение
RuntimeErrorс сообщением о необходимости установки пакета. - Стало: Выбрасывается специализированное исключение
OptionalDependencyError(наследуемое отChutilsException).
Затронутые модули и функции:
chutils.crypto: функцииencrypt_portable,decrypt_portable,encrypt_file,decrypt_file(требуютchutils[crypto]).chutils.text: функцияis_significant_difference(требуетchutils[text]).chutils.config: функцияstart_config_watcher(требуетchutils[watch]) и использование Pydantic-моделей в геттерах (требуетchutils[pydantic]).chutils.secret_manager: провайдерKeyringProvider(требуетchutils[secrets]).
Что нужно изменить:
Если в вашем коде перехватывалось исключение RuntimeError для обработки отсутствующих библиотек, обновите его на
перехват OptionalDependencyError или базового ChutilsException:
# Было (v2):
try:
from chutils.crypto import encrypt_portable
encrypt_portable("data", "seed")
except RuntimeError as e:
print("Установите chutils[crypto]!")
# Стало (v3):
from chutils.exceptions import OptionalDependencyError
try:
from chutils.crypto import encrypt_portable
encrypt_portable("data", "seed")
except OptionalDependencyError as e:
print(f"Ошибка: {e.message}")
print(f"Совет: {e.hint}")
3. Удаление устаревших (Deprecated) переменных и функций в chutils.config
В рамках очистки публичного API перед релизом 3.0.0 были полностью удалены приватные глобальные переменные и функции
обратной совместимости, которые временно поддерживались с выдачей предупреждений DeprecationWarning.
Удаленные приватные переменные модуля config:
config._BASE_DIR— используйте публичную функциюconfig.get_base_dir().config._CONFIG_FILE_PATH— используйте публичную функциюconfig.get_config_file_path().config._paths_initialized— используйте публичную функциюconfig.are_paths_initialized().config._config_object— используйте публичную функциюconfig.get_config().config._config_loaded— используйте публичную функциюconfig.is_config_loaded().config._get_config_paths— используйте публичные функцииconfig.get_config_paths()илиconfig.get_all_config_paths().
Удаленные функции:
config._initialize_paths()— пути теперь инициализируются автоматически при первом вызове любого публичного геттера. Если в тестах или инфраструктурном коде вам необходимо вручную инициализировать пути, импортируйте внутренний менеджер конфигурации_cmи функцию поиска корня:python from chutils.config import _cm, find_project_root _cm.initialize_paths(find_project_root)config._sync_legacy_state()— синхронизация устаревшего глобального состояния больше не поддерживается.
При обращении к любым из этих удаленных атрибутов теперь будет выбрасываться стандартное исключение AttributeError.
4. Ограничение прямого доступа к внутреннему менеджеру _cm
Прямой импорт или доступ к менеджеру конфигурации _cm через chutils.config._cm теперь считается деталью внутренней
реализации.
- По возможности используйте только стабильный публичный API модуля
chutils.config(get_config_value,get_base_dirи т.д.). - Доступ к
_cmсохранен для написания тестов и расширения возможностей библиотеки, однако при прямом использовании в бизнес-логике приложений рекомендуется мигрировать на официальный публичный интерфейс.
5. Перевод системного хранилища keyring в разряд опциональных зависимостей
Для предотвращения проблем сборки в изолированных окружениях (например, в Docker-контейнерах на Linux), библиотека
keyring была переведена в категорию дополнительных зависимостей.
Что изменилось:
- Установка по умолчанию больше не включает пакет
keyring. Для использования системного хранилища необходимо явно установить:pip install chutils[secrets](илиpip install chutils[full]). - Изящная деградация
SecretManager: Если пакетkeyringотсутствует, классSecretManagerне падает с ошибкой, а автоматически отключаетKeyringProviderи использует только Env и DotEnv провайдеры (переменные окружения и.envфайлы). - Логирование и предупреждения: При отсутствии
keyringв логах будет выведено однократное предупреждение уровняwarning. Его можно заглушить, если задать переменную окруженияCH_DISABLE_KEYRING_WARNING=trueили настроить соответствующее значение в конфигурации. - Изменение в CLI: При отсутствии установленной зависимости
secretsCLI-команды для управления секретами (chutils secrets ...) автоматически скрываются из справки--help. При попытке вызвать скрытую команду напрямую, CLI выброситCommandErrorс сообщением о необходимости установкиchutils[keyring].
6. Строгий режим и унификация в Config API и SecretManager
В версии 3.0.0 оба API доступа к настройкам и секретам были унифицированы для поддержки строгого режима работы (
паттерн Fail-Fast).
Что изменилось:
-
Строгий режим в Config API:
- Во все геттеры конфигурации (
get_config_value,get_config_int,get_config_float,get_config_boolean,get_config_list,get_config_path,get_config_section) добавлен необязательный параметрrequired: bool = False. - Если задан
required=Trueи запрашиваемый ключ или секция отсутствуют в конфигурации (или равныNone/пустой строке""), выбрасывается исключениеConfigKeyNotFoundError(наследуется отConfigError).
- Во все геттеры конфигурации (
-
Строгий режим и унификация в SecretManager:
- В методы
get_secretиaget_secretклассаSecretManagerдобавлены параметрыfallback: Optional[str] = Noneиrequired: bool = False. - Если задан
required=Trueи секрет отсутствует в провайдерах, выбрасывается исключениеSecretNotFoundError( наследуется отSecretError). - Добавлена новая CLI-подкоманда
chutils secrets get <key> [--service <name>] [--fallback <val>] [--required].
- В методы
[!NOTE] Все изменения полностью обратно совместимы. По умолчанию
required=False, и при отсутствии данных геттеры тихо возвращают значениеfallback.
Примеры использования:
Работа с Config API:
from chutils import get_config_value, get_config_int
from chutils.exceptions import ConfigKeyNotFoundError
# 1. Обычный режим (v2-совместимый)
port = get_config_int("Database", "missing_port", fallback=5432) # 5432
# 2. Строгий режим (fail-fast)
try:
host = get_config_value("Database", "host", required=True)
except ConfigKeyNotFoundError:
print("Ошибка: хост базы данных не задан в конфигурации!")
Работа с SecretManager:
from chutils.secret_manager import SecretManager
from chutils.exceptions import SecretNotFoundError
sm = SecretManager("my_app")
# 1. Использование fallback (как в Config API)
key = sm.get_secret("STRIPE_KEY", fallback="default_val") # "default_val"
# 2. Строгий режим
try:
key = sm.get_secret("STRIPE_KEY", required=True)
except SecretNotFoundError:
print("Ошибка: обязательный ключ STRIPE_KEY не найден в хранилище!")
7. Переход на стандартные Exception Groups
В версии 3.0.0 класс группы исключений шины событий EventBusExceptionGroup был переведен на наследование от
стандартного класса ExceptionGroup (появившегося в Python 3.11).
Что изменилось:
- Базовый класс:
EventBusExceptionGroupтеперь наследуется отExceptionGroup(встроенного в Python 3.11+ или бэкпорта из пакетаexceptiongroupдля Python 3.10) иEventBusError. - Перехват через
except*: На Python >= 3.11 появилась возможность перехватывать отдельные типы ошибок из группы с помощью встроенного синтаксисаexcept*. - Полная обратная совместимость: Традиционный перехват
except EventBusExceptionGroup as e:и доступ к списку ошибок через.exceptions(в виде кортежа/списка) полностью сохранены для всех версий Python (>= 3.10).
Примеры использования:
from chutils.events import EventBus, ErrorStrategy
from chutils.exceptions import EventBusExceptionGroup
bus = EventBus(error_strategy=ErrorStrategy.COLLECT)
# При наличии ошибок публикации выбрасывается EventBusExceptionGroup
# Вариант 1. Традиционный перехват (совместим с Python 3.10)
try:
bus.publish("my_event")
except EventBusExceptionGroup as e:
for err in e.exceptions:
print(f"Ошибка: {err}")
# Вариант 2. Использование except* (только на Python >= 3.11)
# try:
# bus.publish("my_event")
# except* ValueError as eg:
# print(f"Обработаны ошибки ValueError: {eg.exceptions}")