2
2
import logging
3
3
from functools import partial
4
4
from itertools import cycle
5
- from typing import Optional , Type , Union
5
+ from typing import Optional
6
6
7
7
from async_substrate_interface .async_substrate import AsyncSubstrateInterface
8
8
from async_substrate_interface .errors import MaxRetriesExceeded
9
9
from async_substrate_interface .sync_substrate import SubstrateInterface
10
10
11
- SubstrateClass = Type [Union [SubstrateInterface , AsyncSubstrateInterface ]]
12
11
12
+ RETRY_METHODS = [
13
+ "_get_block_handler" ,
14
+ "apply_type_registry_presets" ,
15
+ "close" ,
16
+ "compose_call" ,
17
+ "connect" ,
18
+ "create_scale_object" ,
19
+ "create_signed_extrinsic" ,
20
+ "create_storage_key" ,
21
+ "decode_scale" ,
22
+ "encode_scale" ,
23
+ "extension_call" ,
24
+ "filter_events" ,
25
+ "filter_extrinsics" ,
26
+ "generate_signature_payload" ,
27
+ "get_account_next_index" ,
28
+ "get_account_nonce" ,
29
+ "get_block" ,
30
+ "get_block_hash" ,
31
+ "get_block_header" ,
32
+ "get_block_metadata" ,
33
+ "get_block_number" ,
34
+ "get_block_runtime_info" ,
35
+ "get_block_runtime_version_for" ,
36
+ "get_block_timestamp" ,
37
+ "get_chain_finalised_head" ,
38
+ "get_chain_head" ,
39
+ "get_constant" ,
40
+ "get_events" ,
41
+ "get_extrinsics" ,
42
+ "get_metadata_call_function" ,
43
+ "get_metadata_constant" ,
44
+ "get_metadata_error" ,
45
+ "get_metadata_errors" ,
46
+ "get_metadata_module" ,
47
+ "get_metadata_modules" ,
48
+ "get_metadata_runtime_call_function" ,
49
+ "get_metadata_runtime_call_functions" ,
50
+ "get_metadata_storage_function" ,
51
+ "get_metadata_storage_functions" ,
52
+ "get_parent_block_hash" ,
53
+ "get_payment_info" ,
54
+ "get_storage_item" ,
55
+ "get_type_definition" ,
56
+ "get_type_registry" ,
57
+ "init_runtime" ,
58
+ "initialize" ,
59
+ "is_valid_ss58_address" ,
60
+ "load_runtime" ,
61
+ "make_payload" ,
62
+ "query" ,
63
+ "query_map" ,
64
+ "query_multi" ,
65
+ "query_multiple" ,
66
+ "reload_type_registry" ,
67
+ "retrieve_extrinsic_by_hash" ,
68
+ "retrieve_extrinsic_by_identifier" ,
69
+ "rpc_request" ,
70
+ "runtime_call" ,
71
+ "search_block_number" ,
72
+ "serialize_constant" ,
73
+ "serialize_module_call" ,
74
+ "serialize_module_error" ,
75
+ "serialize_module_event" ,
76
+ "serialize_storage_item" ,
77
+ "ss58_decode" ,
78
+ "ss58_encode" ,
79
+ "submit_extrinsic" ,
80
+ "subscribe_block_headers" ,
81
+ "supports_rpc_method" ,
82
+ ]
13
83
14
- class RetrySubstrate :
84
+
85
+ class RetrySyncSubstrate (SubstrateInterface ):
15
86
def __init__ (
16
87
self ,
17
- substrate : SubstrateClass ,
18
- main_url : str ,
88
+ url : str ,
19
89
use_remote_preset : bool = False ,
20
90
fallback_chains : Optional [list [str ]] = None ,
21
91
retry_forever : bool = False ,
@@ -28,187 +98,152 @@ def __init__(
28
98
_mock : bool = False ,
29
99
):
30
100
fallback_chains = fallback_chains or []
31
- self ._substrate_class : SubstrateClass = substrate
32
- self .ss58_format : int = ss58_format
33
- self .type_registry : dict = type_registry
34
- self .use_remote_preset : bool = use_remote_preset
35
- self .chain_name : Optional [str ] = chain_name
36
- self .max_retries : int = max_retries
37
- self .retry_timeout : float = retry_timeout
38
- self ._mock = _mock
39
- self .type_registry_preset : Optional [str ] = type_registry_preset
40
101
self .fallback_chains = (
41
102
iter (fallback_chains )
42
103
if not retry_forever
43
- else cycle (fallback_chains + [main_url ])
104
+ else cycle (fallback_chains + [url ])
44
105
)
106
+ self .use_remote_preset = use_remote_preset
107
+ self .chain_name = chain_name
108
+ self ._mock = _mock
109
+ self .retry_timeout = retry_timeout
110
+ self .max_retries = max_retries
45
111
initialized = False
46
- for chain_url in [main_url ] + fallback_chains :
112
+ for chain_url in [url ] + fallback_chains :
47
113
try :
48
- self . _substrate = self . _substrate_class (
114
+ super (). __init__ (
49
115
url = chain_url ,
50
116
ss58_format = ss58_format ,
51
117
type_registry = type_registry ,
52
118
use_remote_preset = use_remote_preset ,
119
+ type_registry_preset = type_registry_preset ,
53
120
chain_name = chain_name ,
54
121
_mock = _mock ,
122
+ retry_timeout = retry_timeout ,
123
+ max_retries = max_retries ,
55
124
)
56
125
initialized = True
57
126
break
58
127
except ConnectionError :
59
128
logging .warning (f"Unable to connect to { chain_url } " )
60
129
if not initialized :
61
130
raise ConnectionError (
62
- f"Unable to connect at any chains specified: { [main_url ] + fallback_chains } "
131
+ f"Unable to connect at any chains specified: { [url ] + fallback_chains } "
63
132
)
64
-
65
- # retries
66
-
67
- # TODO: properties that need retry logic
68
- # properties
69
- # version
70
- # token_decimals
71
- # token_symbol
72
- # name
73
-
74
- retry = (
75
- self ._async_retry
76
- if self ._substrate_class == AsyncSubstrateInterface
77
- else self ._retry
78
- )
79
-
80
- self ._get_block_handler = partial (retry , "_get_block_handler" )
81
- self .apply_type_registry_presets = partial (retry , "apply_type_registry_presets" )
82
- self .close = partial (retry , "close" )
83
- self .compose_call = partial (retry , "compose_call" )
84
- self .connect = partial (retry , "connect" )
85
- self .create_scale_object = partial (retry , "create_scale_object" )
86
- self .create_signed_extrinsic = partial (retry , "create_signed_extrinsic" )
87
- self .create_storage_key = partial (retry , "create_storage_key" )
88
- self .decode_scale = partial (retry , "decode_scale" )
89
- self .encode_scale = partial (retry , "encode_scale" )
90
- self .extension_call = partial (retry , "extension_call" )
91
- self .filter_events = partial (retry , "filter_events" )
92
- self .filter_extrinsics = partial (retry , "filter_extrinsics" )
93
- self .generate_signature_payload = partial (retry , "generate_signature_payload" )
94
- self .get_account_next_index = partial (retry , "get_account_next_index" )
95
- self .get_account_nonce = partial (retry , "get_account_nonce" )
96
- self .get_block = partial (retry , "get_block" )
97
- self .get_block_hash = partial (retry , "get_block_hash" )
98
- self .get_block_header = partial (retry , "get_block_header" )
99
- self .get_block_metadata = partial (retry , "get_block_metadata" )
100
- self .get_block_number = partial (retry , "get_block_number" )
101
- self .get_block_runtime_info = partial (retry , "get_block_runtime_info" )
102
- self .get_block_runtime_version_for = partial (
103
- retry , "get_block_runtime_version_for"
104
- )
105
- self .get_block_timestamp = partial (retry , "get_block_timestamp" )
106
- self .get_chain_finalised_head = partial (retry , "get_chain_finalised_head" )
107
- self .get_chain_head = partial (retry , "get_chain_head" )
108
- self .get_constant = partial (retry , "get_constant" )
109
- self .get_events = partial (retry , "get_events" )
110
- self .get_extrinsics = partial (retry , "get_extrinsics" )
111
- self .get_metadata_call_function = partial (retry , "get_metadata_call_function" )
112
- self .get_metadata_constant = partial (retry , "get_metadata_constant" )
113
- self .get_metadata_error = partial (retry , "get_metadata_error" )
114
- self .get_metadata_errors = partial (retry , "get_metadata_errors" )
115
- self .get_metadata_module = partial (retry , "get_metadata_module" )
116
- self .get_metadata_modules = partial (retry , "get_metadata_modules" )
117
- self .get_metadata_runtime_call_function = partial (
118
- retry , "get_metadata_runtime_call_function"
119
- )
120
- self .get_metadata_runtime_call_functions = partial (
121
- retry , "get_metadata_runtime_call_functions"
122
- )
123
- self .get_metadata_storage_function = partial (
124
- retry , "get_metadata_storage_function"
125
- )
126
- self .get_metadata_storage_functions = partial (
127
- retry , "get_metadata_storage_functions"
128
- )
129
- self .get_parent_block_hash = partial (retry , "get_parent_block_hash" )
130
- self .get_payment_info = partial (retry , "get_payment_info" )
131
- self .get_storage_item = partial (retry , "get_storage_item" )
132
- self .get_type_definition = partial (retry , "get_type_definition" )
133
- self .get_type_registry = partial (retry , "get_type_registry" )
134
- self .init_runtime = partial (retry , "init_runtime" )
135
- self .initialize = partial (retry , "initialize" )
136
- self .is_valid_ss58_address = partial (retry , "is_valid_ss58_address" )
137
- self .load_runtime = partial (retry , "load_runtime" )
138
- self .make_payload = partial (retry , "make_payload" )
139
- self .query = partial (retry , "query" )
140
- self .query_map = partial (retry , "query_map" )
141
- self .query_multi = partial (retry , "query_multi" )
142
- self .query_multiple = partial (retry , "query_multiple" )
143
- self .reload_type_registry = partial (retry , "reload_type_registry" )
144
- self .retrieve_extrinsic_by_hash = partial (retry , "retrieve_extrinsic_by_hash" )
145
- self .retrieve_extrinsic_by_identifier = partial (
146
- retry , "retrieve_extrinsic_by_identifier"
147
- )
148
- self .rpc_request = partial (retry , "rpc_request" )
149
- self .runtime_call = partial (retry , "runtime_call" )
150
- self .search_block_number = partial (retry , "search_block_number" )
151
- self .serialize_constant = partial (retry , "serialize_constant" )
152
- self .serialize_module_call = partial (retry , "serialize_module_call" )
153
- self .serialize_module_error = partial (retry , "serialize_module_error" )
154
- self .serialize_module_event = partial (retry , "serialize_module_event" )
155
- self .serialize_storage_item = partial (retry , "serialize_storage_item" )
156
- self .ss58_decode = partial (retry , "ss58_decode" )
157
- self .ss58_encode = partial (retry , "ss58_encode" )
158
- self .submit_extrinsic = partial (retry , "submit_extrinsic" )
159
- self .subscribe_block_headers = partial (retry , "subscribe_block_headers" )
160
- self .supports_rpc_method = partial (retry , "supports_rpc_method" )
161
- self .ws = self ._substrate .ws
133
+ for method in RETRY_METHODS :
134
+ setattr (self , method , partial (self ._retry , method ))
162
135
163
136
def _retry (self , method , * args , ** kwargs ):
164
137
try :
165
- method_ = getattr (self . _substrate , method )
138
+ method_ = getattr (self , method )
166
139
return method_ (* args , ** kwargs )
167
140
except (MaxRetriesExceeded , ConnectionError , ConnectionRefusedError ) as e :
168
141
try :
169
142
self ._reinstantiate_substrate (e )
170
- method_ = getattr (self . _substrate , method )
143
+ method_ = getattr (self , method )
171
144
return self ._retry (method_ (* args , ** kwargs ))
172
145
except StopIteration :
173
146
logging .error (
174
- f"Max retries exceeded with { self ._substrate . url } . No more fallback chains."
147
+ f"Max retries exceeded with { self .url } . No more fallback chains."
175
148
)
176
149
raise MaxRetriesExceeded
177
150
178
- async def _async_retry (self , method , * args , ** kwargs ):
151
+ def _reinstantiate_substrate (self , e : Optional [Exception ] = None ) -> None :
152
+ next_network = next (self .fallback_chains )
153
+ if e .__class__ == MaxRetriesExceeded :
154
+ logging .error (
155
+ f"Max retries exceeded with { self .url } . Retrying with { next_network } ."
156
+ )
157
+ else :
158
+ print (f"Connection error. Trying again with { next_network } " )
159
+ super ().__init__ (
160
+ url = next_network ,
161
+ ss58_format = self .ss58_format ,
162
+ type_registry = self .type_registry ,
163
+ use_remote_preset = self .use_remote_preset ,
164
+ chain_name = self .chain_name ,
165
+ _mock = self ._mock ,
166
+ retry_timeout = self .retry_timeout ,
167
+ max_retries = self .max_retries ,
168
+ )
169
+
170
+
171
+ class RetryAsyncSubstrate (AsyncSubstrateInterface ):
172
+ def __init__ (
173
+ self ,
174
+ url : str ,
175
+ use_remote_preset : bool = False ,
176
+ fallback_chains : Optional [list [str ]] = None ,
177
+ retry_forever : bool = False ,
178
+ ss58_format : Optional [int ] = None ,
179
+ type_registry : Optional [dict ] = None ,
180
+ type_registry_preset : Optional [str ] = None ,
181
+ chain_name : str = "" ,
182
+ max_retries : int = 5 ,
183
+ retry_timeout : float = 60.0 ,
184
+ _mock : bool = False ,
185
+ ):
186
+ fallback_chains = fallback_chains or []
187
+ self .fallback_chains = (
188
+ iter (fallback_chains )
189
+ if not retry_forever
190
+ else cycle (fallback_chains + [url ])
191
+ )
192
+ self .use_remote_preset = use_remote_preset
193
+ self .chain_name = chain_name
194
+ self ._mock = _mock
195
+ self .retry_timeout = retry_timeout
196
+ self .max_retries = max_retries
197
+ super ().__init__ (
198
+ url = url ,
199
+ ss58_format = ss58_format ,
200
+ type_registry = type_registry ,
201
+ use_remote_preset = use_remote_preset ,
202
+ type_registry_preset = type_registry_preset ,
203
+ chain_name = chain_name ,
204
+ _mock = _mock ,
205
+ retry_timeout = retry_timeout ,
206
+ max_retries = max_retries ,
207
+ )
208
+ for method in RETRY_METHODS :
209
+ setattr (self , method , partial (self ._retry , method ))
210
+
211
+ def _reinstantiate_substrate (self , e : Optional [Exception ] = None ) -> None :
212
+ next_network = next (self .fallback_chains )
213
+ if e .__class__ == MaxRetriesExceeded :
214
+ logging .error (
215
+ f"Max retries exceeded with { self .url } . Retrying with { next_network } ."
216
+ )
217
+ else :
218
+ print (f"Connection error. Trying again with { next_network } " )
219
+ super ().__init__ (
220
+ url = next_network ,
221
+ ss58_format = self .ss58_format ,
222
+ type_registry = self .type_registry ,
223
+ use_remote_preset = self .use_remote_preset ,
224
+ chain_name = self .chain_name ,
225
+ _mock = self ._mock ,
226
+ retry_timeout = self .retry_timeout ,
227
+ max_retries = self .max_retries ,
228
+ )
229
+
230
+ async def _retry (self , method , * args , ** kwargs ):
179
231
try :
180
- method_ = getattr (self . _substrate , method )
232
+ method_ = getattr (self , method )
181
233
if asyncio .iscoroutinefunction (method_ ):
182
234
return await method_ (* args , ** kwargs )
183
235
else :
184
236
return method_ (* args , ** kwargs )
185
237
except (MaxRetriesExceeded , ConnectionError , ConnectionRefusedError ) as e :
186
238
try :
187
239
self ._reinstantiate_substrate (e )
188
- method_ = getattr (self . _substrate , method )
240
+ method_ = getattr (self , method )
189
241
if asyncio .iscoroutinefunction (method_ ):
190
242
return await method_ (* args , ** kwargs )
191
243
else :
192
244
return method_ (* args , ** kwargs )
193
245
except StopIteration :
194
246
logging .error (
195
- f"Max retries exceeded with { self ._substrate . url } . No more fallback chains."
247
+ f"Max retries exceeded with { self .url } . No more fallback chains."
196
248
)
197
249
raise MaxRetriesExceeded
198
-
199
- def _reinstantiate_substrate (self , e : Optional [Exception ] = None ) -> None :
200
- next_network = next (self .fallback_chains )
201
- if e .__class__ == MaxRetriesExceeded :
202
- logging .error (
203
- f"Max retries exceeded with { self ._substrate .url } . Retrying with { next_network } ."
204
- )
205
- else :
206
- print (f"Connection error. Trying again with { next_network } " )
207
- self ._substrate = self ._substrate_class (
208
- url = next_network ,
209
- ss58_format = self .ss58_format ,
210
- type_registry = self .type_registry ,
211
- use_remote_preset = self .use_remote_preset ,
212
- chain_name = self .chain_name ,
213
- _mock = self ._mock ,
214
- )
0 commit comments