# Проектирование системы обработки ошибок для breakshaft ## Текущее состояние Сейчас библиотека использует только `ValueError` и `TypeError` с краткими сообщениями. Это затрудняет отладку, особенно при использовании библиотеки как системы внедрения зависимостей. ### Существующие ошибки: 1. `ValueError: Function ... provided as injector, but return-type is not specified` 2. `ValueError: All callgraph subgraphs must be solved for callseq generation` 3. `ValueError: Unable to compute conversion graph on ...` 4. `ValueError: Unable to select conversion path` 5. `ValueError: Conversion path is not commutative` 6. `TypeError: Param ... must be type-annotated` --- ## Категории ошибок ### 1. Ошибки регистрации инжекторов (Injector Registration Errors) | Код | Название | Описание | |-----|----------|----------| | `INJECTOR_001` | MissingReturnType | У функции-инжектора не указан тип возврата | | `INJECTOR_002` | MissingParamType | У параметра инжектора не указан тип | | `INJECTOR_003` | CircularDependency | Обнаружена циклическая зависимость при регистрации | | `INJECTOR_004` | DuplicateInjector | Зарегистрировано несколько одинаковых инжекторов | | `INJECTOR_005` | InvalidInjectorSignature | Некорректная сигнатура функции-инжектора | ### 2. Ошибки построения графа (Graph Construction Errors) | Код | Название | Описание | |-----|----------|----------| | `GRAPH_001` | NoConversionPath | Невозможно построить путь преобразования между типами | | `GRAPH_002` | AmbiguousPath | Найдено несколько путей преобразования (некоммутативность) | | `GRAPH_003` | CycleDetected | Обнаружен цикл в графе преобразований | | `GRAPH_004` | TypeMismatch | Тип аргумента не соответствует ожидаемому | | `GRAPH_005` | MissingDependency | Зависимость не может быть удовлетворена | ### 3. Ошибки генерации кода (Code Generation Errors) | Код | Название | Описание | |-----|----------|----------| | `CODEGEN_001` | TemplateRenderError | Ошибка при рендеринге Jinja2-шаблона | | `CODEGEN_002` | InvalidGeneratedCode | Сгенерированный код некорректен | | `CODEGEN_003` | NameCollision | Конфликт имён в сгенерированном коде | ### 4. Ошибки выполнения (Runtime Errors) | Код | Название | Описание | |-----|----------|----------| | `RUNTIME_001` | InjectorCallFailed | Ошибка при вызове функции-инжектора | | `RUNTIME_002` | ContextManagerError | Ошибка при входе/выходе из контекст-менеджера | | `RUNTIME_003` | AsyncExecutionError | Ошибка при выполнении асинхронной операции | ### 5. Ошибки конфигурации (Configuration Errors) | Код | Название | Описание | |-----|----------|----------| | `CONFIG_001` | InvalidOptions | Некорректные опции (force_commutative + allow_async и т.д.) | | `CONFIG_002` | IncompatibleSettings | Несовместимые настройки | --- ## Иерархия исключений ``` BreakshaftError (базовое) ├── InjectorError │ ├── MissingReturnType │ ├── MissingParamType │ ├── CircularDependency │ ├── DuplicateInjector │ └── InvalidInjectorSignature ├── GraphError │ ├── NoConversionPath │ ├── AmbiguousPath │ ├── CycleDetected │ ├── TypeMismatch │ └── MissingDependency ├── CodegenError │ ├── TemplateRenderError │ ├── InvalidGeneratedCode │ └── NameCollision ├── RuntimeError │ ├── InjectorCallFailed │ ├── ContextManagerError │ └── AsyncExecutionError └── ConfigurationError ├── InvalidOptions └── IncompatibleSettings ``` --- ## Детальное описание ошибок с примерами ### INJECTOR_001: MissingReturnType ```python @repo.mark_injector() def int_to_a(i: int): # Нет -> A return A(i) # Ошибка: INJECTOR_001: Function 'int_to_a' missing return type annotation # Решение: Добавить аннотацию возврата: def int_to_a(i: int) -> A: ``` ### INJECTOR_002: MissingParamType ```python @repo.mark_injector() def convert(value) -> A: # Нет типа у параметра return A(value) # Ошибка: INJECTOR_002: Parameter 'value' missing type annotation # Решение: Добавить аннотацию: def convert(value: int) -> A: ``` ### INJECTOR_003: CircularDependency ```python @repo.mark_injector() def a_to_b(a: A) -> B: ... @repo.mark_injector() def b_to_c(b: B) -> C: ... @repo.mark_injector() def c_to_a(c: C) -> A: ... # Замыкает цикл # Ошибка: INJECTOR_003: Circular dependency detected: A -> B -> C -> A # Решение: Разорвать цикл или использовать force_commutative=False ``` ### GRAPH_001: NoConversionPath ```python @repo.mark_injector() def int_to_a(i: int) -> A: ... def consumer(dep: B) -> str: ... # B нельзя получить из int repo.get_conversion((int,), consumer) # Ошибка: GRAPH_001: No conversion path from (int,) to consumer # Доступные типы: {A} # Требуемые типы: {B} # Решение: Добавить инжектор для получения B ``` ### GRAPH_002: AmbiguousPath ```python @repo.mark_injector() def int_to_a_direct(i: int) -> A: return A(i) @repo.mark_injector() def int_to_b(i: int) -> B: return B(float(i)) @repo.mark_injector() def b_to_a(b: B) -> A: return A(int(b.b)) def consumer(dep: A) -> int: ... repo.get_conversion((int,), consumer, force_commutative=True) # Два пути: int->A и int->B->A дают разные результаты # Ошибка: GRAPH_002: Ambiguous conversion path (non-commutative graph) # Путь 1: int -> A (прямой) # Путь 2: int -> B -> A (через B) # Решение: Использовать force_commutative=False или убрать один из путей ``` ### GRAPH_004: TypeMismatch ```python @repo.mark_injector() def convert(s: str) -> A: ... repo.get_conversion((int,), ...) # int нельзя преобразовать в str # Ошибка: GRAPH_004: Type mismatch - expected str, got int ``` ### CONFIG_001: InvalidOptions ```python repo.get_conversion(..., allow_async=False, force_async=True) # Ошибка: CONFIG_001: Invalid options - force_async=True but allow_async=False # Решение: Установить allow_async=True или force_async=False ``` --- ## Формат сообщений об ошибках Каждое сообщение должно содержать: 1. **Код ошибки** (для поиска в документации) 2. **Краткое описание** (что произошло) 3. **Контекст** (где произошло, какие типы задействованы) 4. **Подсказку** (как исправить) ### Пример формата: ``` BreakshaftError [GRAPH_001]: No conversion path found Cannot build conversion from source types to target function. Context: Source types: (int, float) Target function: my_module.consumer Required types: {A, B} Available types: {A, C, D} Missing types: {B} Suggestions: 1. Add an injector that produces type 'B' 2. Check if type annotations are correct 3. Use force_commutative=False if multiple paths are expected Documentation: https://breakshaft.readthedocs.io/errors/GRAPH_001 ``` --- ## План реализации 1. **Создать модуль `exceptions.py`** с иерархией исключений 2. **Добавить базовый класс `BreakshaftError`** с форматированием сообщений 3. **Заменить все `raise ValueError`** на специфичные исключения 4. **Добавить дополнительные проверки** (валидация на входе) 5. **Написать тесты** для всех типов ошибок 6. **Добавить документацию** по ошибкам