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:
@@ -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:
|
||||
# Переупаковываем в наше исключение
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user