Skip to content

Commit 9899b1b

Browse files
merged [default] with [dev] branch
2 parents 633bd6b + cbb5236 commit 9899b1b

File tree

5 files changed

+192
-128
lines changed

5 files changed

+192
-128
lines changed

robottools/library/context/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
# From .method:
2929
'contextmethod']
3030

31+
from moretools import qualname
32+
3133
from robottools.library.session.metaoptions import Meta
3234
from robottools.library.keywords import KeywordsDict
3335

@@ -66,6 +68,9 @@ class ContextHandlerMeta(type):
6668
## return type.__new__(metacls, clsname, bases, clsattrs)
6769

6870
def __init__(cls, clsname, bases, clsattrs):
71+
excname = cls.__name__ + 'Error'
72+
cls.ContextError = type(excname, (RuntimeError, ), {})
73+
6974
try:
7075
names = cls.contexts
7176
except AttributeError:
@@ -87,12 +92,17 @@ def switch_context(self, name):
8792
break
8893
for context in cls.contexts:
8994
if context.name == name:
95+
if switch_func: # Custom switch hook
96+
try:
97+
switch_func(self, name)
98+
except Exception as exc:
99+
raise cls.ContextError(
100+
"Couldn't switch context to %s (%s: %s)"
101+
% (repr(name), qualname(type(exc)), exc))
90102
self.contexts.remove(current)
91103
self.contexts.append(context)
92-
if switch_func: # Custom switch hook
93-
switch_func(self, name)
94104
return
95-
raise ValueError(name)
105+
raise cls.ContextError("Context not found: %s" % repr(name))
96106

97107
keyword_name = switch_context.__name__ = 'switch_' + clsname.lower()
98108
cls.keywords[keyword_name] = switch_context

robottools/library/keywords/__init__.py

Lines changed: 88 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
# from .errors:
3232
'InvalidKeywordOption', 'KeywordNotDefined']
3333

34+
import sys
35+
from six import reraise
3436
from itertools import chain
3537
from 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):

robottools/library/session/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def switch_session(cls, name):
6363
"""
6464
name = str(name)
6565
try:
66-
session = cls.session = cls.sessions[name]
66+
session = cls.sessions[name]
6767
except KeyError:
6868
raise cls.SessionError('Session not found: %s' % repr(name))
6969
return session

robottools/library/session/meta.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import re
3030
from copy import deepcopy
3131

32-
from moretools import dictitems
32+
from moretools import qualname, dictitems
3333

3434
from robottools.library.keywords import KeywordsDict
3535
from .metaoptions import Meta
@@ -61,7 +61,7 @@ def __new__(mcs, clsname, bases, clsattrs):
6161
clsattrs['meta'] = meta
6262

6363
excname = meta.upper_identifier_name + 'Error'
64-
clsattrs['SessionError'] = type(excname, (RuntimeError,), {})
64+
clsattrs['SessionError'] = type(excname, (RuntimeError, ), {})
6565

6666
# The handler's dictionary of opened sessions
6767
clsattrs['sessions'] = {}
@@ -190,9 +190,21 @@ def switch_session(self, name):
190190
if session is previous:
191191
break
192192
else:
193-
close_func(self, previous)
193+
try:
194+
close_func(self, previous)
195+
except Exception as exc:
196+
raise cls.SessionError(
197+
"Couldn't close unnamed session "
198+
"on switching to %s (%s: %s)"
199+
% (repr(name), qualname(type(exc)), exc))
194200
if switch_func:
195-
switch_func(self, active)
201+
try:
202+
switch_func(self, active)
203+
except Exception as exc:
204+
raise cls.SessionError(
205+
"Couldn't switch session to %s (%s: %s)"
206+
% (repr(name), qualname(type(exc)), exc))
207+
cls.session = active
196208

197209
keywordname = 'switch_' + meta.identifier_name
198210
cls.keywords[keywordname] = switch_session

0 commit comments

Comments
 (0)