@@ -591,8 +591,8 @@ class OperationHandlerTransactionPatternConfig(PatternConfig, StorageTypeMixin,
591591 """
592592
593593 type : Literal ['transaction' ] = 'transaction'
594- source : str | ContractConfig | None = None
595- destination : str | ContractConfig | None = None
594+ source : ContractConfig | None = None
595+ destination : ContractConfig | None = None
596596 entrypoint : str | None = None
597597 optional : bool = False
598598 alias : str | None = None
@@ -605,17 +605,17 @@ def __post_init_post_parse__(self) -> None:
605605 raise ConfigurationError ('Transactions with entrypoint must also have destination' )
606606
607607 def iter_imports (self , package : str ) -> Iterator [tuple [str , str ]]:
608- if self .entrypoint :
609- module_name = self .destination_contract_config .module_name
608+ if self .entrypoint and self . destination :
609+ module_name = self .destination .module_name
610610 yield 'dipdup.models' , 'Transaction'
611611 yield self .format_parameter_import (package , module_name , self .entrypoint , self .alias )
612612 yield self .format_storage_import (package , module_name )
613613 else :
614614 yield self .format_untyped_operation_import ()
615615
616616 def iter_arguments (self ) -> Iterator [tuple [str , str ]]:
617- if self .entrypoint :
618- module_name = self .destination_contract_config .module_name
617+ if self .entrypoint and self . destination :
618+ module_name = self .destination .module_name
619619 yield self .format_operation_argument (
620620 module_name ,
621621 self .entrypoint ,
@@ -630,18 +630,6 @@ def iter_arguments(self) -> Iterator[tuple[str, str]]:
630630 self .alias ,
631631 )
632632
633- @cached_property
634- def source_contract_config (self ) -> ContractConfig :
635- if not isinstance (self .source , ContractConfig ):
636- raise ConfigInitializationException
637- return self .source
638-
639- @cached_property
640- def destination_contract_config (self ) -> ContractConfig :
641- if not isinstance (self .destination , ContractConfig ):
642- raise ConfigInitializationException
643- return self .destination
644-
645633
646634@dataclass
647635class OperationHandlerOriginationPatternConfig (PatternConfig , StorageTypeMixin , SubgroupIndexMixin ):
@@ -657,9 +645,9 @@ class OperationHandlerOriginationPatternConfig(PatternConfig, StorageTypeMixin,
657645 """
658646
659647 type : Literal ['origination' ] = 'origination'
660- source : str | ContractConfig | None = None
661- similar_to : str | ContractConfig | None = None
662- originated_contract : str | ContractConfig | None = None
648+ source : ContractConfig | None = None
649+ similar_to : ContractConfig | None = None
650+ originated_contract : ContractConfig | None = None
663651 optional : bool = False
664652 strict : bool = False
665653 alias : str | None = None
@@ -678,18 +666,18 @@ def __hash__(self) -> int:
678666 return hash (
679667 '' .join (
680668 [
681- self .source_contract_config .address if self .source else '' ,
682- self .similar_to_contract_config .address if self .similar_to else '' ,
683- self .originated_contract_config .address if self .originated_contract else '' ,
669+ self .source .address if self .source else '' ,
670+ self .similar_to .address if self .similar_to else '' ,
671+ self .originated_contract .address if self .originated_contract else '' ,
684672 ]
685673 )
686674 )
687675
688676 def iter_imports (self , package : str ) -> Iterator [tuple [str , str ]]:
689677 if self .originated_contract :
690- module_name = self .originated_contract_config .module_name
678+ module_name = self .originated_contract .module_name
691679 elif self .similar_to :
692- module_name = self .similar_to_contract_config .module_name
680+ module_name = self .similar_to .module_name
693681 elif self .source :
694682 yield 'dipdup.models' , 'OperationData'
695683 return
@@ -723,31 +711,13 @@ def module_name(self) -> str:
723711 @cached_property
724712 def contract_config (self ) -> ContractConfig :
725713 if self .originated_contract :
726- return self .originated_contract_config
714+ return self .originated_contract
727715 if self .similar_to :
728- return self .similar_to_contract_config
716+ return self .similar_to
729717 if self .source :
730- return self .source_contract_config
718+ return self .source
731719 raise RuntimeError
732720
733- @cached_property
734- def source_contract_config (self ) -> ContractConfig :
735- if not isinstance (self .source , ContractConfig ):
736- raise ConfigInitializationException
737- return self .source
738-
739- @cached_property
740- def similar_to_contract_config (self ) -> ContractConfig :
741- if not isinstance (self .similar_to , ContractConfig ):
742- raise ConfigInitializationException
743- return self .similar_to
744-
745- @cached_property
746- def originated_contract_config (self ) -> ContractConfig :
747- if not isinstance (self .originated_contract , ContractConfig ):
748- raise ConfigInitializationException
749- return self .originated_contract
750-
751721
752722@dataclass
753723class CallbackMixin (CodegenMixin ):
@@ -870,7 +840,7 @@ class IndexConfig(ABC, TemplateValuesMixin, NameMixin, SubscriptionsMixin, Paren
870840 """
871841
872842 kind : str
873- datasource : str | TzktDatasourceConfig
843+ datasource : TzktDatasourceConfig
874844
875845 def __post_init_post_parse__ (self ) -> None :
876846 TemplateValuesMixin .__post_init_post_parse__ (self )
@@ -920,8 +890,8 @@ class OperationIndexConfig(IndexConfig):
920890
921891 kind : Literal ['operation' ]
922892 handlers : tuple [OperationHandlerConfig , ...]
893+ contracts : list [ContractConfig ] = field (default_factory = list )
923894 types : tuple [OperationType , ...] = (OperationType .transaction ,)
924- contracts : list [str | ContractConfig ] = field (default_factory = list )
925895
926896 first_level : int = 0
927897 last_level : int = 0
@@ -968,10 +938,10 @@ def import_objects(self, package: str) -> None:
968938
969939 for pattern_config in handler_config .pattern :
970940 if isinstance (pattern_config , OperationHandlerTransactionPatternConfig ):
971- if not pattern_config .entrypoint :
941+ if not ( pattern_config .entrypoint and pattern_config . destination ) :
972942 continue
973943
974- module_name = pattern_config .destination_contract_config .module_name
944+ module_name = pattern_config .destination .module_name
975945 pattern_config .initialize_parameter_cls (package , module_name , pattern_config .entrypoint )
976946 pattern_config .initialize_storage_cls (package , module_name )
977947
@@ -994,7 +964,7 @@ class BigMapHandlerConfig(HandlerConfig, kind='handler'):
994964 :param path: Path to big map (alphanumeric string with dots)
995965 """
996966
997- contract : str | ContractConfig
967+ contract : ContractConfig
998968 path : str
999969
1000970 def __post_init_post_parse__ (self ) -> None :
@@ -1077,7 +1047,7 @@ class BigMapIndexConfig(IndexConfig):
10771047 """
10781048
10791049 kind : Literal ['big_map' ]
1080- datasource : str | TzktDatasourceConfig
1050+ datasource : TzktDatasourceConfig
10811051 handlers : tuple [BigMapHandlerConfig , ...]
10821052
10831053 skip_history : SkipHistory = SkipHistory .never
@@ -1119,7 +1089,7 @@ class HeadIndexConfig(IndexConfig):
11191089 """Head block index config"""
11201090
11211091 kind : Literal ['head' ]
1122- datasource : str | TzktDatasourceConfig
1092+ datasource : TzktDatasourceConfig
11231093 handlers : tuple [HeadHandlerConfig , ...]
11241094
11251095 @property
@@ -1137,10 +1107,10 @@ def import_objects(self, package: str) -> None:
11371107
11381108@dataclass
11391109class TokenTransferHandlerConfig (HandlerConfig , kind = 'handler' ):
1140- contract : str | ContractConfig | None = None
1110+ contract : ContractConfig | None = None
11411111 token_id : int | None = None
1142- from_ : str | ContractConfig | None = Field (default = None , alias = 'from' )
1143- to : str | ContractConfig | None = None
1112+ from_ : ContractConfig | None = Field (default = None , alias = 'from' )
1113+ to : ContractConfig | None = None
11441114
11451115 def iter_imports (self , package : str ) -> Iterator [tuple [str , str ]]:
11461116 yield 'dipdup.context' , 'HandlerContext'
@@ -1157,7 +1127,7 @@ class TokenTransferIndexConfig(IndexConfig):
11571127 """Token index config"""
11581128
11591129 kind : Literal ['token_transfer' ]
1160- datasource : str | TzktDatasourceConfig
1130+ datasource : TzktDatasourceConfig
11611131 handlers : tuple [TokenTransferHandlerConfig , ...] = field (default_factory = tuple )
11621132
11631133 first_level : int = 0
@@ -1170,7 +1140,7 @@ def import_objects(self, package: str) -> None:
11701140
11711141@dataclass
11721142class EventHandlerConfig (HandlerConfig , kind = 'handler' ):
1173- contract : str | ContractConfig
1143+ contract : ContractConfig
11741144 tag : str
11751145
11761146 def __post_init_post_parse__ (self ) -> None :
@@ -1216,7 +1186,7 @@ def iter_arguments(self) -> Iterator[tuple[str, str]]:
12161186
12171187@dataclass
12181188class UnknownEventHandlerConfig (HandlerConfig , kind = 'handler' ):
1219- contract : str | ContractConfig
1189+ contract : ContractConfig
12201190
12211191 @cached_property
12221192 def contract_config (self ) -> ContractConfig :
@@ -1240,7 +1210,7 @@ def iter_arguments(self) -> Iterator[tuple[str, str]]:
12401210@dataclass
12411211class EventIndexConfig (IndexConfig ):
12421212 kind : Literal ['event' ]
1243- datasource : str | TzktDatasourceConfig
1213+ datasource : TzktDatasourceConfig
12441214 handlers : tuple [EventHandlerConfigU , ...] = field (default_factory = tuple )
12451215
12461216 first_level : int = 0
@@ -1312,7 +1282,7 @@ class JobConfig(NameMixin):
13121282 :param args: Arguments to pass to the hook
13131283 """
13141284
1315- hook : str | HookConfig
1285+ hook : HookConfig
13161286 crontab : str | None = None
13171287 interval : int | None = None
13181288 daemon : bool = False
@@ -1818,7 +1788,10 @@ def _resolve_index_subscriptions(self, index_config: IndexConfigU) -> None:
18181788 )
18191789
18201790 def _resolve_index_links (self , index_config : ResolvedIndexConfigU ) -> None :
1821- """Resolve contract and datasource configs by aliases"""
1791+ """Resolve contract and datasource configs by aliases.
1792+
1793+ WARNING: str type checks are intentional! See `dipdup.config.patch_annotations`.
1794+ """
18221795 # NOTE: Each index must have a corresponding (currently) TzKT datasource
18231796 if isinstance (index_config .datasource , str ):
18241797 index_config .datasource = self .get_tzkt_datasource (index_config .datasource )
@@ -1896,3 +1869,41 @@ def _set_names(self) -> None:
18961869 for named_configs in named_config_sections :
18971870 for name , config in named_configs .items ():
18981871 config .name = name
1872+
1873+
1874+ yaml_annotations = {
1875+ 'TzktDatasourceConfig' : 'str | TzktDatasourceConfig' ,
1876+ 'ContractConfig' : 'str | ContractConfig' ,
1877+ 'ContractConfig | None' : 'str | ContractConfig | None' ,
1878+ 'list[ContractConfig]' : 'list[str | ContractConfig]' ,
1879+ }
1880+ orinal_annotations = {v : k for k , v in yaml_annotations .items ()}
1881+
1882+
1883+ def patch_annotations (replace_table : dict [str , str ]) -> None :
1884+ """Patch dataclass annotations in runtime to allow using aliases in config files.
1885+
1886+ DipDup config allows to use string aliases for contracts and datasources. During `DipDupConfig.load`
1887+ these aliases are resolved to actual configs and never become strings again. This hack allows to add
1888+ `str` in Unions before loading config so we don't need to write isinstance checks everywhere.
1889+ """
1890+ self = importlib .import_module (__name__ )
1891+
1892+ for attr in dir (self ):
1893+ value = getattr (self , attr )
1894+ if not isinstance (value , type ) or not hasattr (value , '__annotations__' ):
1895+ continue
1896+
1897+ # NOTE: All annotations are strings now
1898+ reload = False
1899+ for name , annotation in value .__annotations__ .items ():
1900+ if new_annotation := replace_table .get (annotation ):
1901+ value .__annotations__ [name ] = new_annotation
1902+ reload = True
1903+
1904+ # NOTE: Wrap dataclass again to recreate magic methods
1905+ if reload :
1906+ setattr (self , attr , dataclass (value ))
1907+
1908+
1909+ patch_annotations (yaml_annotations )
0 commit comments