##// END OF EJS Templates
Merge branch 'master' into kernelmanager...
Brian Granger -
r2752:5016a14f merge
parent child Browse files
Show More
@@ -0,0 +1,139 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 A base class for objects that are configurable.
5
6 Authors:
7
8 * Brian Granger
9 * Fernando Perez
10 """
11
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2010 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 #-----------------------------------------------------------------------------
20 # Imports
21 #-----------------------------------------------------------------------------
22
23 from copy import deepcopy
24 import datetime
25 from weakref import WeakValueDictionary
26
27 from IPython.utils.importstring import import_item
28 from loader import Config
29 from IPython.utils.traitlets import HasTraits, Instance
30
31
32 #-----------------------------------------------------------------------------
33 # Helper classes for Configurables
34 #-----------------------------------------------------------------------------
35
36
37 class ConfigurableError(Exception):
38 pass
39
40
41 #-----------------------------------------------------------------------------
42 # Configurable implementation
43 #-----------------------------------------------------------------------------
44
45
46 class Configurable(HasTraits):
47
48 config = Instance(Config,(),{})
49 created = None
50
51 def __init__(self, **kwargs):
52 """Create a conigurable given a config config.
53
54 Parameters
55 ----------
56 config : Config
57 If this is empty, default values are used. If config is a
58 :class:`Config` instance, it will be used to configure the
59 instance.
60
61 Notes
62 -----
63 Subclasses of Configurable must call the :meth:`__init__` method of
64 :class:`Configurable` *before* doing anything else and using
65 :func:`super`::
66
67 class MyConfigurable(Configurable):
68 def __init__(self, config=None):
69 super(MyConfigurable, self).__init__(config)
70 # Then any other code you need to finish initialization.
71
72 This ensures that instances will be configured properly.
73 """
74 config = kwargs.pop('config', None)
75 if config is not None:
76 # We used to deepcopy, but for now we are trying to just save
77 # by reference. This *could* have side effects as all components
78 # will share config. In fact, I did find such a side effect in
79 # _config_changed below. If a config attribute value was a mutable type
80 # all instances of a component were getting the same copy, effectively
81 # making that a class attribute.
82 # self.config = deepcopy(config)
83 self.config = config
84 # This should go second so individual keyword arguments override
85 # the values in config.
86 super(Configurable, self).__init__(**kwargs)
87 self.created = datetime.datetime.now()
88
89 #-------------------------------------------------------------------------
90 # Static trait notifiations
91 #-------------------------------------------------------------------------
92
93 def _config_changed(self, name, old, new):
94 """Update all the class traits having ``config=True`` as metadata.
95
96 For any class trait with a ``config`` metadata attribute that is
97 ``True``, we update the trait with the value of the corresponding
98 config entry.
99 """
100 # Get all traits with a config metadata entry that is True
101 traits = self.traits(config=True)
102
103 # We auto-load config section for this class as well as any parent
104 # classes that are Configurable subclasses. This starts with Configurable
105 # and works down the mro loading the config for each section.
106 section_names = [cls.__name__ for cls in \
107 reversed(self.__class__.__mro__) if
108 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
109
110 for sname in section_names:
111 # Don't do a blind getattr as that would cause the config to
112 # dynamically create the section with name self.__class__.__name__.
113 if new._has_section(sname):
114 my_config = new[sname]
115 for k, v in traits.items():
116 # Don't allow traitlets with config=True to start with
117 # uppercase. Otherwise, they are confused with Config
118 # subsections. But, developers shouldn't have uppercase
119 # attributes anyways! (PEP 6)
120 if k[0].upper()==k[0] and not k.startswith('_'):
121 raise ConfigurableError('Configurable traitlets with '
122 'config=True must start with a lowercase so they are '
123 'not confused with Config subsections: %s.%s' % \
124 (self.__class__.__name__, k))
125 try:
126 # Here we grab the value from the config
127 # If k has the naming convention of a config
128 # section, it will be auto created.
129 config_value = my_config[k]
130 except KeyError:
131 pass
132 else:
133 # print "Setting %s.%s from %s.%s=%r" % \
134 # (self.__class__.__name__,k,sname,k,config_value)
135 # We have to do a deepcopy here if we don't deepcopy the entire
136 # config object. If we don't, a mutable config_value will be
137 # shared by all instances, effectively making it a class attribute.
138 setattr(self, k, deepcopy(config_value))
139
@@ -0,0 +1,124 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Tests for IPython.config.configurable
5
6 Authors:
7
8 * Brian Granger
9 * Fernando Perez (design help)
10 """
11
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2010 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 #-----------------------------------------------------------------------------
20 # Imports
21 #-----------------------------------------------------------------------------
22
23 from unittest import TestCase
24
25 from IPython.config.configurable import Configurable, ConfigurableError
26 from IPython.utils.traitlets import (
27 TraitError, Int, Float, Str
28 )
29 from IPython.config.loader import Config
30
31
32 #-----------------------------------------------------------------------------
33 # Test cases
34 #-----------------------------------------------------------------------------
35
36
37 class MyConfigurable(Configurable):
38 a = Int(1, config=True)
39 b = Float(1.0, config=True)
40 c = Str('no config')
41
42
43 class Foo(Configurable):
44 a = Int(0, config=True)
45 b = Str('nope', config=True)
46
47
48 class Bar(Foo):
49 b = Str('gotit', config=False)
50 c = Float(config=True)
51
52
53 class TestConfigurableConfig(TestCase):
54
55 def test_default(self):
56 c1 = Configurable()
57 c2 = Configurable(config=c1.config)
58 c3 = Configurable(config=c2.config)
59 self.assertEquals(c1.config, c2.config)
60 self.assertEquals(c2.config, c3.config)
61
62 def test_custom(self):
63 config = Config()
64 config.foo = 'foo'
65 config.bar = 'bar'
66 c1 = Configurable(config=config)
67 c2 = Configurable(config=c1.config)
68 c3 = Configurable(config=c2.config)
69 self.assertEquals(c1.config, config)
70 self.assertEquals(c2.config, config)
71 self.assertEquals(c3.config, config)
72 # Test that copies are not made
73 self.assert_(c1.config is config)
74 self.assert_(c2.config is config)
75 self.assert_(c3.config is config)
76 self.assert_(c1.config is c2.config)
77 self.assert_(c2.config is c3.config)
78
79 def test_inheritance(self):
80 config = Config()
81 config.MyConfigurable.a = 2
82 config.MyConfigurable.b = 2.0
83 c1 = MyConfigurable(config=config)
84 c2 = MyConfigurable(config=c1.config)
85 self.assertEquals(c1.a, config.MyConfigurable.a)
86 self.assertEquals(c1.b, config.MyConfigurable.b)
87 self.assertEquals(c2.a, config.MyConfigurable.a)
88 self.assertEquals(c2.b, config.MyConfigurable.b)
89
90 def test_parent(self):
91 config = Config()
92 config.Foo.a = 10
93 config.Foo.b = "wow"
94 config.Bar.b = 'later'
95 config.Bar.c = 100.0
96 f = Foo(config=config)
97 b = Bar(config=f.config)
98 self.assertEquals(f.a, 10)
99 self.assertEquals(f.b, 'wow')
100 self.assertEquals(b.b, 'gotit')
101 self.assertEquals(b.c, 100.0)
102
103 def test_override1(self):
104 config = Config()
105 config.MyConfigurable.a = 2
106 config.MyConfigurable.b = 2.0
107 c = MyConfigurable(a=3, config=config)
108 self.assertEquals(c.a, 3)
109 self.assertEquals(c.b, config.MyConfigurable.b)
110 self.assertEquals(c.c, 'no config')
111
112 def test_override2(self):
113 config = Config()
114 config.Foo.a = 1
115 config.Bar.b = 'or' # Up above b is config=False, so this won't do it.
116 config.Bar.c = 10.0
117 c = Bar(config=config)
118 self.assertEquals(c.a, config.Foo.a)
119 self.assertEquals(c.b, 'gotit')
120 self.assertEquals(c.c, config.Bar.c)
121 c = Bar(a=2, b='and', c=20.0, config=config)
122 self.assertEquals(c.a, 2)
123 self.assertEquals(c.b, 'and')
124 self.assertEquals(c.c, 20.0)
@@ -0,0 +1,125 b''
1 # encoding: utf-8
2 """A class for managing IPython extensions.
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 import os
21 import sys
22
23 from IPython.config.configurable import Configurable
24 from IPython.utils.traitlets import Instance
25
26 #-----------------------------------------------------------------------------
27 # Main class
28 #-----------------------------------------------------------------------------
29
30 class ExtensionManager(Configurable):
31 """A class to manage IPython extensions.
32
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')
57
58 def __init__(self, shell=None, config=None):
59 super(ExtensionManager, self).__init__(shell=shell, config=config)
60 self.shell.on_trait_change(
61 self._on_ipython_dir_changed, 'ipython_dir'
62 )
63
64 def __del__(self):
65 self.shell.on_trait_change(
66 self._on_ipython_dir_changed, 'ipython_dir', remove=True
67 )
68
69 @property
70 def ipython_extension_dir(self):
71 return os.path.join(self.shell.ipython_dir, u'extensions')
72
73 def _on_ipython_dir_changed(self):
74 if not os.path.isdir(self.ipython_extension_dir):
75 os.makedirs(self.ipython_extension_dir, mode = 0777)
76
77 def load_extension(self, module_str):
78 """Load an IPython extension by its module name.
79
80 If :func:`load_ipython_extension` returns anything, this function
81 will return that object.
82 """
83 from IPython.utils.syspathcontext import prepended_to_syspath
84
85 if module_str not in sys.modules:
86 with prepended_to_syspath(self.ipython_extension_dir):
87 __import__(module_str)
88 mod = sys.modules[module_str]
89 return self._call_load_ipython_extension(mod)
90
91 def unload_extension(self, module_str):
92 """Unload an IPython extension by its module name.
93
94 This function looks up the extension's name in ``sys.modules`` and
95 simply calls ``mod.unload_ipython_extension(self)``.
96 """
97 if module_str in sys.modules:
98 mod = sys.modules[module_str]
99 self._call_unload_ipython_extension(mod)
100
101 def reload_extension(self, module_str):
102 """Reload an IPython extension by calling reload.
103
104 If the module has not been loaded before,
105 :meth:`InteractiveShell.load_extension` is called. Otherwise
106 :func:`reload` is called and then the :func:`load_ipython_extension`
107 function of the module, if it exists is called.
108 """
109 from IPython.utils.syspathcontext import prepended_to_syspath
110
111 with prepended_to_syspath(self.ipython_extension_dir):
112 if module_str in sys.modules:
113 mod = sys.modules[module_str]
114 reload(mod)
115 self._call_load_ipython_extension(mod)
116 else:
117 self.load_extension(module_str)
118
119 def _call_load_ipython_extension(self, mod):
120 if hasattr(mod, 'load_ipython_extension'):
121 return mod.load_ipython_extension(self.shell)
122
123 def _call_unload_ipython_extension(self, mod):
124 if hasattr(mod, 'unload_ipython_extension'):
125 return mod.unload_ipython_extension(self.shell)
@@ -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'))
@@ -0,0 +1,61 b''
1 .. _extensions_overview:
2
3 ==================
4 IPython extensions
5 ==================
6
7 Configuration files are just the first level of customization that IPython
8 supports. The next level is that of extensions. An IPython extension is an
9 importable Python module that has a a few special function. By defining these
10 functions, users can customize IPython by accessing the actual runtime objects
11 of IPython. Here is a sample extension::
12
13 # myextension.py
14
15 def load_ipython_extension(ipython):
16 # The ``ipython`` argument is the currently active
17 # :class:`InteractiveShell` instance that can be used in any way.
18 # This allows you do to things like register new magics, plugins or
19 # aliases.
20
21 def unload_ipython_extension(ipython):
22 # If you want your extension to be unloadable, put that logic here.
23
24 This :func:`load_ipython_extension` function is called after your extension is
25 imported and the currently active :class:`InteractiveShell` instance is passed
26 as the only argument. You can do anything you want with IPython at that point.
27
28 The :func:`load_ipython_extension` will be called again is you load or reload
29 the extension again. It is up to the extension author to add code to manage
30 that.
31
32 You can put your extension modules anywhere you want, as long as they can be
33 imported by Python's standard import mechanism. However, to make it easy to
34 write extensions, you can also put your extensions in
35 ``os.path.join(self.ipython_dir, 'extensions')``. This directory is added to
36 ``sys.path`` automatically.
37
38 Using extensions
39 ================
40
41 There are two ways you can tell IPython to use your extension:
42
43 1. Listing it in a configuration file.
44 2. Using the ``%load_ext`` magic function.
45
46 To load an extension called :file:`myextension.py` add the following logic
47 to your configuration file::
48
49 c.Global.extensions = [
50 'myextension'
51 ]
52
53 To load that same extension at runtime, use the ``%load_ext`` magic::
54
55 .. sourcecode:: ipython
56
57 In [1]: %load_ext myextension
58
59 To summarize, in conjunction with configuration files and profiles, IPython
60 extensions give you complete and flexible control over your IPython
61 setup.
@@ -0,0 +1,23 b''
1 .. _plugins_overview:
2
3 ===============
4 IPython plugins
5 ===============
6
7 IPython has a plugin mechanism that allows users to create new and custom
8 runtime components for IPython. Plugins are different from extensions:
9
10 * Extensions are used to load plugins.
11 * Extensions are a more advanced configuration system that gives you access
12 to the running IPython instance.
13 * Plugins add entirely new capabilities to IPython.
14 * Plugins are traited and configurable.
15
16 At this point, our plugin system is brand new and the documentation is
17 minimal. If you are interested in creating a new plugin, see the following
18 files:
19
20 * :file:`IPython/extensions/parallemagic.py`
21 * :file:`IPython/extensions/pretty.`
22
23 As well as our documentation on the configuration system and extensions.
@@ -0,0 +1,74 b''
1 .. This (-*- rst -*-) format file contains commonly used link targets
2 and name substitutions. It may be included in many files,
3 therefore it should only contain link targets and name
4 substitutions. Try grepping for "^\.\. _" to find plausible
5 candidates for this list.
6
7 NOTE: this file must have an extension *opposite* to that of the main reST
8 files in the manuals, so that we can include it with ".. include::"
9 directives, but without triggering warnings from Sphinx for not being listed
10 in any toctree. Since IPython uses .txt for the main files, this wone will
11 use .rst.
12
13 NOTE: reST targets are
14 __not_case_sensitive__, so only one target definition is needed for
15 ipython, IPython, etc.
16
17 NOTE: Some of these were taken from the nipy links compendium.
18
19 .. Main IPython links
20 .. _ipython: http://ipython.scipy.org
21 .. _`ipython manual`: http://ipython.scipy.org/doc/manual/html
22 .. _ipython_github: http://github.com/ipython/ipython/
23 .. _ipython_github_repo: http://github.com/ipython/ipython/
24 .. _ipython_downloads: http://ipython.scipy.org/dist
25 .. _ipython_pypi: http://pypi.python.org/pypi/ipython
26
27 .. _ZeroMQ: http://zeromq.org
28
29 .. Documentation tools and related links
30 .. _graphviz: http://www.graphviz.org
31 .. _Sphinx: http://sphinx.pocoo.org
32 .. _`Sphinx reST`: http://sphinx.pocoo.org/rest.html
33 .. _sampledoc: http://matplotlib.sourceforge.net/sampledoc
34 .. _reST: http://docutils.sourceforge.net/rst.html
35 .. _docutils: http://docutils.sourceforge.net
36 .. _lyx: http://www.lyx.org
37 .. _pep8: http://www.python.org/dev/peps/pep-0008
38 .. _numpy_coding_guide: http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines
39
40 .. Licenses
41 .. _GPL: http://www.gnu.org/licenses/gpl.html
42 .. _BSD: http://www.opensource.org/licenses/bsd-license.php
43 .. _LGPL: http://www.gnu.org/copyleft/lesser.html
44
45 .. Other python projects
46 .. _numpy: http://numpy.scipy.org
47 .. _scipy: http://www.scipy.org
48 .. _scipy_conference: http://conference.scipy.org
49 .. _matplotlib: http://matplotlib.sourceforge.net
50 .. _pythonxy: http://www.pythonxy.com
51 .. _ETS: http://code.enthought.com/projects/tool-suite.php
52 .. _EPD: http://www.enthought.com/products/epd.php
53 .. _python: http://www.python.org
54 .. _mayavi: http://code.enthought.com/projects/mayavi
55 .. _sympy: http://code.google.com/p/sympy
56 .. _sage: http://sagemath.org
57 .. _pydy: http://code.google.com/p/pydy
58 .. _vpython: http://vpython.org
59 .. _cython: http://cython.org
60 .. _software carpentry: http://software-carpentry.org
61
62 .. Not so python scientific computing tools
63 .. _matlab: http://www.mathworks.com
64 .. _VTK: http://vtk.org
65
66 .. Other organizations
67 .. _enthought: http://www.enthought.com
68 .. _kitware: http://www.kitware.com
69 .. _netlib: http://netlib.org
70
71 .. Other tools and projects
72 .. _indefero: http://www.indefero.net
73 .. _git: http://git-scm.com
74 .. _github: http://github.com
@@ -24,8 +24,8 b' import sys'
24 # Setup everything
24 # Setup everything
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 if sys.version[0:3] < '2.5':
27 if sys.version[0:3] < '2.6':
28 raise ImportError('Python Version 2.5 or above is required for IPython.')
28 raise ImportError('Python Version 2.6 or above is required for IPython.')
29
29
30
30
31 # Make it easy to import extensions - they are always directly on pythonpath.
31 # Make it easy to import extensions - they are always directly on pythonpath.
@@ -1,10 +1,11 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 IPython's alias component
4 System command aliases.
5
5
6 Authors:
6 Authors:
7
7
8 * Fernando Perez
8 * Brian Granger
9 * Brian Granger
9 """
10 """
10
11
@@ -25,10 +26,10 b' import os'
25 import re
26 import re
26 import sys
27 import sys
27
28
28 from IPython.core.component import Component
29 from IPython.config.configurable import Configurable
29 from IPython.core.splitinput import split_user_input
30 from IPython.core.splitinput import split_user_input
30
31
31 from IPython.utils.traitlets import List
32 from IPython.utils.traitlets import List, Instance
32 from IPython.utils.autoattr import auto_attr
33 from IPython.utils.autoattr import auto_attr
33 from IPython.utils.warn import warn, error
34 from IPython.utils.warn import warn, error
34
35
@@ -99,23 +100,18 b' class InvalidAliasError(AliasError):'
99 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
100
101
101
102
102 class AliasManager(Component):
103 class AliasManager(Configurable):
103
104
104 default_aliases = List(default_aliases(), config=True)
105 default_aliases = List(default_aliases(), config=True)
105 user_aliases = List(default_value=[], config=True)
106 user_aliases = List(default_value=[], config=True)
107 shell = Instance('IPython.core.iplib.InteractiveShellABC')
106
108
107 def __init__(self, parent, config=None):
109 def __init__(self, shell=None, config=None):
108 super(AliasManager, self).__init__(parent, config=config)
110 super(AliasManager, self).__init__(shell=shell, config=config)
109 self.alias_table = {}
111 self.alias_table = {}
110 self.exclude_aliases()
112 self.exclude_aliases()
111 self.init_aliases()
113 self.init_aliases()
112
114
113 @auto_attr
114 def shell(self):
115 return Component.get_instances(
116 root=self.root,
117 klass='IPython.core.iplib.InteractiveShell')[0]
118
119 def __contains__(self, name):
115 def __contains__(self, name):
120 if name in self.alias_table:
116 if name in self.alias_table:
121 return True
117 return True
@@ -6,7 +6,7 b' All top-level applications should use the classes in this module for'
6 handling configuration and creating componenets.
6 handling configuration and creating componenets.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the components, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10
10
11 Authors:
11 Authors:
12
12
@@ -76,7 +76,7 b' class BaseAppConfigLoader(ArgParseConfigLoader):'
76
76
77
77
78 class Application(object):
78 class Application(object):
79 """Load a config, construct components and set them running.
79 """Load a config, construct configurables and set them running.
80
80
81 The configuration of an application can be done via three different Config
81 The configuration of an application can be done via three different Config
82 objects, which are loaded and ultimately merged into a single one used
82 objects, which are loaded and ultimately merged into a single one used
@@ -113,7 +113,7 b' class Application(object):'
113 file_config = None
113 file_config = None
114 #: Read from the system's command line flags.
114 #: Read from the system's command line flags.
115 command_line_config = None
115 command_line_config = None
116 #: The final config that will be passed to the component.
116 #: The final config that will be passed to the main object.
117 master_config = None
117 master_config = None
118 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
118 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
119 argv = None
119 argv = None
@@ -223,10 +223,10 b' class Application(object):'
223 """Create defaults that can't be set elsewhere.
223 """Create defaults that can't be set elsewhere.
224
224
225 For the most part, we try to set default in the class attributes
225 For the most part, we try to set default in the class attributes
226 of Components. But, defaults the top-level Application (which is
226 of Configurables. But, defaults the top-level Application (which is
227 not a HasTraits or Component) are not set in this way. Instead
227 not a HasTraits or Configurables) are not set in this way. Instead
228 we set them here. The Global section is for variables like this that
228 we set them here. The Global section is for variables like this that
229 don't belong to a particular component.
229 don't belong to a particular configurable.
230 """
230 """
231 c = Config()
231 c = Config()
232 c.Global.ipython_dir = get_ipython_dir()
232 c.Global.ipython_dir = get_ipython_dir()
@@ -418,8 +418,8 b' class Application(object):'
418 pass
418 pass
419
419
420 def construct(self):
420 def construct(self):
421 """Construct the main components that make up this app."""
421 """Construct the main objects that make up this app."""
422 self.log.debug("Constructing components for application")
422 self.log.debug("Constructing main objects for application")
423
423
424 def post_construct(self):
424 def post_construct(self):
425 """Do actions after construct, but before starting the app."""
425 """Do actions after construct, but before starting the app."""
@@ -21,10 +21,10 b' Authors:'
21
21
22 import __builtin__
22 import __builtin__
23
23
24 from IPython.core.component import Component
24 from IPython.config.configurable import Configurable
25 from IPython.core.quitter import Quitter
25 from IPython.core.quitter import Quitter
26
26
27 from IPython.utils.autoattr import auto_attr
27 from IPython.utils.traitlets import Instance
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
@@ -35,20 +35,17 b' class __BuiltinUndefined(object): pass'
35 BuiltinUndefined = __BuiltinUndefined()
35 BuiltinUndefined = __BuiltinUndefined()
36
36
37
37
38 class BuiltinTrap(Component):
38 class BuiltinTrap(Configurable):
39
39
40 def __init__(self, parent):
40 shell = Instance('IPython.core.iplib.InteractiveShellABC')
41 super(BuiltinTrap, self).__init__(parent, None, None)
41
42 def __init__(self, shell=None):
43 super(BuiltinTrap, self).__init__(shell=shell, config=None)
42 self._orig_builtins = {}
44 self._orig_builtins = {}
43 # We define this to track if a single BuiltinTrap is nested.
45 # We define this to track if a single BuiltinTrap is nested.
44 # Only turn off the trap when the outermost call to __exit__ is made.
46 # Only turn off the trap when the outermost call to __exit__ is made.
45 self._nested_level = 0
47 self._nested_level = 0
46
48 self.shell = shell
47 @auto_attr
48 def shell(self):
49 return Component.get_instances(
50 root=self.root,
51 klass='IPython.core.iplib.InteractiveShell')[0]
52
49
53 def __enter__(self):
50 def __enter__(self):
54 if self._nested_level == 0:
51 if self._nested_level == 0:
@@ -22,34 +22,30 b' Authors:'
22
22
23 import sys
23 import sys
24
24
25 from IPython.core.component import Component
25 from IPython.config.configurable import Configurable
26 from IPython.utils.traitlets import Any
26
27
27 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
28 # Classes and functions
29 # Classes and functions
29 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
30
31
31
32
32 class DisplayTrap(Component):
33 class DisplayTrap(Configurable):
33 """Object to manage sys.displayhook.
34 """Object to manage sys.displayhook.
34
35
35 This came from IPython.core.kernel.display_hook, but is simplified
36 This came from IPython.core.kernel.display_hook, but is simplified
36 (no callbacks or formatters) until more of the core is refactored.
37 (no callbacks or formatters) until more of the core is refactored.
37 """
38 """
38
39
39 def __init__(self, parent, hook):
40 hook = Any
40 super(DisplayTrap, self).__init__(parent, None, None)
41
41 self.hook = hook
42 def __init__(self, hook=None):
43 super(DisplayTrap, self).__init__(hook=hook, config=None)
42 self.old_hook = None
44 self.old_hook = None
43 # We define this to track if a single BuiltinTrap is nested.
45 # We define this to track if a single BuiltinTrap is nested.
44 # Only turn off the trap when the outermost call to __exit__ is made.
46 # Only turn off the trap when the outermost call to __exit__ is made.
45 self._nested_level = 0
47 self._nested_level = 0
46
48
47 # @auto_attr
48 # def shell(self):
49 # return Component.get_instances(
50 # root=self.root,
51 # klass='IPython.core.iplib.InteractiveShell')[0]
52
53 def __enter__(self):
49 def __enter__(self):
54 if self._nested_level == 0:
50 if self._nested_level == 0:
55 self.set()
51 self.set()
@@ -24,13 +24,7 b' has been made into a component, this module will be sent to deathrow.'
24
24
25
25
26 def get():
26 def get():
27 """Get the most recently created InteractiveShell instance."""
27 """Get the global InteractiveShell instance."""
28 from IPython.core.iplib import InteractiveShell
28 from IPython.core.iplib import InteractiveShell
29 insts = InteractiveShell.get_instances()
29 return InteractiveShell.instance()
30 if len(insts)==0:
30
31 return None
32 most_recent = insts[0]
33 for inst in insts[1:]:
34 if inst.created > most_recent.created:
35 most_recent = inst
36 return most_recent
@@ -475,8 +475,8 b' class IPythonApp(Application):'
475 # But that might be the place for them
475 # But that might be the place for them
476 sys.path.insert(0, '')
476 sys.path.insert(0, '')
477
477
478 # Create an InteractiveShell instance
478 # Create an InteractiveShell instance.
479 self.shell = InteractiveShell(None, self.master_config)
479 self.shell = InteractiveShell.instance(config=self.master_config)
480
480
481 def post_construct(self):
481 def post_construct(self):
482 """Do actions after construct, but before starting the app."""
482 """Do actions after construct, but before starting the app."""
@@ -543,7 +543,7 b' class IPythonApp(Application):'
543 def _load_extensions(self):
543 def _load_extensions(self):
544 """Load all IPython extensions in Global.extensions.
544 """Load all IPython extensions in Global.extensions.
545
545
546 This uses the :meth:`InteractiveShell.load_extensions` to load all
546 This uses the :meth:`ExtensionManager.load_extensions` to load all
547 the extensions listed in ``self.master_config.Global.extensions``.
547 the extensions listed in ``self.master_config.Global.extensions``.
548 """
548 """
549 try:
549 try:
@@ -553,7 +553,7 b' class IPythonApp(Application):'
553 for ext in extensions:
553 for ext in extensions:
554 try:
554 try:
555 self.log.info("Loading IPython extension: %s" % ext)
555 self.log.info("Loading IPython extension: %s" % ext)
556 self.shell.load_extension(ext)
556 self.shell.extension_manager.load_extension(ext)
557 except:
557 except:
558 self.log.warn("Error in loading extension: %s" % ext)
558 self.log.warn("Error in loading extension: %s" % ext)
559 self.shell.showtraceback()
559 self.shell.showtraceback()
@@ -1,12 +1,10 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """Main IPython class."""
3 Main IPython Component
4 """
5
3
6 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
7 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
5 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
8 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
9 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2010 The IPython Development Team
10 #
8 #
11 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
@@ -20,6 +18,7 b' from __future__ import with_statement'
20 from __future__ import absolute_import
18 from __future__ import absolute_import
21
19
22 import __builtin__
20 import __builtin__
21 import abc
23 import bdb
22 import bdb
24 import codeop
23 import codeop
25 import exceptions
24 import exceptions
@@ -38,12 +37,14 b' from IPython.core import shadowns'
38 from IPython.core import ultratb
37 from IPython.core import ultratb
39 from IPython.core.alias import AliasManager
38 from IPython.core.alias import AliasManager
40 from IPython.core.builtin_trap import BuiltinTrap
39 from IPython.core.builtin_trap import BuiltinTrap
41 from IPython.core.component import Component
40 from IPython.config.configurable import Configurable
42 from IPython.core.display_trap import DisplayTrap
41 from IPython.core.display_trap import DisplayTrap
43 from IPython.core.error import TryNext, UsageError
42 from IPython.core.error import TryNext, UsageError
43 from IPython.core.extensions import ExtensionManager
44 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
44 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
45 from IPython.core.logger import Logger
45 from IPython.core.logger import Logger
46 from IPython.core.magic import Magic
46 from IPython.core.magic import Magic
47 from IPython.core.plugin import PluginManager
47 from IPython.core.prefilter import PrefilterManager
48 from IPython.core.prefilter import PrefilterManager
48 from IPython.core.prompts import CachedOutput
49 from IPython.core.prompts import CachedOutput
49 from IPython.core.usage import interactive_usage, default_banner
50 from IPython.core.usage import interactive_usage, default_banner
@@ -69,7 +70,7 b' from IPython.utils.syspathcontext import prepended_to_syspath'
69 from IPython.utils.terminal import toggle_set_term_title, set_term_title
70 from IPython.utils.terminal import toggle_set_term_title, set_term_title
70 from IPython.utils.warn import warn, error, fatal
71 from IPython.utils.warn import warn, error, fatal
71 from IPython.utils.traitlets import (
72 from IPython.utils.traitlets import (
72 Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode
73 Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode, Instance
73 )
74 )
74
75
75 # from IPython.utils import growl
76 # from IPython.utils import growl
@@ -196,7 +197,7 b' class SeparateStr(Str):'
196 #-----------------------------------------------------------------------------
197 #-----------------------------------------------------------------------------
197
198
198
199
199 class InteractiveShell(Component, Magic):
200 class InteractiveShell(Configurable, Magic):
200 """An enhanced, interactive shell for Python."""
201 """An enhanced, interactive shell for Python."""
201
202
202 autocall = Enum((0,1,2), default_value=1, config=True)
203 autocall = Enum((0,1,2), default_value=1, config=True)
@@ -281,14 +282,22 b' class InteractiveShell(Component, Magic):'
281 # Subclasses with thread support should override this as needed.
282 # Subclasses with thread support should override this as needed.
282 isthreaded = False
283 isthreaded = False
283
284
284 def __init__(self, parent=None, config=None, ipython_dir=None, usage=None,
285 # Subcomponents of InteractiveShell
286 alias_manager = Instance('IPython.core.alias.AliasManager')
287 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
288 builtin_trap = Instance('IPython.core.builtin_trap.BuiltinTrap')
289 display_trap = Instance('IPython.core.display_trap.DisplayTrap')
290 extension_manager = Instance('IPython.core.extensions.ExtensionManager')
291 plugin_manager = Instance('IPython.core.plugin.PluginManager')
292
293 def __init__(self, config=None, ipython_dir=None, usage=None,
285 user_ns=None, user_global_ns=None,
294 user_ns=None, user_global_ns=None,
286 banner1=None, banner2=None, display_banner=None,
295 banner1=None, banner2=None, display_banner=None,
287 custom_exceptions=((),None)):
296 custom_exceptions=((),None)):
288
297
289 # This is where traits with a config_key argument are updated
298 # This is where traits with a config_key argument are updated
290 # from the values on config.
299 # from the values on config.
291 super(InteractiveShell, self).__init__(parent, config=config)
300 super(InteractiveShell, self).__init__(config=config)
292
301
293 # These are relatively independent and stateless
302 # These are relatively independent and stateless
294 self.init_ipython_dir(ipython_dir)
303 self.init_ipython_dir(ipython_dir)
@@ -334,8 +343,21 b' class InteractiveShell(Component, Magic):'
334 self.init_reload_doctest()
343 self.init_reload_doctest()
335 self.init_magics()
344 self.init_magics()
336 self.init_pdb()
345 self.init_pdb()
346 self.init_extension_manager()
347 self.init_plugin_manager()
337 self.hooks.late_startup_hook()
348 self.hooks.late_startup_hook()
338
349
350 @classmethod
351 def instance(cls, *args, **kwargs):
352 """Returns a global InteractiveShell instance."""
353 if not hasattr(cls, "_instance"):
354 cls._instance = cls(*args, **kwargs)
355 return cls._instance
356
357 @classmethod
358 def initialized(cls):
359 return hasattr(cls, "_instance")
360
339 def get_ipython(self):
361 def get_ipython(self):
340 """Return the currently running IPython instance."""
362 """Return the currently running IPython instance."""
341 return self
363 return self
@@ -353,12 +375,6 b' class InteractiveShell(Component, Magic):'
353 def _ipython_dir_changed(self, name, new):
375 def _ipython_dir_changed(self, name, new):
354 if not os.path.isdir(new):
376 if not os.path.isdir(new):
355 os.makedirs(new, mode = 0777)
377 os.makedirs(new, mode = 0777)
356 if not os.path.isdir(self.ipython_extension_dir):
357 os.makedirs(self.ipython_extension_dir, mode = 0777)
358
359 @property
360 def ipython_extension_dir(self):
361 return os.path.join(self.ipython_dir, 'extensions')
362
378
363 @property
379 @property
364 def usable_screen_length(self):
380 def usable_screen_length(self):
@@ -494,7 +510,7 b' class InteractiveShell(Component, Magic):'
494 self.magic_logstart()
510 self.magic_logstart()
495
511
496 def init_builtins(self):
512 def init_builtins(self):
497 self.builtin_trap = BuiltinTrap(self)
513 self.builtin_trap = BuiltinTrap(shell=self)
498
514
499 def init_inspector(self):
515 def init_inspector(self):
500 # Object inspector
516 # Object inspector
@@ -523,7 +539,7 b' class InteractiveShell(Component, Magic):'
523 pass
539 pass
524
540
525 def init_displayhook(self):
541 def init_displayhook(self):
526 self.display_trap = DisplayTrap(self, self.outputcache)
542 self.display_trap = DisplayTrap(hook=self.outputcache)
527
543
528 def init_reload_doctest(self):
544 def init_reload_doctest(self):
529 # Do a proper resetting of doctest, including the necessary displayhook
545 # Do a proper resetting of doctest, including the necessary displayhook
@@ -1743,10 +1759,20 b' class InteractiveShell(Component, Magic):'
1743 #-------------------------------------------------------------------------
1759 #-------------------------------------------------------------------------
1744
1760
1745 def init_alias(self):
1761 def init_alias(self):
1746 self.alias_manager = AliasManager(self, config=self.config)
1762 self.alias_manager = AliasManager(shell=self, config=self.config)
1747 self.ns_table['alias'] = self.alias_manager.alias_table,
1763 self.ns_table['alias'] = self.alias_manager.alias_table,
1748
1764
1749 #-------------------------------------------------------------------------
1765 #-------------------------------------------------------------------------
1766 # Things related to extensions and plugins
1767 #-------------------------------------------------------------------------
1768
1769 def init_extension_manager(self):
1770 self.extension_manager = ExtensionManager(shell=self, config=self.config)
1771
1772 def init_plugin_manager(self):
1773 self.plugin_manager = PluginManager(config=self.config)
1774
1775 #-------------------------------------------------------------------------
1750 # Things related to the running of code
1776 # Things related to the running of code
1751 #-------------------------------------------------------------------------
1777 #-------------------------------------------------------------------------
1752
1778
@@ -2340,101 +2366,11 b' class InteractiveShell(Component, Magic):'
2340 return lineout
2366 return lineout
2341
2367
2342 #-------------------------------------------------------------------------
2368 #-------------------------------------------------------------------------
2343 # Working with components
2344 #-------------------------------------------------------------------------
2345
2346 def get_component(self, name=None, klass=None):
2347 """Fetch a component by name and klass in my tree."""
2348 c = Component.get_instances(root=self, name=name, klass=klass)
2349 if len(c) == 0:
2350 return None
2351 if len(c) == 1:
2352 return c[0]
2353 else:
2354 return c
2355
2356 #-------------------------------------------------------------------------
2357 # IPython extensions
2358 #-------------------------------------------------------------------------
2359
2360 def load_extension(self, module_str):
2361 """Load an IPython extension by its module name.
2362
2363 An IPython extension is an importable Python module that has
2364 a function with the signature::
2365
2366 def load_ipython_extension(ipython):
2367 # Do things with ipython
2368
2369 This function is called after your extension is imported and the
2370 currently active :class:`InteractiveShell` instance is passed as
2371 the only argument. You can do anything you want with IPython at
2372 that point, including defining new magic and aliases, adding new
2373 components, etc.
2374
2375 The :func:`load_ipython_extension` will be called again is you
2376 load or reload the extension again. It is up to the extension
2377 author to add code to manage that.
2378
2379 You can put your extension modules anywhere you want, as long as
2380 they can be imported by Python's standard import mechanism. However,
2381 to make it easy to write extensions, you can also put your extensions
2382 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2383 is added to ``sys.path`` automatically.
2384
2385 If :func:`load_ipython_extension` returns anything, this function
2386 will return that object.
2387 """
2388 from IPython.utils.syspathcontext import prepended_to_syspath
2389
2390 if module_str not in sys.modules:
2391 with prepended_to_syspath(self.ipython_extension_dir):
2392 __import__(module_str)
2393 mod = sys.modules[module_str]
2394 return self._call_load_ipython_extension(mod)
2395
2396 def unload_extension(self, module_str):
2397 """Unload an IPython extension by its module name.
2398
2399 This function looks up the extension's name in ``sys.modules`` and
2400 simply calls ``mod.unload_ipython_extension(self)``.
2401 """
2402 if module_str in sys.modules:
2403 mod = sys.modules[module_str]
2404 self._call_unload_ipython_extension(mod)
2405
2406 def reload_extension(self, module_str):
2407 """Reload an IPython extension by calling reload.
2408
2409 If the module has not been loaded before,
2410 :meth:`InteractiveShell.load_extension` is called. Otherwise
2411 :func:`reload` is called and then the :func:`load_ipython_extension`
2412 function of the module, if it exists is called.
2413 """
2414 from IPython.utils.syspathcontext import prepended_to_syspath
2415
2416 with prepended_to_syspath(self.ipython_extension_dir):
2417 if module_str in sys.modules:
2418 mod = sys.modules[module_str]
2419 reload(mod)
2420 self._call_load_ipython_extension(mod)
2421 else:
2422 self.load_extension(module_str)
2423
2424 def _call_load_ipython_extension(self, mod):
2425 if hasattr(mod, 'load_ipython_extension'):
2426 return mod.load_ipython_extension(self)
2427
2428 def _call_unload_ipython_extension(self, mod):
2429 if hasattr(mod, 'unload_ipython_extension'):
2430 return mod.unload_ipython_extension(self)
2431
2432 #-------------------------------------------------------------------------
2433 # Things related to the prefilter
2369 # Things related to the prefilter
2434 #-------------------------------------------------------------------------
2370 #-------------------------------------------------------------------------
2435
2371
2436 def init_prefilter(self):
2372 def init_prefilter(self):
2437 self.prefilter_manager = PrefilterManager(self, config=self.config)
2373 self.prefilter_manager = PrefilterManager(shell=self, config=self.config)
2438 # Ultimately this will be refactored in the new interpreter code, but
2374 # Ultimately this will be refactored in the new interpreter code, but
2439 # for now, we should expose the main prefilter method (there's legacy
2375 # for now, we should expose the main prefilter method (there's legacy
2440 # code out there that may rely on this).
2376 # code out there that may rely on this).
@@ -2580,3 +2516,8 b' class InteractiveShell(Component, Magic):'
2580 self.restore_sys_module_state()
2516 self.restore_sys_module_state()
2581
2517
2582
2518
2519 class InteractiveShellABC(object):
2520 """An abstract base class for InteractiveShell."""
2521 __metaclass__ = abc.ABCMeta
2522
2523 InteractiveShellABC.register(InteractiveShell)
@@ -97,11 +97,11 b' def compress_dhist(dh):'
97
97
98 # XXX - for some odd reason, if Magic is made a new-style class, we get errors
98 # XXX - for some odd reason, if Magic is made a new-style class, we get errors
99 # on construction of the main InteractiveShell object. Something odd is going
99 # on construction of the main InteractiveShell object. Something odd is going
100 # on with super() calls, Component and the MRO... For now leave it as-is, but
100 # on with super() calls, Configurable and the MRO... For now leave it as-is, but
101 # eventually this needs to be clarified.
101 # eventually this needs to be clarified.
102 # BG: This is because InteractiveShell inherits from this, but is itself a
102 # BG: This is because InteractiveShell inherits from this, but is itself a
103 # Component. This messes up the MRO in some way. The fix is that we need to
103 # Configurable. This messes up the MRO in some way. The fix is that we need to
104 # make Magic a component that InteractiveShell does not subclass.
104 # make Magic a configurable that InteractiveShell does not subclass.
105
105
106 class Magic:
106 class Magic:
107 """Magic functions for InteractiveShell.
107 """Magic functions for InteractiveShell.
@@ -3586,15 +3586,15 b' Defaulting color scheme to \'NoColor\'"""'
3586
3586
3587 def magic_load_ext(self, module_str):
3587 def magic_load_ext(self, module_str):
3588 """Load an IPython extension by its module name."""
3588 """Load an IPython extension by its module name."""
3589 return self.load_extension(module_str)
3589 return self.extension_manager.load_extension(module_str)
3590
3590
3591 def magic_unload_ext(self, module_str):
3591 def magic_unload_ext(self, module_str):
3592 """Unload an IPython extension by its module name."""
3592 """Unload an IPython extension by its module name."""
3593 self.unload_extension(module_str)
3593 self.extension_manager.unload_extension(module_str)
3594
3594
3595 def magic_reload_ext(self, module_str):
3595 def magic_reload_ext(self, module_str):
3596 """Reload an IPython extension by its module name."""
3596 """Reload an IPython extension by its module name."""
3597 self.reload_extension(module_str)
3597 self.extension_manager.reload_extension(module_str)
3598
3598
3599 @testdec.skip_doctest
3599 @testdec.skip_doctest
3600 def magic_install_profiles(self, s):
3600 def magic_install_profiles(self, s):
@@ -31,11 +31,11 b' import re'
31
31
32 from IPython.core.alias import AliasManager
32 from IPython.core.alias import AliasManager
33 from IPython.core.autocall import IPyAutocall
33 from IPython.core.autocall import IPyAutocall
34 from IPython.core.component import Component
34 from IPython.config.configurable import Configurable
35 from IPython.core.splitinput import split_user_input
35 from IPython.core.splitinput import split_user_input
36 from IPython.core.page import page
36 from IPython.core.page import page
37
37
38 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
38 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool, Instance
39 from IPython.utils.io import Term
39 from IPython.utils.io import Term
40 from IPython.utils.text import make_quoted_expr
40 from IPython.utils.text import make_quoted_expr
41 from IPython.utils.autoattr import auto_attr
41 from IPython.utils.autoattr import auto_attr
@@ -169,7 +169,7 b' class LineInfo(object):'
169 #-----------------------------------------------------------------------------
169 #-----------------------------------------------------------------------------
170
170
171
171
172 class PrefilterManager(Component):
172 class PrefilterManager(Configurable):
173 """Main prefilter component.
173 """Main prefilter component.
174
174
175 The IPython prefilter is run on all user input before it is run. The
175 The IPython prefilter is run on all user input before it is run. The
@@ -210,19 +210,15 b' class PrefilterManager(Component):'
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.InteractiveShellABC')
213
214
214 def __init__(self, parent, config=None):
215 def __init__(self, shell=None, config=None):
215 super(PrefilterManager, self).__init__(parent, config=config)
216 super(PrefilterManager, self).__init__(shell=shell, config=config)
217 self.shell = shell
216 self.init_transformers()
218 self.init_transformers()
217 self.init_handlers()
219 self.init_handlers()
218 self.init_checkers()
220 self.init_checkers()
219
221
220 @auto_attr
221 def shell(self):
222 return Component.get_instances(
223 root=self.root,
224 klass='IPython.core.iplib.InteractiveShell')[0]
225
226 #-------------------------------------------------------------------------
222 #-------------------------------------------------------------------------
227 # API for managing transformers
223 # API for managing transformers
228 #-------------------------------------------------------------------------
224 #-------------------------------------------------------------------------
@@ -231,7 +227,9 b' class PrefilterManager(Component):'
231 """Create the default transformers."""
227 """Create the default transformers."""
232 self._transformers = []
228 self._transformers = []
233 for transformer_cls in _default_transformers:
229 for transformer_cls in _default_transformers:
234 transformer_cls(self, config=self.config)
230 transformer_cls(
231 shell=self.shell, prefilter_manager=self, config=self.config
232 )
235
233
236 def sort_transformers(self):
234 def sort_transformers(self):
237 """Sort the transformers by priority.
235 """Sort the transformers by priority.
@@ -265,7 +263,9 b' class PrefilterManager(Component):'
265 """Create the default checkers."""
263 """Create the default checkers."""
266 self._checkers = []
264 self._checkers = []
267 for checker in _default_checkers:
265 for checker in _default_checkers:
268 checker(self, config=self.config)
266 checker(
267 shell=self.shell, prefilter_manager=self, config=self.config
268 )
269
269
270 def sort_checkers(self):
270 def sort_checkers(self):
271 """Sort the checkers by priority.
271 """Sort the checkers by priority.
@@ -300,7 +300,9 b' class PrefilterManager(Component):'
300 self._handlers = {}
300 self._handlers = {}
301 self._esc_handlers = {}
301 self._esc_handlers = {}
302 for handler in _default_handlers:
302 for handler in _default_handlers:
303 handler(self, config=self.config)
303 handler(
304 shell=self.shell, prefilter_manager=self, config=self.config
305 )
304
306
305 @property
307 @property
306 def handlers(self):
308 def handlers(self):
@@ -445,28 +447,22 b' class PrefilterManager(Component):'
445 #-----------------------------------------------------------------------------
447 #-----------------------------------------------------------------------------
446
448
447
449
448 class PrefilterTransformer(Component):
450 class PrefilterTransformer(Configurable):
449 """Transform a line of user input."""
451 """Transform a line of user input."""
450
452
451 priority = Int(100, config=True)
453 priority = Int(100, config=True)
452 shell = Any
454 # Transformers don't currently use shell or prefilter_manager, but as we
453 prefilter_manager = Any
455 # move away from checkers and handlers, they will need them.
456 shell = Instance('IPython.core.iplib.InteractiveShellABC')
457 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
454 enabled = Bool(True, config=True)
458 enabled = Bool(True, config=True)
455
459
456 def __init__(self, parent, config=None):
460 def __init__(self, shell=None, prefilter_manager=None, config=None):
457 super(PrefilterTransformer, self).__init__(parent, config=config)
461 super(PrefilterTransformer, self).__init__(
462 shell=shell, prefilter_manager=prefilter_manager, config=config
463 )
458 self.prefilter_manager.register_transformer(self)
464 self.prefilter_manager.register_transformer(self)
459
465
460 @auto_attr
461 def shell(self):
462 return Component.get_instances(
463 root=self.root,
464 klass='IPython.core.iplib.InteractiveShell')[0]
465
466 @auto_attr
467 def prefilter_manager(self):
468 return PrefilterManager.get_instances(root=self.root)[0]
469
470 def transform(self, line, continue_prompt):
466 def transform(self, line, continue_prompt):
471 """Transform a line, returning the new one."""
467 """Transform a line, returning the new one."""
472 return None
468 return None
@@ -561,28 +557,20 b' class IPyPromptTransformer(PrefilterTransformer):'
561 #-----------------------------------------------------------------------------
557 #-----------------------------------------------------------------------------
562
558
563
559
564 class PrefilterChecker(Component):
560 class PrefilterChecker(Configurable):
565 """Inspect an input line and return a handler for that line."""
561 """Inspect an input line and return a handler for that line."""
566
562
567 priority = Int(100, config=True)
563 priority = Int(100, config=True)
568 shell = Any
564 shell = Instance('IPython.core.iplib.InteractiveShellABC')
569 prefilter_manager = Any
565 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
570 enabled = Bool(True, config=True)
566 enabled = Bool(True, config=True)
571
567
572 def __init__(self, parent, config=None):
568 def __init__(self, shell=None, prefilter_manager=None, config=None):
573 super(PrefilterChecker, self).__init__(parent, config=config)
569 super(PrefilterChecker, self).__init__(
570 shell=shell, prefilter_manager=prefilter_manager, config=config
571 )
574 self.prefilter_manager.register_checker(self)
572 self.prefilter_manager.register_checker(self)
575
573
576 @auto_attr
577 def shell(self):
578 return Component.get_instances(
579 root=self.root,
580 klass='IPython.core.iplib.InteractiveShell')[0]
581
582 @auto_attr
583 def prefilter_manager(self):
584 return PrefilterManager.get_instances(root=self.root)[0]
585
586 def check(self, line_info):
574 def check(self, line_info):
587 """Inspect line_info and return a handler instance or None."""
575 """Inspect line_info and return a handler instance or None."""
588 return None
576 return None
@@ -709,16 +697,12 b' class AliasChecker(PrefilterChecker):'
709
697
710 priority = Int(800, config=True)
698 priority = Int(800, config=True)
711
699
712 @auto_attr
713 def alias_manager(self):
714 return AliasManager.get_instances(root=self.root)[0]
715
716 def check(self, line_info):
700 def check(self, line_info):
717 "Check if the initital identifier on the line is an alias."
701 "Check if the initital identifier on the line is an alias."
718 # Note: aliases can not contain '.'
702 # Note: aliases can not contain '.'
719 head = line_info.ifun.split('.',1)[0]
703 head = line_info.ifun.split('.',1)[0]
720 if line_info.ifun not in self.alias_manager \
704 if line_info.ifun not in self.shell.alias_manager \
721 or head not in self.alias_manager \
705 or head not in self.shell.alias_manager \
722 or is_shadowed(head, self.shell):
706 or is_shadowed(head, self.shell):
723 return None
707 return None
724
708
@@ -766,31 +750,23 b' class AutocallChecker(PrefilterChecker):'
766 #-----------------------------------------------------------------------------
750 #-----------------------------------------------------------------------------
767
751
768
752
769 class PrefilterHandler(Component):
753 class PrefilterHandler(Configurable):
770
754
771 handler_name = Str('normal')
755 handler_name = Str('normal')
772 esc_strings = List([])
756 esc_strings = List([])
773 shell = Any
757 shell = Instance('IPython.core.iplib.InteractiveShellABC')
774 prefilter_manager = Any
758 prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager')
775
759
776 def __init__(self, parent, config=None):
760 def __init__(self, shell=None, prefilter_manager=None, config=None):
777 super(PrefilterHandler, self).__init__(parent, config=config)
761 super(PrefilterHandler, self).__init__(
762 shell=shell, prefilter_manager=prefilter_manager, config=config
763 )
778 self.prefilter_manager.register_handler(
764 self.prefilter_manager.register_handler(
779 self.handler_name,
765 self.handler_name,
780 self,
766 self,
781 self.esc_strings
767 self.esc_strings
782 )
768 )
783
769
784 @auto_attr
785 def shell(self):
786 return Component.get_instances(
787 root=self.root,
788 klass='IPython.core.iplib.InteractiveShell')[0]
789
790 @auto_attr
791 def prefilter_manager(self):
792 return PrefilterManager.get_instances(root=self.root)[0]
793
794 def handle(self, line_info):
770 def handle(self, line_info):
795 # print "normal: ", line_info
771 # print "normal: ", line_info
796 """Handle normal input lines. Use as a template for handlers."""
772 """Handle normal input lines. Use as a template for handlers."""
@@ -827,13 +803,9 b' class AliasHandler(PrefilterHandler):'
827
803
828 handler_name = Str('alias')
804 handler_name = Str('alias')
829
805
830 @auto_attr
831 def alias_manager(self):
832 return AliasManager.get_instances(root=self.root)[0]
833
834 def handle(self, line_info):
806 def handle(self, line_info):
835 """Handle alias input lines. """
807 """Handle alias input lines. """
836 transformed = self.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest)
808 transformed = self.shell.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest)
837 # pre is needed, because it carries the leading whitespace. Otherwise
809 # pre is needed, because it carries the leading whitespace. Otherwise
838 # aliases won't work in indented sections.
810 # aliases won't work in indented sections.
839 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
811 line_out = '%sget_ipython().system(%s)' % (line_info.pre_whitespace,
@@ -894,7 +866,7 b' class AutoHandler(PrefilterHandler):'
894 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
866 esc_strings = List([ESC_PAREN, ESC_QUOTE, ESC_QUOTE2])
895
867
896 def handle(self, line_info):
868 def handle(self, line_info):
897 """Hande lines which can be auto-executed, quoting if requested."""
869 """Handle lines which can be auto-executed, quoting if requested."""
898 line = line_info.line
870 line = line_info.line
899 ifun = line_info.ifun
871 ifun = line_info.ifun
900 the_rest = line_info.the_rest
872 the_rest = line_info.the_rest
@@ -23,19 +23,25 b' from IPython.core import ipapi'
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Globals
24 # Globals
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 ip = ipapi.get()
27
26
28 if not '_refbug_cache' in ip.user_ns:
27 # This needs to be here because nose and other test runners will import
29 ip.user_ns['_refbug_cache'] = []
28 # this module. Importing this module has potential side effects that we
29 # want to prevent.
30 if __name__ == '__main__':
30
31
32 ip = ipapi.get()
31
33
32 aglobal = 'Hello'
34 if not '_refbug_cache' in ip.user_ns:
33 def f():
35 ip.user_ns['_refbug_cache'] = []
34 return aglobal
35
36
36 cache = ip.user_ns['_refbug_cache']
37 cache.append(f)
38
37
39 def call_f():
38 aglobal = 'Hello'
40 for func in cache:
39 def f():
41 print 'lowercased:',func().lower()
40 return aglobal
41
42 cache = ip.user_ns['_refbug_cache']
43 cache.append(f)
44
45 def call_f():
46 for func in cache:
47 print 'lowercased:',func().lower()
@@ -1,13 +1,2 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """This directory is meant for special-purpose extensions to IPython.
2 """This directory is meant for IPython extensions."""
3
4 This can include things which alter the syntax processing stage (see
5 PhysicalQ_Input for an example of how to do this).
6
7 Any file located here can be called with an 'execfile =' option as
8
9 execfile = extensions/filename.py
10
11 since the IPython directory itself is already part of the search path for
12 files listed as 'execfile ='.
13 """
@@ -16,8 +16,8 b''
16
16
17 import new
17 import new
18
18
19 from IPython.core.component import Component
19 from IPython.core.plugin import Plugin
20 from IPython.utils.traitlets import Bool, Any
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
23
23
@@ -31,28 +31,19 b' Use activate() on a MultiEngineClient object to activate it for magics.'
31 """
31 """
32
32
33
33
34 class ParalleMagicComponent(Component):
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.InteractiveShellABC')
39
40
40 def __init__(self, parent, name=None, config=None):
41 def __init__(self, shell=None, config=None):
41 super(ParalleMagicComponent, self).__init__(parent, name=name, config=config)
42 super(ParalleMagic, self).__init__(shell=shell, config=config)
42 self._define_magics()
43 self._define_magics()
43 # A flag showing if autopx is activated or not
44 # A flag showing if autopx is activated or not
44 self.autopx = False
45 self.autopx = False
45
46
46 # Access other components like this rather than by a regular attribute.
47 # This won't lookup the InteractiveShell object until it is used and
48 # then it is cached. This is both efficient and couples this class
49 # more loosely to InteractiveShell.
50 @auto_attr
51 def shell(self):
52 return Component.get_instances(
53 root=self.root,
54 klass='IPython.core.iplib.InteractiveShell')[0]
55
56 def _define_magics(self):
47 def _define_magics(self):
57 """Define the magic functions."""
48 """Define the magic functions."""
58 self.shell.define_magic('result', self.magic_result)
49 self.shell.define_magic('result', self.magic_result)
@@ -204,6 +195,7 b' def load_ipython_extension(ip):'
204 """Load the extension in IPython."""
195 """Load the extension in IPython."""
205 global _loaded
196 global _loaded
206 if not _loaded:
197 if not _loaded:
207 prd = ParalleMagicComponent(ip, name='parallel_magic')
198 plugin = ParalleMagic(shell=ip, config=ip.config)
199 ip.plugin_manager.register_plugin('parallel_magic', plugin)
208 _loaded = True
200 _loaded = True
209
201
@@ -37,8 +37,8 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.core.component import Component
40 from IPython.core.plugin import Plugin
41 from IPython.utils.traitlets import Bool, List
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
44 from IPython.utils.importstring import import_item
44 from IPython.utils.importstring import import_item
@@ -51,10 +51,11 b' from IPython.utils.importstring import import_item'
51 _loaded = False
51 _loaded = False
52
52
53
53
54 class PrettyResultDisplay(Component):
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.InteractiveShellABC')
58
59
59 # A list of (type, func_name), like
60 # A list of (type, func_name), like
60 # [(dict, 'my_dict_printer')]
61 # [(dict, 'my_dict_printer')]
@@ -66,8 +67,8 b' class PrettyResultDisplay(Component):'
66 # The final argument can also be a callable
67 # The final argument can also be a callable
67 defaults_for_type_by_name = List(default_value=[], config=True)
68 defaults_for_type_by_name = List(default_value=[], config=True)
68
69
69 def __init__(self, parent, name=None, config=None):
70 def __init__(self, shell=None, config=None):
70 super(PrettyResultDisplay, self).__init__(parent, name=name, config=config)
71 super(PrettyResultDisplay, self).__init__(shell=shell, config=config)
71 self._setup_defaults()
72 self._setup_defaults()
72
73
73 def _setup_defaults(self):
74 def _setup_defaults(self):
@@ -87,16 +88,6 b' class PrettyResultDisplay(Component):'
87 else:
88 else:
88 raise TypeError('func_name must be a str or callable, got: %r' % func_name)
89 raise TypeError('func_name must be a str or callable, got: %r' % func_name)
89
90
90 # Access other components like this rather than by a regular attribute.
91 # This won't lookup the InteractiveShell object until it is used and
92 # then it is cached. This is both efficient and couples this class
93 # more loosely to InteractiveShell.
94 @auto_attr
95 def shell(self):
96 return Component.get_instances(
97 root=self.root,
98 klass='IPython.core.iplib.InteractiveShell')[0]
99
100 def __call__(self, otherself, arg):
91 def __call__(self, otherself, arg):
101 """Uber-pretty-printing display hook.
92 """Uber-pretty-printing display hook.
102
93
@@ -132,10 +123,10 b' def load_ipython_extension(ip):'
132 """Load the extension in IPython as a hook."""
123 """Load the extension in IPython as a hook."""
133 global _loaded
124 global _loaded
134 if not _loaded:
125 if not _loaded:
135 prd = PrettyResultDisplay(ip, name='pretty_result_display')
126 plugin = PrettyResultDisplay(shell=ip, config=ip.config)
136 ip.set_hook('result_display', prd, priority=99)
127 ip.set_hook('result_display', plugin, priority=99)
137 _loaded = True
128 _loaded = True
138 return prd
129 ip.plugin_manager.register_plugin('pretty_result_display', plugin)
139
130
140 def unload_ipython_extension(ip):
131 def unload_ipython_extension(ip):
141 """Unload the extension."""
132 """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(shell=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.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
@@ -26,7 +26,7 b' from twisted.python import log'
26
26
27 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
28 from IPython.core.application import Application, BaseAppConfigLoader
29 from IPython.core.component import Component
29 from IPython.config.configurable import Configurable
30 from IPython.core.crashhandler import CrashHandler
30 from IPython.core.crashhandler import CrashHandler
31 from IPython.core import release
31 from IPython.core import release
32 from IPython.utils.path import (
32 from IPython.utils.path import (
@@ -63,7 +63,7 b' class PIDFileError(Exception):'
63 # Class for managing cluster directories
63 # Class for managing cluster directories
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 class ClusterDir(Component):
66 class ClusterDir(Configurable):
67 """An object to manage the cluster directory and its resources.
67 """An object to manage the cluster directory and its resources.
68
68
69 The cluster directory is used by :command:`ipcontroller`,
69 The cluster directory is used by :command:`ipcontroller`,
@@ -82,9 +82,8 b' class ClusterDir(Component):'
82 pid_dir = Unicode(u'')
82 pid_dir = Unicode(u'')
83 location = Unicode(u'')
83 location = Unicode(u'')
84
84
85 def __init__(self, location):
85 def __init__(self, location=u''):
86 super(ClusterDir, self).__init__(None)
86 super(ClusterDir, self).__init__(location=location)
87 self.location = location
88
87
89 def _location_changed(self, name, old, new):
88 def _location_changed(self, name, old, new):
90 if not os.path.isdir(new):
89 if not os.path.isdir(new):
@@ -166,7 +165,7 b' class ClusterDir(Component):'
166 The full path to the cluster directory. If it does exist, it will
165 The full path to the cluster directory. If it does exist, it will
167 be used. If not, it will be created.
166 be used. If not, it will be created.
168 """
167 """
169 return ClusterDir(cluster_dir)
168 return ClusterDir(location=cluster_dir)
170
169
171 @classmethod
170 @classmethod
172 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
171 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
@@ -183,7 +182,7 b' class ClusterDir(Component):'
183 if not os.path.isdir(path):
182 if not os.path.isdir(path):
184 raise ClusterDirError('Directory not found: %s' % path)
183 raise ClusterDirError('Directory not found: %s' % path)
185 cluster_dir = os.path.join(path, u'cluster_' + profile)
184 cluster_dir = os.path.join(path, u'cluster_' + profile)
186 return ClusterDir(cluster_dir)
185 return ClusterDir(location=cluster_dir)
187
186
188 @classmethod
187 @classmethod
189 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
188 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
@@ -216,7 +215,7 b' class ClusterDir(Component):'
216 for p in paths:
215 for p in paths:
217 cluster_dir = os.path.join(p, dirname)
216 cluster_dir = os.path.join(p, dirname)
218 if os.path.isdir(cluster_dir):
217 if os.path.isdir(cluster_dir):
219 return ClusterDir(cluster_dir)
218 return ClusterDir(location=cluster_dir)
220 else:
219 else:
221 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
220 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
222
221
@@ -235,7 +234,7 b' class ClusterDir(Component):'
235 cluster_dir = expand_path(cluster_dir)
234 cluster_dir = expand_path(cluster_dir)
236 if not os.path.isdir(cluster_dir):
235 if not os.path.isdir(cluster_dir):
237 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
236 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
238 return ClusterDir(cluster_dir)
237 return ClusterDir(location=cluster_dir)
239
238
240
239
241 #-----------------------------------------------------------------------------
240 #-----------------------------------------------------------------------------
@@ -18,7 +18,7 b' configuration system.'
18
18
19 import zope.interface as zi
19 import zope.interface as zi
20
20
21 from IPython.core.component import Component
21 from IPython.config.configurable import Configurable
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
@@ -29,7 +29,7 b' class IConfiguredObjectFactory(zi.Interface):'
29 """I am a component that creates a configured object.
29 """I am a component that creates a configured object.
30
30
31 This class is useful if you want to configure a class that is not a
31 This class is useful if you want to configure a class that is not a
32 subclass of :class:`IPython.core.component.Component`.
32 subclass of :class:`IPython.config.configurable.Configurable`.
33 """
33 """
34
34
35 def __init__(config):
35 def __init__(config):
@@ -39,12 +39,12 b' class IConfiguredObjectFactory(zi.Interface):'
39 """Return an instance of the configured object."""
39 """Return an instance of the configured object."""
40
40
41
41
42 class ConfiguredObjectFactory(Component):
42 class ConfiguredObjectFactory(Configurable):
43
43
44 zi.implements(IConfiguredObjectFactory)
44 zi.implements(IConfiguredObjectFactory)
45
45
46 def __init__(self, config):
46 def __init__(self, config=None):
47 super(ConfiguredObjectFactory, self).__init__(None, config=config)
47 super(ConfiguredObjectFactory, self).__init__(config=config)
48
48
49 def create(self):
49 def create(self):
50 raise NotImplementedError('create must be implemented in a subclass')
50 raise NotImplementedError('create must be implemented in a subclass')
@@ -56,24 +56,24 b' class IAdaptedConfiguredObjectFactory(zi.Interface):'
56 This class is useful if you have the adapt an instance and configure it.
56 This class is useful if you have the adapt an instance and configure it.
57 """
57 """
58
58
59 def __init__(config, adaptee=None):
59 def __init__(config=None, adaptee=None):
60 """Get ready to adapt adaptee and then configure it using config."""
60 """Get ready to adapt adaptee and then configure it using config."""
61
61
62 def create():
62 def create():
63 """Return an instance of the adapted and configured object."""
63 """Return an instance of the adapted and configured object."""
64
64
65
65
66 class AdaptedConfiguredObjectFactory(Component):
66 class AdaptedConfiguredObjectFactory(Configurable):
67
67
68 # zi.implements(IAdaptedConfiguredObjectFactory)
68 # zi.implements(IAdaptedConfiguredObjectFactory)
69
69
70 def __init__(self, config, adaptee):
70 def __init__(self, config=None, adaptee=None):
71 # print
71 # print
72 # print "config pre:", config
72 # print "config pre:", config
73 super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config)
73 super(AdaptedConfiguredObjectFactory, self).__init__(config=config)
74 # print
74 # print
75 # print "config post:", config
75 # print "config post:", config
76 self.adaptee = adaptee
76 self.adaptee = adaptee
77
77
78 def create(self):
78 def create(self):
79 raise NotImplementedError('create must be implemented in a subclass') No newline at end of file
79 raise NotImplementedError('create must be implemented in a subclass')
@@ -191,9 +191,9 b' class FCServiceFactory(AdaptedConfiguredObjectFactory):'
191 """This class creates a tub with various services running in it.
191 """This class creates a tub with various services running in it.
192
192
193 The basic idea is that :meth:`create` returns a running :class:`Tub`
193 The basic idea is that :meth:`create` returns a running :class:`Tub`
194 instance that has a number of Foolscap references registered in it.
194 instance that has a number of Foolscap references registered in it. This
195 This class is a subclass of :class:`IPython.core.component.Component`
195 class is a subclass of :class:`IPython.config.configurable.Configurable`
196 so the IPython configuration and component system are used.
196 so the IPython configuration system is used.
197
197
198 Attributes
198 Attributes
199 ----------
199 ----------
@@ -19,7 +19,7 b' import os'
19 import re
19 import re
20 import sys
20 import sys
21
21
22 from IPython.core.component import Component
22 from IPython.config.configurable import Configurable
23 from IPython.external import Itpl
23 from IPython.external import Itpl
24 from IPython.utils.traitlets import Str, Int, List, Unicode
24 from IPython.utils.traitlets import Str, Int, List, Unicode
25 from IPython.utils.path import get_ipython_module_path
25 from IPython.utils.path import get_ipython_module_path
@@ -77,7 +77,7 b' class UnknownStatus(LauncherError):'
77 pass
77 pass
78
78
79
79
80 class BaseLauncher(Component):
80 class BaseLauncher(Configurable):
81 """An asbtraction for starting, stopping and signaling a process."""
81 """An asbtraction for starting, stopping and signaling a process."""
82
82
83 # In all of the launchers, the work_dir is where child processes will be
83 # In all of the launchers, the work_dir is where child processes will be
@@ -89,9 +89,8 b' class BaseLauncher(Component):'
89 # the --work-dir option.
89 # the --work-dir option.
90 work_dir = Unicode(u'')
90 work_dir = Unicode(u'')
91
91
92 def __init__(self, work_dir, parent=None, name=None, config=None):
92 def __init__(self, work_dir=u'', config=None):
93 super(BaseLauncher, self).__init__(parent, name, config)
93 super(BaseLauncher, self).__init__(work_dir=work_dir, config=config)
94 self.work_dir = work_dir
95 self.state = 'before' # can be before, running, after
94 self.state = 'before' # can be before, running, after
96 self.stop_deferreds = []
95 self.stop_deferreds = []
97 self.start_data = None
96 self.start_data = None
@@ -265,9 +264,9 b' class LocalProcessLauncher(BaseLauncher):'
265 # spawnProcess.
264 # spawnProcess.
266 cmd_and_args = List([])
265 cmd_and_args = List([])
267
266
268 def __init__(self, work_dir, parent=None, name=None, config=None):
267 def __init__(self, work_dir=u'', config=None):
269 super(LocalProcessLauncher, self).__init__(
268 super(LocalProcessLauncher, self).__init__(
270 work_dir, parent, name, config
269 work_dir=work_dir, config=config
271 )
270 )
272 self.process_protocol = None
271 self.process_protocol = None
273 self.start_deferred = None
272 self.start_deferred = None
@@ -356,9 +355,9 b' class LocalEngineSetLauncher(BaseLauncher):'
356 ['--log-to-file','--log-level', '40'], config=True
355 ['--log-to-file','--log-level', '40'], config=True
357 )
356 )
358
357
359 def __init__(self, work_dir, parent=None, name=None, config=None):
358 def __init__(self, work_dir=u'', config=None):
360 super(LocalEngineSetLauncher, self).__init__(
359 super(LocalEngineSetLauncher, self).__init__(
361 work_dir, parent, name, config
360 work_dir=work_dir, config=config
362 )
361 )
363 self.launchers = []
362 self.launchers = []
364
363
@@ -367,7 +366,7 b' class LocalEngineSetLauncher(BaseLauncher):'
367 self.cluster_dir = unicode(cluster_dir)
366 self.cluster_dir = unicode(cluster_dir)
368 dlist = []
367 dlist = []
369 for i in range(n):
368 for i in range(n):
370 el = LocalEngineLauncher(self.work_dir, self)
369 el = LocalEngineLauncher(work_dir=self.work_dir, config=self.config)
371 # Copy the engine args over to each engine launcher.
370 # Copy the engine args over to each engine launcher.
372 import copy
371 import copy
373 el.engine_args = copy.deepcopy(self.engine_args)
372 el.engine_args = copy.deepcopy(self.engine_args)
@@ -560,9 +559,9 b' class WindowsHPCLauncher(BaseLauncher):'
560 scheduler = Str('', config=True)
559 scheduler = Str('', config=True)
561 job_cmd = Str(find_job_cmd(), config=True)
560 job_cmd = Str(find_job_cmd(), config=True)
562
561
563 def __init__(self, work_dir, parent=None, name=None, config=None):
562 def __init__(self, work_dir=u'', config=None):
564 super(WindowsHPCLauncher, self).__init__(
563 super(WindowsHPCLauncher, self).__init__(
565 work_dir, parent, name, config
564 work_dir=work_dir, config=config
566 )
565 )
567
566
568 @property
567 @property
@@ -633,9 +632,9 b' class WindowsHPCControllerLauncher(WindowsHPCLauncher):'
633 extra_args = List([], config=False)
632 extra_args = List([], config=False)
634
633
635 def write_job_file(self, n):
634 def write_job_file(self, n):
636 job = IPControllerJob(self)
635 job = IPControllerJob(config=self.config)
637
636
638 t = IPControllerTask(self)
637 t = IPControllerTask(config=self.config)
639 # The tasks work directory is *not* the actual work directory of
638 # The tasks work directory is *not* the actual work directory of
640 # the controller. It is used as the base path for the stdout/stderr
639 # the controller. It is used as the base path for the stdout/stderr
641 # files that the scheduler redirects to.
640 # files that the scheduler redirects to.
@@ -664,10 +663,10 b' class WindowsHPCEngineSetLauncher(WindowsHPCLauncher):'
664 extra_args = List([], config=False)
663 extra_args = List([], config=False)
665
664
666 def write_job_file(self, n):
665 def write_job_file(self, n):
667 job = IPEngineSetJob(self)
666 job = IPEngineSetJob(config=self.config)
668
667
669 for i in range(n):
668 for i in range(n):
670 t = IPEngineTask(self)
669 t = IPEngineTask(config=self.config)
671 # The tasks work directory is *not* the actual work directory of
670 # The tasks work directory is *not* the actual work directory of
672 # the engine. It is used as the base path for the stdout/stderr
671 # the engine. It is used as the base path for the stdout/stderr
673 # files that the scheduler redirects to.
672 # files that the scheduler redirects to.
@@ -725,9 +724,9 b' class BatchSystemLauncher(BaseLauncher):'
725 # The full path to the instantiated batch script.
724 # The full path to the instantiated batch script.
726 batch_file = Unicode(u'')
725 batch_file = Unicode(u'')
727
726
728 def __init__(self, work_dir, parent=None, name=None, config=None):
727 def __init__(self, work_dir=u'', config=None):
729 super(BatchSystemLauncher, self).__init__(
728 super(BatchSystemLauncher, self).__init__(
730 work_dir, parent, name, config
729 work_dir=work_dir, config=config
731 )
730 )
732 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
731 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
733 self.context = {}
732 self.context = {}
@@ -24,14 +24,14 b' import uuid'
24
24
25 from xml.etree import ElementTree as ET
25 from xml.etree import ElementTree as ET
26
26
27 from IPython.core.component import Component
27 from IPython.config.configurable import Configurable
28 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
29 Str, Int, List, Instance,
29 Str, Int, List, Instance,
30 Enum, Bool, CStr
30 Enum, Bool, CStr
31 )
31 )
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Job and Task Component
34 # Job and Task classes
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37
37
@@ -74,7 +74,7 b' def find_username():'
74 return '%s\\%s' % (domain, username)
74 return '%s\\%s' % (domain, username)
75
75
76
76
77 class WinHPCJob(Component):
77 class WinHPCJob(Configurable):
78
78
79 job_id = Str('')
79 job_id = Str('')
80 job_name = Str('MyJob', config=True)
80 job_name = Str('MyJob', config=True)
@@ -165,7 +165,7 b' class WinHPCJob(Component):'
165 self.tasks.append(task)
165 self.tasks.append(task)
166
166
167
167
168 class WinHPCTask(Component):
168 class WinHPCTask(Configurable):
169
169
170 task_id = Str('')
170 task_id = Str('')
171 task_name = Str('')
171 task_name = Str('')
@@ -261,8 +261,8 b' class IPControllerTask(WinHPCTask):'
261 unit_type = Str("Core", config=False)
261 unit_type = Str("Core", config=False)
262 work_directory = CStr('', config=False)
262 work_directory = CStr('', config=False)
263
263
264 def __init__(self, parent, name=None, config=None):
264 def __init__(self, config=None):
265 super(IPControllerTask, self).__init__(parent, name, config)
265 super(IPControllerTask, self).__init__(config=config)
266 the_uuid = uuid.uuid1()
266 the_uuid = uuid.uuid1()
267 self.std_out_file_path = os.path.join('log','ipcontroller-%s.out' % the_uuid)
267 self.std_out_file_path = os.path.join('log','ipcontroller-%s.out' % the_uuid)
268 self.std_err_file_path = os.path.join('log','ipcontroller-%s.err' % the_uuid)
268 self.std_err_file_path = os.path.join('log','ipcontroller-%s.err' % the_uuid)
@@ -289,8 +289,8 b' class IPEngineTask(WinHPCTask):'
289 unit_type = Str("Core", config=False)
289 unit_type = Str("Core", config=False)
290 work_directory = CStr('', config=False)
290 work_directory = CStr('', config=False)
291
291
292 def __init__(self, parent, name=None, config=None):
292 def __init__(self, config=None):
293 super(IPEngineTask,self).__init__(parent, name, config)
293 super(IPEngineTask,self).__init__(config=config)
294 the_uuid = uuid.uuid1()
294 the_uuid = uuid.uuid1()
295 self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid)
295 self.std_out_file_path = os.path.join('log','ipengine-%s.out' % the_uuid)
296 self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)
296 self.std_err_file_path = os.path.join('log','ipengine-%s.err' % the_uuid)
@@ -136,8 +136,8 b' def start_ipython():'
136 config = tools.default_config()
136 config = tools.default_config()
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.instance(
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
@@ -360,6 +360,13 b' class TestHasTraits(TestCase):'
360 traits = a.traits(config_key=lambda v: True)
360 traits = a.traits(config_key=lambda v: True)
361 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
361 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
362
362
363 def test_init(self):
364 class A(HasTraits):
365 i = Int()
366 x = Float()
367 a = A(i=1, x=10.0)
368 self.assertEquals(a.i, 1)
369 self.assertEquals(a.x, 10.0)
363
370
364 #-----------------------------------------------------------------------------
371 #-----------------------------------------------------------------------------
365 # Tests for specific trait types
372 # Tests for specific trait types
@@ -372,14 +372,14 b' class HasTraits(object):'
372
372
373 __metaclass__ = MetaHasTraits
373 __metaclass__ = MetaHasTraits
374
374
375 def __new__(cls, *args, **kw):
375 def __new__(cls, **kw):
376 # This is needed because in Python 2.6 object.__new__ only accepts
376 # This is needed because in Python 2.6 object.__new__ only accepts
377 # the cls argument.
377 # the cls argument.
378 new_meth = super(HasTraits, cls).__new__
378 new_meth = super(HasTraits, cls).__new__
379 if new_meth is object.__new__:
379 if new_meth is object.__new__:
380 inst = new_meth(cls)
380 inst = new_meth(cls)
381 else:
381 else:
382 inst = new_meth(cls, *args, **kw)
382 inst = new_meth(cls, **kw)
383 inst._trait_values = {}
383 inst._trait_values = {}
384 inst._trait_notifiers = {}
384 inst._trait_notifiers = {}
385 # Here we tell all the TraitType instances to set their default
385 # Here we tell all the TraitType instances to set their default
@@ -398,9 +398,12 b' class HasTraits(object):'
398
398
399 return inst
399 return inst
400
400
401 # def __init__(self):
401 def __init__(self, **kw):
402 # self._trait_values = {}
402 # Allow trait values to be set using keyword arguments.
403 # self._trait_notifiers = {}
403 # We need to use setattr for this to trigger validation and
404 # notifications.
405 for key, value in kw.iteritems():
406 setattr(self, key, value)
404
407
405 def _notify_trait(self, name, old_value, new_value):
408 def _notify_trait(self, name, old_value, new_value):
406
409
@@ -1018,6 +1021,28 b' class List(Instance):'
1018 allow_none=allow_none, **metadata)
1021 allow_none=allow_none, **metadata)
1019
1022
1020
1023
1024 class Dict(Instance):
1025 """An instance of a Python dict."""
1026
1027 def __init__(self, default_value=None, allow_none=True, **metadata):
1028 """Create a dict trait type from a dict.
1029
1030 The default value is created by doing ``dict(default_value)``,
1031 which creates a copy of the ``default_value``.
1032 """
1033 if default_value is None:
1034 args = ((),)
1035 elif isinstance(default_value, dict):
1036 args = (default_value,)
1037 elif isinstance(default_value, SequenceTypes):
1038 args = (default_value,)
1039 else:
1040 raise TypeError('default value of Dict was %s' % default_value)
1041
1042 super(Dict,self).__init__(klass=dict, args=args,
1043 allow_none=allow_none, **metadata)
1044
1045
1021 class TCPAddress(TraitType):
1046 class TCPAddress(TraitType):
1022 """A trait for an (ip, port) tuple.
1047 """A trait for an (ip, port) tuple.
1023
1048
@@ -1035,4 +1060,3 b' class TCPAddress(TraitType):'
1035 if port >= 0 and port <= 65535:
1060 if port >= 0 and port <= 65535:
1036 return value
1061 return value
1037 self.error(obj, value)
1062 self.error(obj, value)
1038
@@ -39,8 +39,9 b' pdf: latex'
39
39
40 all: html pdf
40 all: html pdf
41
41
42 dist: clean all
42 dist: all
43 mkdir -p dist
43 mkdir -p dist
44 rm -rf dist/*
44 ln build/latex/ipython.pdf dist/
45 ln build/latex/ipython.pdf dist/
45 cp -al build/html dist/
46 cp -al build/html dist/
46 @echo "Build finished. Final docs are in dist/"
47 @echo "Build finished. Final docs are in dist/"
@@ -98,3 +99,6 b' linkcheck:'
98 gitwash-update:
99 gitwash-update:
99 python ../tools/gitwash_dumper.py source/development ipython
100 python ../tools/gitwash_dumper.py source/development ipython
100 cd source/development/gitwash && rename 's/.rst/.txt/' *.rst
101 cd source/development/gitwash && rename 's/.rst/.txt/' *.rst
102
103 nightly: dist
104 rsync -avH --delete dist/ ipython:www/doc/nightly No newline at end of file
@@ -27,7 +27,12 b" if __name__ == '__main__':"
27 r'\.config\.default',
27 r'\.config\.default',
28 r'\.config\.profile',
28 r'\.config\.profile',
29 r'\.frontend',
29 r'\.frontend',
30 r'\.gui'
30 r'\.gui',
31 # For now, the zmq code has
32 # unconditional top-level code so it's
33 # not import safe. This needs fixing
34 # soon.
35 r'\.zmq',
31 ]
36 ]
32
37
33 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
38 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
@@ -38,7 +43,7 b" if __name__ == '__main__':"
38 # for each group copied below
43 # for each group copied below
39
44
40 # AttributeError: __abstractmethods__
45 # AttributeError: __abstractmethods__
41 r'\.core\.component',
46 r'\.config\.configurable',
42 r'\.utils\.traitlets',
47 r'\.utils\.traitlets',
43
48
44 # AttributeError: __provides__
49 # AttributeError: __provides__
@@ -8,6 +8,8 b' Configuration and customization'
8 :maxdepth: 2
8 :maxdepth: 2
9
9
10 overview.txt
10 overview.txt
11 extensions.txt
12 plugins.txt
11 ipython.txt
13 ipython.txt
12 editors.txt
14 editors.txt
13 old.txt
15 old.txt
@@ -133,4 +133,4 b' attributes::'
133
133
134 c.AliasManager.user_aliases = [
134 c.AliasManager.user_aliases = [
135 ('la', 'ls -al')
135 ('la', 'ls -al')
136 ] No newline at end of file
136 ]
@@ -42,29 +42,26 b' Application: :class:`~IPython.core.application.Application`'
42 application is the :command:`ipython` command line program. Each
42 application is the :command:`ipython` command line program. Each
43 application reads a *single* configuration file and command line options
43 application reads a *single* configuration file and command line options
44 and then produces a master configuration object for the application. This
44 and then produces a master configuration object for the application. This
45 configuration object is then passed to the components that the application
45 configuration object is then passed to the configurable objects that the
46 creates. Components implement the actual logic of the application and know
46 application creates. These configurable objects implement the actual logic
47 how to configure themselves given the configuration object.
47 of the application and know how to configure themselves given the
48
48 configuration object.
49 Component: :class:`~IPython.core.component.Component`
49
50 A component is a regular Python class that serves as a base class for all
50 Component: :class:`~IPython.config.configurable.Configurable`
51 main classes in an application. The
51 A configurable is a regular Python class that serves as a base class for
52 :class:`~IPython.core.component.Component` base class is lightweight and
52 all main classes in an application. The
53 only does two main things.
53 :class:`~IPython.config.configurable.Configurable` base class is
54 lightweight and only does one things.
55
56 This :class:`~IPython.config.configurable.Configurable` is a subclass
57 of :class:`~IPython.utils.traitlets.HasTraits` that knows how to configure
58 itself. Class level traits with the metadata ``config=True`` become
59 values that can be configured from the command line and configuration
60 files.
54
61
55 First, it keeps track of all instances of itself and provides an
62 Developers create :class:`~IPython.config.configurable.Configurable`
56 interfaces for querying those instances. This enables components to get
63 subclasses that implement all of the logic in the application. Each of
57 references to other components, even though they are not "nearby" in the
64 these subclasses has its own configuration information that controls how
58 runtime object graph.
59
60 Second, it declares what class attributes are configurable and specifies
61 the default types and values of those attributes. This information is used
62 to automatically configure instances given the applications configuration
63 object.
64
65 Developers create :class:`~IPython.core.component.Component` subclasses
66 that implement all of the logic in the application. Each of these
67 subclasses has its own configuration information that controls how
68 instances are created.
65 instances are created.
69
66
70 Having described these main concepts, we can now state the main idea in our
67 Having described these main concepts, we can now state the main idea in our
@@ -73,7 +70,9 b' attributes to be controlled on a class by class basis*. Thus all instances of'
73 a given class are configured in the same way. Furthermore, if two instances
70 a given class are configured in the same way. Furthermore, if two instances
74 need to be configured differently, they need to be instances of two different
71 need to be configured differently, they need to be instances of two different
75 classes. While this model may seem a bit restrictive, we have found that it
72 classes. While this model may seem a bit restrictive, we have found that it
76 expresses most things that need to be configured extremely well.
73 expresses most things that need to be configured extremely well. However, it
74 is possible to create two instances of the same class that have different
75 trait values. This is done by overriding the configuration.
77
76
78 Now, we show what our configuration objects and files look like.
77 Now, we show what our configuration objects and files look like.
79
78
@@ -98,33 +97,34 b' attributes on it. All you have to know is:'
98 * The type of each attribute.
97 * The type of each attribute.
99
98
100 The answers to these two questions are provided by the various
99 The answers to these two questions are provided by the various
101 :class:`~IPython.core.component.Component` subclasses that an application
100 :class:`~IPython.config.configurable.Configurable` subclasses that an
102 uses. Let's look at how this would work for a simple component subclass::
101 application uses. Let's look at how this would work for a simple component
102 subclass::
103
103
104 # Sample component that can be configured.
104 # Sample component that can be configured.
105 from IPython.core.component import Component
105 from IPython.config.configurable import Configurable
106 from IPython.utils.traitlets import Int, Float, Str, Bool
106 from IPython.utils.traitlets import Int, Float, Str, Bool
107
107
108 class MyComponent(Component):
108 class MyClass(Configurable):
109 name = Str('defaultname', config=True)
109 name = Str('defaultname', config=True)
110 ranking = Int(0, config=True)
110 ranking = Int(0, config=True)
111 value = Float(99.0)
111 value = Float(99.0)
112 # The rest of the class implementation would go here..
112 # The rest of the class implementation would go here..
113
113
114 In this example, we see that :class:`MyComponent` has three attributes, two
114 In this example, we see that :class:`MyClass` has three attributes, two
115 of whom (``name``, ``ranking``) can be configured. All of the attributes
115 of whom (``name``, ``ranking``) can be configured. All of the attributes
116 are given types and default values. If a :class:`MyComponent` is instantiated,
116 are given types and default values. If a :class:`MyClass` is instantiated,
117 but not configured, these default values will be used. But let's see how
117 but not configured, these default values will be used. But let's see how
118 to configure this class in a configuration file::
118 to configure this class in a configuration file::
119
119
120 # Sample config file
120 # Sample config file
121 c = get_config()
121 c = get_config()
122
122
123 c.MyComponent.name = 'coolname'
123 c.MyClass.name = 'coolname'
124 c.MyComponent.ranking = 10
124 c.MyClass.ranking = 10
125
125
126 After this configuration file is loaded, the values set in it will override
126 After this configuration file is loaded, the values set in it will override
127 the class defaults anytime a :class:`MyComponent` is created. Furthermore,
127 the class defaults anytime a :class:`MyClass` is created. Furthermore,
128 these attributes will be type checked and validated anytime they are set.
128 these attributes will be type checked and validated anytime they are set.
129 This type checking is handled by the :mod:`IPython.utils.traitlets` module,
129 This type checking is handled by the :mod:`IPython.utils.traitlets` module,
130 which provides the :class:`Str`, :class:`Int` and :class:`Float` types. In
130 which provides the :class:`Str`, :class:`Int` and :class:`Float` types. In
@@ -133,7 +133,7 b' traitlets for a number of other types.'
133
133
134 .. note::
134 .. note::
135
135
136 Underneath the hood, the :class:`Component` base class is a subclass of
136 Underneath the hood, the :class:`Configurable` base class is a subclass of
137 :class:`IPython.utils.traitlets.HasTraits`. The
137 :class:`IPython.utils.traitlets.HasTraits`. The
138 :mod:`IPython.utils.traitlets` module is a lightweight version of
138 :mod:`IPython.utils.traitlets` module is a lightweight version of
139 :mod:`enthought.traits`. Our implementation is a pure Python subset
139 :mod:`enthought.traits`. Our implementation is a pure Python subset
@@ -157,7 +157,7 b' attribute of ``c`` is not the actual class, but instead is another'
157
157
158 .. note::
158 .. note::
159
159
160 The careful reader may wonder how the ``ClassName`` (``MyComponent`` in
160 The careful reader may wonder how the ``ClassName`` (``MyClass`` in
161 the above example) attribute of the configuration object ``c`` gets
161 the above example) attribute of the configuration object ``c`` gets
162 created. These attributes are created on the fly by the
162 created. These attributes are created on the fly by the
163 :class:`~IPython.config.loader.Config` instance, using a simple naming
163 :class:`~IPython.config.loader.Config` instance, using a simple naming
@@ -165,8 +165,7 b' attribute of ``c`` is not the actual class, but instead is another'
165 instance whose name begins with an uppercase character is assumed to be a
165 instance whose name begins with an uppercase character is assumed to be a
166 sub-configuration and a new empty :class:`~IPython.config.loader.Config`
166 sub-configuration and a new empty :class:`~IPython.config.loader.Config`
167 instance is dynamically created for that attribute. This allows deeply
167 instance is dynamically created for that attribute. This allows deeply
168 hierarchical information created easily (``c.Foo.Bar.value``) on the
168 hierarchical information created easily (``c.Foo.Bar.value``) on the fly.
169 fly.
170
169
171 Configuration files inheritance
170 Configuration files inheritance
172 ===============================
171 ===============================
@@ -179,8 +178,8 b' example that loads all of the values from the file :file:`base_config.py`::'
179
178
180 # base_config.py
179 # base_config.py
181 c = get_config()
180 c = get_config()
182 c.MyComponent.name = 'coolname'
181 c.MyClass.name = 'coolname'
183 c.MyComponent.ranking = 100
182 c.MyClass.ranking = 100
184
183
185 into the configuration file :file:`main_config.py`::
184 into the configuration file :file:`main_config.py`::
186
185
@@ -191,7 +190,7 b' into the configuration file :file:`main_config.py`::'
191 load_subconfig('base_config.py')
190 load_subconfig('base_config.py')
192
191
193 # Now override one of the values
192 # Now override one of the values
194 c.MyComponent.name = 'bettername'
193 c.MyClass.name = 'bettername'
195
194
196 In a situation like this the :func:`load_subconfig` makes sure that the
195 In a situation like this the :func:`load_subconfig` makes sure that the
197 search path for sub-configuration files is inherited from that of the parent.
196 search path for sub-configuration files is inherited from that of the parent.
@@ -205,10 +204,10 b' There is another aspect of configuration where inheritance comes into play.'
205 Sometimes, your classes will have an inheritance hierarchy that you want
204 Sometimes, your classes will have an inheritance hierarchy that you want
206 to be reflected in the configuration system. Here is a simple example::
205 to be reflected in the configuration system. Here is a simple example::
207
206
208 from IPython.core.component import Component
207 from IPython.config.configurable import Configurable
209 from IPython.utils.traitlets import Int, Float, Str, Bool
208 from IPython.utils.traitlets import Int, Float, Str, Bool
210
209
211 class Foo(Component):
210 class Foo(Configurable):
212 name = Str('fooname', config=True)
211 name = Str('fooname', config=True)
213 value = Float(100.0, config=True)
212 value = Float(100.0, config=True)
214
213
@@ -328,4 +327,3 b' Here are the main requirements we wanted our configuration system to have:'
328 dynamic language and you don't always know everything that needs to be
327 dynamic language and you don't always know everything that needs to be
329 configured when a program starts.
328 configured when a program starts.
330
329
331
This diff has been collapsed as it changes many lines, (575 lines changed) Show them Hide them
@@ -1,106 +1,579 b''
1 =====================
1 ======================
2 Message Specification
2 Messaging in IPython
3 =====================
3 ======================
4
4
5 Note: not all of these have yet been fully fleshed out, but the key ones are,
6 see kernel and frontend files for actual implementation details.
7
5
8 General Message Format
6 Introduction
7 ============
8
9 This document explains the basic communications design and messaging
10 specification for how the various IPython objects interact over a network
11 transport. The current implementation uses the ZeroMQ_ library for messaging
12 within and between hosts.
13
14 .. Note::
15
16 This document should be considered the authoritative description of the
17 IPython messaging protocol, and all developers are strongly encouraged to
18 keep it updated as the implementation evolves, so that we have a single
19 common reference for all protocol details.
20
21 The basic design is explained in the following diagram:
22
23 .. image:: frontend-kernel.png
24 :width: 450px
25 :alt: IPython kernel/frontend messaging architecture.
26 :align: center
27 :target: ../_images/frontend-kernel.png
28
29 A single kernel can be simultaneously connected to one or more frontends. The
30 kernel has three sockets that serve the following functions:
31
32 1. REQ: this socket is connected to a *single* frontend at a time, and it allows
33 the kernel to request input from a frontend when :func:`raw_input` is called.
34 The frontend holding the matching REP socket acts as a 'virtual keyboard'
35 for the kernel while this communication is happening (illustrated in the
36 figure by the black outline around the central keyboard). In practice,
37 frontends may display such kernel requests using a special input widget or
38 otherwise indicating that the user is to type input for the kernel instead
39 of normal commands in the frontend.
40
41 2. XREP: this single sockets allows multiple incoming connections from
42 frontends, and this is the socket where requests for code execution, object
43 information, prompts, etc. are made to the kernel by any frontend. The
44 communication on this socket is a sequence of request/reply actions from
45 each frontend and the kernel.
46
47 3. PUB: this socket is the 'broadcast channel' where the kernel publishes all
48 side effects (stdout, stderr, etc.) as well as the requests coming from any
49 client over the XREP socket and its own requests on the REP socket. There
50 are a number of actions in Python which generate side effects: :func:`print`
51 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
52 a multi-client scenario, we want all frontends to be able to know what each
53 other has sent to the kernel (this can be useful in collaborative scenarios,
54 for example). This socket allows both side effects and the information
55 about communications taking place with one client over the XREQ/XREP channel
56 to be made available to all clients in a uniform manner.
57
58 All messages are tagged with enough information (details below) for clients
59 to know which messages come from their own interaction with the kernel and
60 which ones are from other clients, so they can display each type
61 appropriately.
62
63 The actual format of the messages allowed on each of these channels is
64 specified below. Messages are dicts of dicts with string keys and values that
65 are reasonably representable in JSON. Our current implementation uses JSON
66 explicitly as its message format, but this shouldn't be considered a permanent
67 feature. As we've discovered that JSON has non-trivial performance issues due
68 to excessive copying, we may in the future move to a pure pickle-based raw
69 message format. However, it should be possible to easily convert from the raw
70 objects to JSON, since we may have non-python clients (e.g. a web frontend).
71 As long as it's easy to make a JSON version of the objects that is a faithful
72 representation of all the data, we can communicate with such clients.
73
74 .. Note::
75
76 Not all of these have yet been fully fleshed out, but the key ones are, see
77 kernel and frontend files for actual implementation details.
78
79
80 Python functional API
9 =====================
81 =====================
10
82
11 General message format::
83 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
84 should develop, at a few key points, functional forms of all the requests that
85 take arguments in this manner and automatically construct the necessary dict
86 for sending.
87
88
89 General Message Format
90 ======================
12
91
92 All messages send or received by any IPython process should have the following
93 generic structure::
94
13 {
95 {
14 header : { 'msg_id' : 10, # start with 0
96 # The message header contains a pair of unique identifiers for the
15 'username' : 'name',
97 # originating session and the actual message id, in addition to the
98 # username for the process that generated the message. This is useful in
99 # collaborative settings where multiple users may be interacting with the
100 # same kernel simultaneously, so that frontends can label the various
101 # messages in a meaningful way.
102 'header' : { 'msg_id' : uuid,
103 'username' : str,
16 'session' : uuid
104 'session' : uuid
17 },
105 },
18 parent_header : dict,
106
19 msg_type : 'string_message_type',
107 # In a chain of messages, the header from the parent is copied so that
20 content : blackbox_dict , # Must be a dict
108 # clients can track where messages come from.
109 'parent_header' : dict,
110
111 # All recognized message type strings are listed below.
112 'msg_type' : str,
113
114 # The actual content of the message must be a dict, whose structure
115 # depends on the message type.x
116 'content' : dict,
21 }
117 }
22
118
23 Side effect: (PUB/SUB)
119 For each message type, the actual content will differ and all existing message
24 ======================
120 types are specified in what follows of this document.
25
121
26 # msg_type = 'stream'::
122
123 Messages on the XREP/XREQ socket
124 ================================
125
126 .. _execute:
127
128 Execute
129 -------
130
131 The execution request contains a single string, but this may be a multiline
132 string. The kernel is responsible for splitting this into possibly more than
133 one block and deciding whether to compile these in 'single' or 'exec' mode.
134 We're still sorting out this policy. The current inputsplitter is capable of
135 splitting the input for blocks that can all be run as 'single', but in the long
136 run it may prove cleaner to only use 'single' mode for truly single-line
137 inputs, and run all multiline input in 'exec' mode. This would preserve the
138 natural behavior of single-line inputs while allowing long cells to behave more
139 likea a script. This design will be refined as we complete the implementation.
140
141 Message type: ``execute_request``::
27
142
28 content = {
143 content = {
29 name : 'stdout',
144 # Source code to be executed by the kernel, one or more lines.
30 data : 'blob',
145 'code' : str,
146
147 # A boolean flag which, if True, signals the kernel to execute this
148 # code as quietly as possible. This means that the kernel will compile
149 # the code with 'exec' instead of 'single' (so sys.displayhook will not
150 # fire), and will *not*:
151 # - broadcast exceptions on the PUB socket
152 # - do any logging
153 # - populate any history
154 # The default is False.
155 'silent' : bool,
31 }
156 }
32
157
33 # msg_type = 'pyin'::
158 Upon execution, the kernel *always* sends a reply, with a status code
159 indicating what happened and additional data depending on the outcome.
160
161 Message type: ``execute_reply``::
34
162
35 content = {
163 content = {
36 code = 'x=1',
164 # One of: 'ok' OR 'error' OR 'abort'
165 'status' : str,
166
167 # Any additional data depends on status value
37 }
168 }
38
169
39 # msg_type = 'pyout'::
170 When status is 'ok', the following extra fields are present::
171
172 {
173 # This has the same structure as the output of a prompt request, but is
174 # for the client to set up the *next* prompt (with identical limitations
175 # to a prompt request)
176 'next_prompt' : {
177 'prompt_string' : str,
178 'prompt_number' : int,
179 },
180
181 # The prompt number of the actual execution for this code, which may be
182 # different from the one used when the code was typed, which was the
183 # 'next_prompt' field of the *previous* request. They will differ in the
184 # case where there is more than one client talking simultaneously to a
185 # kernel, since the numbers can go out of sync. GUI clients can use this
186 # to correct the previously written number in-place, terminal ones may
187 # re-print a corrected one if desired.
188 'prompt_number' : int,
189
190 # The kernel will often transform the input provided to it. This
191 # contains the transformed code, which is what was actually executed.
192 'transformed_code' : str,
193
194 # The execution payload is a dict with string keys that may have been
195 # produced by the code being executed. It is retrieved by the kernel at
196 # the end of the execution and sent back to the front end, which can take
197 # action on it as needed. See main text for further details.
198 'payload' : dict,
199 }
200
201 .. admonition:: Execution payloads
202
203 The notion of an 'execution payload' is different from a return value of a
204 given set of code, which normally is just displayed on the pyout stream
205 through the PUB socket. The idea of a payload is to allow special types of
206 code, typically magics, to populate a data container in the IPython kernel
207 that will be shipped back to the caller via this channel. The kernel will
208 have an API for this, probably something along the lines of::
209
210 ip.exec_payload_add(key, value)
211
212 though this API is still in the design stages. The data returned in this
213 payload will allow frontends to present special views of what just happened.
214
215
216 When status is 'error', the following extra fields are present::
217
218 {
219 'exc_name' : str, # Exception name, as a string
220 'exc_value' : str, # Exception value, as a string
221
222 # The traceback will contain a list of frames, represented each as a
223 # string. For now we'll stick to the existing design of ultraTB, which
224 # controls exception level of detail statefully. But eventually we'll
225 # want to grow into a model where more information is collected and
226 # packed into the traceback object, with clients deciding how little or
227 # how much of it to unpack. But for now, let's start with a simple list
228 # of strings, since that requires only minimal changes to ultratb as
229 # written.
230 'traceback' : list,
231 }
232
233
234 When status is 'abort', there are for now no additional data fields. This
235 happens when the kernel was interrupted by a signal.
236
237
238 Prompt
239 ------
240
241 A simple request for a current prompt string.
242
243 Message type: ``prompt_request``::
244
245 content = {}
246
247 In the reply, the prompt string comes back with the prompt number placeholder
248 *unevaluated*. The message format is:
249
250 Message type: ``prompt_reply``::
251
252 content = {
253 'prompt_string' : str,
254 'prompt_number' : int,
255 }
256
257 Clients can produce a prompt with ``prompt_string.format(prompt_number)``, but
258 they should be aware that the actual prompt number for that input could change
259 later, in the case where multiple clients are interacting with a single
260 kernel.
261
262
263 Object information
264 ------------------
265
266 One of IPython's most used capabilities is the introspection of Python objects
267 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
268 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
269 enough that it warrants an explicit message type, especially because frontends
270 may want to get object information in response to user keystrokes (like Tab or
271 F1) besides from the user explicitly typing code like ``x??``.
272
273 Message type: ``object_info_request``::
40
274
41 content = {
275 content = {
42 data = 'repr(obj)',
276 # The (possibly dotted) name of the object to be searched in all
43 prompt_number = 10
277 # relevant namespaces
278 'name' : str,
279
280 # The level of detail desired. The default (0) is equivalent to typing
281 # 'x?' at the prompt, 1 is equivalent to 'x??'.
282 'detail_level' : int,
44 }
283 }
45
284
46 # msg_type = 'pyerr'::
285 The returned information will be a dictionary with keys very similar to the
286 field names that IPython prints at the terminal.
287
288 Message type: ``object_info_reply``::
47
289
48 content = {
290 content = {
49 traceback : 'full traceback',
291 # Flags for magics and system aliases
50 exc_type : 'TypeError',
292 'ismagic' : bool,
51 exc_value : 'msg'
293 'isalias' : bool,
294
295 # The name of the namespace where the object was found ('builtin',
296 # 'magics', 'alias', 'interactive', etc.)
297 'namespace' : str,
298
299 # The type name will be type.__name__ for normal Python objects, but it
300 # can also be a string like 'Magic function' or 'System alias'
301 'type_name' : str,
302
303 'string_form' : str,
304
305 # For objects with a __class__ attribute this will be set
306 'base_class' : str,
307
308 # For objects with a __len__ attribute this will be set
309 'length' : int,
310
311 # If the object is a function, class or method whose file we can find,
312 # we give its full path
313 'file' : str,
314
315 # For pure Python callable objects, we can reconstruct the object
316 # definition line which provides its call signature
317 'definition' : str,
318
319 # For instances, provide the constructor signature (the definition of
320 # the __init__ method):
321 'init_definition' : str,
322
323 # Docstrings: for any object (function, method, module, package) with a
324 # docstring, we show it. But in addition, we may provide additional
325 # docstrings. For example, for instances we will show the constructor
326 # and class docstrings as well, if available.
327 'docstring' : str,
328
329 # For instances, provide the constructor and class docstrings
330 'init_docstring' : str,
331 'class_docstring' : str,
332
333 # If detail_level was 1, we also try to find the source code that
334 # defines the object, if possible. The string 'None' will indicate
335 # that no source was found.
336 'source' : str,
52 }
337 }
53
338
54 # msg_type = 'file':
339
340 Complete
341 --------
342
343 Message type: ``complete_request``::
344
55 content = {
345 content = {
56 path = 'cool.jpg',
346 # The text to be completed, such as 'a.is'
57 data : 'blob'
347 'text' : str,
348
349 # The full line, such as 'print a.is'. This allows completers to
350 # make decisions that may require information about more than just the
351 # current word.
352 'line' : str,
58 }
353 }
59
354
60 Request/Reply
355 Message type: ``complete_reply``::
61 =============
62
356
63 Execute
357 content = {
358 # The list of all matches to the completion request, such as
359 # ['a.isalnum', 'a.isalpha'] for the above example.
360 'matches' : list
361 }
362
363
364 History
64 -------
365 -------
65
366
66 Request:
367 For clients to explicitly request history from a kernel. The kernel has all
368 the actual execution history stored in a single location, so clients can
369 request it from the kernel when needed.
67
370
68 # msg_type = 'execute_request'::
371 Message type: ``history_request``::
69
372
70 content = {
373 content = {
71 code : 'a = 10',
374
375 # If true, also return output history in the resulting dict.
376 'output' : bool,
377
378 # This parameter can be one of: A number, a pair of numbers, 'all'
379 # If not given, last 40 are returned.
380 # - number n: return the last n entries.
381 # - pair n1, n2: return entries in the range(n1, n2).
382 # - 'all': return all history
383 'range' : n or (n1, n2) or 'all',
384
385 # If a filter is given, it is treated as a regular expression and only
386 # matching entries are returned. re.search() is used to find matches.
387 'filter' : str,
72 }
388 }
73
389
74 Reply:
390 Message type: ``history_reply``::
391
392 content = {
393 # A list of (number, input) pairs
394 'input' : list,
395
396 # A list of (number, output) pairs
397 'output' : list,
398 }
75
399
76 # msg_type = 'execute_reply'::
400
401 Messages on the PUB/SUB socket
402 ==============================
403
404 Streams (stdout, stderr, etc)
405 ------------------------------
406
407 Message type: ``stream``::
77
408
78 content = {
409 content = {
79 'status' : 'ok' OR 'error' OR 'abort'
410 # The name of the stream is one of 'stdin', 'stdout', 'stderr'
80 # data depends on status value
411 'name' : str,
412
413 # The data is an arbitrary string to be written to that stream
414 'data' : str,
81 }
415 }
82
416
83 Complete
417 When a kernel receives a raw_input call, it should also broadcast it on the pub
84 --------
418 socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
419 to monitor/display kernel interactions and possibly replay them to their user
420 or otherwise expose them.
85
421
86 # msg_type = 'complete_request'::
422 Python inputs
423 -------------
424
425 These messages are the re-broadcast of the ``execute_request``.
426
427 Message type: ``pyin``::
87
428
88 content = {
429 content = {
89 text : 'a.f', # complete on this
430 # Source code to be executed, one or more lines
90 line : 'print a.f' # full line
431 'code' : str
91 }
432 }
92
433
93 # msg_type = 'complete_reply'::
434 Python outputs
435 --------------
436
437 When Python produces output from code that has been compiled in with the
438 'single' flag to :func:`compile`, any expression that produces a value (such as
439 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
440 this value whatever it wants. The default behavior of ``sys.displayhook`` in
441 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
442 the value as long as it is not ``None`` (which isn't printed at all). In our
443 case, the kernel instantiates as ``sys.displayhook`` an object which has
444 similar behavior, but which instead of printing to stdout, broadcasts these
445 values as ``pyout`` messages for clients to display appropriately.
446
447 Message type: ``pyout``::
94
448
95 content = {
449 content = {
96 matches : ['a.foo', 'a.bar']
450 # The data is typically the repr() of the object.
451 'data' : str,
452
453 # The prompt number for this execution is also provided so that clients
454 # can display it, since IPython automatically creates variables called
455 # _N (for prompt N).
456 'prompt_number' : int,
97 }
457 }
458
459 Python errors
460 -------------
98
461
99 Control
462 When an error occurs during code execution
100 -------
101
463
102 # msg_type = 'heartbeat'::
464 Message type: ``pyerr``::
103
465
104 content = {
466 content = {
105 # XXX - unfinished
467 # Similar content to the execute_reply messages for the 'error' case,
468 # except the 'status' field is omitted.
106 }
469 }
470
471 Kernel crashes
472 --------------
473
474 When the kernel has an unexpected exception, caught by the last-resort
475 sys.excepthook, we should broadcast the crash handler's output before exiting.
476 This will allow clients to notice that a kernel died, inform the user and
477 propose further actions.
478
479 Message type: ``crash``::
480
481 content = {
482 # Similarly to the 'error' case for execute_reply messages, this will
483 # contain exc_name, exc_type and traceback fields.
484
485 # An additional field with supplementary information such as where to
486 # send the crash message
487 'info' : str,
488 }
489
490
491 Future ideas
492 ------------
493
494 Other potential message types, currently unimplemented, listed below as ideas.
495
496 Message type: ``file``::
497
498 content = {
499 'path' : 'cool.jpg',
500 'mimetype' : str,
501 'data' : str,
502 }
503
504
505 Messages on the REQ/REP socket
506 ==============================
507
508 This is a socket that goes in the opposite direction: from the kernel to a
509 *single* frontend, and its purpose is to allow ``raw_input`` and similar
510 operations that read from ``sys.stdin`` on the kernel to be fulfilled by the
511 client. For now we will keep these messages as simple as possible, since they
512 basically only mean to convey the ``raw_input(prompt)`` call.
513
514 Message type: ``input_request``::
515
516 content = { 'prompt' : str }
517
518 Message type: ``input_reply``::
519
520 content = { 'value' : str }
521
522 .. Note::
523
524 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
525 practice the kernel should behave like an interactive program. When a
526 program is opened on the console, the keyboard effectively takes over the
527 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
528 Since the IPython kernel effectively behaves like a console program (albeit
529 one whose "keyboard" is actually living in a separate process and
530 transported over the zmq connection), raw ``stdin`` isn't expected to be
531 available.
532
533
534 Heartbeat for kernels
535 =====================
536
537 Initially we had considered using messages like those above over ZMQ for a
538 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
539 alive at all, even if it may be busy executing user code). But this has the
540 problem that if the kernel is locked inside extension code, it wouldn't execute
541 the python heartbeat code. But it turns out that we can implement a basic
542 heartbeat with pure ZMQ, without using any Python messaging at all.
543
544 The monitor sends out a single zmq message (right now, it is a str of the
545 monitor's lifetime in seconds), and gets the same message right back, prefixed
546 with the zmq identity of the XREQ socket in the heartbeat process. This can be
547 a uuid, or even a full message, but there doesn't seem to be a need for packing
548 up a message when the sender and receiver are the exact same Python object.
549
550 The model is this::
551
552 monitor.send(str(self.lifetime)) # '1.2345678910'
553
554 and the monitor receives some number of messages of the form::
555
556 ['uuid-abcd-dead-beef', '1.2345678910']
557
558 where the first part is the zmq.IDENTITY of the heart's XREQ on the engine, and
559 the rest is the message sent by the monitor. No Python code ever has any
560 access to the message between the monitor's send, and the monitor's recv.
561
562
563 ToDo
564 ====
565
566 Missing things include:
567
568 * Important: finish thinking through the payload concept and API.
569
570 * Important: ensure that we have a good solution for magics like %edit. It's
571 likely that with the payload concept we can build a full solution, but not
572 100% clear yet.
573
574 * Finishing the details of the heartbeat protocol.
575
576 * Signal handling: specify what kind of information kernel should broadcast (or
577 not) when it receives signals.
578
579 .. include:: ../links.rst
@@ -220,7 +220,7 b' class EmbeddedSphinxShell(object):'
220 config.InteractiveShell.colors = 'NoColor'
220 config.InteractiveShell.colors = 'NoColor'
221
221
222 # Create and initialize ipython, but don't start its mainloop
222 # Create and initialize ipython, but don't start its mainloop
223 IP = InteractiveShell(parent=None, config=config)
223 IP = InteractiveShell.instance(config=config)
224
224
225 # Store a few parts of IPython we'll need.
225 # Store a few parts of IPython we'll need.
226 self.IP = IP
226 self.IP = IP
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now