##// END OF EJS Templates
Finishing work on configurables, plugins and extensions.
Brian Granger -
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 default_aliases = List(default_aliases(), config=True)
105 default_aliases = List(default_aliases(), config=True)
106 user_aliases = List(default_value=[], config=True)
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 def __init__(self, shell, config=None):
109 def __init__(self, shell, config=None):
110 super(AliasManager, self).__init__(config=config)
110 super(AliasManager, self).__init__(config=config)
@@ -37,7 +37,7 b' BuiltinUndefined = __BuiltinUndefined()'
37
37
38 class BuiltinTrap(Configurable):
38 class BuiltinTrap(Configurable):
39
39
40 shell = Instance('IPython.core.iplib.InteractiveShell')
40 shell = Instance('IPython.core.iplib.InteractiveShellABC')
41
41
42 def __init__(self, shell):
42 def __init__(self, shell):
43 super(BuiltinTrap, self).__init__(None)
43 super(BuiltinTrap, self).__init__(None)
@@ -28,8 +28,32 b' from IPython.utils.traitlets import Instance'
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 class ExtensionManager(Configurable):
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 def __init__(self, shell, config=None):
58 def __init__(self, shell, config=None):
35 super(ExtensionManager, self).__init__(config=config)
59 super(ExtensionManager, self).__init__(config=config)
@@ -54,28 +78,6 b' class ExtensionManager(Configurable):'
54 def load_extension(self, module_str):
78 def load_extension(self, module_str):
55 """Load an IPython extension by its module name.
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 If :func:`load_ipython_extension` returns anything, this function
81 If :func:`load_ipython_extension` returns anything, this function
80 will return that object.
82 will return that object.
81 """
83 """
@@ -121,4 +123,4 b' class ExtensionManager(Configurable):'
121
123
122 def _call_unload_ipython_extension(self, mod):
124 def _call_unload_ipython_extension(self, mod):
123 if hasattr(mod, 'unload_ipython_extension'):
125 if hasattr(mod, 'unload_ipython_extension'):
124 return mod.unload_ipython_extension(self.shell) No newline at end of file
126 return mod.unload_ipython_extension(self.shell)
@@ -18,6 +18,7 b' from __future__ import with_statement'
18 from __future__ import absolute_import
18 from __future__ import absolute_import
19
19
20 import __builtin__
20 import __builtin__
21 import abc
21 import bdb
22 import bdb
22 import codeop
23 import codeop
23 import exceptions
24 import exceptions
@@ -43,6 +44,7 b' from IPython.core.extensions import ExtensionManager'
43 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
44 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
44 from IPython.core.logger import Logger
45 from IPython.core.logger import Logger
45 from IPython.core.magic import Magic
46 from IPython.core.magic import Magic
47 from IPython.core.plugin import PluginManager
46 from IPython.core.prefilter import PrefilterManager
48 from IPython.core.prefilter import PrefilterManager
47 from IPython.core.prompts import CachedOutput
49 from IPython.core.prompts import CachedOutput
48 from IPython.core.usage import interactive_usage, default_banner
50 from IPython.core.usage import interactive_usage, default_banner
@@ -286,6 +288,7 b' class InteractiveShell(Configurable, Magic):'
286 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
288 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
287 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
289 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
288 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
290 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
291 plugin_manager = Instance('IPython.core.plugin.PluginManager')
289
292
290 def __init__(self, config=None, ipython_dir=None, usage=None,
293 def __init__(self, config=None, ipython_dir=None, usage=None,
291 user_ns=None, user_global_ns=None,
294 user_ns=None, user_global_ns=None,
@@ -341,6 +344,7 b' class InteractiveShell(Configurable, Magic):'
341 self.init_magics()
344 self.init_magics()
342 self.init_pdb()
345 self.init_pdb()
343 self.init_extension_manager()
346 self.init_extension_manager()
347 self.init_plugin_manager()
344 self.hooks.late_startup_hook()
348 self.hooks.late_startup_hook()
345
349
346 @classmethod
350 @classmethod
@@ -1759,12 +1763,15 b' class InteractiveShell(Configurable, Magic):'
1759 self.ns_table['alias'] = self.alias_manager.alias_table,
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 def init_extension_manager(self):
1769 def init_extension_manager(self):
1766 self.extension_manager = ExtensionManager(self, config=self.config)
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 # Things related to the running of code
1776 # Things related to the running of code
1770 #-------------------------------------------------------------------------
1777 #-------------------------------------------------------------------------
@@ -2509,3 +2516,8 b' class InteractiveShell(Configurable, Magic):'
2509 self.restore_sys_module_state()
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 multi_line_specials = CBool(True, config=True)
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 def __init__(self, shell, config=None):
215 def __init__(self, shell, config=None):
216 super(PrefilterManager, self).__init__(config=config)
216 super(PrefilterManager, self).__init__(config=config)
@@ -447,7 +447,7 b' class PrefilterTransformer(Configurable):'
447 priority = Int(100, config=True)
447 priority = Int(100, config=True)
448 # Transformers don't currently use shell or prefilter_manager, but as we
448 # Transformers don't currently use shell or prefilter_manager, but as we
449 # move away from checkers and handlers, they will need them.
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 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
451 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
452 enabled = Bool(True, config=True)
452 enabled = Bool(True, config=True)
453
453
@@ -555,7 +555,7 b' class PrefilterChecker(Configurable):'
555 """Inspect an input line and return a handler for that line."""
555 """Inspect an input line and return a handler for that line."""
556
556
557 priority = Int(100, config=True)
557 priority = Int(100, config=True)
558 shell = Instance('IPython.core.iplib.InteractiveShell')
558 shell = Instance('IPython.core.iplib.InteractiveShellABC')
559 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
559 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
560 enabled = Bool(True, config=True)
560 enabled = Bool(True, config=True)
561
561
@@ -748,7 +748,7 b' class PrefilterHandler(Configurable):'
748
748
749 handler_name = Str('normal')
749 handler_name = Str('normal')
750 esc_strings = List([])
750 esc_strings = List([])
751 shell = Instance('IPython.core.iplib.InteractiveShell')
751 shell = Instance('IPython.core.iplib.InteractiveShellABC')
752 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
752 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
753
753
754 def __init__(self, shell, prefilter_manager, config=None):
754 def __init__(self, shell, prefilter_manager, config=None):
@@ -16,7 +16,7 b''
16
16
17 import new
17 import new
18
18
19 from IPython.config.configurable import Configurable
19 from IPython.core.plugin import Plugin
20 from IPython.utils.traitlets import Bool, Any, Instance
20 from IPython.utils.traitlets import Bool, Any, Instance
21 from IPython.utils.autoattr import auto_attr
21 from IPython.utils.autoattr import auto_attr
22 from IPython.testing import decorators as testdec
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 ParalleMagicComponent(Configurable):
34 class ParalleMagic(Plugin):
35 """A component to manage the %result, %px and %autopx magics."""
35 """A component to manage the %result, %px and %autopx magics."""
36
36
37 active_multiengine_client = Any()
37 active_multiengine_client = Any()
38 verbose = Bool(False, config=True)
38 verbose = Bool(False, config=True)
39 shell = Instance('IPython.core.iplib.InteractiveShell')
39 shell = Instance('IPython.core.iplib.InteractiveShellABC')
40
40
41 def __init__(self, shell, config=None):
41 def __init__(self, shell, config=None):
42 super(ParalleMagicComponent, self).__init__(config=config)
42 super(ParalleMagic, self).__init__(config=config)
43 self.shell = shell
43 self.shell = shell
44 self._define_magics()
44 self._define_magics()
45 # A flag showing if autopx is activated or not
45 # A flag showing if autopx is activated or not
@@ -196,6 +196,7 b' def load_ipython_extension(ip):'
196 """Load the extension in IPython."""
196 """Load the extension in IPython."""
197 global _loaded
197 global _loaded
198 if not _loaded:
198 if not _loaded:
199 prd = ParalleMagicComponent(ip, config=ip.config)
199 plugin = ParalleMagic(ip, config=ip.config)
200 ip.plugin_manager.register_plugin('parallel_magic', plugin)
200 _loaded = True
201 _loaded = True
201
202
@@ -37,7 +37,7 b' by doing::'
37
37
38 from IPython.core.error import TryNext
38 from IPython.core.error import TryNext
39 from IPython.external import pretty
39 from IPython.external import pretty
40 from IPython.config.configurable import Configurable
40 from IPython.core.plugin import Plugin
41 from IPython.utils.traitlets import Bool, List, Instance
41 from IPython.utils.traitlets import Bool, List, Instance
42 from IPython.utils.io import Term
42 from IPython.utils.io import Term
43 from IPython.utils.autoattr import auto_attr
43 from IPython.utils.autoattr import auto_attr
@@ -51,11 +51,11 b' from IPython.utils.importstring import import_item'
51 _loaded = False
51 _loaded = False
52
52
53
53
54 class PrettyResultDisplay(Configurable):
54 class PrettyResultDisplay(Plugin):
55 """A component for pretty printing on steroids."""
55 """A component for pretty printing on steroids."""
56
56
57 verbose = Bool(False, config=True)
57 verbose = Bool(False, config=True)
58 shell = Instance('IPython.core.iplib.InteractiveShell')
58 shell = Instance('IPython.core.iplib.InteractiveShellABC')
59
59
60 # A list of (type, func_name), like
60 # A list of (type, func_name), like
61 # [(dict, 'my_dict_printer')]
61 # [(dict, 'my_dict_printer')]
@@ -124,10 +124,10 b' def load_ipython_extension(ip):'
124 """Load the extension in IPython as a hook."""
124 """Load the extension in IPython as a hook."""
125 global _loaded
125 global _loaded
126 if not _loaded:
126 if not _loaded:
127 prd = PrettyResultDisplay(ip, config=ip.config)
127 plugin = PrettyResultDisplay(ip, config=ip.config)
128 ip.set_hook('result_display', prd, priority=99)
128 ip.set_hook('result_display', plugin, priority=99)
129 _loaded = True
129 _loaded = True
130 return prd
130 ip.plugin_manager.register_plugin('pretty_result_display', plugin)
131
131
132 def unload_ipython_extension(ip):
132 def unload_ipython_extension(ip):
133 """Unload the extension."""
133 """Unload the extension."""
@@ -17,8 +17,8 b' Simple tests for :mod:`IPython.extensions.pretty`.'
17
17
18 from unittest import TestCase
18 from unittest import TestCase
19
19
20 from IPython.core.component import Component, masquerade_as
20 from IPython.config.configurable import Configurable
21 from IPython.core.iplib import InteractiveShell
21 from IPython.core.iplib import InteractiveShellABC
22 from IPython.extensions import pretty as pretty_ext
22 from IPython.extensions import pretty as pretty_ext
23 from IPython.external import pretty
23 from IPython.external import pretty
24 from IPython.testing import decorators as dec
24 from IPython.testing import decorators as dec
@@ -29,9 +29,11 b' from IPython.utils.traitlets import Bool'
29 # Tests
29 # Tests
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 class InteractiveShellStub(Component):
32 class InteractiveShellStub(Configurable):
33 pprint = Bool(True)
33 pprint = Bool(True)
34
34
35 InteractiveShellABC.register(InteractiveShellStub)
36
35 class A(object):
37 class A(object):
36 pass
38 pass
37
39
@@ -41,12 +43,8 b' def a_pprinter(o, p, c):'
41 class TestPrettyResultDisplay(TestCase):
43 class TestPrettyResultDisplay(TestCase):
42
44
43 def setUp(self):
45 def setUp(self):
44 self.ip = InteractiveShellStub(None)
46 self.ip = InteractiveShellStub()
45 # This allows our stub to be retrieved instead of the real
47 self.prd = pretty_ext.PrettyResultDisplay(self.ip, config=None)
46 # InteractiveShell
47 masquerade_as(self.ip, InteractiveShell)
48 self.prd = pretty_ext.PrettyResultDisplay(self.ip,
49 name='pretty_result_display')
50
48
51 def test_for_type(self):
49 def test_for_type(self):
52 self.prd.for_type(A, a_pprinter)
50 self.prd.for_type(A, a_pprinter)
@@ -77,7 +75,8 b' a'
77 b
75 b
78
76
79 ip = get_ipython()
77 ip = get_ipython()
80 prd = ip.extension_manager.load_extension('pretty')
78 ip.extension_manager.load_extension('pretty')
79 prd = ip.plugin_manager.get_plugin('pretty_result_display')
81 prd.for_type(A, a_pretty_printer)
80 prd.for_type(A, a_pretty_printer)
82 prd.for_type_by_name(B.__module__, B.__name__, b_pretty_printer)
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 # Create and initialize our test-friendly IPython instance.
138 # Create and initialize our test-friendly IPython instance.
139 shell = iplib.InteractiveShell(
139 shell = iplib.InteractiveShell(
140 parent=None, config=config,
140 config=config,
141 user_ns=ipnsdict(), user_global_ns={}
141 user_ns=ipnsdict(), user_global_ns={}
142 )
142 )
143
143
@@ -1023,3 +1023,25 b' class List(Instance):'
1023
1023
1024 super(List,self).__init__(klass=list, args=args,
1024 super(List,self).__init__(klass=list, args=args,
1025 allow_none=allow_none, **metadata)
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