##// 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 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 """
@@ -121,4 +123,4 b' class ExtensionManager(Configurable):'
121 123
122 124 def _call_unload_ipython_extension(self, mod):
123 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 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 ParalleMagicComponent(Configurable):
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(ParalleMagicComponent, self).__init__(config=config)
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 prd = ParalleMagicComponent(ip, config=ip.config)
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(Configurable):
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 prd = PrettyResultDisplay(ip, config=ip.config)
128 ip.set_hook('result_display', prd, priority=99)
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.core.component import Component, masquerade_as
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(Component):
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(None)
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 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 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 parent=None, config=config,
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