7
7
8
8
9
9
class WizardException (Exception ):
10
- """Base exception class for the Wizards"""
10
+ """Base exception class for the Wizards. """
11
11
pass
12
12
13
13
14
14
class WizardLoader (object ):
15
- """This class is responsible for searching various paths to locate wizard
16
- models. Given a wizard name it will return a wizard object representing the
17
- wizard. Delegates to botocore for finding and loading the JSON models.
15
+ """This class is responsible for searching various paths to locate wizards.
16
+
17
+ Given a wizard name it will return a wizard object representing the wizard.
18
+ Delegates to botocore for finding and loading the JSON models.
18
19
"""
19
20
20
- def __init__ (self ):
21
- self ._session = botocore .session .get_session ()
21
+ def __init__ (self , session = None ):
22
+ """Initialize a wizard factory.
23
+
24
+ :type session: :class:`botocore.session.Session`
25
+ :param session: (Optional) The botocore session to be used when loading
26
+ models and retrieving clients.
27
+ """
28
+ self ._session = session
29
+ if session is None :
30
+ self ._session = botocore .session .Session ()
22
31
self ._loader = self ._session .get_component ('data_loader' )
23
32
24
33
def load_wizard (self , name ):
25
- """Given a wizard's name, returns an instance of that wizard.
34
+ """Given a wizard's name, return an instance of that wizard.
26
35
27
36
:type name: str
28
37
:param name: The name of the desired wizard.
38
+
39
+ :rtype: :class:`Wizard`
40
+ :return: The wizard object loaded.
29
41
"""
30
42
# TODO possible naming collisions here, always pick first for now
31
43
# Need to discuss and specify wizard invocation
32
44
services = self ._loader .list_available_services (type_name = name )
33
45
model = self ._loader .load_service_model (services [0 ], name )
34
- return Wizard (model )
46
+ return self .create_wizard (model )
47
+
48
+ def create_wizard (self , model ):
49
+ """Given a wizard specification, return an instance of that wizard.
50
+
51
+ :type model: dict
52
+ :param model: The wizard specification to be used.
53
+
54
+ :rtype: :class:`Wizard`
55
+ :return: The wizard object created.
56
+
57
+ :raises: :class:`WizardException`
58
+ """
59
+ start_stage = model .get ('StartStage' )
60
+ if not start_stage :
61
+ raise WizardException ('Start stage not specified' )
62
+ stages = model .get ('Stages' )
63
+ return Wizard (start_stage , stages , self ._session )
35
64
36
65
37
66
class Wizard (object ):
38
- """The main wizard object containing all of the stages, the environment,
39
- botocore sessions, and the logic to drive the wizards.
40
- """
67
+ """Main wizard object. Contains main wizard driving logic."""
68
+
69
+ def __init__ (self , start_stage , stages , session ):
70
+ """Construct a new Wizard.
71
+
72
+ :type start_stage: str
73
+ :param start_stage: The name of the starting stage for the wizard.
41
74
42
- def __init__ ( self , spec = None ):
43
- """Constructs a new Wizard
75
+ :type stages: array of dict
76
+ :param stages: An array of stage models to generate stages from.
44
77
45
- :type spec: dict
46
- :param spec: (Optional) Constructs the wizard by loading the given
47
- model specification.
78
+ :type session: :class:`botocore.session.Session`
79
+ :param session: The botocore session to be used when creating clients.
48
80
"""
49
81
self .env = Environment ()
50
- self ._session = botocore . session . get_session ()
82
+ self ._session = session
51
83
self ._cached_creator = index .CachedClientCreator (self ._session )
52
- if spec :
53
- self .load_from_dict ( spec )
84
+ self . start_stage = start_stage
85
+ self ._load_stages ( stages )
54
86
55
- def load_from_dict (self , spec ):
56
- """Loads the model specification from a dict, populating the
57
- wizard object.
87
+ def _load_stages (self , stages ):
88
+ """Load the stages dictionary from the given array of stage models.
58
89
59
- :type spec: dict
60
- :param spec: The model specification.
61
-
62
- :raises: :class:`WizardException`
90
+ :type stages: array of dict
91
+ :param stages: An array of stage models to be converted into objects.
63
92
"""
64
- self .start_stage = spec .get ('StartStage' , None )
65
- if not self .start_stage :
66
- raise WizardException ("Start stage not specified" )
67
93
self .stages = {}
68
- for s in spec ['Stages' ]:
69
- stage = Stage (s , self )
94
+ for stage_model in stages :
95
+ stage_attrs = {
96
+ 'name' : stage_model .get ('Name' ),
97
+ 'prompt' : stage_model .get ('Prompt' ),
98
+ 'retrieval' : stage_model .get ('Retrieval' ),
99
+ 'next_stage' : stage_model .get ('NextStage' ),
100
+ 'resolution' : stage_model .get ('Resolution' ),
101
+ 'interaction' : stage_model .get ('Interaction' ),
102
+ }
103
+ stage = Stage (self .env , self ._cached_creator , ** stage_attrs )
70
104
self .stages [stage .name ] = stage
71
105
72
106
def execute (self ):
73
- """Runs the wizard. Executes Stages until a final stage is reached.
107
+ """Run the wizard. Execute Stages until a final stage is reached.
74
108
75
- :raises: :class:WizardException
109
+ :raises: :class:` WizardException`
76
110
"""
77
111
current_stage = self .start_stage
78
112
while current_stage :
79
- stage = self .stages .get (current_stage , None )
113
+ stage = self .stages .get (current_stage )
80
114
if not stage :
81
- raise WizardException (" Stage not found: %s" % current_stage )
115
+ raise WizardException (' Stage not found: %s' % current_stage )
82
116
stage .execute ()
83
117
current_stage = stage .get_next_stage ()
84
118
# TODO decouple wizard from all I/O
85
- sys .stdout .write (str (self .env )+ '\n ' )
119
+ sys .stdout .write (str (self .env ) + '\n ' )
120
+ sys .stdout .flush ()
86
121
87
122
88
123
class Stage (object ):
89
- """The Stage object contains the meta information for a stage and logic
90
- required to perform all steps present.
91
- """
124
+ """The Stage object. Contains logic to run all steps of the stage."""
125
+
126
+ def __init__ (self , env , creator , name = None , prompt = None , retrieval = None ,
127
+ next_stage = None , resolution = None , interaction = None ):
128
+ """Construct a new Stage object.
129
+
130
+ :type env: :class:`Environment`
131
+ :param env: The environment this stage is based in.
132
+
133
+ :type creator: :class:`CachedClientCreator`
134
+ :param creator: A botocore client creator that supports caching.
92
135
93
- def __init__ (self , spec , wizard ):
94
- """Constructs a new Stage object.
136
+ :type name: str
137
+ :param name: A unique identifier for the stage.
138
+
139
+ :type prompt: str
140
+ :param prompt: A simple message on the overall goal of the stage.
141
+
142
+ :type retrieval: dict
143
+ :param retrieval: The source of data for this stage.
144
+
145
+ :type next_stage: dict
146
+ :param next_stage: Describes what stage comes after this one.
95
147
96
- :type spec : dict
97
- :param spec: The stage specification model .
148
+ :type resolution : dict
149
+ :param resolution: Describes what data to store in the environment .
98
150
99
- :type wizard: :class:`Wizard`
100
- :param wizard: The wizard that this stage is a part of.
151
+ :type interaction: dict
152
+ :param interaction: Describes what type of screen is to be used for
153
+ interaction.
101
154
"""
102
- self ._wizard = wizard
103
- self .name = spec .get ('Name' , None )
104
- self .prompt = spec .get ('Prompt' , None )
105
- self .retrieval = spec .get ('Retrieval' , None )
106
- self .next_stage = spec .get ('NextStage' , None )
107
- self .resolution = spec .get ('Resolution' , None )
108
- self .interaction = spec .get ('Interaction' , None )
155
+ self ._env = env
156
+ self ._cached_creator = creator
157
+ self .name = name
158
+ self .prompt = prompt
159
+ self .retrieval = retrieval
160
+ self .next_stage = next_stage
161
+ self .resolution = resolution
162
+ self .interaction = interaction
109
163
110
164
def __handle_static_retrieval (self ):
111
165
return self .retrieval .get ('Resource' )
112
166
113
167
def __handle_request_retrieval (self ):
114
168
req = self .retrieval ['Resource' ]
115
169
# get client from wizard's cache
116
- client = self ._wizard . _cached_creator .create_client (req ['Service' ])
170
+ client = self ._cached_creator .create_client (req ['Service' ])
117
171
# get the operation from the client
118
172
operation = getattr (client , xform_name (req ['Operation' ]))
119
173
# get any parameters
120
174
parameters = req .get ('Parameters' , {})
121
175
env_parameters = \
122
- self ._wizard . env .resolve_parameters (req .get ('EnvParameters' , {}))
176
+ self ._env .resolve_parameters (req .get ('EnvParameters' , {}))
123
177
# union of parameters and env_parameters, conflicts favor env_params
124
178
parameters = dict (parameters , ** env_parameters )
125
179
# execute operation passing all parameters
126
180
return operation (** parameters )
127
181
128
182
def _handle_retrieval (self ):
129
183
# TODO decouple wizard from all I/O
130
- sys .stdout .write (self .prompt + '\n ' )
184
+ sys .stdout .write (self .prompt + '\n ' )
185
+ sys .stdout .flush ()
131
186
# In case of no retrieval, empty dict
132
187
if not self .retrieval :
133
188
return {}
@@ -156,10 +211,10 @@ def _handle_resolution(self, data):
156
211
if self .resolution :
157
212
if self .resolution .get ('Path' ):
158
213
data = jmespath .search (self .resolution ['Path' ], data )
159
- self ._wizard . env .store (self .resolution ['Key' ], data )
214
+ self ._env .store (self .resolution ['Key' ], data )
160
215
161
216
def get_next_stage (self ):
162
- """Resolves the next stage name for the stage after this one.
217
+ """Resolve the next stage name for the stage after this one.
163
218
164
219
:rtype: str
165
220
:return: The name of the next stage.
@@ -169,11 +224,12 @@ def get_next_stage(self):
169
224
elif self .next_stage ['Type' ] == 'Name' :
170
225
return self .next_stage ['Name' ]
171
226
elif self .next_stage ['Type' ] == 'Variable' :
172
- return self ._wizard . env .retrieve (self .next_stage ['Name' ])
227
+ return self ._env .retrieve (self .next_stage ['Name' ])
173
228
174
229
# Executes all three steps of the stage
175
230
def execute (self ):
176
- """Executes all steps in the stage if they are present.
231
+ """Execute all steps in the stage if they are present.
232
+
177
233
1) Perform Retrieval.
178
234
2) Perform Interaction on retrieved data.
179
235
3) Perform Resolution to store data in the environment.
@@ -184,9 +240,7 @@ def execute(self):
184
240
185
241
186
242
class Environment (object ):
187
- """This class is used to store variables into a dict and retrieve them
188
- via JMESPath queries instead of normal keys.
189
- """
243
+ """Store vars into a dict and retrives them with JMESPath queries."""
190
244
191
245
def __init__ (self ):
192
246
self ._variables = {}
@@ -195,7 +249,7 @@ def __str__(self):
195
249
return json .dumps (self ._variables , indent = 4 , sort_keys = True )
196
250
197
251
def store (self , key , val ):
198
- """Stores a variable under the given key.
252
+ """Store a variable under the given key.
199
253
200
254
:type key: str
201
255
:param key: The key to store the value as.
@@ -206,18 +260,19 @@ def store(self, key, val):
206
260
self ._variables [key ] = val
207
261
208
262
def retrieve (self , path ):
209
- """Retrieves the variable corresponding to the given JMESPath query.
263
+ """Retrieve the variable corresponding to the given JMESPath query.
210
264
211
265
:type path: str
212
266
:param path: The JMESPath query to be used when locating the variable.
213
267
"""
214
268
return jmespath .search (path , self ._variables )
215
269
216
270
def resolve_parameters (self , keys ):
217
- """Resolves all keys in the given keys dict. Expects all values in the
218
- keys dict to be JMESPath queries to be used when retrieving from the
219
- environment. Interpolates all values from their path to the actual
220
- value stored in the environment.
271
+ """Resolve all keys in the given keys dict.
272
+
273
+ Expects all values in the keys dict to be JMESPath queries to be used
274
+ when retrieving from the environment. Interpolates all values from
275
+ their path to the actual value stored in the environment.
221
276
222
277
:type keys: dict
223
278
:param keys: A dict of keys to paths that need to be resolved.
0 commit comments