Show More
@@ -0,0 +1,139 b'' | |||
|
1 | """Infrastructure for registering and firing callbacks on application events. | |
|
2 | ||
|
3 | Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to | |
|
4 | be called at specific times, or a collection of alternative methods to try, | |
|
5 | callbacks are designed to be used by extension authors. A number of callbacks | |
|
6 | can be registered for the same event without needing to be aware of one another. | |
|
7 | ||
|
8 | The functions defined in this module are no-ops indicating the names of available | |
|
9 | events and the arguments which will be passed to them. | |
|
10 | ||
|
11 | .. note:: | |
|
12 | ||
|
13 | This API is experimental in IPython 2.0, and may be revised in future versions. | |
|
14 | """ | |
|
15 | from __future__ import print_function | |
|
16 | ||
|
17 | class EventManager(object): | |
|
18 | """Manage a collection of events and a sequence of callbacks for each. | |
|
19 | ||
|
20 | This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell` | |
|
21 | instances as an ``events`` attribute. | |
|
22 | ||
|
23 | .. note:: | |
|
24 | ||
|
25 | This API is experimental in IPython 2.0, and may be revised in future versions. | |
|
26 | """ | |
|
27 | def __init__(self, shell, available_events): | |
|
28 | """Initialise the :class:`CallbackManager`. | |
|
29 | ||
|
30 | Parameters | |
|
31 | ---------- | |
|
32 | shell | |
|
33 | The :class:`~IPython.core.interactiveshell.InteractiveShell` instance | |
|
34 | available_callbacks | |
|
35 | An iterable of names for callback events. | |
|
36 | """ | |
|
37 | self.shell = shell | |
|
38 | self.callbacks = {n:[] for n in available_events} | |
|
39 | ||
|
40 | def register(self, event, function): | |
|
41 | """Register a new event callback | |
|
42 | ||
|
43 | Parameters | |
|
44 | ---------- | |
|
45 | event : str | |
|
46 | The event for which to register this callback. | |
|
47 | function : callable | |
|
48 | A function to be called on the given event. It should take the same | |
|
49 | parameters as the appropriate callback prototype. | |
|
50 | ||
|
51 | Raises | |
|
52 | ------ | |
|
53 | TypeError | |
|
54 | If ``function`` is not callable. | |
|
55 | KeyError | |
|
56 | If ``event`` is not one of the known events. | |
|
57 | """ | |
|
58 | if not callable(function): | |
|
59 | raise TypeError('Need a callable, got %r' % function) | |
|
60 | self.callbacks[event].append(function) | |
|
61 | ||
|
62 | def unregister(self, event, function): | |
|
63 | """Remove a callback from the given event.""" | |
|
64 | self.callbacks[event].remove(function) | |
|
65 | ||
|
66 | def reset(self, event): | |
|
67 | """Clear all callbacks for the given event.""" | |
|
68 | self.callbacks[event] = [] | |
|
69 | ||
|
70 | def reset_all(self): | |
|
71 | """Clear all callbacks for all events.""" | |
|
72 | self.callbacks = {n:[] for n in self.callbacks} | |
|
73 | ||
|
74 | def trigger(self, event, *args, **kwargs): | |
|
75 | """Call callbacks for ``event``. | |
|
76 | ||
|
77 | Any additional arguments are passed to all callbacks registered for this | |
|
78 | event. Exceptions raised by callbacks are caught, and a message printed. | |
|
79 | """ | |
|
80 | for func in self.callbacks[event]: | |
|
81 | try: | |
|
82 | func(*args, **kwargs) | |
|
83 | except Exception: | |
|
84 | print("Error in callback {} (for {}):".format(func, event)) | |
|
85 | self.shell.showtraceback() | |
|
86 | ||
|
87 | # event_name -> prototype mapping | |
|
88 | available_events = {} | |
|
89 | ||
|
90 | def _define_event(callback_proto): | |
|
91 | available_events[callback_proto.__name__] = callback_proto | |
|
92 | return callback_proto | |
|
93 | ||
|
94 | # ------------------------------------------------------------------------------ | |
|
95 | # Callback prototypes | |
|
96 | # | |
|
97 | # No-op functions which describe the names of available events and the | |
|
98 | # signatures of callbacks for those events. | |
|
99 | # ------------------------------------------------------------------------------ | |
|
100 | ||
|
101 | @_define_event | |
|
102 | def pre_execute(): | |
|
103 | """Fires before code is executed in response to user/frontend action. | |
|
104 | ||
|
105 | This includes comm and widget messages and silent execution, as well as user | |
|
106 | code cells.""" | |
|
107 | pass | |
|
108 | ||
|
109 | @_define_event | |
|
110 | def pre_run_cell(): | |
|
111 | """Fires before user-entered code runs.""" | |
|
112 | pass | |
|
113 | ||
|
114 | @_define_event | |
|
115 | def post_execute(): | |
|
116 | """Fires after code is executed in response to user/frontend action. | |
|
117 | ||
|
118 | This includes comm and widget messages and silent execution, as well as user | |
|
119 | code cells.""" | |
|
120 | pass | |
|
121 | ||
|
122 | @_define_event | |
|
123 | def post_run_cell(): | |
|
124 | """Fires after user-entered code runs.""" | |
|
125 | pass | |
|
126 | ||
|
127 | @_define_event | |
|
128 | def shell_initialized(ip): | |
|
129 | """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`. | |
|
130 | ||
|
131 | This is before extensions and startup scripts are loaded, so it can only be | |
|
132 | set by subclassing. | |
|
133 | ||
|
134 | Parameters | |
|
135 | ---------- | |
|
136 | ip : :class:`~IPython.core.interactiveshell.InteractiveShell` | |
|
137 | The newly initialised shell. | |
|
138 | """ | |
|
139 | pass |
@@ -0,0 +1,46 b'' | |||
|
1 | import unittest | |
|
2 | try: # Python 3.3 + | |
|
3 | from unittest.mock import Mock | |
|
4 | except ImportError: | |
|
5 | from mock import Mock | |
|
6 | ||
|
7 | from IPython.core import events | |
|
8 | import IPython.testing.tools as tt | |
|
9 | ||
|
10 | def ping_received(): | |
|
11 | pass | |
|
12 | ||
|
13 | class CallbackTests(unittest.TestCase): | |
|
14 | def setUp(self): | |
|
15 | self.em = events.EventManager(get_ipython(), {'ping_received': ping_received}) | |
|
16 | ||
|
17 | def test_register_unregister(self): | |
|
18 | cb = Mock() | |
|
19 | ||
|
20 | self.em.register('ping_received', cb) | |
|
21 | self.em.trigger('ping_received') | |
|
22 | self.assertEqual(cb.call_count, 1) | |
|
23 | ||
|
24 | self.em.unregister('ping_received', cb) | |
|
25 | self.em.trigger('ping_received') | |
|
26 | self.assertEqual(cb.call_count, 1) | |
|
27 | ||
|
28 | def test_reset(self): | |
|
29 | cb = Mock() | |
|
30 | self.em.register('ping_received', cb) | |
|
31 | self.em.reset('ping_received') | |
|
32 | self.em.trigger('ping_received') | |
|
33 | assert not cb.called | |
|
34 | ||
|
35 | def test_reset_all(self): | |
|
36 | cb = Mock() | |
|
37 | self.em.register('ping_received', cb) | |
|
38 | self.em.reset_all() | |
|
39 | self.em.trigger('ping_received') | |
|
40 | assert not cb.called | |
|
41 | ||
|
42 | def test_cb_error(self): | |
|
43 | cb = Mock(side_effect=ValueError) | |
|
44 | self.em.register('ping_received', cb) | |
|
45 | with tt.AssertPrints("Error in callback"): | |
|
46 | self.em.trigger('ping_received') No newline at end of file |
@@ -0,0 +1,42 b'' | |||
|
1 | ===================== | |
|
2 | Registering callbacks | |
|
3 | ===================== | |
|
4 | ||
|
5 | Extension code can register callbacks functions which will be called on specific | |
|
6 | events within the IPython code. You can see the current list of available | |
|
7 | callbacks, and the parameters that will be passed with each, in the callback | |
|
8 | prototype functions defined in :mod:`IPython.core.callbacks`. | |
|
9 | ||
|
10 | To register callbacks, use :meth:`IPython.core.events.EventManager.register`. | |
|
11 | For example:: | |
|
12 | ||
|
13 | class VarWatcher(object): | |
|
14 | def __init__(self, ip): | |
|
15 | self.shell = ip | |
|
16 | self.last_x = None | |
|
17 | ||
|
18 | def pre_execute(self): | |
|
19 | self.last_x = self.shell.user_ns.get('x', None) | |
|
20 | ||
|
21 | def post_execute(self): | |
|
22 | if self.shell.user_ns.get('x', None) != self.last_x: | |
|
23 | print("x changed!") | |
|
24 | ||
|
25 | def load_ipython_extension(ip): | |
|
26 | vw = VarWatcher(ip) | |
|
27 | ip.events.register('pre_execute', vw.pre_execute) | |
|
28 | ip.events.register('post_execute', vw.post_execute) | |
|
29 | ||
|
30 | .. note:: | |
|
31 | ||
|
32 | This API is experimental in IPython 2.0, and may be revised in future versions. | |
|
33 | ||
|
34 | .. seealso:: | |
|
35 | ||
|
36 | Module :mod:`IPython.core.hooks` | |
|
37 | The older 'hooks' system allows end users to customise some parts of | |
|
38 | IPython's behaviour. | |
|
39 | ||
|
40 | :doc:`inputtransforms` | |
|
41 | By registering input transformers that don't change code, you can monitor | |
|
42 | what is being executed. |
@@ -0,0 +1,1 b'' | |||
|
1 | * A new callback system has been introduced. For details, see :doc:`/config/callbacks`. |
@@ -7,7 +7,7 b' before_install:' | |||
|
7 | 7 | # workaround for https://github.com/travis-ci/travis-cookbooks/issues/155 |
|
8 | 8 | - sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm |
|
9 | 9 | - easy_install -q pyzmq |
|
10 | - pip install jinja2 sphinx pygments tornado requests | |
|
10 | - pip install jinja2 sphinx pygments tornado requests mock | |
|
11 | 11 | # Pierre Carrier's PPA for PhantomJS and CasperJS |
|
12 | 12 | - sudo add-apt-repository -y ppa:pcarrier/ppa |
|
13 | 13 | - sudo apt-get update |
@@ -49,6 +49,11 b" __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor'," | |||
|
49 | 49 | 'show_in_pager','pre_prompt_hook', |
|
50 | 50 | 'pre_run_code_hook', 'clipboard_get'] |
|
51 | 51 | |
|
52 | deprecated = {'pre_run_code_hook': "a callback for the 'pre_execute' or 'pre_run_cell' event", | |
|
53 | 'late_startup_hook': "a callback for the 'shell_initialized' event", | |
|
54 | 'shutdown_hook': "the atexit module", | |
|
55 | } | |
|
56 | ||
|
52 | 57 | def editor(self, filename, linenum=None, wait=True): |
|
53 | 58 | """Open the default editor at the given filename and linenumber. |
|
54 | 59 |
@@ -41,6 +41,7 b' from IPython.core import ultratb' | |||
|
41 | 41 | from IPython.core.alias import AliasManager, AliasError |
|
42 | 42 | from IPython.core.autocall import ExitAutocall |
|
43 | 43 | from IPython.core.builtin_trap import BuiltinTrap |
|
44 | from IPython.core.events import EventManager, available_events | |
|
44 | 45 | from IPython.core.compilerop import CachingCompiler, check_linecache_ipython |
|
45 | 46 | from IPython.core.display_trap import DisplayTrap |
|
46 | 47 | from IPython.core.displayhook import DisplayHook |
@@ -467,6 +468,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
467 | 468 | |
|
468 | 469 | self.init_syntax_highlighting() |
|
469 | 470 | self.init_hooks() |
|
471 | self.init_events() | |
|
470 | 472 | self.init_pushd_popd_magic() |
|
471 | 473 | # self.init_traceback_handlers use to be here, but we moved it below |
|
472 | 474 | # because it and init_io have to come after init_readline. |
@@ -510,6 +512,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
510 | 512 | self.init_payload() |
|
511 | 513 | self.init_comms() |
|
512 | 514 | self.hooks.late_startup_hook() |
|
515 | self.events.trigger('shell_initialized', self) | |
|
513 | 516 | atexit.register(self.atexit_operations) |
|
514 | 517 | |
|
515 | 518 | def get_ipython(self): |
@@ -785,9 +788,10 b' class InteractiveShell(SingletonConfigurable):' | |||
|
785 | 788 | for hook_name in hooks.__all__: |
|
786 | 789 | # default hooks have priority 100, i.e. low; user hooks should have |
|
787 | 790 | # 0-100 priority |
|
788 | self.set_hook(hook_name,getattr(hooks,hook_name), 100) | |
|
791 | self.set_hook(hook_name,getattr(hooks,hook_name), 100, _warn_deprecated=False) | |
|
789 | 792 | |
|
790 |
def set_hook(self,name,hook, priority |
|
|
793 | def set_hook(self,name,hook, priority=50, str_key=None, re_key=None, | |
|
794 | _warn_deprecated=True): | |
|
791 | 795 | """set_hook(name,hook) -> sets an internal IPython hook. |
|
792 | 796 | |
|
793 | 797 | IPython exposes some of its internal API as user-modifiable hooks. By |
@@ -816,6 +820,11 b' class InteractiveShell(SingletonConfigurable):' | |||
|
816 | 820 | if name not in IPython.core.hooks.__all__: |
|
817 | 821 | print("Warning! Hook '%s' is not one of %s" % \ |
|
818 | 822 | (name, IPython.core.hooks.__all__ )) |
|
823 | ||
|
824 | if _warn_deprecated and (name in IPython.core.hooks.deprecated): | |
|
825 | alternative = IPython.core.hooks.deprecated[name] | |
|
826 | warn("Hook {} is deprecated. Use {} instead.".format(name, alternative)) | |
|
827 | ||
|
819 | 828 | if not dp: |
|
820 | 829 | dp = IPython.core.hooks.CommandChainDispatcher() |
|
821 | 830 | |
@@ -827,12 +836,21 b' class InteractiveShell(SingletonConfigurable):' | |||
|
827 | 836 | |
|
828 | 837 | setattr(self.hooks,name, dp) |
|
829 | 838 | |
|
839 | #------------------------------------------------------------------------- | |
|
840 | # Things related to events | |
|
841 | #------------------------------------------------------------------------- | |
|
842 | ||
|
843 | def init_events(self): | |
|
844 | self.events = EventManager(self, available_events) | |
|
845 | ||
|
830 | 846 | def register_post_execute(self, func): |
|
831 | """Register a function for calling after code execution. | |
|
847 | """DEPRECATED: Use ip.events.register('post_run_cell', func) | |
|
848 | ||
|
849 | Register a function for calling after code execution. | |
|
832 | 850 | """ |
|
833 | if not callable(func): | |
|
834 | raise ValueError('argument %s must be callable' % func) | |
|
835 | self._post_execute[func] = True | |
|
851 | warn("ip.register_post_execute is deprecated, use " | |
|
852 | "ip.events.register('post_run_cell', func) instead.") | |
|
853 | self.events.register('post_run_cell', func) | |
|
836 | 854 | |
|
837 | 855 | #------------------------------------------------------------------------- |
|
838 | 856 | # Things related to the "main" module |
@@ -2649,6 +2667,10 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2649 | 2667 | if silent: |
|
2650 | 2668 | store_history = False |
|
2651 | 2669 | |
|
2670 | self.events.trigger('pre_execute') | |
|
2671 | if not silent: | |
|
2672 | self.events.trigger('pre_run_cell') | |
|
2673 | ||
|
2652 | 2674 | # If any of our input transformation (input_transformer_manager or |
|
2653 | 2675 | # prefilter_manager) raises an exception, we store it in this variable |
|
2654 | 2676 | # so that we can display the error after logging the input and storing |
@@ -2718,27 +2740,9 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2718 | 2740 | self.run_ast_nodes(code_ast.body, cell_name, |
|
2719 | 2741 | interactivity=interactivity, compiler=compiler) |
|
2720 | 2742 | |
|
2721 | # Execute any registered post-execution functions. | |
|
2722 |
|
|
|
2723 | post_exec = [] if silent else iteritems(self._post_execute) | |
|
2724 | ||
|
2725 | for func, status in post_exec: | |
|
2726 | if self.disable_failing_post_execute and not status: | |
|
2727 | continue | |
|
2728 | try: | |
|
2729 | func() | |
|
2730 | except KeyboardInterrupt: | |
|
2731 | print("\nKeyboardInterrupt", file=io.stderr) | |
|
2732 | except Exception: | |
|
2733 | # register as failing: | |
|
2734 | self._post_execute[func] = False | |
|
2735 | self.showtraceback() | |
|
2736 | print('\n'.join([ | |
|
2737 | "post-execution function %r produced an error." % func, | |
|
2738 | "If this problem persists, you can disable failing post-exec functions with:", | |
|
2739 | "", | |
|
2740 | " get_ipython().disable_failing_post_execute = True" | |
|
2741 | ]), file=io.stderr) | |
|
2743 | self.events.trigger('post_execute') | |
|
2744 | if not silent: | |
|
2745 | self.events.trigger('post_run_cell') | |
|
2742 | 2746 | |
|
2743 | 2747 | if store_history: |
|
2744 | 2748 | # Write output to the database. Does nothing unless |
@@ -353,7 +353,7 b' def configure_inline_support(shell, backend):' | |||
|
353 | 353 | |
|
354 | 354 | if backend == backends['inline']: |
|
355 | 355 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
356 |
shell.register |
|
|
356 | shell.events.register('post_execute', flush_figures) | |
|
357 | 357 | |
|
358 | 358 | # Save rcParams that will be overwrittern |
|
359 | 359 | shell._saved_rcParams = dict() |
@@ -363,8 +363,10 b' def configure_inline_support(shell, backend):' | |||
|
363 | 363 | pyplot.rcParams.update(cfg.rc) |
|
364 | 364 | else: |
|
365 | 365 | from IPython.kernel.zmq.pylab.backend_inline import flush_figures |
|
366 | if flush_figures in shell._post_execute: | |
|
367 |
shell. |
|
|
366 | try: | |
|
367 | shell.events.unregister('post_execute', flush_figures) | |
|
368 | except ValueError: | |
|
369 | pass | |
|
368 | 370 | if hasattr(shell, '_saved_rcParams'): |
|
369 | 371 | pyplot.rcParams.update(shell._saved_rcParams) |
|
370 | 372 | del shell._saved_rcParams |
@@ -27,6 +27,10 b' import shutil' | |||
|
27 | 27 | import sys |
|
28 | 28 | import tempfile |
|
29 | 29 | import unittest |
|
30 | try: | |
|
31 | from unittest import mock | |
|
32 | except ImportError: | |
|
33 | import mock | |
|
30 | 34 | from os.path import join |
|
31 | 35 | |
|
32 | 36 | # third-party |
@@ -277,21 +281,32 b' class InteractiveShellTestCase(unittest.TestCase):' | |||
|
277 | 281 | # ZeroDivisionError |
|
278 | 282 | self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}") |
|
279 | 283 | |
|
280 |
def test_silent_ |
|
|
281 |
"""run_cell(silent=True) doesn't invoke p |
|
|
282 | d = dict(called=False) | |
|
283 | def set_called(): | |
|
284 | d['called'] = True | |
|
284 | def test_silent_postexec(self): | |
|
285 | """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks""" | |
|
286 | pre_explicit = mock.Mock() | |
|
287 | pre_always = mock.Mock() | |
|
288 | post_explicit = mock.Mock() | |
|
289 | post_always = mock.Mock() | |
|
290 | ||
|
291 | ip.events.register('pre_run_cell', pre_explicit) | |
|
292 | ip.events.register('pre_execute', pre_always) | |
|
293 | ip.events.register('post_run_cell', post_explicit) | |
|
294 | ip.events.register('post_execute', post_always) | |
|
285 | 295 | |
|
286 | ip.register_post_execute(set_called) | |
|
296 | try: | |
|
287 | 297 | ip.run_cell("1", silent=True) |
|
288 |
|
|
|
298 | assert pre_always.called | |
|
299 | assert not pre_explicit.called | |
|
300 | assert post_always.called | |
|
301 | assert not post_explicit.called | |
|
289 | 302 | # double-check that non-silent exec did what we expected |
|
290 | 303 | # silent to avoid |
|
291 | 304 | ip.run_cell("1") |
|
292 | self.assertTrue(d['called']) | |
|
305 | assert pre_explicit.called | |
|
306 | assert post_explicit.called | |
|
307 | finally: | |
|
293 | 308 | # remove post-exec |
|
294 | ip._post_execute.pop(set_called) | |
|
309 | ip.events.reset_all() | |
|
295 | 310 | |
|
296 | 311 | def test_silent_noadvance(self): |
|
297 | 312 | """run_cell(silent=True) doesn't advance execution_count""" |
@@ -392,7 +392,6 b' def superreload(module, reload=reload, old_objects={}):' | |||
|
392 | 392 | # IPython connectivity |
|
393 | 393 | #------------------------------------------------------------------------------ |
|
394 | 394 | |
|
395 | from IPython.core.hooks import TryNext | |
|
396 | 395 | from IPython.core.magic import Magics, magics_class, line_magic |
|
397 | 396 | |
|
398 | 397 | @magics_class |
@@ -491,9 +490,8 b' class AutoreloadMagics(Magics):' | |||
|
491 | 490 | # Inject module to user namespace |
|
492 | 491 | self.shell.push({top_name: top_module}) |
|
493 | 492 | |
|
494 |
def pre_run_c |
|
|
495 |
if |
|
|
496 | raise TryNext | |
|
493 | def pre_run_cell(self): | |
|
494 | if self._reloader.enabled: | |
|
497 | 495 | try: |
|
498 | 496 | self._reloader.check() |
|
499 | 497 | except: |
@@ -504,4 +502,4 b' def load_ipython_extension(ip):' | |||
|
504 | 502 | """Load the extension in IPython.""" |
|
505 | 503 | auto_reload = AutoreloadMagics(ip) |
|
506 | 504 | ip.register_magics(auto_reload) |
|
507 |
ip. |
|
|
505 | ip.events.register('pre_run_cell', auto_reload.pre_run_cell) |
@@ -23,7 +23,7 b' import nose.tools as nt' | |||
|
23 | 23 | import IPython.testing.tools as tt |
|
24 | 24 | |
|
25 | 25 | from IPython.extensions.autoreload import AutoreloadMagics |
|
26 |
from IPython.core. |
|
|
26 | from IPython.core.events import EventManager, pre_run_cell | |
|
27 | 27 | from IPython.utils.py3compat import PY3 |
|
28 | 28 | |
|
29 | 29 | if PY3: |
@@ -41,15 +41,14 b' class FakeShell(object):' | |||
|
41 | 41 | |
|
42 | 42 | def __init__(self): |
|
43 | 43 | self.ns = {} |
|
44 | self.events = EventManager(self, {'pre_run_cell', pre_run_cell}) | |
|
44 | 45 | self.auto_magics = AutoreloadMagics(shell=self) |
|
46 | self.events.register('pre_run_cell', self.auto_magics.pre_run_cell) | |
|
45 | 47 | |
|
46 | 48 | register_magics = set_hook = noop |
|
47 | 49 | |
|
48 | 50 | def run_code(self, code): |
|
49 | try: | |
|
50 | self.auto_magics.pre_run_code_hook(self) | |
|
51 | except TryNext: | |
|
52 | pass | |
|
51 | self.events.trigger('pre_run_cell') | |
|
53 | 52 | exec(code, self.ns) |
|
54 | 53 | |
|
55 | 54 | def push(self, items): |
@@ -134,7 +134,9 b' class Comm(LoggingConfigurable):' | |||
|
134 | 134 | """Handle a comm_msg message""" |
|
135 | 135 | self.log.debug("handle_msg[%s](%s)", self.comm_id, msg) |
|
136 | 136 | if self._msg_callback: |
|
137 | self.shell.events.trigger('pre_execute') | |
|
137 | 138 | self._msg_callback(msg) |
|
139 | self.shell.events.trigger('post_execute') | |
|
138 | 140 | |
|
139 | 141 | |
|
140 | 142 | __all__ = ['Comm'] |
@@ -1,7 +1,7 b'' | |||
|
1 | """Function signature objects for callables | |
|
1 | """Function signature objects for callables. | |
|
2 | 2 | |
|
3 | 3 | Back port of Python 3.3's function signature tools from the inspect module, |
|
4 |
modified to be compatible with Python 2. |
|
|
4 | modified to be compatible with Python 2.7 and 3.2+. | |
|
5 | 5 | """ |
|
6 | 6 | |
|
7 | 7 | #----------------------------------------------------------------------------- |
@@ -346,19 +346,19 b' class Parameter(object):' | |||
|
346 | 346 | |
|
347 | 347 | |
|
348 | 348 | class BoundArguments(object): |
|
349 | '''Result of `Signature.bind` call. Holds the mapping of arguments | |
|
349 | '''Result of :meth:`Signature.bind` call. Holds the mapping of arguments | |
|
350 | 350 | to the function's parameters. |
|
351 | 351 | |
|
352 | 352 | Has the following public attributes: |
|
353 | 353 | |
|
354 |
|
|
|
354 | arguments : :class:`collections.OrderedDict` | |
|
355 | 355 |
|
|
356 | 356 |
|
|
357 |
|
|
|
357 | signature : :class:`Signature` | |
|
358 | 358 |
|
|
359 |
|
|
|
359 | args : tuple | |
|
360 | 360 |
|
|
361 |
|
|
|
361 | kwargs : dict | |
|
362 | 362 |
|
|
363 | 363 | ''' |
|
364 | 364 | |
@@ -447,22 +447,16 b' class Signature(object):' | |||
|
447 | 447 | It stores a Parameter object for each parameter accepted by the |
|
448 | 448 | function, as well as information specific to the function itself. |
|
449 | 449 | |
|
450 |
A Signature object has the following public attributes |
|
|
450 | A Signature object has the following public attributes: | |
|
451 | 451 | |
|
452 |
|
|
|
452 | parameters : :class:`collections.OrderedDict` | |
|
453 | 453 |
|
|
454 | 454 |
|
|
455 | 455 |
|
|
456 |
|
|
|
456 | return_annotation | |
|
457 | 457 |
|
|
458 | 458 |
|
|
459 | 459 |
|
|
460 | * bind(*args, **kwargs) -> BoundArguments | |
|
461 | Creates a mapping from positional and keyword arguments to | |
|
462 | parameters. | |
|
463 | * bind_partial(*args, **kwargs) -> BoundArguments | |
|
464 | Creates a partial mapping from positional and keyword arguments | |
|
465 | to parameters (simulating 'functools.partial' behavior.) | |
|
466 | 460 | ''' |
|
467 | 461 | |
|
468 | 462 | __slots__ = ('_return_annotation', '_parameters') |
@@ -775,16 +769,16 b' class Signature(object):' | |||
|
775 | 769 | return self._bound_arguments_cls(self, arguments) |
|
776 | 770 | |
|
777 | 771 | def bind(self, *args, **kwargs): |
|
778 | '''Get a BoundArguments object, that maps the passed `args` | |
|
779 | and `kwargs` to the function's signature. Raises `TypeError` | |
|
772 | '''Get a :class:`BoundArguments` object, that maps the passed `args` | |
|
773 | and `kwargs` to the function's signature. Raises :exc:`TypeError` | |
|
780 | 774 | if the passed arguments can not be bound. |
|
781 | 775 | ''' |
|
782 | 776 | return self._bind(args, kwargs) |
|
783 | 777 | |
|
784 | 778 | def bind_partial(self, *args, **kwargs): |
|
785 | '''Get a BoundArguments object, that partially maps the | |
|
779 | '''Get a :class:`BoundArguments` object, that partially maps the | |
|
786 | 780 | passed `args` and `kwargs` to the function's signature. |
|
787 | Raises `TypeError` if the passed arguments can not be bound. | |
|
781 | Raises :exc:`TypeError` if the passed arguments can not be bound. | |
|
788 | 782 | ''' |
|
789 | 783 | return self._bind(args, kwargs, partial=True) |
|
790 | 784 |
@@ -28,3 +28,4 b' Extending and integrating with IPython' | |||
|
28 | 28 | extensions/index |
|
29 | 29 | integrating |
|
30 | 30 | inputtransforms |
|
31 | callbacks |
@@ -273,10 +273,14 b' extras_require = dict(' | |||
|
273 | 273 | notebook = ['tornado>=3.1', 'pyzmq>=2.1.11', 'jinja2'], |
|
274 | 274 | nbconvert = ['pygments', 'jinja2', 'Sphinx>=0.3'] |
|
275 | 275 | ) |
|
276 | if sys.version_info < (3, 3): | |
|
277 | extras_require['test'].append('mock') | |
|
278 | ||
|
276 | 279 | everything = set() |
|
277 | 280 | for deps in extras_require.values(): |
|
278 | 281 | everything.update(deps) |
|
279 | 282 | extras_require['all'] = everything |
|
283 | ||
|
280 | 284 | install_requires = [] |
|
281 | 285 | if sys.platform == 'darwin': |
|
282 | 286 | if any(arg.startswith('bdist') for arg in sys.argv) or not setupext.check_for_readline(): |
@@ -639,10 +639,11 b' def get_bdist_wheel():' | |||
|
639 | 639 | if found: |
|
640 | 640 | lis.pop(idx) |
|
641 | 641 | |
|
642 | for pkg in ("gnureadline", "pyreadline"): | |
|
642 | for pkg in ("gnureadline", "pyreadline", "mock"): | |
|
643 | 643 | _remove_startswith(requires, pkg) |
|
644 | 644 | requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'") |
|
645 | 645 | requires.append("pyreadline (>=2.0); sys.platform == 'win32' and platform.python_implementation == 'CPython'") |
|
646 | requires.append("mock; extra == 'test' and python_version < '3.3'") | |
|
646 | 647 | for r in requires: |
|
647 | 648 | pkg_info['Requires-Dist'] = r |
|
648 | 649 | write_pkg_info(metadata_path, pkg_info) |
General Comments 0
You need to be logged in to leave comments.
Login now