22from .loader import ScenarioLoader
33from .rpc_caller import BitcoinRPC
44from .actions import ActionExecutor
5- from typing import Dict , Any , List
5+ from typing import Dict , Any
6+
67
78# Error classes for ScenarioRunner
89class ScenarioRunnerError (Exception ):
910 """Base class for all scenario runner errors."""
11+
1012 pass
13+
14+
1115class ScenarioNotLoadedError (ScenarioRunnerError ):
1216 """Raised when a scenario is tried to be runned but not loaded."""
17+
1318 def __init__ (self ):
1419 super ().__init__ ("No scenario loaded. Please load a scenario first." )
1520
21+
1622class ScenarioRunner :
17- """ScenarioRunner is a class that manages the execution of Bitcoin scenarios.
18- """
19- def __init__ (self ,
20- rpc_user : str ,
21- rpc_password : str ,
22- scenarios_dir : str = "./scenarios" ,
23- base_port : int = 18443 ,
24- ):
25-
23+ """ScenarioRunner is a class that manages the execution of Bitcoin scenarios."""
24+
25+ def __init__ (
26+ self ,
27+ rpc_user : str ,
28+ rpc_password : str ,
29+ scenarios_dir : str = "./scenarios" ,
30+ base_port : int = 18443 ,
31+ ):
2632 self .loader = ScenarioLoader (scenarios_dir )
27- self .variables = {} # Store scenario variables
28-
33+ self .variables = {} # Store scenario variables
34+
2935 rpc = BitcoinRPC (rpc_user , rpc_password , base_port )
3036 self .executor = ActionExecutor (rpc )
31-
37+
3238 self .scenario = None # Will hold the loaded scenario
33- self .config = None # Will hold the scenario configuration
34-
35- def load_scenario (self , scenario_name : str ):
39+ self .config = None # Will hold the scenario configuration
40+
41+ def load_scenario (self , scenario_name : str ):
3642 """Load a scenario by its name. Required before running it.
3743
3844 Args:
3945 scenario_name (str): Name of the scenario to load (without .toml extension).
4046 """
41-
47+
4248 self .scenario = self .loader .load_scenario (scenario_name )
43- self .config = self .scenario ["config" ] # so we dont have to load it again
44-
49+ self .config = self .scenario ["config" ] # so we dont have to load it again
50+
4551 # print infos :
4652 print ("==========================================" )
4753 print (f"Name: { self .scenario ['scenario' ]['name' ]} \n " )
4854 print (f"Description: { self .scenario ['scenario' ]['description' ]} \n " )
4955 print (f"Written by: { self .scenario ['scenario' ]['author' ]} \n " )
5056 print (f"Date: { self .scenario ['scenario' ]['date' ]} " )
5157 print ("==========================================" )
52-
58+
5359 def list_scenarios (self ):
5460 """List available scenarios"""
5561 print ("Available scenarios:" )
5662 scenarios = self .loader .list_scenarios ()
5763 print ("\n " .join (scenarios ) if scenarios else "No scenarios found." )
58-
64+
5965 def _substitute_variables (self , params : Any ) -> Any :
6066 """Replace ${var} with actual values"""
6167 if isinstance (params , str ):
@@ -67,52 +73,53 @@ def _substitute_variables(self, params: Any) -> Any:
6773 elif isinstance (params , list ):
6874 return [self ._substitute_variables (item ) for item in params ]
6975 return params
70-
76+
7177 # ==== runners ====
72-
73- def _run_step (self , step : Dict [str , Any ]) -> None :
74-
78+
79+ def _run_step (self , step : Dict [str , Any ]) -> None :
7580 # exctract actions details
7681 # → the scenario is valid so we can assume that the step has the required keys
7782 action_name = step ["name" ]
7883 action = step ["action" ]
7984 node = step .get ("node" , self .config ["default_node" ])
8085 args = self ._substitute_variables (step .get ("args" , {}))
81-
86+
8287 # execute the action
8388 print (f"Running step: { action_name } (on node: { node } )" )
8489 result = self .executor .execute (action , node , args )
85-
90+
8691 # == deal with options ==
8792 if step .get ("print" , False ):
8893 print (f"Result: \n → { result } \n " )
89- if args .get ("store_result" ,"" ):
94+ if args .get ("store_result" , "" ):
9095 # store the result in the variables dict
9196 var_name = args .get ("store_result" )
9297 self .variables [var_name ] = result
9398 print (f"Stored result in variable: { var_name } = { result } " )
94-
99+
95100 # time and wait
96101 time .sleep (step .get ("wait_after" , self .config ["default_wait" ]))
97-
102+
98103 def run_scenario (self ) -> None :
99104 """Run the loaded scenario step by step"""
100- if not hasattr ( self , ' scenario' ) :
105+ if self . scenario is None :
101106 raise ScenarioNotLoadedError ()
102-
107+
103108 print (f"[SCENARIO] Running scenario: { self .scenario ['scenario' ]['name' ]} " )
104-
109+
105110 # timeout sleep:
106111 time .sleep (self .config .get ("timeout" ))
107-
112+
108113 for i , ststep_name in enumerate (self .scenario ["steps" ]):
109114 print (f"\n [SCENARIO] Step { i + 1 } /{ len (self .scenario ['steps' ])} " )
110115 step = self .scenario ["steps" ][ststep_name ]
111-
116+
112117 try :
113118 self ._run_step (step )
114119 except Exception as e :
115- print (f"[ERROR] An error occurred while running step '{ ststep_name } ': { e } " )
120+ print (
121+ f"[ERROR] An error occurred while running step '{ ststep_name } ': { e } "
122+ )
116123 raise e
117-
118- print ("[SCENARIO] Scenario execution completed." )
124+
125+ print ("[SCENARIO] Scenario execution completed." )
0 commit comments