3131 # from .errors:
3232 'InvalidKeywordOption' , 'KeywordNotDefined' ]
3333
34+ import sys
35+ from six import reraise
3436from itertools import chain
3537from textwrap import dedent
3638
@@ -130,6 +132,8 @@ def __call__(self, *args, **kwargs):
130132 """Call the Keyword's actual function with the given arguments.
131133 """
132134 func = self .func
135+ # the exception to finally reraise (if any)
136+ error = None
133137 # look for explicit <session>= and <context>= switching options
134138 # in kwargs and store the currently active
135139 # session aliases and context names
@@ -145,62 +149,100 @@ def __call__(self, *args, **kwargs):
145149 except KeyError :
146150 pass
147151 else :
148- current_sessions [identifier , plural_identifier ] = getattr (
149- self .libinstance , identifier )
150- getattr (self .libinstance , 'switch_' + identifier )(sname )
152+ previous = getattr (self .libinstance , identifier )
153+ switch = getattr (self .libinstance , 'switch_' + identifier )
154+ try :
155+ switch (sname )
156+ except hcls .SessionError :
157+ error = sys .exc_info ()
158+ # don't switch any more sessions
159+ break
160+ # store previous session for switching back later
161+ current_sessions [identifier , plural_identifier ] = previous
162+ # only perform explicit context switching
163+ # if explicit session switching didn't raise any error
151164 current_contexts = {}
152- for hcls in self .context_handlers :
153- if not getattr (hcls , 'auto_explicit' , False ):
154- continue
155- identifier = hcls .__name__ .lower ()
156- try :
157- ctxname = kwargs .pop (identifier )
158- except KeyError :
159- pass
165+ if error is None :
166+ for hcls in self .context_handlers :
167+ if not getattr (hcls , 'auto_explicit' , False ):
168+ continue
169+ identifier = hcls .__name__ .lower ()
170+ try :
171+ ctxname = kwargs .pop (identifier )
172+ except KeyError :
173+ pass
174+ else :
175+ previous = getattr (self .libinstance , identifier )
176+ switch = getattr (self .libinstance ,
177+ 'switch_' + identifier )
178+ try :
179+ switch (ctxname )
180+ except hcls .ContextError :
181+ error = sys .exc_info ()
182+ # don't switch any more contexts
183+ break
184+ # store previous context for switching back later
185+ current_contexts [identifier ] = previous
186+ # only call the acutal keyword func
187+ # if explicit session and context switching didn't raise any error
188+ if error is None :
189+ # Look for arg type specs:
190+ if func .argtypes :
191+ casted = []
192+ for arg , argtype in zip (args , func .argtypes ):
193+ if not isinstance (arg , argtype ):
194+ arg = argtype (arg )
195+ casted .append (arg )
196+ args = tuple (casted ) + args [len (func .argtypes ):]
197+ # Look for context specific implementation of the Keyword function
198+ for context , context_func in dictitems (func .contexts ):
199+ if context in self .libinstance .contexts :
200+ func = context_func
201+ # Does the keyword support **kwargs?
202+ if self .func .argspec .keywords or not kwargs :
203+ try :
204+ result = func (self .libinstance , * args , ** kwargs )
205+ except Exception :
206+ error = sys .exc_info ()
160207 else :
161- current_contexts [identifier ] = getattr (
162- self .libinstance , identifier )
163- getattr (self .libinstance , 'switch_' + identifier )(ctxname )
164- # Look for arg type specs:
165- if func .argtypes :
166- casted = []
167- for arg , argtype in zip (args , func .argtypes ):
168- if not isinstance (arg , argtype ):
169- arg = argtype (arg )
170- casted .append (arg )
171- args = tuple (casted ) + args [len (func .argtypes ):]
172- # Look for context specific implementation of the Keyword function:
173- for context , context_func in dictitems (func .contexts ):
174- if context in self .libinstance .contexts :
175- func = context_func
176- # Does the keyword support **kwargs?
177- if self .func .argspec .keywords or not kwargs :
178- result = func (self .libinstance , * args , ** kwargs )
179- else :
180- # resolve **kwargs to positional args...
181- posargs = []
182- # (argspec.args start index includes self)
183- for name in self .func .argspec .args [1 + len (args ):]:
184- if name in kwargs :
185- posargs .append (kwargs .pop (name ))
186- # and turn the rest into *varargs in 'key=value' style
187- varargs = ['%s=%s' % (key , kwargs .pop (key ))
188- for key in list (kwargs )
189- if key not in self .func .argspec .args ]
190- result = func (self .libinstance , * chain (args , posargs , varargs ),
191- # if **kwargs left ==> TypeError from Python
192- ** kwargs )
193- # Switch back contexts and sessions (reverse order):
208+ # resolve **kwargs to positional args...
209+ posargs = []
210+ # (argspec.args start index includes self)
211+ for name in self .func .argspec .args [1 + len (args ):]:
212+ if name in kwargs :
213+ posargs .append (kwargs .pop (name ))
214+ # and turn the rest into *varargs in 'key=value' style
215+ varargs = ['%s=%s' % (key , kwargs .pop (key ))
216+ for key in list (kwargs )
217+ if key not in self .func .argspec .args ]
218+ try :
219+ result = func (self .libinstance ,
220+ * chain (args , posargs , varargs ),
221+ # if **kwargs left ==> TypeError from Python
222+ ** kwargs )
223+ except :
224+ error = sys .exc_info ()
225+ # finally try to switch back contexts and sessions (in reverse order)
226+ # before either returning result or reraising any error catched above
194227 for identifier , ctxname in dictitems (current_contexts ):
195- getattr (self .libinstance , 'switch_' + identifier )(ctxname )
228+ switch = getattr (self .libinstance , 'switch_' + identifier )
229+ # don't catch anything here. just step out on error
230+ switch (ctxname )
196231 for (identifier , plural_identifier ), session in dictitems (
197232 current_sessions
198233 ):
199234 for sname , sinstance in dictitems (getattr (
200235 self .libinstance , plural_identifier
201236 )):
202237 if sinstance is session :
203- getattr (self .libinstance , 'switch_' + identifier )(sname )
238+ switch = getattr (self .libinstance , 'switch_' + identifier )
239+ # don't catch anything here. just step out on error
240+ switch (sname )
241+ # was an error catched on initial session or context switching
242+ # or on calling the actual keyword func?
243+ if error is not None :
244+ reraise (* error )
245+ # great! everything went fine :)
204246 return result
205247
206248 def __repr__ (self ):
0 commit comments