Coverage for bioimageio/spec/_internal/node_converter.py: 100%

22 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-02 14:21 +0000

1from __future__ import annotations 

2 

3from abc import ABC, abstractmethod 

4from typing import ( 

5 Any, 

6 Dict, 

7 Final, 

8 Generic, 

9 Type, 

10 Union, 

11 cast, 

12) 

13 

14from typing_extensions import ( 

15 TypeVar, 

16 TypeVarTuple, 

17 Unpack, 

18) 

19 

20from .node import Node 

21from .utils import ( 

22 assert_all_params_set_explicitly, 

23) 

24from .validated_string import ValidatedString 

25 

26SRC = TypeVar("SRC", bound=Union[Node, ValidatedString]) 

27TGT = TypeVar("TGT", bound=Node) 

28 

29 

30# converter without any additional args or kwargs: 

31# class Converter(Generic[SRC, TGT], ABC): 

32# # src: ClassVar[Type[SRC]] 

33# # tgt: ClassVar[Type[TGT]] 

34# # note: the above is not yet possible, see https://github.com/python/typing/discussions/1424 

35# # we therefore use an instance 

36# def __init__(self, src: Type[SRC], tgt: Type[TGT], /): 

37# super().__init__() 

38# self.src: Final[Type[SRC]] = src 

39# self.tgt: Final[Type[TGT]] = tgt 

40 

41# @abstractmethod 

42# def _convert(self, src: SRC, tgt: "type[TGT | dict[str, Any]] ", /) -> "TGT | dict[str, Any]": 

43# ... 

44 

45# def convert(self, source: SRC, /) -> TGT: 

46# """convert `source` node 

47 

48# Args: 

49# source: A bioimageio description node 

50 

51# Raises: 

52# ValidationError: conversion failed 

53# """ 

54# data = self.convert_as_dict(source) 

55# return assert_all_params_set_explicitly(self.tgt)(**data) 

56 

57# def convert_as_dict(self, source: SRC) -> Dict[str, Any]: 

58# return cast(Dict[str, Any], self._convert(source, dict)) 

59 

60 

61# A TypeVar bound to a TypedDict seemed like a good way to add converter kwargs: 

62# ``` 

63# class ConverterKwargs(TypedDict): 

64# pass 

65# KW = TypeVar("KW", bound=ConverterKwargs, default=ConverterKwargs) 

66# ``` 

67# sadly we cannot use a TypeVar bound to TypedDict and then unpack it in the Converter methods, 

68# see https://github.com/python/typing/issues/1399 

69# Therefore we use a TypeVarTuple and positional only args instead 

70# (We are avoiding ParamSpec for its ambiguity 'args vs kwargs') 

71CArgs = TypeVarTuple("CArgs") 

72 

73 

74class Converter(Generic[SRC, TGT, Unpack[CArgs]], ABC): 

75 # src: ClassVar[Type[SRC]] 

76 # tgt: ClassVar[Type[TGT]] 

77 # note: the above is not yet possible, see https://github.com/python/typing/discussions/1424 

78 # we therefore use an instance 

79 def __init__(self, src: Type[SRC], tgt: Type[TGT], /): 

80 super().__init__() 

81 self.src: Final[Type[SRC]] = src 

82 self.tgt: Final[Type[TGT]] = tgt 

83 

84 @abstractmethod 

85 def _convert( 

86 self, src: SRC, tgt: "type[TGT | dict[str, Any]]", /, *args: Unpack[CArgs] 

87 ) -> "TGT | dict[str, Any]": ... 

88 

89 # note: the following is not (yet) allowed, see https://github.com/python/typing/issues/1399 

90 # we therefore use `kwargs` (and not `**kwargs`) 

91 # def convert(self, source: SRC, /, **kwargs: Unpack[KW]) -> TGT: 

92 def convert(self, source: SRC, /, *args: Unpack[CArgs]) -> TGT: 

93 """convert `source` node 

94 

95 Args: 

96 source: A bioimageio description node 

97 

98 Raises: 

99 ValidationError: conversion failed 

100 """ 

101 data = self.convert_as_dict(source, *args) 

102 return assert_all_params_set_explicitly(self.tgt)(**data) 

103 

104 def convert_as_dict(self, source: SRC, /, *args: Unpack[CArgs]) -> Dict[str, Any]: 

105 return cast(Dict[str, Any], self._convert(source, dict, *args))