refactor: ленивый резолв приоритетов без замены в репозитории

Изменения:
- _resolve_relative_priorities() возвращает словарь вместо замены
- Приоритеты не заменяются в ConversionPoint
- resolved_priorities передаётся в filter_exploded_callgraph_branch
- get_aggregate_priority использует resolved_priorities если есть

Преимущества:
- Относительные приоритеты сохраняются в репозитории
- Можно добавлять новые инжекторы после get_conversion()
- Нет мутации состояния репозитория
- Каждый вызов get_conversion() использует актуальные приоритеты

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Qwen Code Assistant
2026-03-28 14:37:50 +00:00
parent 4c1568fd47
commit a71e9fd424
3 changed files with 44 additions and 34 deletions

View File

@@ -52,9 +52,9 @@ class ConvRepo:
allow_sync: bool = True,
force_async: bool = False
):
# Разрешаем относительные приоритеты
self._resolve_relative_priorities()
# Разрешаем относительные приоритеты (не заменяя, а получая словарь)
resolved_priorities = self._resolve_relative_priorities()
filtered_injectors = self.filtered_injectors(allow_async, allow_sync)
pipeline_callseq = []
orig_from_types = tuple(from_types)
@@ -71,7 +71,13 @@ class ConvRepo:
else:
injects = extract_return_type(fn)
callseq = self.get_callseq(filtered_injectors, frozenset(from_types), fn, force_commutative)
callseq = self.get_callseq(
filtered_injectors,
frozenset(from_types),
fn,
force_commutative,
resolved_priorities
)
pipeline_callseq += callseq
@@ -133,7 +139,8 @@ class ConvRepo:
injectors: frozenset[ConversionPoint],
from_types: frozenset[type],
fn: Callable | Iterable[ConversionPoint] | ConversionPoint,
force_commutative: bool) -> list[ConversionPoint]:
force_commutative: bool,
resolved_priorities: Optional[dict[ConversionPoint, float]] = None) -> list[ConversionPoint]:
cg = self.walker.generate_callgraph(injectors, from_types, fn)
if cg is None:
@@ -142,12 +149,12 @@ class ConvRepo:
for inj in injectors:
available_types.add(inj.injects)
available_types.update(inj.requires)
# Определяем требуемые типы
required_types = set()
if callable(fn):
required_types = extract_func_argtypes(fn)
raise NoConversionPath(
from_types=tuple(from_types),
target=fn,
@@ -157,7 +164,11 @@ class ConvRepo:
exploded = self.walker.explode_callgraph_branches(cg, from_types)
selected = self.walker.filter_exploded_callgraph_branch(exploded)
# Передаём resolved_priorities в filter_exploded_callgraph_branch
selected = self.walker.filter_exploded_callgraph_branch(
exploded,
resolved_priorities=resolved_priorities
)
if len(selected) == 0:
raise NoConversionPath(
from_types=tuple(from_types),
@@ -222,8 +233,8 @@ class ConvRepo:
reason="force_async=True requires allow_async=True"
)
# Разрешаем относительные приоритеты
self._resolve_relative_priorities()
# Разрешаем относительные приоритеты (не заменяя, а получая словарь)
resolved_priorities = self._resolve_relative_priorities()
filtered_injectors = self.filtered_injectors(allow_async, allow_sync)
@@ -231,7 +242,8 @@ class ConvRepo:
filtered_injectors,
frozenset(from_types),
fn,
force_commutative
force_commutative,
resolved_priorities
)
ret_fn = self.renderer.render(from_types, callseq, force_async=force_async, store_sources=self.store_sources)
@@ -253,30 +265,22 @@ class ConvRepo:
"""
Разрешить относительные приоритеты и вычислить абсолютные значения.
Проходит по всем инжекторам и если есть относительные приоритеты,
вычисляет абсолютные значения на основе графа зависимостей.
Не заменяет приоритеты в репозитории, а возвращает словарь
{ConversionPoint: float_priority} для использования в graph_walker.
Returns:
Dict[ConversionPoint, float] или None если нет относительных приоритетов
"""
injectors = list(self.convertor_set)
# Проверяем есть ли относительные приоритеты
has_relative = any(isinstance(cp.priority, RelativePriority) for cp in injectors)
if not has_relative:
return
return None
try:
priorities = resolve_priorities(injectors)
# Применяем разрешённые приоритеты
# Создаём новые ConversionPoint с абсолютными приоритетами
new_injectors = set()
for cp in injectors:
if cp in priorities:
new_injectors.add(cp.copy_with(priority=priorities[cp]))
else:
new_injectors.add(cp)
# Обновляем репозиторий
self._convertor_set = new_injectors
return priorities
except CycleDetectedError as e:
# Переупаковываем в наше исключение

View File

@@ -134,7 +134,8 @@ class GraphWalker:
def filter_exploded_callgraph_branch(cls,
variants: list[CallgraphVariant],
priority_injectors: Optional[frozenset[ConversionPoint | Callable]] = None,
relevance_metric: Optional[Callable[[CallgraphVariant], int | float]] = None) \
relevance_metric: Optional[Callable[[CallgraphVariant], int | float]] = None,
resolved_priorities: Optional[dict[ConversionPoint, float]] = None) \
-> list[CallgraphVariant]:
if relevance_metric is None:
@@ -148,7 +149,7 @@ class GraphWalker:
for metric in template_metrics:
if len(variants) == 1:
break
new_variants = cls.filter_exploded_callgraph_branch(variants, priority_injectors, metric)
new_variants = cls.filter_exploded_callgraph_branch(variants, priority_injectors, metric, resolved_priorities)
if len(new_variants) > 0:
variants = new_variants
@@ -156,21 +157,26 @@ class GraphWalker:
if len(variants) > 1:
# Вычисляем aggregate priority для каждого варианта (сумма приоритетов всех инжекторов в пути)
def get_aggregate_priority(variant: CallgraphVariant) -> float:
priority = variant.injector.priority
# Используем resolved_priorities если есть, иначе берём из cp.priority
if resolved_priorities and variant.injector in resolved_priorities:
priority = resolved_priorities[variant.injector]
else:
priority = variant.injector.priority if isinstance(variant.injector.priority, (int, float)) else 0.0
for subg in variant.subgraphs:
for subv in subg.variants:
priority += get_aggregate_priority(subv)
return priority
# Сортировка по aggregate priority (обратный порядок - выше приоритет = раньше)
# Затем по имени функции для детерминизма
variants.sort(key=lambda x: (-get_aggregate_priority(x), universal_qualname(x.injector.fn)))
# Выбираем вариант с наивысшим aggregate приоритетом
max_priority = get_aggregate_priority(variants[0])
selected = [v for v in variants if get_aggregate_priority(v) == max_priority]
variants = selected
return variants
if len(variants) < 2: