Show More
@@ -0,0 +1,51 b'' | |||
|
1 | # encoding: utf-8 | |
|
2 | """IPython plugins. | |
|
3 | ||
|
4 | Authors: | |
|
5 | ||
|
6 | * Brian Granger | |
|
7 | """ | |
|
8 | ||
|
9 | #----------------------------------------------------------------------------- | |
|
10 | # Copyright (C) 2010 The IPython Development Team | |
|
11 | # | |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
|
13 | # the file COPYING, distributed as part of this software. | |
|
14 | #----------------------------------------------------------------------------- | |
|
15 | ||
|
16 | #----------------------------------------------------------------------------- | |
|
17 | # Imports | |
|
18 | #----------------------------------------------------------------------------- | |
|
19 | ||
|
20 | from IPython.config.configurable import Configurable | |
|
21 | from IPython.utils.traitlets import Dict | |
|
22 | ||
|
23 | #----------------------------------------------------------------------------- | |
|
24 | # Main class | |
|
25 | #----------------------------------------------------------------------------- | |
|
26 | ||
|
27 | class PluginManager(Configurable): | |
|
28 | """A manager for IPython plugins.""" | |
|
29 | ||
|
30 | plugins = Dict({}) | |
|
31 | ||
|
32 | def __init__(self, config=None): | |
|
33 | super(PluginManager, self).__init__(config=config) | |
|
34 | ||
|
35 | def register_plugin(self, name, plugin): | |
|
36 | if not isinstance(plugin, Plugin): | |
|
37 | raise TypeError('Expected Plugin, got: %r' % plugin) | |
|
38 | if self.plugins.has_key(name): | |
|
39 | raise KeyError('Plugin with name already exists: %r' % name) | |
|
40 | self.plugins[name] = plugin | |
|
41 | ||
|
42 | def unregister_plugin(self, name): | |
|
43 | del self.plugins[name] | |
|
44 | ||
|
45 | def get_plugin(self, name, default=None): | |
|
46 | return self.plugins.get(name, default) | |
|
47 | ||
|
48 | ||
|
49 | class Plugin(Configurable): | |
|
50 | """Base class for IPython plugins.""" | |
|
51 | pass |
@@ -0,0 +1,46 b'' | |||
|
1 | """Tests for plugin.py""" | |
|
2 | ||
|
3 | #----------------------------------------------------------------------------- | |
|
4 | # Imports | |
|
5 | #----------------------------------------------------------------------------- | |
|
6 | ||
|
7 | from unittest import TestCase | |
|
8 | ||
|
9 | from IPython.core.plugin import Plugin, PluginManager | |
|
10 | ||
|
11 | #----------------------------------------------------------------------------- | |
|
12 | # Tests | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | class FooPlugin(Plugin): | |
|
16 | pass | |
|
17 | ||
|
18 | ||
|
19 | class BarPlugin(Plugin): | |
|
20 | pass | |
|
21 | ||
|
22 | ||
|
23 | class BadPlugin(object): | |
|
24 | pass | |
|
25 | ||
|
26 | ||
|
27 | class PluginTest(TestCase): | |
|
28 | ||
|
29 | def setUp(self): | |
|
30 | self.manager = PluginManager() | |
|
31 | ||
|
32 | def test_register_get(self): | |
|
33 | self.assertEquals(None, self.manager.get_plugin('foo')) | |
|
34 | foo = FooPlugin() | |
|
35 | self.manager.register_plugin('foo', foo) | |
|
36 | self.assertEquals(foo, self.manager.get_plugin('foo')) | |
|
37 | bar = BarPlugin() | |
|
38 | self.assertRaises(KeyError, self.manager.register_plugin, 'foo', bar) | |
|
39 | bad = BadPlugin() | |
|
40 | self.assertRaises(TypeError, self.manager.register_plugin, 'bad') | |
|
41 | ||
|
42 | def test_unregister(self): | |
|
43 | foo = FooPlugin() | |
|
44 | self.manager.register_plugin('foo', foo) | |
|
45 | self.manager.unregister_plugin('foo') | |
|
46 | self.assertEquals(None, self.manager.get_plugin('foo')) |
@@ -104,7 +104,7 b' class AliasManager(Configurable):' | |||
|
104 | 104 | |
|
105 | 105 | default_aliases = List(default_aliases(), config=True) |
|
106 | 106 | user_aliases = List(default_value=[], config=True) |
|
107 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
107 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
108 | 108 | |
|
109 | 109 | def __init__(self, shell, config=None): |
|
110 | 110 | super(AliasManager, self).__init__(config=config) |
@@ -37,7 +37,7 b' BuiltinUndefined = __BuiltinUndefined()' | |||
|
37 | 37 | |
|
38 | 38 | class BuiltinTrap(Configurable): |
|
39 | 39 | |
|
40 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
40 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
41 | 41 | |
|
42 | 42 | def __init__(self, shell): |
|
43 | 43 | super(BuiltinTrap, self).__init__(None) |
@@ -28,8 +28,32 b' from IPython.utils.traitlets import Instance' | |||
|
28 | 28 | #----------------------------------------------------------------------------- |
|
29 | 29 | |
|
30 | 30 | class ExtensionManager(Configurable): |
|
31 | """A class to manage IPython extensions. | |
|
31 | 32 |
|
|
32 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
33 | An IPython extension is an importable Python module that has | |
|
34 | a function with the signature:: | |
|
35 | ||
|
36 | def load_ipython_extension(ipython): | |
|
37 | # Do things with ipython | |
|
38 | ||
|
39 | This function is called after your extension is imported and the | |
|
40 | currently active :class:`InteractiveShell` instance is passed as | |
|
41 | the only argument. You can do anything you want with IPython at | |
|
42 | that point, including defining new magic and aliases, adding new | |
|
43 | components, etc. | |
|
44 | ||
|
45 | The :func:`load_ipython_extension` will be called again is you | |
|
46 | load or reload the extension again. It is up to the extension | |
|
47 | author to add code to manage that. | |
|
48 | ||
|
49 | You can put your extension modules anywhere you want, as long as | |
|
50 | they can be imported by Python's standard import mechanism. However, | |
|
51 | to make it easy to write extensions, you can also put your extensions | |
|
52 | in ``os.path.join(self.ipython_dir, 'extensions')``. This directory | |
|
53 | is added to ``sys.path`` automatically. | |
|
54 | """ | |
|
55 | ||
|
56 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
33 | 57 | |
|
34 | 58 | def __init__(self, shell, config=None): |
|
35 | 59 | super(ExtensionManager, self).__init__(config=config) |
@@ -54,28 +78,6 b' class ExtensionManager(Configurable):' | |||
|
54 | 78 | def load_extension(self, module_str): |
|
55 | 79 | """Load an IPython extension by its module name. |
|
56 | 80 | |
|
57 | An IPython extension is an importable Python module that has | |
|
58 | a function with the signature:: | |
|
59 | ||
|
60 | def load_ipython_extension(ipython): | |
|
61 | # Do things with ipython | |
|
62 | ||
|
63 | This function is called after your extension is imported and the | |
|
64 | currently active :class:`InteractiveShell` instance is passed as | |
|
65 | the only argument. You can do anything you want with IPython at | |
|
66 | that point, including defining new magic and aliases, adding new | |
|
67 | components, etc. | |
|
68 | ||
|
69 | The :func:`load_ipython_extension` will be called again is you | |
|
70 | load or reload the extension again. It is up to the extension | |
|
71 | author to add code to manage that. | |
|
72 | ||
|
73 | You can put your extension modules anywhere you want, as long as | |
|
74 | they can be imported by Python's standard import mechanism. However, | |
|
75 | to make it easy to write extensions, you can also put your extensions | |
|
76 | in ``os.path.join(self.ipython_dir, 'extensions')``. This directory | |
|
77 | is added to ``sys.path`` automatically. | |
|
78 | ||
|
79 | 81 | If :func:`load_ipython_extension` returns anything, this function |
|
80 | 82 | will return that object. |
|
81 | 83 | """ |
@@ -18,6 +18,7 b' from __future__ import with_statement' | |||
|
18 | 18 | from __future__ import absolute_import |
|
19 | 19 | |
|
20 | 20 | import __builtin__ |
|
21 | import abc | |
|
21 | 22 | import bdb |
|
22 | 23 | import codeop |
|
23 | 24 | import exceptions |
@@ -43,6 +44,7 b' from IPython.core.extensions import ExtensionManager' | |||
|
43 | 44 | from IPython.core.fakemodule import FakeModule, init_fakemod_dict |
|
44 | 45 | from IPython.core.logger import Logger |
|
45 | 46 | from IPython.core.magic import Magic |
|
47 | from IPython.core.plugin import PluginManager | |
|
46 | 48 | from IPython.core.prefilter import PrefilterManager |
|
47 | 49 | from IPython.core.prompts import CachedOutput |
|
48 | 50 | from IPython.core.usage import interactive_usage, default_banner |
@@ -286,6 +288,7 b' class InteractiveShell(Configurable, Magic):' | |||
|
286 | 288 | builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap') |
|
287 | 289 | display_trap = Instance('IPython.core.display_trap.DisplayTrap') |
|
288 | 290 | extension_manager = Instance('IPython.core.extensions.ExtensionManager') |
|
291 | plugin_manager = Instance('IPython.core.plugin.PluginManager') | |
|
289 | 292 | |
|
290 | 293 | def __init__(self, config=None, ipython_dir=None, usage=None, |
|
291 | 294 | user_ns=None, user_global_ns=None, |
@@ -341,6 +344,7 b' class InteractiveShell(Configurable, Magic):' | |||
|
341 | 344 | self.init_magics() |
|
342 | 345 | self.init_pdb() |
|
343 | 346 | self.init_extension_manager() |
|
347 | self.init_plugin_manager() | |
|
344 | 348 | self.hooks.late_startup_hook() |
|
345 | 349 | |
|
346 | 350 | @classmethod |
@@ -1759,12 +1763,15 b' class InteractiveShell(Configurable, Magic):' | |||
|
1759 | 1763 | self.ns_table['alias'] = self.alias_manager.alias_table, |
|
1760 | 1764 | |
|
1761 | 1765 | #------------------------------------------------------------------------- |
|
1762 | # Things related to extensions | |
|
1766 | # Things related to extensions and plugins | |
|
1763 | 1767 | #------------------------------------------------------------------------- |
|
1764 | 1768 | |
|
1765 | 1769 | def init_extension_manager(self): |
|
1766 | 1770 | self.extension_manager = ExtensionManager(self, config=self.config) |
|
1767 | 1771 | |
|
1772 | def init_plugin_manager(self): | |
|
1773 | self.plugin_manager = PluginManager(config=self.config) | |
|
1774 | ||
|
1768 | 1775 | #------------------------------------------------------------------------- |
|
1769 | 1776 | # Things related to the running of code |
|
1770 | 1777 | #------------------------------------------------------------------------- |
@@ -2509,3 +2516,8 b' class InteractiveShell(Configurable, Magic):' | |||
|
2509 | 2516 | self.restore_sys_module_state() |
|
2510 | 2517 | |
|
2511 | 2518 | |
|
2519 | class InteractiveShellABC(object): | |
|
2520 | """An abstract base class for InteractiveShell.""" | |
|
2521 | __metaclass__ = abc.ABCMeta | |
|
2522 | ||
|
2523 | InteractiveShellABC.register(InteractiveShell) |
@@ -210,7 +210,7 b' class PrefilterManager(Configurable):' | |||
|
210 | 210 | """ |
|
211 | 211 | |
|
212 | 212 | multi_line_specials = CBool(True, config=True) |
|
213 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
213 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
214 | 214 | |
|
215 | 215 | def __init__(self, shell, config=None): |
|
216 | 216 | super(PrefilterManager, self).__init__(config=config) |
@@ -447,7 +447,7 b' class PrefilterTransformer(Configurable):' | |||
|
447 | 447 | priority = Int(100, config=True) |
|
448 | 448 | # Transformers don't currently use shell or prefilter_manager, but as we |
|
449 | 449 | # move away from checkers and handlers, they will need them. |
|
450 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
450 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
451 | 451 | prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') |
|
452 | 452 | enabled = Bool(True, config=True) |
|
453 | 453 | |
@@ -555,7 +555,7 b' class PrefilterChecker(Configurable):' | |||
|
555 | 555 | """Inspect an input line and return a handler for that line.""" |
|
556 | 556 | |
|
557 | 557 | priority = Int(100, config=True) |
|
558 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
558 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
559 | 559 | prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') |
|
560 | 560 | enabled = Bool(True, config=True) |
|
561 | 561 | |
@@ -748,7 +748,7 b' class PrefilterHandler(Configurable):' | |||
|
748 | 748 | |
|
749 | 749 | handler_name = Str('normal') |
|
750 | 750 | esc_strings = List([]) |
|
751 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
751 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
752 | 752 | prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') |
|
753 | 753 | |
|
754 | 754 | def __init__(self, shell, prefilter_manager, config=None): |
@@ -16,7 +16,7 b'' | |||
|
16 | 16 | |
|
17 | 17 | import new |
|
18 | 18 | |
|
19 | from IPython.config.configurable import Configurable | |
|
19 | from IPython.core.plugin import Plugin | |
|
20 | 20 | from IPython.utils.traitlets import Bool, Any, Instance |
|
21 | 21 | from IPython.utils.autoattr import auto_attr |
|
22 | 22 | from IPython.testing import decorators as testdec |
@@ -31,15 +31,15 b' Use activate() on a MultiEngineClient object to activate it for magics.' | |||
|
31 | 31 | """ |
|
32 | 32 | |
|
33 | 33 | |
|
34 |
class ParalleMagic |
|
|
34 | class ParalleMagic(Plugin): | |
|
35 | 35 | """A component to manage the %result, %px and %autopx magics.""" |
|
36 | 36 | |
|
37 | 37 | active_multiengine_client = Any() |
|
38 | 38 | verbose = Bool(False, config=True) |
|
39 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
39 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
40 | 40 | |
|
41 | 41 | def __init__(self, shell, config=None): |
|
42 |
super(ParalleMagic |
|
|
42 | super(ParalleMagic, self).__init__(config=config) | |
|
43 | 43 | self.shell = shell |
|
44 | 44 | self._define_magics() |
|
45 | 45 | # A flag showing if autopx is activated or not |
@@ -196,6 +196,7 b' def load_ipython_extension(ip):' | |||
|
196 | 196 | """Load the extension in IPython.""" |
|
197 | 197 | global _loaded |
|
198 | 198 | if not _loaded: |
|
199 |
p |
|
|
199 | plugin = ParalleMagic(ip, config=ip.config) | |
|
200 | ip.plugin_manager.register_plugin('parallel_magic', plugin) | |
|
200 | 201 | _loaded = True |
|
201 | 202 |
@@ -37,7 +37,7 b' by doing::' | |||
|
37 | 37 | |
|
38 | 38 | from IPython.core.error import TryNext |
|
39 | 39 | from IPython.external import pretty |
|
40 | from IPython.config.configurable import Configurable | |
|
40 | from IPython.core.plugin import Plugin | |
|
41 | 41 | from IPython.utils.traitlets import Bool, List, Instance |
|
42 | 42 | from IPython.utils.io import Term |
|
43 | 43 | from IPython.utils.autoattr import auto_attr |
@@ -51,11 +51,11 b' from IPython.utils.importstring import import_item' | |||
|
51 | 51 | _loaded = False |
|
52 | 52 | |
|
53 | 53 | |
|
54 |
class PrettyResultDisplay( |
|
|
54 | class PrettyResultDisplay(Plugin): | |
|
55 | 55 | """A component for pretty printing on steroids.""" |
|
56 | 56 | |
|
57 | 57 | verbose = Bool(False, config=True) |
|
58 | shell = Instance('IPython.core.iplib.InteractiveShell') | |
|
58 | shell = Instance('IPython.core.iplib.InteractiveShellABC') | |
|
59 | 59 | |
|
60 | 60 | # A list of (type, func_name), like |
|
61 | 61 | # [(dict, 'my_dict_printer')] |
@@ -124,10 +124,10 b' def load_ipython_extension(ip):' | |||
|
124 | 124 | """Load the extension in IPython as a hook.""" |
|
125 | 125 | global _loaded |
|
126 | 126 | if not _loaded: |
|
127 |
p |
|
|
128 |
ip.set_hook('result_display', p |
|
|
127 | plugin = PrettyResultDisplay(ip, config=ip.config) | |
|
128 | ip.set_hook('result_display', plugin, priority=99) | |
|
129 | 129 | _loaded = True |
|
130 | return prd | |
|
130 | ip.plugin_manager.register_plugin('pretty_result_display', plugin) | |
|
131 | 131 | |
|
132 | 132 | def unload_ipython_extension(ip): |
|
133 | 133 | """Unload the extension.""" |
@@ -17,8 +17,8 b' Simple tests for :mod:`IPython.extensions.pretty`.' | |||
|
17 | 17 | |
|
18 | 18 | from unittest import TestCase |
|
19 | 19 | |
|
20 |
from IPython.co |
|
|
21 | from IPython.core.iplib import InteractiveShell | |
|
20 | from IPython.config.configurable import Configurable | |
|
21 | from IPython.core.iplib import InteractiveShellABC | |
|
22 | 22 | from IPython.extensions import pretty as pretty_ext |
|
23 | 23 | from IPython.external import pretty |
|
24 | 24 | from IPython.testing import decorators as dec |
@@ -29,9 +29,11 b' from IPython.utils.traitlets import Bool' | |||
|
29 | 29 | # Tests |
|
30 | 30 | #----------------------------------------------------------------------------- |
|
31 | 31 | |
|
32 |
class InteractiveShellStub(Co |
|
|
32 | class InteractiveShellStub(Configurable): | |
|
33 | 33 | pprint = Bool(True) |
|
34 | 34 | |
|
35 | InteractiveShellABC.register(InteractiveShellStub) | |
|
36 | ||
|
35 | 37 | class A(object): |
|
36 | 38 | pass |
|
37 | 39 | |
@@ -41,12 +43,8 b' def a_pprinter(o, p, c):' | |||
|
41 | 43 | class TestPrettyResultDisplay(TestCase): |
|
42 | 44 | |
|
43 | 45 | def setUp(self): |
|
44 |
self.ip = InteractiveShellStub( |
|
|
45 | # This allows our stub to be retrieved instead of the real | |
|
46 | # InteractiveShell | |
|
47 | masquerade_as(self.ip, InteractiveShell) | |
|
48 | self.prd = pretty_ext.PrettyResultDisplay(self.ip, | |
|
49 | name='pretty_result_display') | |
|
46 | self.ip = InteractiveShellStub() | |
|
47 | self.prd = pretty_ext.PrettyResultDisplay(self.ip, config=None) | |
|
50 | 48 | |
|
51 | 49 | def test_for_type(self): |
|
52 | 50 | self.prd.for_type(A, a_pprinter) |
@@ -77,7 +75,8 b' a' | |||
|
77 | 75 | b |
|
78 | 76 | |
|
79 | 77 | ip = get_ipython() |
|
80 |
|
|
|
78 | ip.extension_manager.load_extension('pretty') | |
|
79 | prd = ip.plugin_manager.get_plugin('pretty_result_display') | |
|
81 | 80 | prd.for_type(A, a_pretty_printer) |
|
82 | 81 | prd.for_type_by_name(B.__module__, B.__name__, b_pretty_printer) |
|
83 | 82 |
@@ -137,7 +137,7 b' def start_ipython():' | |||
|
137 | 137 | |
|
138 | 138 | # Create and initialize our test-friendly IPython instance. |
|
139 | 139 | shell = iplib.InteractiveShell( |
|
140 |
|
|
|
140 | config=config, | |
|
141 | 141 | user_ns=ipnsdict(), user_global_ns={} |
|
142 | 142 | ) |
|
143 | 143 |
@@ -1023,3 +1023,25 b' class List(Instance):' | |||
|
1023 | 1023 | |
|
1024 | 1024 | super(List,self).__init__(klass=list, args=args, |
|
1025 | 1025 | allow_none=allow_none, **metadata) |
|
1026 | ||
|
1027 | ||
|
1028 | class Dict(Instance): | |
|
1029 | """An instance of a Python dict.""" | |
|
1030 | ||
|
1031 | def __init__(self, default_value=None, allow_none=True, **metadata): | |
|
1032 | """Create a dict trait type from a dict. | |
|
1033 | ||
|
1034 | The default value is created by doing ``dict(default_value)``, | |
|
1035 | which creates a copy of the ``default_value``. | |
|
1036 | """ | |
|
1037 | if default_value is None: | |
|
1038 | args = ((),) | |
|
1039 | elif isinstance(default_value, dict): | |
|
1040 | args = (default_value,) | |
|
1041 | elif isinstance(default_value, SequenceTypes): | |
|
1042 | args = (default_value,) | |
|
1043 | else: | |
|
1044 | raise TypeError('default value of Dict was %s' % default_value) | |
|
1045 | ||
|
1046 | super(Dict,self).__init__(klass=dict, args=args, | |
|
1047 | allow_none=allow_none, **metadata) |
General Comments 0
You need to be logged in to leave comments.
Login now