##// END OF EJS Templates
Merge pull request #1850 from bfroehle/_1736_try_next...
Thomas Kluyver -
r7341:d7c7a8f5 merge
parent child Browse files
Show More
@@ -0,0 +1,75
1 # -*- coding: utf-8 -*-
2 """Tests for CommandChainDispatcher."""
3
4 from __future__ import absolute_import
5
6 #-----------------------------------------------------------------------------
7 # Imports
8 #-----------------------------------------------------------------------------
9
10 import nose.tools as nt
11 from IPython.core.error import TryNext
12 from IPython.core.hooks import CommandChainDispatcher
13
14 #-----------------------------------------------------------------------------
15 # Local utilities
16 #-----------------------------------------------------------------------------
17
18 # Define two classes, one which succeeds and one which raises TryNext. Each
19 # sets the attribute `called` to True when it is called.
20 class Okay(object):
21 def __init__(self, message):
22 self.message = message
23 self.called = False
24 def __call__(self):
25 self.called = True
26 return self.message
27
28 class Fail(object):
29 def __init__(self, message):
30 self.message = message
31 self.called = False
32 def __call__(self):
33 self.called = True
34 raise TryNext(self.message)
35
36 #-----------------------------------------------------------------------------
37 # Test functions
38 #-----------------------------------------------------------------------------
39
40 def test_command_chain_dispatcher_ff():
41 """Test two failing hooks"""
42 fail1 = Fail(u'fail1')
43 fail2 = Fail(u'fail2')
44 dp = CommandChainDispatcher([(0, fail1),
45 (10, fail2)])
46
47 try:
48 dp()
49 except TryNext as e:
50 nt.assert_equal(str(e), u'fail2')
51 else:
52 assert False, "Expected exception was not raised."
53
54 nt.assert_true(fail1.called)
55 nt.assert_true(fail2.called)
56
57 def test_command_chain_dispatcher_fofo():
58 """Test a mixture of failing and succeeding hooks."""
59 fail1 = Fail(u'fail1')
60 fail2 = Fail(u'fail2')
61 okay1 = Okay(u'okay1')
62 okay2 = Okay(u'okay2')
63
64 dp = CommandChainDispatcher([(0, fail1),
65 # (5, okay1), # add this later
66 (10, fail2),
67 (15, okay2)])
68 dp.add(okay1, 5)
69
70 nt.assert_equal(dp(), u'okay1')
71
72 nt.assert_true(fail1.called)
73 nt.assert_true(okay1.called)
74 nt.assert_false(fail2.called)
75 nt.assert_false(okay2.called)
@@ -35,22 +35,9 class TryNext(IPythonCoreError):
35 35 """Try next hook exception.
36 36
37 37 Raise this in your hook function to indicate that the next hook handler
38 should be used to handle the operation. If you pass arguments to the
39 constructor those arguments will be used by the next hook instead of the
40 original ones.
41
42 A _msg argument will not be passed on, so it can be used as a displayable
43 error message.
38 should be used to handle the operation.
44 39 """
45 40
46 def __init__(self, _msg="", *args, **kwargs):
47 self.args = args
48 self.kwargs = kwargs
49 self.msg = _msg
50
51 def __str__(self):
52 return str(self.msg)
53
54 41 class UsageError(IPythonCoreError):
55 42 """Error in magic function arguments, etc.
56 43
@@ -131,17 +131,15 class CommandChainDispatcher:
131 131 This will call all funcs in chain with the same args as were given to
132 132 this function, and return the result of first func that didn't raise
133 133 TryNext"""
134
134 last_exc = TryNext()
135 135 for prio,cmd in self.chain:
136 136 #print "prio",prio,"cmd",cmd #dbg
137 137 try:
138 138 return cmd(*args, **kw)
139 except TryNext, exc:
140 if exc.args or exc.kwargs:
141 args = exc.args
142 kw = exc.kwargs
139 except TryNext as exc:
140 last_exc = exc
143 141 # if no function will accept it, raise TryNext up to the caller
144 raise TryNext(*args, **kw)
142 raise last_exc
145 143
146 144 def __str__(self):
147 145 return str(self.chain)
@@ -15,9 +15,8 def win32_clipboard_get():
15 15 try:
16 16 import win32clipboard
17 17 except ImportError:
18 message = ("Getting text from the clipboard requires the pywin32 "
19 "extensions: http://sourceforge.net/projects/pywin32/")
20 raise TryNext(_msg=message)
18 raise TryNext("Getting text from the clipboard requires the pywin32 "
19 "extensions: http://sourceforge.net/projects/pywin32/")
21 20 win32clipboard.OpenClipboard()
22 21 text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)
23 22 # FIXME: convert \r\n to \n?
@@ -44,9 +43,8 def tkinter_clipboard_get():
44 43 try:
45 44 import Tkinter
46 45 except ImportError:
47 message = ("Getting text from the clipboard on this platform "
48 "requires Tkinter.")
49 raise TryNext(_msg=message)
46 raise TryNext("Getting text from the clipboard on this platform "
47 "requires Tkinter.")
50 48 root = Tkinter.Tk()
51 49 root.withdraw()
52 50 text = root.clipboard_get()
@@ -25,3 +25,11 Other new features
25 25 :meth:`~IPython.core.interactiveshell.InteractiveShell.run_cell` to
26 26 :meth:`~IPython.core.interactiveshell.InteractiveShell.run_ast_nodes`
27 27 is now configurable.
28
29 Backwards incompatible changes
30 ------------------------------
31
32 * The exception :exc:`IPython.core.error.TryNext` previously accepted
33 arguments and keyword arguments to be passed to the next implementation
34 of the hook. This feature was removed as it made error message propagation
35 difficult and violated the principle of loose coupling.
General Comments 0
You need to be logged in to leave comments. Login now