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

8.8 KiB
Raw Permalink Blame History

Проектирование системы обработки ошибок для 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

@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

@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

@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

@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

@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

@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

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. Добавить документацию по ошибкам