Coverage for src/bioimageio/spec/_internal/node_converter.py: 100%
20 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 09:17 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 09:17 +0000
1from __future__ import annotations
3from abc import ABC, abstractmethod
4from typing import (
5 Any,
6 Dict,
7 Final,
8 Generic,
9 Union,
10 cast,
11)
13from typing_extensions import (
14 TypeVar,
15 TypeVarTuple,
16 Unpack,
17)
19from .node import Node
20from .utils import (
21 assert_all_params_set_explicitly,
22)
23from .validated_string import ValidatedString
25SRC = TypeVar("SRC", bound=Union[Node, ValidatedString])
26TGT = TypeVar("TGT", bound=Node)
29# converter without any additional args or kwargs:
30# class Converter(Generic[SRC, TGT], ABC):
31# # src: ClassVar[Type[SRC]]
32# # tgt: ClassVar[Type[TGT]]
33# # note: the above is not yet possible, see https://github.com/python/typing/discussions/1424
34# # we therefore use an instance
35# def __init__(self, src: Type[SRC], tgt: Type[TGT], /):
36# super().__init__()
37# self.src: Final[Type[SRC]] = src
38# self.tgt: Final[Type[TGT]] = tgt
40# @abstractmethod
41# def _convert(self, src: SRC, tgt: "type[TGT | dict[str, Any]] ", /) -> "TGT | dict[str, Any]":
42# ...
44# def convert(self, source: SRC, /) -> TGT:
45# """convert `source` node
47# Args:
48# source: A bioimageio description node
50# Raises:
51# ValidationError: conversion failed
52# """
53# data = self.convert_as_dict(source)
54# return assert_all_params_set_explicitly(self.tgt)(**data)
56# def convert_as_dict(self, source: SRC) -> Dict[str, Any]:
57# return cast(Dict[str, Any], self._convert(source, dict))
60# A TypeVar bound to a TypedDict seemed like a good way to add converter kwargs:
61# ```
62# class ConverterKwargs(TypedDict):
63# pass
64# KW = TypeVar("KW", bound=ConverterKwargs, default=ConverterKwargs)
65# ```
66# sadly we cannot use a TypeVar bound to TypedDict and then unpack it in the Converter methods,
67# see https://github.com/python/typing/issues/1399
68# Therefore we use a TypeVarTuple and positional only args instead
69# (We are avoiding ParamSpec for its ambiguity 'args vs kwargs')
70CArgs = TypeVarTuple("CArgs")
73class Converter(ABC, Generic[SRC, TGT, Unpack[CArgs]]):
74 # src: ClassVar[Type[SRC]]
75 # tgt: ClassVar[Type[TGT]]
76 # note: the above is not yet possible, see https://github.com/python/typing/discussions/1424
77 # we therefore use an instance
78 def __init__(self, src: type[SRC], tgt: type[TGT], /):
79 super().__init__()
80 self.src: Final[type[SRC]] = src
81 self.tgt: Final[type[TGT]] = tgt
83 @abstractmethod
84 def _convert(
85 self, src: SRC, tgt: type[TGT | dict[str, Any]], /, *args: Unpack[CArgs]
86 ) -> TGT | dict[str, Any]: ...
88 # note: the following is not (yet) allowed, see https://github.com/python/typing/issues/1399
89 # we therefore use `kwargs` (and not `**kwargs`)
90 # def convert(self, source: SRC, /, **kwargs: Unpack[KW]) -> TGT:
91 def convert(self, source: SRC, /, *args: Unpack[CArgs]) -> TGT:
92 """convert `source` node
94 Args:
95 source: A bioimageio description node
97 Raises:
98 ValidationError: conversion failed
99 """
100 data = self.convert_as_dict(source, *args)
101 return assert_all_params_set_explicitly(self.tgt)(**data)
103 def convert_as_dict(self, source: SRC, /, *args: Unpack[CArgs]) -> dict[str, Any]:
104 return cast(Dict[str, Any], self._convert(source, dict, *args))