Skip to content

Commit a61c1d4

Browse files
Set config annotations in runtime to allow unresolved links (#549)
1 parent 25ce418 commit a61c1d4

11 files changed

+126
-98
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning].
66

7+
## [Unreleased]
8+
9+
### Fixed
10+
11+
- metadata: Add `limanet` to supported networks.
12+
713
## [6.3.1] - 2022-11-25
814

915
### Fixed

docs/config/reference.md

Lines changed: 22 additions & 13 deletions
Large diffs are not rendered by default.

src/dipdup/codegen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ async def _fetch_operation_pattern_schema(
163163
operation_pattern_config: OperationHandlerPatternConfigU,
164164
datasource_config: TzktDatasourceConfig,
165165
) -> None:
166-
if _is_typed_transaction(operation_pattern_config):
167-
contract_config = operation_pattern_config.destination_contract_config
166+
if _is_typed_transaction(operation_pattern_config) and operation_pattern_config.destination:
167+
contract_config = operation_pattern_config.destination
168168
elif _is_typed_origination(operation_pattern_config):
169169
contract_config = operation_pattern_config.contract_config
170170
else:

src/dipdup/config.py

Lines changed: 74 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -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
647635
class 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
753723
class 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
11391109
class 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
11721142
class 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
12181188
class 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
12411211
class 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)

src/dipdup/context.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from dipdup.config import PostgresDatabaseConfig
4040
from dipdup.config import ResolvedIndexConfigU
4141
from dipdup.config import TokenTransferIndexConfig
42-
from dipdup.config import TzktDatasourceConfig
4342
from dipdup.datasources.coinbase.datasource import CoinbaseDatasource
4443
from dipdup.datasources.datasource import Datasource
4544
from dipdup.datasources.datasource import HttpDatasource
@@ -279,7 +278,7 @@ async def _spawn_index(self, name: str, state: Optional[Index] = None) -> None:
279278
index_config = cast(ResolvedIndexConfigU, self.config.get_index(name))
280279
index: OperationIndex | BigMapIndex | HeadIndex | TokenTransferIndex | EventIndex
281280

282-
datasource_name = cast(TzktDatasourceConfig, index_config.datasource).name
281+
datasource_name = index_config.datasource.name
283282
datasource = self.get_tzkt_datasource(datasource_name)
284283

285284
if isinstance(index_config, OperationIndexConfig):

src/dipdup/datasources/metadata/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ class MetadataNetwork(Enum):
77
jakartanet = 'jakartanet'
88
kathmandunet = 'kathmandunet'
99
mainnet = 'mainnet'
10+
limanet = 'limanet'

0 commit comments

Comments
 (0)