Основные изменения: - Добавлена иерархия исключений (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>
738 lines
28 KiB
Markdown
738 lines
28 KiB
Markdown
# Масштабное проектирование: Решение проблемы некоммутативных преобразований в breakshaft
|
||
|
||
## Содержание
|
||
1. [Постановка проблемы](#1-постановка-проблемы)
|
||
2. [Анализ текущей ситуации](#2-анализ-текущей-ситуации)
|
||
3. [Варианты решений](#3-варианты-решений)
|
||
4. [Сравнительная таблица](#4-сравнительная-таблица)
|
||
5. [Рекомендации](#5-рекомендации)
|
||
|
||
---
|
||
|
||
## 1. Постановка проблемы
|
||
|
||
### 1.1. Что такое некоммутативность в breakshaft?
|
||
|
||
**Некоммутативное преобразование** — ситуация, когда существует несколько путей преобразования типов, дающих **разные результаты**.
|
||
|
||
#### Пример:
|
||
```python
|
||
@repo.mark_injector()
|
||
def int_to_a_v1(i: int) -> A:
|
||
return A(i * 10) # Путь 1: A(420) из int=42
|
||
|
||
@repo.mark_injector()
|
||
def int_to_a_v2(i: int) -> A:
|
||
return A(i + 100) # Путь 2: A(142) из int=42
|
||
|
||
def consumer(dep: A) -> int:
|
||
return dep.a
|
||
|
||
# Два пути дают разные результаты: 420 vs 142
|
||
```
|
||
|
||
### 1.2. Почему это проблема?
|
||
|
||
| Аспект | Проблема |
|
||
|--------|----------|
|
||
| **Детерминизм** | Один и тот же код может давать разные результаты |
|
||
| **Отладка** | Сложно понять, какой путь был выбран |
|
||
| **Предсказуемость** | Поведение зависит от внутреннего порядка обхода графа |
|
||
| **Тестирование** | Тесты могут проходить/падать недетерминированно |
|
||
|
||
### 1.3. Где возникает в коде?
|
||
|
||
```
|
||
src/breakshaft/
|
||
├── graph_walker.py
|
||
│ ├── generate_callgraph() # Построение графа
|
||
│ ├── explode_callgraph_branches() # Комбинаторный взрыв вариантов
|
||
│ └── filter_exploded_callgraph_branch() # Фильтрация (выбор пути)
|
||
└── convertor.py
|
||
└── get_callseq() # Проверка force_commutative
|
||
```
|
||
|
||
**Критическое место** — `filter_exploded_callgraph_branch()`:
|
||
- Использует эвристики для выбора пути
|
||
- Порядок обхода не гарантирован
|
||
- При `force_commutative=True` выбрасывает ошибку если >1 пути
|
||
|
||
---
|
||
|
||
## 2. Анализ текущей ситуации
|
||
|
||
### 2.1. Текущий алгоритм выбора пути
|
||
|
||
```python
|
||
# graph_walker.py: filter_exploded_callgraph_branch()
|
||
|
||
# Эвристики (применяются последовательно):
|
||
template_metrics = [
|
||
lambda x: len(x.consumed_from_types), # 1. Максимум потреблённых типов
|
||
lambda x: x.consumed_cumsum, # 2. Максимум кумулятивного потребления
|
||
lambda x: -x.invokes, # 3. Минимум вызовов
|
||
]
|
||
|
||
# Если после фильтрации >1 варианта:
|
||
if len(variants) > 1:
|
||
# Сортировка по имени функции (недетерминировано!)
|
||
variants.sort(key=lambda x: universal_qualname(x.injector.fn))
|
||
```
|
||
|
||
### 2.2. Проблемы текущей реализации
|
||
|
||
| Проблема | Описание | Влияние |
|
||
|----------|----------|---------|
|
||
| **P1. Недетерминированная сортировка** | `universal_qualname()` не гарантирует порядок | Разные результаты на разных машинах |
|
||
| **P2. Эвристики не семантические** | Выбор по метрикам графа, не по логике | Может выбрать "неправильный" путь |
|
||
| **P3. Комбинаторный взрыв** | `explode_callgraph_branches()` генерирует все варианты | O(n!) сложность |
|
||
| **P4. Нет кэширования** | Граф пересчитывается каждый раз | Усугубляет P3 |
|
||
| **P5. Нет явного приоритета** | Все инжекторы равны | Невозможно указать "предпочтительный" путь |
|
||
|
||
### 2.3. Статистика (из тестов)
|
||
|
||
```
|
||
test_performance_many_injectors: 20 инжекторов → ~0.5 сек
|
||
test_performance_many_injectors: 50 инжекторов → TIMEOUT (комбинаторный взрыв)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Варианты решений
|
||
|
||
### Вариант 1: Явные приоритеты инжекторов
|
||
|
||
#### Описание
|
||
Добавить параметр `priority` к `mark_injector()`. При выборе пути предпочитать инжекторы с высшим приоритетом.
|
||
|
||
#### Реализация
|
||
```python
|
||
# Использование
|
||
@repo.mark_injector(priority=10) # Высокий приоритет
|
||
def int_to_a_preferred(i: int) -> A:
|
||
return A(i * 10)
|
||
|
||
@repo.mark_injector(priority=1) # Низкий приоритет
|
||
def int_to_a_fallback(i: int) -> A:
|
||
return A(i + 100)
|
||
|
||
# Изменения в моделях
|
||
@dataclass(frozen=True)
|
||
class ConversionPoint:
|
||
fn: Callable
|
||
injects: type
|
||
# ...
|
||
priority: int = 0 # Новый параметр
|
||
|
||
# Изменения в graph_walker.py
|
||
def filter_exploded_callgraph_branch(variants, priority_injectors=None):
|
||
# Сортировка по приоритету
|
||
variants.sort(key=lambda x: -x.injector.priority)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Контроль** | Разработчик явно указывает предпочтения |
|
||
| **Детерминизм** | Приоритеты дают однозначный выбор |
|
||
| **Гибкость** | Можно менять приоритеты без изменения кода |
|
||
| **Обратная совместимость** | `priority=0` по умолчанию не ломает существующий код |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность API** | Новый параметр для изучения |
|
||
| **Конфликты приоритетов** | Одинаковые приоритеты → снова недетерминизм |
|
||
| **Не решает комбинаторный взрыв** | Всё ещё генерируются все варианты |
|
||
| **Субъективность** | Приоритеты могут быть произвольными |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~50 строк изменений
|
||
- **Тесты**: ~10 новых тестов
|
||
- **Риск**: Низкий
|
||
|
||
---
|
||
|
||
### Вариант 2: Именованные пути (Named Paths)
|
||
|
||
#### Описание
|
||
Разработчик явно именовывает пути преобразования и выбирает их по имени.
|
||
|
||
#### Реализация
|
||
```python
|
||
# Регистрация именованных путей
|
||
@repo.mark_path(name="multiply")
|
||
def int_to_a_mult(i: int) -> A:
|
||
return A(i * 10)
|
||
|
||
@repo.mark_path(name="add")
|
||
def int_to_a_add(i: int) -> A:
|
||
return A(i + 100)
|
||
|
||
# Выбор пути при использовании
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_preference="multiply" # Явный выбор пути
|
||
)
|
||
|
||
# Или глобальная настройка
|
||
repo.set_path_preference("multiply")
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Полный контроль** | Разработчик всегда выбирает путь |
|
||
| **Читаемость** | Код явно показывает какой путь используется |
|
||
| **Документированность** | Имена путей служат документацией |
|
||
| **Тестируемость** | Можно тестировать разные пути явно |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Бойлерплейт** | Нужно именовать каждый путь |
|
||
| **Сложность** | Управление именами в больших проектах |
|
||
| **Конфликты имён** | Нужна проверка уникальности |
|
||
| **Не решает комбинаторный взрыв** | Генерация всех вариантов остаётся |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~150 строк изменений
|
||
- **Тесты**: ~15 новых тестов
|
||
- **Риск**: Средний
|
||
|
||
---
|
||
|
||
### Вариант 3: Стратегии выбора пути (Path Selection Strategies)
|
||
|
||
#### Описание
|
||
Встроенные стратегии выбора пути с возможностью расширения.
|
||
|
||
#### Реализация
|
||
```python
|
||
from breakshaft import PathStrategy
|
||
|
||
# Встроенные стратегии:
|
||
# - SHORTEST: кратчайший путь (минимум вызовов)
|
||
# - LONGEST: длиннейший путь (максимум преобразований)
|
||
# - FIRST: первый найденный (быстро, недетерминировано)
|
||
# - EXPLICIT: только явные пути (ошибка если несколько)
|
||
# - CUSTOM: пользовательская функция
|
||
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_strategy=PathStrategy.SHORTEST # или PathStrategy.LONGEST
|
||
)
|
||
|
||
# Пользовательская стратегия
|
||
def my_strategy(paths: list[Path]) -> Path:
|
||
# Логика выбора
|
||
return min(paths, key=lambda p: p.complexity)
|
||
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_strategy=PathStrategy.CUSTOM(my_strategy)
|
||
)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Гибкость** | Разные стратегии для разных случаев |
|
||
| **Расширяемость** | Пользовательские стратегии |
|
||
| **Явность** | Стратегия видна в коде вызова |
|
||
| **Переиспользование** | Стратегии можно переиспользовать |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность API** | 5+ стратегий для изучения |
|
||
| **Не решает комбинаторный взрыв** | Стратегия применяется после генерации |
|
||
| **Производительность** | Некоторые стратегии дорогие |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~200 строк изменений
|
||
- **Тесты**: ~20 новых тестов
|
||
- **Риск**: Средний
|
||
|
||
---
|
||
|
||
### Вариант 4: Ограничение глубины графа (Depth Limiting)
|
||
|
||
#### Описание
|
||
Ограничить максимальную глубину/сложность графа преобразований.
|
||
|
||
#### Реализация
|
||
```python
|
||
repo = ConvRepo(
|
||
max_depth=5, # Максимум 5 преобразований в цепочке
|
||
max_branches=10, # Максимум ветвей в узле
|
||
max_total_paths=100 # Максимум путей для рассмотрения
|
||
)
|
||
|
||
# Или при вызове
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
max_depth=3 # Переопределение для конкретного вызова
|
||
)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Защита от взрыва** | Гарантированная верхняя граница сложности |
|
||
| **Производительность** | Предсказуемое время выполнения |
|
||
| **Простота** | Один параметр для настройки |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Ограничения** | Может отсечь валидные пути |
|
||
| **Не детерминизм** | Не решает проблему выбора пути |
|
||
| **Настройка** | Нужно подбирать значения |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~80 строк изменений
|
||
- **Тесты**: ~8 новых тестов
|
||
- **Риск**: Низкий
|
||
|
||
---
|
||
|
||
### Вариант 5: Кэширование графов (Graph Caching)
|
||
|
||
#### Описание
|
||
Кэшировать построенные графы преобразований для повторного использования.
|
||
|
||
#### Реализация
|
||
```python
|
||
from breakshaft import LRUCache, PersistentCache
|
||
|
||
# Кэш в памяти (LRU)
|
||
repo = ConvRepo(cache=LRUCache(max_size=1000))
|
||
|
||
# Персистентный кэш (на диске)
|
||
repo = ConvRepo(cache=PersistentCache(path=".breakshaft_cache"))
|
||
|
||
# Декоратор для кэширования
|
||
@repo.cached
|
||
def get_converter(from_types, to_type):
|
||
return repo.get_conversion(from_types, to_type)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Производительность** | Повторные вызовы мгновенные |
|
||
| **Масштабируемость** | Работает с большим числом инжекторов |
|
||
| **Прозрачность** | Кэш прозрачен для пользователя |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Память** | Кэш потребляет память |
|
||
| **Инвалидация** | Сложность при изменении инжекторов |
|
||
| **Не решает выбор пути** | Кэширует выбранный путь, но не детерминирует выбор |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~250 строк изменений
|
||
- **Тесты**: ~25 новых тестов
|
||
- **Риск**: Высокий (состояние, гонки)
|
||
|
||
---
|
||
|
||
### Вариант 6: Статический анализ графа (Static Graph Analysis)
|
||
|
||
#### Описание
|
||
Анализировать граф на этапе регистрации инжекторов, обнаруживать проблемы заранее.
|
||
|
||
#### Реализация
|
||
```python
|
||
# Предварительный анализ
|
||
repo = ConvRepo(validate_on_register=True)
|
||
|
||
@repo.mark_injector()
|
||
def int_to_a_v1(i: int) -> A:
|
||
return A(i * 10)
|
||
|
||
@repo.mark_injector()
|
||
def int_to_a_v2(i: int) -> A:
|
||
return A(i + 100) # Warning: Ambiguous path detected!
|
||
|
||
# Явная валидация
|
||
warnings = repo.validate()
|
||
for w in warnings:
|
||
print(f"Warning: {w}")
|
||
# Warning: Ambiguous path for A: 2 injectors found
|
||
|
||
# Разрешение конфликта
|
||
repo.resolve_ambiguity(A, preferred=int_to_a_v1)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Раннее обнаружение** | Ошибки на этапе регистрации |
|
||
| **Документированность** | Явное разрешение конфликтов |
|
||
| **Безопасность** | Невозможно создать неоднозначность |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность** | Анализ графа дорог |
|
||
| **Жёсткость** | Может быть слишком ограничительно |
|
||
| **Не решает комбинаторный взрыв** | Анализ добавляет overhead |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~300 строк изменений
|
||
- **Тесты**: ~30 новых тестов
|
||
- **Риск**: Высокий
|
||
|
||
---
|
||
|
||
### Вариант 7: Версионирование путей (Path Versioning)
|
||
|
||
#### Описание
|
||
Каждый путь имеет версию, можно выбирать конкретную версию.
|
||
|
||
#### Реализация
|
||
```python
|
||
@repo.mark_injector(version="1.0")
|
||
def int_to_a(i: int) -> A:
|
||
return A(i * 10)
|
||
|
||
@repo.mark_injector(version="2.0") # Новая версия
|
||
def int_to_a(i: int) -> A:
|
||
return A(i + 100)
|
||
|
||
# Выбор версии
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_version="1.0" # Использовать старую версию
|
||
)
|
||
|
||
# Или диапазон
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_version=">=1.0,<3.0"
|
||
)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Эволюция** | Плавный переход между версиями |
|
||
| **Совместимость** | Старый код продолжает работать |
|
||
| **Контроль** | Явный выбор версии |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность** | Управление версиями |
|
||
| **Бойлерплейт** | Версии для каждого пути |
|
||
| **Не решает комбинаторный взрыв** | Все версии генерируются |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~200 строк изменений
|
||
- **Тесты**: ~20 новых тестов
|
||
- **Риск**: Средний
|
||
|
||
---
|
||
|
||
### Вариант 8: Комбинированный подход (Hybrid Solution)
|
||
|
||
#### Описание
|
||
Комбинация нескольких подходов для максимального эффекта.
|
||
|
||
#### Реализация
|
||
```python
|
||
repo = ConvRepo(
|
||
# Приоритеты по умолчанию
|
||
default_priority=0,
|
||
|
||
# Кэширование
|
||
cache=LRUCache(max_size=500),
|
||
|
||
# Ограничения
|
||
max_depth=10,
|
||
max_total_paths=1000,
|
||
|
||
# Стратегия по умолчанию
|
||
default_strategy=PathStrategy.SHORTEST,
|
||
|
||
# Валидация
|
||
validate_on_register=True,
|
||
)
|
||
|
||
# Гибкое переопределение
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
priority_override={int_to_a_v1: 10}, # Приоритет для конкретных
|
||
strategy=PathStrategy.EXPLICIT,
|
||
cache_key="my_custom_key"
|
||
)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Максимальная гибкость** | Все инструменты доступны |
|
||
| **Масштабируемость** | Работает с большими графами |
|
||
| **Контроль** | Полный контроль над поведением |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность** | Много параметров для настройки |
|
||
| **Обучение** | Крутая кривая обучения |
|
||
| **Риск ошибок** | Неправильная конфигурация |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~500 строк изменений
|
||
- **Тесты**: ~50 новых тестов
|
||
- **Риск**: Высокий
|
||
|
||
---
|
||
|
||
### Вариант 9: Декларативное описание графа (Declarative Graph)
|
||
|
||
#### Описание
|
||
Полностью декларативное описание путей преобразования вместо автоматического вывода.
|
||
|
||
#### Реализация
|
||
```python
|
||
# Декларативное описание
|
||
repo.define_graph({
|
||
"paths": [
|
||
{
|
||
"name": "multiply_path",
|
||
"steps": [
|
||
{"from": int, "to": A, "using": int_to_a_mult},
|
||
{"from": A, "to": B, "using": a_to_b},
|
||
],
|
||
"priority": 10
|
||
},
|
||
{
|
||
"name": "add_path",
|
||
"steps": [
|
||
{"from": int, "to": A, "using": int_to_a_add},
|
||
],
|
||
"priority": 1
|
||
}
|
||
]
|
||
})
|
||
|
||
# Выбор пути по имени
|
||
fn = repo.get_conversion(
|
||
(int,),
|
||
consumer,
|
||
path_name="multiply_path"
|
||
)
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Полный контроль** | Явное описание всех путей |
|
||
| **Детерминизм** | Никакой неявной логики |
|
||
| **Документированность** | Граф виден в коде |
|
||
| **Нет комбинаторного взрыва** | Только явные пути |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Бойлерплейт** | Много кода для описания |
|
||
| **Потеря автоматизма** | Нет автоматического вывода путей |
|
||
| **Сложность поддержки** | Изменение графа требует правки описания |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~400 строк изменений
|
||
- **Тесты**: ~40 новых тестов
|
||
- **Риск**: Высокий (меняет парадигму)
|
||
|
||
---
|
||
|
||
### Вариант 10: Машинное обучение для выбора пути (ML-Based Selection)
|
||
|
||
#### Описание
|
||
Использовать ML для предсказания "лучшего" пути на основе истории использования.
|
||
|
||
#### Реализация
|
||
```python
|
||
from breakshaft import MLPathSelector
|
||
|
||
repo = ConvRepo(
|
||
path_selector=MLPathSelector(
|
||
training_data="usage_history.json",
|
||
features=["execution_time", "memory_usage", "success_rate"]
|
||
)
|
||
)
|
||
|
||
# ML выбирает путь на основе:
|
||
# - Истории успешных выполнений
|
||
# - Времени выполнения
|
||
# - Потребления памяти
|
||
# - Контекста (типы, размеры данных)
|
||
|
||
fn = repo.get_conversion((int,), consumer)
|
||
# Путь выбирается автоматически на основе модели
|
||
```
|
||
|
||
#### Сильные стороны
|
||
| + | Описание |
|
||
|---|----------|
|
||
| **Адаптивность** | Учится на использовании |
|
||
| **Оптимизация** | Выбирает эффективные пути |
|
||
| **Автоматизм** | Не требует ручной настройки |
|
||
|
||
#### Слабые стороны
|
||
| - | Описание |
|
||
|---|----------|
|
||
| **Сложность** | ML модель + обучение |
|
||
| **Непредсказуемость** | ML может выбрать неожиданно |
|
||
| **Зависимость от данных** | Нужна история для обучения |
|
||
| **Overhead** | Предсказание модели |
|
||
|
||
#### Оценка сложности
|
||
- **Код**: ~600 строк изменений
|
||
- **Тесты**: ~60 новых тестов
|
||
- **Риск**: Очень высокий
|
||
|
||
---
|
||
|
||
## 4. Сравнительная таблица
|
||
|
||
| Вариант | Детерминизм | Производительность | Сложность | Обратная совместимость | Риск |
|
||
|---------|-------------|-------------------|-----------|----------------------|------|
|
||
| **1. Приоритеты** | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ | Низкий |
|
||
| **2. Именованные пути** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | Средний |
|
||
| **3. Стратегии** | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | Средний |
|
||
| **4. Ограничение глубины** | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ | Низкий |
|
||
| **5. Кэширование** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Высокий |
|
||
| **6. Статический анализ** | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | Высокий |
|
||
| **7. Версионирование** | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | Средний |
|
||
| **8. Комбинированный** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Высокий |
|
||
| **9. Декларативный** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | Высокий |
|
||
| **10. ML** | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Очень высокий |
|
||
|
||
### Легенда:
|
||
- ⭐⭐⭐⭐⭐ — Отлично
|
||
- ⭐⭐⭐⭐ — Хорошо
|
||
- ⭐⭐⭐ — Удовлетворительно
|
||
- ⭐⭐ — Плохо
|
||
- ⭐ — Очень плохо
|
||
|
||
---
|
||
|
||
## 5. Рекомендации
|
||
|
||
### 5.1. Краткосрочные решения (быстрая победа)
|
||
|
||
**Вариант 1 + Вариант 4**: Приоритеты + Ограничение глубины
|
||
|
||
```python
|
||
# Минимальные изменения для детерминизма
|
||
@repo.mark_injector(priority=10)
|
||
def preferred_converter(...): ...
|
||
|
||
repo = ConvRepo(max_depth=10)
|
||
```
|
||
|
||
**Преимущества:**
|
||
- ~130 строк кода
|
||
- Низкий риск
|
||
- Обратная совместимость
|
||
- Решает 80% проблем
|
||
|
||
### 5.2. Среднесрочные решения (баланс)
|
||
|
||
**Вариант 3 + Вариант 5**: Стратегии + Кэширование
|
||
|
||
```python
|
||
repo = ConvRepo(
|
||
cache=LRUCache(1000),
|
||
default_strategy=PathStrategy.SHORTEST
|
||
)
|
||
```
|
||
|
||
**Преимущества:**
|
||
- Хорошая производительность
|
||
- Гибкость для пользователей
|
||
- Решает проблему комбинаторного взрыва
|
||
|
||
### 5.3. Долгосрочные решения (полное решение)
|
||
|
||
**Вариант 8 (Комбинированный) с элементами Варианта 9**
|
||
|
||
```python
|
||
repo = HybridConvRepo(
|
||
priorities=True,
|
||
strategies=True,
|
||
cache=True,
|
||
validation=True,
|
||
declarative_mode=False # Опционально
|
||
)
|
||
```
|
||
|
||
**Преимущества:**
|
||
- Полное решение проблемы
|
||
- Масштабируемость
|
||
- Гибкость
|
||
|
||
### 5.4. Дорожная карта
|
||
|
||
```
|
||
Фаза 1 (2 недели):
|
||
├── Приоритеты инжекторов
|
||
├── Ограничение глубины
|
||
└── Тесты
|
||
|
||
Фаза 2 (4 недели):
|
||
├── Стратегии выбора пути
|
||
├── Базовое кэширование
|
||
└── Документация
|
||
|
||
Фаза 3 (6 недель):
|
||
├── Статический анализ
|
||
├── Продвинутое кэширование
|
||
├── Декларативный режим (опционально)
|
||
└── Полное тестирование
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Заключение
|
||
|
||
### 6.1. Выводы
|
||
|
||
1. **Нет серебряной пули** — каждый вариант имеет компромиссы
|
||
2. **Комбинированный подход** даёт лучший результат
|
||
3. **Начинать с простого** — приоритеты + ограничения
|
||
4. **Итеративное улучшение** — добавлять функции постепенно
|
||
|
||
### 6.2. Риски
|
||
|
||
| Риск | Вероятность | Влияние | Митигация |
|
||
|------|-------------|---------|-----------|
|
||
| Ломает обратную совместимость | Низкая | Высокое | Поэтапное внедрение |
|
||
| Усложнение API | Средняя | Среднее | Хорошая документация |
|
||
| Производительность | Низкая | Высокое | Бенчмарки на каждом этапе |
|
||
| Комбинаторный взрыв | Средняя | Высокое | Ограничения + кэш |
|
||
|
||
### 6.3. Следующие шаги
|
||
|
||
1. **Выбрать подход** для Фазы 1
|
||
2. **Создать PR** с приоритетами и ограничениями
|
||
3. **Собрать фидбэк** от пользователей
|
||
4. **Итеративно улучшать**
|
||
|
||
---
|
||
|
||
*Документ создан для breakshaft v0.1.6*
|
||
*Дата: 2026-03-28*
|