22import logging
33from functools import partial
44from itertools import cycle
5- from typing import Optional , Type , Union
5+ from typing import Optional
66
77from async_substrate_interface .async_substrate import AsyncSubstrateInterface
88from async_substrate_interface .errors import MaxRetriesExceeded
99from async_substrate_interface .sync_substrate import SubstrateInterface
1010
11- SubstrateClass = Type [Union [SubstrateInterface , AsyncSubstrateInterface ]]
1211
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+ ]
1383
14- class RetrySubstrate :
84+
85+ class RetrySyncSubstrate (SubstrateInterface ):
1586 def __init__ (
1687 self ,
17- substrate : SubstrateClass ,
18- main_url : str ,
88+ url : str ,
1989 use_remote_preset : bool = False ,
2090 fallback_chains : Optional [list [str ]] = None ,
2191 retry_forever : bool = False ,
@@ -28,187 +98,152 @@ def __init__(
2898 _mock : bool = False ,
2999 ):
30100 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
40101 self .fallback_chains = (
41102 iter (fallback_chains )
42103 if not retry_forever
43- else cycle (fallback_chains + [main_url ])
104+ else cycle (fallback_chains + [url ])
44105 )
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
45111 initialized = False
46- for chain_url in [main_url ] + fallback_chains :
112+ for chain_url in [url ] + fallback_chains :
47113 try :
48- self . _substrate = self . _substrate_class (
114+ super (). __init__ (
49115 url = chain_url ,
50116 ss58_format = ss58_format ,
51117 type_registry = type_registry ,
52118 use_remote_preset = use_remote_preset ,
119+ type_registry_preset = type_registry_preset ,
53120 chain_name = chain_name ,
54121 _mock = _mock ,
122+ retry_timeout = retry_timeout ,
123+ max_retries = max_retries ,
55124 )
56125 initialized = True
57126 break
58127 except ConnectionError :
59128 logging .warning (f"Unable to connect to { chain_url } " )
60129 if not initialized :
61130 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 } "
63132 )
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 ))
162135
163136 def _retry (self , method , * args , ** kwargs ):
164137 try :
165- method_ = getattr (self . _substrate , method )
138+ method_ = getattr (self , method )
166139 return method_ (* args , ** kwargs )
167140 except (MaxRetriesExceeded , ConnectionError , ConnectionRefusedError ) as e :
168141 try :
169142 self ._reinstantiate_substrate (e )
170- method_ = getattr (self . _substrate , method )
143+ method_ = getattr (self , method )
171144 return self ._retry (method_ (* args , ** kwargs ))
172145 except StopIteration :
173146 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."
175148 )
176149 raise MaxRetriesExceeded
177150
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 ):
179231 try :
180- method_ = getattr (self . _substrate , method )
232+ method_ = getattr (self , method )
181233 if asyncio .iscoroutinefunction (method_ ):
182234 return await method_ (* args , ** kwargs )
183235 else :
184236 return method_ (* args , ** kwargs )
185237 except (MaxRetriesExceeded , ConnectionError , ConnectionRefusedError ) as e :
186238 try :
187239 self ._reinstantiate_substrate (e )
188- method_ = getattr (self . _substrate , method )
240+ method_ = getattr (self , method )
189241 if asyncio .iscoroutinefunction (method_ ):
190242 return await method_ (* args , ** kwargs )
191243 else :
192244 return method_ (* args , ** kwargs )
193245 except StopIteration :
194246 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."
196248 )
197249 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