Files
breakshaft/ERROR_DESIGN.md
Qwen Code Assistant ca605001b3 feat: масштабное улучшение системы обработки ошибок и тестирования
Основные изменения:
- Добавлена иерархия исключений (17 классов) с кодами ошибок и контекстом
- Улучшена обработка ошибок: детальные сообщения с подсказками
- Добавлено 24 теста для экстремальных случаев (комбинаторика, циклы, async)
- Добавлено 23 теста для системы обработки ошибок
- Исправлен баг с optional-аргументами в renderer.py
- Обновлены импорты в тестах (src.breakshaft → breakshaft)

Документация:
- ERROR_DESIGN.md — проектирование системы ошибок
- COMMUTATIVITY_DESIGN.md — анализ проблемы некоммутативности (10 вариантов решений)

Файлы:
- src/breakshaft/exceptions.py (новый) — модуль исключений
- tests/test_error_handling.py (новый) — тесты ошибок
- tests/test_extreme_cases.py (новый) — экстремальные кейсы

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-28 13:42:04 +00:00

231 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Проектирование системы обработки ошибок для 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. **Добавить документацию** по ошибкам