##// END OF EJS Templates
merged from ~fdo.perez/ipython/trunk-dev
Dav Clark -
r2469:81d5b6be merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,184 b''
1 import os
2
3 c = get_config()
4
5 #-----------------------------------------------------------------------------
6 # Select which launchers to use
7 #-----------------------------------------------------------------------------
8
9 # This allows you to control what method is used to start the controller
10 # and engines. The following methods are currently supported:
11 # - Start as a regular process on localhost.
12 # - Start using mpiexec.
13 # - Start using the Windows HPC Server 2008 scheduler
14 # - Start using PBS
15 # - Start using SSH (currently broken)
16
17
18 # The selected launchers can be configured below.
19
20 # Options are:
21 # - LocalControllerLauncher
22 # - MPIExecControllerLauncher
23 # - PBSControllerLauncher
24 # - WindowsHPCControllerLauncher
25 # c.Global.controller_launcher = 'IPython.kernel.launcher.LocalControllerLauncher'
26
27 # Options are:
28 # - LocalEngineSetLauncher
29 # - MPIExecEngineSetLauncher
30 # - PBSEngineSetLauncher
31 # - WindowsHPCEngineSetLauncher
32 # c.Global.engine_launcher = 'IPython.kernel.launcher.LocalEngineSetLauncher'
33
34 #-----------------------------------------------------------------------------
35 # Global configuration
36 #-----------------------------------------------------------------------------
37
38 # The default number of engines that will be started. This is overridden by
39 # the -n command line option: "ipcluster start -n 4"
40 # c.Global.n = 2
41
42 # Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
43 # c.Global.log_to_file = False
44
45 # Remove old logs from cluster_dir/log before starting.
46 # c.Global.clean_logs = True
47
48 # The working directory for the process. The application will use os.chdir
49 # to change to this directory before starting.
50 # c.Global.work_dir = os.getcwd()
51
52
53 #-----------------------------------------------------------------------------
54 # Local process launchers
55 #-----------------------------------------------------------------------------
56
57 # The command line arguments to call the controller with.
58 # c.LocalControllerLauncher.controller_args = \
59 # ['--log-to-file','--log-level', '40']
60
61 # The working directory for the controller
62 # c.LocalEngineSetLauncher.work_dir = u''
63
64 # Command line argument passed to the engines.
65 # c.LocalEngineSetLauncher.engine_args = ['--log-to-file','--log-level', '40']
66
67 #-----------------------------------------------------------------------------
68 # MPIExec launchers
69 #-----------------------------------------------------------------------------
70
71 # The mpiexec/mpirun command to use in started the controller.
72 # c.MPIExecControllerLauncher.mpi_cmd = ['mpiexec']
73
74 # Additional arguments to pass to the actual mpiexec command.
75 # c.MPIExecControllerLauncher.mpi_args = []
76
77 # The command line argument to call the controller with.
78 # c.MPIExecControllerLauncher.controller_args = \
79 # ['--log-to-file','--log-level', '40']
80
81
82 # The mpiexec/mpirun command to use in started the controller.
83 # c.MPIExecEngineSetLauncher.mpi_cmd = ['mpiexec']
84
85 # Additional arguments to pass to the actual mpiexec command.
86 # c.MPIExecEngineSetLauncher.mpi_args = []
87
88 # Command line argument passed to the engines.
89 # c.MPIExecEngineSetLauncher.engine_args = ['--log-to-file','--log-level', '40']
90
91 # The default number of engines to start if not given elsewhere.
92 # c.MPIExecEngineSetLauncher.n = 1
93
94 #-----------------------------------------------------------------------------
95 # SSH launchers
96 #-----------------------------------------------------------------------------
97
98 # Todo
99
100
101 #-----------------------------------------------------------------------------
102 # Unix batch (PBS) schedulers launchers
103 #-----------------------------------------------------------------------------
104
105 # The command line program to use to submit a PBS job.
106 # c.PBSControllerLauncher.submit_command = 'qsub'
107
108 # The command line program to use to delete a PBS job.
109 # c.PBSControllerLauncher.delete_command = 'qdel'
110
111 # A regular expression that takes the output of qsub and find the job id.
112 # c.PBSControllerLauncher.job_id_regexp = r'\d+'
113
114 # The batch submission script used to start the controller. This is where
115 # environment variables would be setup, etc. This string is interpolated using
116 # the Itpl module in IPython.external. Basically, you can use ${n} for the
117 # number of engine and ${cluster_dir} for the cluster_dir.
118 # c.PBSControllerLauncher.batch_template = """"""
119
120 # The name of the instantiated batch script that will actually be used to
121 # submit the job. This will be written to the cluster directory.
122 # c.PBSControllerLauncher.batch_file_name = u'pbs_batch_script_controller'
123
124
125 # The command line program to use to submit a PBS job.
126 # c.PBSEngineSetLauncher.submit_command = 'qsub'
127
128 # The command line program to use to delete a PBS job.
129 # c.PBSEngineSetLauncher.delete_command = 'qdel'
130
131 # A regular expression that takes the output of qsub and find the job id.
132 # c.PBSEngineSetLauncher.job_id_regexp = r'\d+'
133
134 # The batch submission script used to start the engines. This is where
135 # environment variables would be setup, etc. This string is interpolated using
136 # the Itpl module in IPython.external. Basically, you can use ${n} for the
137 # number of engine and ${cluster_dir} for the cluster_dir.
138 # c.PBSEngineSetLauncher.batch_template = """"""
139
140 # The name of the instantiated batch script that will actually be used to
141 # submit the job. This will be written to the cluster directory.
142 # c.PBSEngineSetLauncher.batch_file_name = u'pbs_batch_script_engines'
143
144 #-----------------------------------------------------------------------------
145 # Windows HPC Server 2008 launcher configuration
146 #-----------------------------------------------------------------------------
147
148 # c.IPControllerJob.job_name = 'IPController'
149 # c.IPControllerJob.is_exclusive = False
150 # c.IPControllerJob.username = r'USERDOMAIN\USERNAME'
151 # c.IPControllerJob.priority = 'Highest'
152 # c.IPControllerJob.requested_nodes = ''
153 # c.IPControllerJob.project = 'MyProject'
154
155 # c.IPControllerTask.task_name = 'IPController'
156 # c.IPControllerTask.controller_cmd = [u'ipcontroller.exe']
157 # c.IPControllerTask.controller_args = ['--log-to-file', '--log-level', '40']
158 # c.IPControllerTask.environment_variables = {}
159
160 # c.WindowsHPCControllerLauncher.scheduler = 'HEADNODE'
161 # c.WindowsHPCControllerLauncher.job_file_name = u'ipcontroller_job.xml'
162
163
164 # c.IPEngineSetJob.job_name = 'IPEngineSet'
165 # c.IPEngineSetJob.is_exclusive = False
166 # c.IPEngineSetJob.username = r'USERDOMAIN\USERNAME'
167 # c.IPEngineSetJob.priority = 'Highest'
168 # c.IPEngineSetJob.requested_nodes = ''
169 # c.IPEngineSetJob.project = 'MyProject'
170
171 # c.IPEngineTask.task_name = 'IPEngine'
172 # c.IPEngineTask.engine_cmd = [u'ipengine.exe']
173 # c.IPEngineTask.engine_args = ['--log-to-file', '--log-level', '40']
174 # c.IPEngineTask.environment_variables = {}
175
176 # c.WindowsHPCEngineSetLauncher.scheduler = 'HEADNODE'
177 # c.WindowsHPCEngineSetLauncher.job_file_name = u'ipengineset_job.xml'
178
179
180
181
182
183
184
@@ -0,0 +1,136 b''
1 from IPython.config.loader import Config
2
3 c = get_config()
4
5 #-----------------------------------------------------------------------------
6 # Global configuration
7 #-----------------------------------------------------------------------------
8
9 # Basic Global config attributes
10
11 # Start up messages are logged to stdout using the logging module.
12 # These all happen before the twisted reactor is started and are
13 # useful for debugging purposes. Can be (10=DEBUG,20=INFO,30=WARN,40=CRITICAL)
14 # and smaller is more verbose.
15 # c.Global.log_level = 20
16
17 # Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
18 # c.Global.log_to_file = False
19
20 # Remove old logs from cluster_dir/log before starting.
21 # c.Global.clean_logs = True
22
23 # A list of Python statements that will be run before starting the
24 # controller. This is provided because occasionally certain things need to
25 # be imported in the controller for pickling to work.
26 # c.Global.import_statements = ['import math']
27
28 # Reuse the controller's FURL files. If False, FURL files are regenerated
29 # each time the controller is run. If True, they will be reused, *but*, you
30 # also must set the network ports by hand. If set, this will override the
31 # values set for the client and engine connections below.
32 # c.Global.reuse_furls = True
33
34 # Enable SSL encryption on all connections to the controller. If set, this
35 # will override the values set for the client and engine connections below.
36 # c.Global.secure = True
37
38 # The working directory for the process. The application will use os.chdir
39 # to change to this directory before starting.
40 # c.Global.work_dir = os.getcwd()
41
42 #-----------------------------------------------------------------------------
43 # Configure the client services
44 #-----------------------------------------------------------------------------
45
46 # Basic client service config attributes
47
48 # The network interface the controller will listen on for client connections.
49 # This should be an IP address or hostname of the controller's host. The empty
50 # string means listen on all interfaces.
51 # c.FCClientServiceFactory.ip = ''
52
53 # The TCP/IP port the controller will listen on for client connections. If 0
54 # a random port will be used. If the controller's host has a firewall running
55 # it must allow incoming traffic on this port.
56 # c.FCClientServiceFactory.port = 0
57
58 # The client learns how to connect to the controller by looking at the
59 # location field embedded in the FURL. If this field is empty, all network
60 # interfaces that the controller is listening on will be listed. To have the
61 # client connect on a particular interface, list it here.
62 # c.FCClientServiceFactory.location = ''
63
64 # Use SSL encryption for the client connection.
65 # c.FCClientServiceFactory.secure = True
66
67 # Reuse the client FURL each time the controller is started. If set, you must
68 # also pick a specific network port above (FCClientServiceFactory.port).
69 # c.FCClientServiceFactory.reuse_furls = False
70
71 #-----------------------------------------------------------------------------
72 # Configure the engine services
73 #-----------------------------------------------------------------------------
74
75 # Basic config attributes for the engine services.
76
77 # The network interface the controller will listen on for engine connections.
78 # This should be an IP address or hostname of the controller's host. The empty
79 # string means listen on all interfaces.
80 # c.FCEngineServiceFactory.ip = ''
81
82 # The TCP/IP port the controller will listen on for engine connections. If 0
83 # a random port will be used. If the controller's host has a firewall running
84 # it must allow incoming traffic on this port.
85 # c.FCEngineServiceFactory.port = 0
86
87 # The engine learns how to connect to the controller by looking at the
88 # location field embedded in the FURL. If this field is empty, all network
89 # interfaces that the controller is listening on will be listed. To have the
90 # client connect on a particular interface, list it here.
91 # c.FCEngineServiceFactory.location = ''
92
93 # Use SSL encryption for the engine connection.
94 # c.FCEngineServiceFactory.secure = True
95
96 # Reuse the client FURL each time the controller is started. If set, you must
97 # also pick a specific network port above (FCClientServiceFactory.port).
98 # c.FCEngineServiceFactory.reuse_furls = False
99
100 #-----------------------------------------------------------------------------
101 # Developer level configuration attributes
102 #-----------------------------------------------------------------------------
103
104 # You shouldn't have to modify anything in this section. These attributes
105 # are more for developers who want to change the behavior of the controller
106 # at a fundamental level.
107
108 # c.FCClientServiceFactory.cert_file = u'ipcontroller-client.pem'
109
110 # default_client_interfaces = Config()
111 # default_client_interfaces.Task.interface_chain = [
112 # 'IPython.kernel.task.ITaskController',
113 # 'IPython.kernel.taskfc.IFCTaskController'
114 # ]
115 #
116 # default_client_interfaces.Task.furl_file = u'ipcontroller-tc.furl'
117 #
118 # default_client_interfaces.MultiEngine.interface_chain = [
119 # 'IPython.kernel.multiengine.IMultiEngine',
120 # 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
121 # ]
122 #
123 # default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
124 #
125 # c.FCEngineServiceFactory.interfaces = default_client_interfaces
126
127 # c.FCEngineServiceFactory.cert_file = u'ipcontroller-engine.pem'
128
129 # default_engine_interfaces = Config()
130 # default_engine_interfaces.Default.interface_chain = [
131 # 'IPython.kernel.enginefc.IFCControllerBase'
132 # ]
133 #
134 # default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
135 #
136 # c.FCEngineServiceFactory.interfaces = default_engine_interfaces
@@ -0,0 +1,90 b''
1 c = get_config()
2
3 #-----------------------------------------------------------------------------
4 # Global configuration
5 #-----------------------------------------------------------------------------
6
7 # Start up messages are logged to stdout using the logging module.
8 # These all happen before the twisted reactor is started and are
9 # useful for debugging purposes. Can be (10=DEBUG,20=INFO,30=WARN,40=CRITICAL)
10 # and smaller is more verbose.
11 # c.Global.log_level = 20
12
13 # Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
14 # c.Global.log_to_file = False
15
16 # Remove old logs from cluster_dir/log before starting.
17 # c.Global.clean_logs = True
18
19 # A list of strings that will be executed in the users namespace on the engine
20 # before it connects to the controller.
21 # c.Global.exec_lines = ['import numpy']
22
23 # The engine will try to connect to the controller multiple times, to allow
24 # the controller time to startup and write its FURL file. These parameters
25 # control the number of retries (connect_max_tries) and the initial delay
26 # (connect_delay) between attemps. The actual delay between attempts gets
27 # longer each time by a factor of 1.5 (delay[i] = 1.5*delay[i-1])
28 # those attemps.
29 # c.Global.connect_delay = 0.1
30 # c.Global.connect_max_tries = 15
31
32 # By default, the engine will look for the controller's FURL file in its own
33 # cluster directory. Sometimes, the FURL file will be elsewhere and this
34 # attribute can be set to the full path of the FURL file.
35 # c.Global.furl_file = u''
36
37 # The working directory for the process. The application will use os.chdir
38 # to change to this directory before starting.
39 # c.Global.work_dir = os.getcwd()
40
41 #-----------------------------------------------------------------------------
42 # MPI configuration
43 #-----------------------------------------------------------------------------
44
45 # Upon starting the engine can be configured to call MPI_Init. This section
46 # configures that.
47
48 # Select which MPI section to execute to setup MPI. The value of this
49 # attribute must match the name of another attribute in the MPI config
50 # section (mpi4py, pytrilinos, etc.). This can also be set by the --mpi
51 # command line option.
52 # c.MPI.use = ''
53
54 # Initialize MPI using mpi4py. To use this, set c.MPI.use = 'mpi4py' to use
55 # --mpi=mpi4py at the command line.
56 # c.MPI.mpi4py = """from mpi4py import MPI as mpi
57 # mpi.size = mpi.COMM_WORLD.Get_size()
58 # mpi.rank = mpi.COMM_WORLD.Get_rank()
59 # """
60
61 # Initialize MPI using pytrilinos. To use this, set c.MPI.use = 'pytrilinos'
62 # to use --mpi=pytrilinos at the command line.
63 # c.MPI.pytrilinos = """from PyTrilinos import Epetra
64 # class SimpleStruct:
65 # pass
66 # mpi = SimpleStruct()
67 # mpi.rank = 0
68 # mpi.size = 0
69 # """
70
71 #-----------------------------------------------------------------------------
72 # Developer level configuration attributes
73 #-----------------------------------------------------------------------------
74
75 # You shouldn't have to modify anything in this section. These attributes
76 # are more for developers who want to change the behavior of the controller
77 # at a fundamental level.
78
79 # You should not have to change these attributes.
80
81 # c.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
82
83 # c.Global.furl_file_name = u'ipcontroller-engine.furl'
84
85
86
87
88
89
90
@@ -0,0 +1,24 b''
1 c = get_config()
2
3 # This can be used at any point in a config file to load a sub config
4 # and merge it into the current one.
5 load_subconfig('ipython_config.py')
6
7 lines = """
8 from IPython.kernel.client import *
9 """
10
11 # You have to make sure that attributes that are containers already
12 # exist before using them. Simple assigning a new list will override
13 # all previous values.
14 if hasattr(c.Global, 'exec_lines'):
15 c.Global.exec_lines.append(lines)
16 else:
17 c.Global.exec_lines = [lines]
18
19 # Load the parallelmagic extension to enable %result, %px, %autopx magics.
20 if hasattr(c.Global, 'extensions'):
21 c.Global.extensions.append('parallelmagic')
22 else:
23 c.Global.extensions = ['parallelmagic']
24
@@ -0,0 +1,145 b''
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities.
3
4 Authors
5 -------
6 Fernando Perez.
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2009 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 # Imports
17 #-----------------------------------------------------------------------------
18 from IPython.utils.genutils import flag_calls
19
20 #-----------------------------------------------------------------------------
21 # Main classes and functions
22 #-----------------------------------------------------------------------------
23
24 def pylab_activate(user_ns, gui=None, import_all=True):
25 """Activate pylab mode in the user's namespace.
26
27 Loads and initializes numpy, matplotlib and friends for interactive use.
28
29 Parameters
30 ----------
31 user_ns : dict
32 Namespace where the imports will occur.
33
34 gui : optional, string
35 A valid gui name following the conventions of the %gui magic.
36
37 import_all : optional, boolean
38 If true, an 'import *' is done from numpy and pylab.
39
40 Returns
41 -------
42 The actual gui used (if not given as input, it was obtained from matplotlib
43 itself, and will be needed next to configure IPython's gui integration.
44 """
45
46 # Initialize matplotlib to interactive mode always
47 import matplotlib
48
49 # If user specifies a GUI, that dictates the backend, otherwise we read the
50 # user's mpl default from the mpl rc structure
51 g2b = {'tk': 'TkAgg',
52 'gtk': 'GTKAgg',
53 'wx': 'WXAgg',
54 'qt': 'Qt4Agg', # qt3 not supported
55 'qt4': 'Qt4Agg' }
56
57 if gui:
58 # select backend based on requested gui
59 backend = g2b[gui]
60 else:
61 backend = matplotlib.rcParams['backend']
62 # In this case, we need to find what the appropriate gui selection call
63 # should be for IPython, so we can activate inputhook accordingly
64 b2g = dict(zip(g2b.values(),g2b.keys()))
65 gui = b2g[backend]
66
67 # We must set the desired backend before importing pylab
68 matplotlib.use(backend)
69
70 # This must be imported last in the matplotlib series, after
71 # backend/interactivity choices have been made
72 import matplotlib.pylab as pylab
73
74 # XXX For now leave this commented out, but depending on discussions with
75 # mpl-dev, we may be able to allow interactive switching...
76 #import matplotlib.pyplot
77 #matplotlib.pyplot.switch_backend(backend)
78
79 pylab.show._needmain = False
80 # We need to detect at runtime whether show() is called by the user.
81 # For this, we wrap it into a decorator which adds a 'called' flag.
82 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
83
84 # Import numpy as np/pyplot as plt are conventions we're trying to
85 # somewhat standardize on. Making them available to users by default
86 # will greatly help this.
87 exec ("import numpy\n"
88 "import matplotlib\n"
89 "from matplotlib import pylab, mlab, pyplot\n"
90 "np = numpy\n"
91 "plt = pyplot\n"
92 ) in user_ns
93
94 if import_all:
95 exec("from matplotlib.pylab import *\n"
96 "from numpy import *\n") in user_ns
97
98 matplotlib.interactive(True)
99
100 print """
101 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
102 For more information, type 'help(pylab)'.""" % backend
103
104 return gui
105
106 # We need a little factory function here to create the closure where
107 # safe_execfile can live.
108 def mpl_runner(safe_execfile):
109 """Factory to return a matplotlib-enabled runner for %run.
110
111 Parameters
112 ----------
113 safe_execfile : function
114 This must be a function with the same interface as the
115 :meth:`safe_execfile` method of IPython.
116
117 Returns
118 -------
119 A function suitable for use as the ``runner`` argument of the %run magic
120 function.
121 """
122
123 def mpl_execfile(fname,*where,**kw):
124 """matplotlib-aware wrapper around safe_execfile.
125
126 Its interface is identical to that of the :func:`execfile` builtin.
127
128 This is ultimately a call to execfile(), but wrapped in safeties to
129 properly handle interactive rendering."""
130
131 import matplotlib
132 import matplotlib.pylab as pylab
133
134 #print '*** Matplotlib runner ***' # dbg
135 # turn off rendering until end of script
136 is_interactive = matplotlib.rcParams['interactive']
137 matplotlib.interactive(False)
138 safe_execfile(fname,*where,**kw)
139 matplotlib.interactive(is_interactive)
140 # make rendering call now, if the user tried to do it
141 if pylab.draw_if_interactive.called:
142 pylab.draw()
143 pylab.draw_if_interactive.called = False
144
145 return mpl_execfile
@@ -0,0 +1,32 b''
1 """Error script. DO NOT EDIT FURTHER! It will break exception doctests!!!"""
2 import sys
3
4 def div0():
5 "foo"
6 x = 1
7 y = 0
8 x/y
9
10 def sysexit(stat, mode):
11 raise SystemExit(stat, 'Mode = %s' % mode)
12
13 def bar(mode):
14 "bar"
15 if mode=='div':
16 div0()
17 elif mode=='exit':
18 try:
19 stat = int(sys.argv[2])
20 except:
21 stat = 1
22 sysexit(stat, mode)
23 else:
24 raise ValueError('Unknown mode')
25
26 if __name__ == '__main__':
27 try:
28 mode = sys.argv[1]
29 except IndexError:
30 mode = 'div'
31
32 bar(mode)
@@ -0,0 +1,35 b''
1 """Tests for the IPython tab-completion machinery.
2 """
3 #-----------------------------------------------------------------------------
4 # Module imports
5 #-----------------------------------------------------------------------------
6
7 # stdlib
8 import sys
9
10 # third party
11 import nose.tools as nt
12
13 # our own packages
14 from IPython.core import completer
15
16 #-----------------------------------------------------------------------------
17 # Test functions
18 #-----------------------------------------------------------------------------
19 def test_protect_filename():
20 pairs = [ ('abc','abc'),
21 (' abc',r'\ abc'),
22 ('a bc',r'a\ bc'),
23 ('a bc',r'a\ \ bc'),
24 (' bc',r'\ \ bc'),
25 ]
26 # On posix, we also protect parens
27 if sys.platform != 'win32':
28 pairs.extend( [('a(bc',r'a\(bc'),
29 ('a)bc',r'a\)bc'),
30 ('a( )bc',r'a\(\ \)bc'),
31 ] )
32 # run the actual tests
33 for s1, s2 in pairs:
34 s1p = completer.protect_filename(s1)
35 nt.assert_equals(s1p, s2)
@@ -0,0 +1,34 b''
1 """Tests for input manipulation machinery."""
2
3 #-----------------------------------------------------------------------------
4 # Imports
5 #-----------------------------------------------------------------------------
6 import nose.tools as nt
7
8 from IPython.testing import tools as tt, decorators as dec
9
10 #-----------------------------------------------------------------------------
11 # Tests
12 #-----------------------------------------------------------------------------
13 @dec.parametric
14 def test_prefilter():
15 """Test user input conversions"""
16
17 # pairs of (raw, expected correct) input
18 pairs = [ ('2+2','2+2'),
19 ('>>> 2+2','2+2'),
20 ('>>> # This is a comment\n'
21 '... 2+2',
22 '# This is a comment\n'
23 '2+2'),
24 # Some IPython input
25 ('In [1]: 1', '1'),
26 ('In [2]: for i in range(5):\n'
27 ' ...: print i,',
28 'for i in range(5):\n'
29 ' print i,'),
30 ]
31
32 ip = get_ipython()
33 for raw, correct in pairs:
34 yield nt.assert_equals(ip.prefilter(raw), correct)
@@ -0,0 +1,174 b''
1 """Tests for code execution (%run and related), which is particularly tricky.
2
3 Because of how %run manages namespaces, and the fact that we are trying here to
4 verify subtle object deletion and reference counting issues, the %run tests
5 will be kept in this separate file. This makes it easier to aggregate in one
6 place the tricks needed to handle it; most other magics are much easier to test
7 and we do so in a common test_magic file.
8 """
9 from __future__ import absolute_import
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 # stdlib
16 import os
17 import sys
18 import tempfile
19
20 # third-party
21 import nose.tools as nt
22
23 # our own
24 from IPython.utils.platutils import find_cmd
25 from IPython.utils import genutils
26 from IPython.testing import decorators as dec
27 from IPython.testing import tools as tt
28
29 #-----------------------------------------------------------------------------
30 # Test functions begin
31 #-----------------------------------------------------------------------------
32
33 def doctest_refbug():
34 """Very nasty problem with references held by multiple runs of a script.
35 See: https://bugs.launchpad.net/ipython/+bug/269966
36
37 In [1]: _ip.clear_main_mod_cache()
38 # random
39
40 In [2]: %run refbug
41
42 In [3]: call_f()
43 lowercased: hello
44
45 In [4]: %run refbug
46
47 In [5]: call_f()
48 lowercased: hello
49 lowercased: hello
50 """
51
52
53 def doctest_run_builtins():
54 r"""Check that %run doesn't damage __builtins__.
55
56 In [1]: import tempfile
57
58 In [2]: bid1 = id(__builtins__)
59
60 In [3]: fname = tempfile.mkstemp('.py')[1]
61
62 In [3]: f = open(fname,'w')
63
64 In [4]: f.write('pass\n')
65
66 In [5]: f.flush()
67
68 In [6]: t1 = type(__builtins__)
69
70 In [7]: %run "$fname"
71
72 In [7]: f.close()
73
74 In [8]: bid2 = id(__builtins__)
75
76 In [9]: t2 = type(__builtins__)
77
78 In [10]: t1 == t2
79 Out[10]: True
80
81 In [10]: bid1 == bid2
82 Out[10]: True
83
84 In [12]: try:
85 ....: os.unlink(fname)
86 ....: except:
87 ....: pass
88 ....:
89 """
90
91 # For some tests, it will be handy to organize them in a class with a common
92 # setup that makes a temp file
93
94 class TestMagicRunPass(tt.TempFileMixin):
95
96 def setup(self):
97 """Make a valid python temp file."""
98 self.mktmp('pass\n')
99
100 def run_tmpfile(self):
101 _ip = get_ipython()
102 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
103 # See below and ticket https://bugs.launchpad.net/bugs/366353
104 _ip.magic('run "%s"' % self.fname)
105
106 def test_builtins_id(self):
107 """Check that %run doesn't damage __builtins__ """
108 _ip = get_ipython()
109 # Test that the id of __builtins__ is not modified by %run
110 bid1 = id(_ip.user_ns['__builtins__'])
111 self.run_tmpfile()
112 bid2 = id(_ip.user_ns['__builtins__'])
113 tt.assert_equals(bid1, bid2)
114
115 def test_builtins_type(self):
116 """Check that the type of __builtins__ doesn't change with %run.
117
118 However, the above could pass if __builtins__ was already modified to
119 be a dict (it should be a module) by a previous use of %run. So we
120 also check explicitly that it really is a module:
121 """
122 _ip = get_ipython()
123 self.run_tmpfile()
124 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
125
126 def test_prompts(self):
127 """Test that prompts correctly generate after %run"""
128 self.run_tmpfile()
129 _ip = get_ipython()
130 p2 = str(_ip.outputcache.prompt2).strip()
131 nt.assert_equals(p2[:3], '...')
132
133
134 class TestMagicRunSimple(tt.TempFileMixin):
135
136 def test_simpledef(self):
137 """Test that simple class definitions work."""
138 src = ("class foo: pass\n"
139 "def f(): return foo()")
140 self.mktmp(src)
141 _ip.magic('run %s' % self.fname)
142 _ip.runlines('t = isinstance(f(), foo)')
143 nt.assert_true(_ip.user_ns['t'])
144
145 # We have to skip these in win32 because genutils.getoutputerr() crashes,
146 # due to the fact that subprocess does not support close_fds when
147 # redirecting stdout/err. So unless someone who knows more tells us how to
148 # implement genutils.getoutputerr() in win32, we're stuck avoiding these.
149 @dec.skip_win32
150 def test_obj_del(self):
151 """Test that object's __del__ methods are called on exit."""
152
153 # This test is known to fail on win32.
154 # See ticket https://bugs.launchpad.net/bugs/366334
155 src = ("class A(object):\n"
156 " def __del__(self):\n"
157 " print 'object A deleted'\n"
158 "a = A()\n")
159 self.mktmp(src)
160 tt.ipexec_validate(self.fname, 'object A deleted')
161
162 @dec.skip_win32
163 def test_tclass(self):
164 mydir = os.path.dirname(__file__)
165 tc = os.path.join(mydir, 'tclass')
166 src = ("%%run '%s' C-first\n"
167 "%%run '%s' C-second\n") % (tc, tc)
168 self.mktmp(src, '.ipy')
169 out = """\
170 ARGV 1-: ['C-first']
171 ARGV 1-: ['C-second']
172 tclass.py: deleting object: C-first
173 """
174 tt.ipexec_validate(self.fname, out)
@@ -0,0 +1,209 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4 """Magic command interface for interactive parallel work."""
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import new
18
19 from IPython.core.component import Component
20 from IPython.utils.traitlets import Bool, Any
21 from IPython.utils.autoattr import auto_attr
22 from IPython.testing import decorators as testdec
23
24 #-----------------------------------------------------------------------------
25 # Definitions of magic functions for use with IPython
26 #-----------------------------------------------------------------------------
27
28
29 NO_ACTIVE_MULTIENGINE_CLIENT = """
30 Use activate() on a MultiEngineClient object to activate it for magics.
31 """
32
33
34 class ParalleMagicComponent(Component):
35 """A component to manage the %result, %px and %autopx magics."""
36
37 active_multiengine_client = Any()
38 verbose = Bool(False, config=True)
39
40 def __init__(self, parent, name=None, config=None):
41 super(ParalleMagicComponent, self).__init__(parent, name=name, config=config)
42 self._define_magics()
43 # A flag showing if autopx is activated or not
44 self.autopx = False
45
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):
57 """Define the magic functions."""
58 self.shell.define_magic('result', self.magic_result)
59 self.shell.define_magic('px', self.magic_px)
60 self.shell.define_magic('autopx', self.magic_autopx)
61
62 @testdec.skip_doctest
63 def magic_result(self, ipself, parameter_s=''):
64 """Print the result of command i on all engines..
65
66 To use this a :class:`MultiEngineClient` instance must be created
67 and then activated by calling its :meth:`activate` method.
68
69 Then you can do the following::
70
71 In [23]: %result
72 Out[23]:
73 <Results List>
74 [0] In [6]: a = 10
75 [1] In [6]: a = 10
76
77 In [22]: %result 6
78 Out[22]:
79 <Results List>
80 [0] In [6]: a = 10
81 [1] In [6]: a = 10
82 """
83 if self.active_multiengine_client is None:
84 print NO_ACTIVE_MULTIENGINE_CLIENT
85 return
86
87 try:
88 index = int(parameter_s)
89 except:
90 index = None
91 result = self.active_multiengine_client.get_result(index)
92 return result
93
94 @testdec.skip_doctest
95 def magic_px(self, ipself, parameter_s=''):
96 """Executes the given python command in parallel.
97
98 To use this a :class:`MultiEngineClient` instance must be created
99 and then activated by calling its :meth:`activate` method.
100
101 Then you can do the following::
102
103 In [24]: %px a = 5
104 Parallel execution on engines: all
105 Out[24]:
106 <Results List>
107 [0] In [7]: a = 5
108 [1] In [7]: a = 5
109 """
110
111 if self.active_multiengine_client is None:
112 print NO_ACTIVE_MULTIENGINE_CLIENT
113 return
114 print "Parallel execution on engines: %s" % self.active_multiengine_client.targets
115 result = self.active_multiengine_client.execute(parameter_s)
116 return result
117
118 @testdec.skip_doctest
119 def magic_autopx(self, ipself, parameter_s=''):
120 """Toggles auto parallel mode.
121
122 To use this a :class:`MultiEngineClient` instance must be created
123 and then activated by calling its :meth:`activate` method. Once this
124 is called, all commands typed at the command line are send to
125 the engines to be executed in parallel. To control which engine
126 are used, set the ``targets`` attributed of the multiengine client
127 before entering ``%autopx`` mode.
128
129 Then you can do the following::
130
131 In [25]: %autopx
132 %autopx to enabled
133
134 In [26]: a = 10
135 <Results List>
136 [0] In [8]: a = 10
137 [1] In [8]: a = 10
138
139
140 In [27]: %autopx
141 %autopx disabled
142 """
143 if self.autopx:
144 self._disable_autopx()
145 else:
146 self._enable_autopx()
147
148 def _enable_autopx(self):
149 """Enable %autopx mode by saving the original runsource and installing
150 pxrunsource.
151 """
152 if self.active_multiengine_client is None:
153 print NO_ACTIVE_MULTIENGINE_CLIENT
154 return
155
156 self._original_runsource = self.shell.runsource
157 self.shell.runsource = new.instancemethod(
158 self.pxrunsource, self.shell, self.shell.__class__
159 )
160 self.autopx = True
161 print "%autopx enabled"
162
163 def _disable_autopx(self):
164 """Disable %autopx by restoring the original InteractiveShell.runsource."""
165 if self.autopx:
166 self.shell.runsource = self._original_runsource
167 self.autopx = False
168 print "%autopx disabled"
169
170 def pxrunsource(self, ipself, source, filename="<input>", symbol="single"):
171 """A parallel replacement for InteractiveShell.runsource."""
172
173 try:
174 code = ipself.compile(source, filename, symbol)
175 except (OverflowError, SyntaxError, ValueError):
176 # Case 1
177 ipself.showsyntaxerror(filename)
178 return None
179
180 if code is None:
181 # Case 2
182 return True
183
184 # Case 3
185 # Because autopx is enabled, we now call executeAll or disable autopx if
186 # %autopx or autopx has been called
187 if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source:
188 self._disable_autopx()
189 return False
190 else:
191 try:
192 result = self.active_multiengine_client.execute(source)
193 except:
194 ipself.showtraceback()
195 else:
196 print result.__repr__()
197 return False
198
199
200 _loaded = False
201
202
203 def load_ipython_extension(ip):
204 """Load the extension in IPython."""
205 global _loaded
206 if not _loaded:
207 prd = ParalleMagicComponent(ip, name='parallel_magic')
208 _loaded = True
209
@@ -0,0 +1,120 b''
1 # IPython: modified copy of numpy.testing.utils, so numpy.testing.decorators
2 # works without numpy being installed.
3 """
4 Utility function to facilitate testing.
5 """
6
7 import os
8 import sys
9 import re
10 import operator
11 import types
12 import warnings
13
14 # The following two classes are copied from python 2.6 warnings module (context
15 # manager)
16 class WarningMessage(object):
17
18 """
19 Holds the result of a single showwarning() call.
20
21 Notes
22 -----
23 `WarningMessage` is copied from the Python 2.6 warnings module,
24 so it can be used in NumPy with older Python versions.
25
26 """
27
28 _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
29 "line")
30
31 def __init__(self, message, category, filename, lineno, file=None,
32 line=None):
33 local_values = locals()
34 for attr in self._WARNING_DETAILS:
35 setattr(self, attr, local_values[attr])
36 if category:
37 self._category_name = category.__name__
38 else:
39 self._category_name = None
40
41 def __str__(self):
42 return ("{message : %r, category : %r, filename : %r, lineno : %s, "
43 "line : %r}" % (self.message, self._category_name,
44 self.filename, self.lineno, self.line))
45
46 class WarningManager:
47 """
48 A context manager that copies and restores the warnings filter upon
49 exiting the context.
50
51 The 'record' argument specifies whether warnings should be captured by a
52 custom implementation of ``warnings.showwarning()`` and be appended to a
53 list returned by the context manager. Otherwise None is returned by the
54 context manager. The objects appended to the list are arguments whose
55 attributes mirror the arguments to ``showwarning()``.
56
57 The 'module' argument is to specify an alternative module to the module
58 named 'warnings' and imported under that name. This argument is only useful
59 when testing the warnings module itself.
60
61 Notes
62 -----
63 `WarningManager` is a copy of the ``catch_warnings`` context manager
64 from the Python 2.6 warnings module, with slight modifications.
65 It is copied so it can be used in NumPy with older Python versions.
66
67 """
68 def __init__(self, record=False, module=None):
69 self._record = record
70 if module is None:
71 self._module = sys.modules['warnings']
72 else:
73 self._module = module
74 self._entered = False
75
76 def __enter__(self):
77 if self._entered:
78 raise RuntimeError("Cannot enter %r twice" % self)
79 self._entered = True
80 self._filters = self._module.filters
81 self._module.filters = self._filters[:]
82 self._showwarning = self._module.showwarning
83 if self._record:
84 log = []
85 def showwarning(*args, **kwargs):
86 log.append(WarningMessage(*args, **kwargs))
87 self._module.showwarning = showwarning
88 return log
89 else:
90 return None
91
92 def __exit__(self):
93 if not self._entered:
94 raise RuntimeError("Cannot exit %r without entering first" % self)
95 self._module.filters = self._filters
96 self._module.showwarning = self._showwarning
97
98 def assert_warns(warning_class, func, *args, **kw):
99 """Fail unless a warning of class warning_class is thrown by callable when
100 invoked with arguments args and keyword arguments kwargs.
101
102 If a different type of warning is thrown, it will not be caught, and the
103 test case will be deemed to have suffered an error.
104 """
105
106 # XXX: once we may depend on python >= 2.6, this can be replaced by the
107 # warnings module context manager.
108 ctx = WarningManager(record=True)
109 l = ctx.__enter__()
110 warnings.simplefilter('always')
111 try:
112 func(*args, **kw)
113 if not len(l) > 0:
114 raise AssertionError("No warning raised when calling %s"
115 % func.__name__)
116 if not l[0].category is warning_class:
117 raise AssertionError("First warning for %s is not a " \
118 "%s( is %s)" % (func.__name__, warning_class, l[0]))
119 finally:
120 ctx.__exit__()
@@ -0,0 +1,284 b''
1 """
2 Decorators for labeling and modifying behavior of test objects.
3
4 Decorators that merely return a modified version of the original
5 function object are straightforward. Decorators that return a new
6 function object need to use
7 ::
8
9 nose.tools.make_decorator(original_function)(decorator)
10
11 in returning the decorator, in order to preserve meta-data such as
12 function name, setup and teardown functions and so on - see
13 ``nose.tools`` for more information.
14
15 """
16 import warnings
17 import sys
18
19 # IPython changes: make this work if numpy not available
20 # Original code:
21 #from numpy.testing.utils import \
22 # WarningManager, WarningMessage
23 # Our version:
24 try:
25 from numpy.testing.utils import WarningManager, WarningMessage
26 except ImportError:
27 from _numpy_testing_utils import WarningManager, WarningMessage
28
29 # End IPython changes
30
31 def slow(t):
32 """
33 Label a test as 'slow'.
34
35 The exact definition of a slow test is obviously both subjective and
36 hardware-dependent, but in general any individual test that requires more
37 than a second or two should be labeled as slow (the whole suite consits of
38 thousands of tests, so even a second is significant).
39
40 Parameters
41 ----------
42 t : callable
43 The test to label as slow.
44
45 Returns
46 -------
47 t : callable
48 The decorated test `t`.
49
50 Examples
51 --------
52 The `numpy.testing` module includes ``import decorators as dec``.
53 A test can be decorated as slow like this::
54
55 from numpy.testing import *
56
57 @dec.slow
58 def test_big(self):
59 print 'Big, slow test'
60
61 """
62
63 t.slow = True
64 return t
65
66 def setastest(tf=True):
67 """
68 Signals to nose that this function is or is not a test.
69
70 Parameters
71 ----------
72 tf : bool
73 If True, specifies that the decorated callable is a test.
74 If False, specifies that the decorated callable is not a test.
75 Default is True.
76
77 Notes
78 -----
79 This decorator can't use the nose namespace, because it can be
80 called from a non-test module. See also ``istest`` and ``nottest`` in
81 ``nose.tools``.
82
83 Examples
84 --------
85 `setastest` can be used in the following way::
86
87 from numpy.testing.decorators import setastest
88
89 @setastest(False)
90 def func_with_test_in_name(arg1, arg2):
91 pass
92
93 """
94 def set_test(t):
95 t.__test__ = tf
96 return t
97 return set_test
98
99 def skipif(skip_condition, msg=None):
100 """
101 Make function raise SkipTest exception if a given condition is true.
102
103 If the condition is a callable, it is used at runtime to dynamically
104 make the decision. This is useful for tests that may require costly
105 imports, to delay the cost until the test suite is actually executed.
106
107 Parameters
108 ----------
109 skip_condition : bool or callable
110 Flag to determine whether to skip the decorated test.
111 msg : str, optional
112 Message to give on raising a SkipTest exception. Default is None.
113
114 Returns
115 -------
116 decorator : function
117 Decorator which, when applied to a function, causes SkipTest
118 to be raised when `skip_condition` is True, and the function
119 to be called normally otherwise.
120
121 Notes
122 -----
123 The decorator itself is decorated with the ``nose.tools.make_decorator``
124 function in order to transmit function name, and various other metadata.
125
126 """
127
128 def skip_decorator(f):
129 # Local import to avoid a hard nose dependency and only incur the
130 # import time overhead at actual test-time.
131 import nose
132
133 # Allow for both boolean or callable skip conditions.
134 if callable(skip_condition):
135 skip_val = lambda : skip_condition()
136 else:
137 skip_val = lambda : skip_condition
138
139 def get_msg(func,msg=None):
140 """Skip message with information about function being skipped."""
141 if msg is None:
142 out = 'Test skipped due to test condition'
143 else:
144 out = '\n'+msg
145
146 return "Skipping test: %s%s" % (func.__name__,out)
147
148 # We need to define *two* skippers because Python doesn't allow both
149 # return with value and yield inside the same function.
150 def skipper_func(*args, **kwargs):
151 """Skipper for normal test functions."""
152 if skip_val():
153 raise nose.SkipTest(get_msg(f,msg))
154 else:
155 return f(*args, **kwargs)
156
157 def skipper_gen(*args, **kwargs):
158 """Skipper for test generators."""
159 if skip_val():
160 raise nose.SkipTest(get_msg(f,msg))
161 else:
162 for x in f(*args, **kwargs):
163 yield x
164
165 # Choose the right skipper to use when building the actual decorator.
166 if nose.util.isgenerator(f):
167 skipper = skipper_gen
168 else:
169 skipper = skipper_func
170
171 return nose.tools.make_decorator(f)(skipper)
172
173 return skip_decorator
174
175
176 def knownfailureif(fail_condition, msg=None):
177 """
178 Make function raise KnownFailureTest exception if given condition is true.
179
180 If the condition is a callable, it is used at runtime to dynamically
181 make the decision. This is useful for tests that may require costly
182 imports, to delay the cost until the test suite is actually executed.
183
184 Parameters
185 ----------
186 fail_condition : bool or callable
187 Flag to determine whether to mark the decorated test as a known
188 failure (if True) or not (if False).
189 msg : str, optional
190 Message to give on raising a KnownFailureTest exception.
191 Default is None.
192
193 Returns
194 -------
195 decorator : function
196 Decorator, which, when applied to a function, causes SkipTest
197 to be raised when `skip_condition` is True, and the function
198 to be called normally otherwise.
199
200 Notes
201 -----
202 The decorator itself is decorated with the ``nose.tools.make_decorator``
203 function in order to transmit function name, and various other metadata.
204
205 """
206 if msg is None:
207 msg = 'Test skipped due to known failure'
208
209 # Allow for both boolean or callable known failure conditions.
210 if callable(fail_condition):
211 fail_val = lambda : fail_condition()
212 else:
213 fail_val = lambda : fail_condition
214
215 def knownfail_decorator(f):
216 # Local import to avoid a hard nose dependency and only incur the
217 # import time overhead at actual test-time.
218 import nose
219 from noseclasses import KnownFailureTest
220 def knownfailer(*args, **kwargs):
221 if fail_val():
222 raise KnownFailureTest, msg
223 else:
224 return f(*args, **kwargs)
225 return nose.tools.make_decorator(f)(knownfailer)
226
227 return knownfail_decorator
228
229 def deprecated(conditional=True):
230 """
231 Filter deprecation warnings while running the test suite.
232
233 This decorator can be used to filter DeprecationWarning's, to avoid
234 printing them during the test suite run, while checking that the test
235 actually raises a DeprecationWarning.
236
237 Parameters
238 ----------
239 conditional : bool or callable, optional
240 Flag to determine whether to mark test as deprecated or not. If the
241 condition is a callable, it is used at runtime to dynamically make the
242 decision. Default is True.
243
244 Returns
245 -------
246 decorator : function
247 The `deprecated` decorator itself.
248
249 Notes
250 -----
251 .. versionadded:: 1.4.0
252
253 """
254 def deprecate_decorator(f):
255 # Local import to avoid a hard nose dependency and only incur the
256 # import time overhead at actual test-time.
257 import nose
258 from noseclasses import KnownFailureTest
259
260 def _deprecated_imp(*args, **kwargs):
261 # Poor man's replacement for the with statement
262 ctx = WarningManager(record=True)
263 l = ctx.__enter__()
264 warnings.simplefilter('always')
265 try:
266 f(*args, **kwargs)
267 if not len(l) > 0:
268 raise AssertionError("No warning raised when calling %s"
269 % f.__name__)
270 if not l[0].category is DeprecationWarning:
271 raise AssertionError("First warning for %s is not a " \
272 "DeprecationWarning( is %s)" % (f.__name__, l[0]))
273 finally:
274 ctx.__exit__()
275
276 if callable(conditional):
277 cond = conditional()
278 else:
279 cond = conditional
280 if cond:
281 return nose.tools.make_decorator(f)(_deprecated_imp)
282 else:
283 return f
284 return deprecate_decorator
@@ -0,0 +1,450 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 The IPython cluster directory
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 from __future__ import with_statement
19
20 import os
21 import shutil
22 import sys
23
24 from twisted.python import log
25
26 from IPython.core import release
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application
29 from IPython.core.component import Component
30 from IPython.utils.traitlets import Unicode, Bool
31 from IPython.utils import genutils
32
33 #-----------------------------------------------------------------------------
34 # Imports
35 #-----------------------------------------------------------------------------
36
37
38 class ClusterDirError(Exception):
39 pass
40
41
42 class PIDFileError(Exception):
43 pass
44
45
46 class ClusterDir(Component):
47 """An object to manage the cluster directory and its resources.
48
49 The cluster directory is used by :command:`ipcontroller`,
50 :command:`ipcontroller` and :command:`ipcontroller` to manage the
51 configuration, logging and security of these applications.
52
53 This object knows how to find, create and manage these directories. This
54 should be used by any code that want's to handle cluster directories.
55 """
56
57 security_dir_name = Unicode('security')
58 log_dir_name = Unicode('log')
59 pid_dir_name = Unicode('pid')
60 security_dir = Unicode(u'')
61 log_dir = Unicode(u'')
62 pid_dir = Unicode(u'')
63 location = Unicode(u'')
64
65 def __init__(self, location):
66 super(ClusterDir, self).__init__(None)
67 self.location = location
68
69 def _location_changed(self, name, old, new):
70 if not os.path.isdir(new):
71 os.makedirs(new)
72 self.security_dir = os.path.join(new, self.security_dir_name)
73 self.log_dir = os.path.join(new, self.log_dir_name)
74 self.pid_dir = os.path.join(new, self.pid_dir_name)
75 self.check_dirs()
76
77 def _log_dir_changed(self, name, old, new):
78 self.check_log_dir()
79
80 def check_log_dir(self):
81 if not os.path.isdir(self.log_dir):
82 os.mkdir(self.log_dir)
83
84 def _security_dir_changed(self, name, old, new):
85 self.check_security_dir()
86
87 def check_security_dir(self):
88 if not os.path.isdir(self.security_dir):
89 os.mkdir(self.security_dir, 0700)
90 os.chmod(self.security_dir, 0700)
91
92 def _pid_dir_changed(self, name, old, new):
93 self.check_pid_dir()
94
95 def check_pid_dir(self):
96 if not os.path.isdir(self.pid_dir):
97 os.mkdir(self.pid_dir, 0700)
98 os.chmod(self.pid_dir, 0700)
99
100 def check_dirs(self):
101 self.check_security_dir()
102 self.check_log_dir()
103 self.check_pid_dir()
104
105 def load_config_file(self, filename):
106 """Load a config file from the top level of the cluster dir.
107
108 Parameters
109 ----------
110 filename : unicode or str
111 The filename only of the config file that must be located in
112 the top-level of the cluster directory.
113 """
114 loader = PyFileConfigLoader(filename, self.location)
115 return loader.load_config()
116
117 def copy_config_file(self, config_file, path=None, overwrite=False):
118 """Copy a default config file into the active cluster directory.
119
120 Default configuration files are kept in :mod:`IPython.config.default`.
121 This function moves these from that location to the working cluster
122 directory.
123 """
124 if path is None:
125 import IPython.config.default
126 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
127 path = os.path.sep.join(path)
128 src = os.path.join(path, config_file)
129 dst = os.path.join(self.location, config_file)
130 if not os.path.isfile(dst) or overwrite:
131 shutil.copy(src, dst)
132
133 def copy_all_config_files(self, path=None, overwrite=False):
134 """Copy all config files into the active cluster directory."""
135 for f in [u'ipcontroller_config.py', u'ipengine_config.py',
136 u'ipcluster_config.py']:
137 self.copy_config_file(f, path=path, overwrite=overwrite)
138
139 @classmethod
140 def create_cluster_dir(csl, cluster_dir):
141 """Create a new cluster directory given a full path.
142
143 Parameters
144 ----------
145 cluster_dir : str
146 The full path to the cluster directory. If it does exist, it will
147 be used. If not, it will be created.
148 """
149 return ClusterDir(cluster_dir)
150
151 @classmethod
152 def create_cluster_dir_by_profile(cls, path, profile=u'default'):
153 """Create a cluster dir by profile name and path.
154
155 Parameters
156 ----------
157 path : str
158 The path (directory) to put the cluster directory in.
159 profile : str
160 The name of the profile. The name of the cluster directory will
161 be "cluster_<profile>".
162 """
163 if not os.path.isdir(path):
164 raise ClusterDirError('Directory not found: %s' % path)
165 cluster_dir = os.path.join(path, u'cluster_' + profile)
166 return ClusterDir(cluster_dir)
167
168 @classmethod
169 def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
170 """Find an existing cluster dir by profile name, return its ClusterDir.
171
172 This searches through a sequence of paths for a cluster dir. If it
173 is not found, a :class:`ClusterDirError` exception will be raised.
174
175 The search path algorithm is:
176 1. ``os.getcwd()``
177 2. ``ipython_dir``
178 3. The directories found in the ":" separated
179 :env:`IPCLUSTER_DIR_PATH` environment variable.
180
181 Parameters
182 ----------
183 ipython_dir : unicode or str
184 The IPython directory to use.
185 profile : unicode or str
186 The name of the profile. The name of the cluster directory
187 will be "cluster_<profile>".
188 """
189 dirname = u'cluster_' + profile
190 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
191 if cluster_dir_paths:
192 cluster_dir_paths = cluster_dir_paths.split(':')
193 else:
194 cluster_dir_paths = []
195 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
196 for p in paths:
197 cluster_dir = os.path.join(p, dirname)
198 if os.path.isdir(cluster_dir):
199 return ClusterDir(cluster_dir)
200 else:
201 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
202
203 @classmethod
204 def find_cluster_dir(cls, cluster_dir):
205 """Find/create a cluster dir and return its ClusterDir.
206
207 This will create the cluster directory if it doesn't exist.
208
209 Parameters
210 ----------
211 cluster_dir : unicode or str
212 The path of the cluster directory. This is expanded using
213 :func:`IPython.utils.genutils.expand_path`.
214 """
215 cluster_dir = genutils.expand_path(cluster_dir)
216 if not os.path.isdir(cluster_dir):
217 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
218 return ClusterDir(cluster_dir)
219
220
221 # Default command line options for IPython cluster applications.
222 cl_args = (
223 (('--ipython-dir',), dict(
224 dest='Global.ipython_dir',type=unicode,
225 help='Set to override default location of Global.ipython_dir.',
226 metavar='Global.ipython_dir') ),
227 (('-p', '--profile',), dict(
228 dest='Global.profile',type=unicode,
229 help=
230 """The string name of the profile to be used. This determines the name
231 of the cluster dir as: cluster_<profile>. The default profile is named
232 'default'. The cluster directory is resolve this way if the
233 --cluster-dir option is not used.""",
234 metavar='Global.profile') ),
235 (('--cluster-dir',), dict(
236 dest='Global.cluster_dir',type=unicode,
237 help="""Set the cluster dir. This overrides the logic used by the
238 --profile option.""",
239 metavar='Global.cluster_dir') ),
240 (('--work-dir',), dict(
241 dest='Global.work_dir',type=unicode,
242 help='Set the working dir for the process.',
243 metavar='Global.work_dir') ),
244 (('--clean-logs',), dict(
245 dest='Global.clean_logs', action='store_true',
246 help='Delete old log flies before starting.') ),
247 (('--no-clean-logs',), dict(
248 dest='Global.clean_logs', action='store_false',
249 help="Don't Delete old log flies before starting.") ),
250 )
251
252
253 class ApplicationWithClusterDir(Application):
254 """An application that puts everything into a cluster directory.
255
256 Instead of looking for things in the ipython_dir, this type of application
257 will use its own private directory called the "cluster directory"
258 for things like config files, log files, etc.
259
260 The cluster directory is resolved as follows:
261
262 * If the ``--cluster-dir`` option is given, it is used.
263 * If ``--cluster-dir`` is not given, the application directory is
264 resolve using the profile name as ``cluster_<profile>``. The search
265 path for this directory is then i) cwd if it is found there
266 and ii) in ipython_dir otherwise.
267
268 The config file for the application is to be put in the cluster
269 dir and named the value of the ``config_file_name`` class attribute.
270 """
271
272 auto_create_cluster_dir = True
273
274 cl_arguments = Application.cl_arguments + cl_args
275
276 def create_default_config(self):
277 super(ApplicationWithClusterDir, self).create_default_config()
278 self.default_config.Global.profile = u'default'
279 self.default_config.Global.cluster_dir = u''
280 self.default_config.Global.work_dir = os.getcwd()
281 self.default_config.Global.log_to_file = False
282 self.default_config.Global.clean_logs = False
283
284 def find_resources(self):
285 """This resolves the cluster directory.
286
287 This tries to find the cluster directory and if successful, it will
288 have done:
289 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
290 the application.
291 * Sets ``self.cluster_dir`` attribute of the application and config
292 objects.
293
294 The algorithm used for this is as follows:
295 1. Try ``Global.cluster_dir``.
296 2. Try using ``Global.profile``.
297 3. If both of these fail and ``self.auto_create_cluster_dir`` is
298 ``True``, then create the new cluster dir in the IPython directory.
299 4. If all fails, then raise :class:`ClusterDirError`.
300 """
301
302 try:
303 cluster_dir = self.command_line_config.Global.cluster_dir
304 except AttributeError:
305 cluster_dir = self.default_config.Global.cluster_dir
306 cluster_dir = genutils.expand_path(cluster_dir)
307 try:
308 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
309 except ClusterDirError:
310 pass
311 else:
312 self.log.info('Using existing cluster dir: %s' % \
313 self.cluster_dir_obj.location
314 )
315 self.finish_cluster_dir()
316 return
317
318 try:
319 self.profile = self.command_line_config.Global.profile
320 except AttributeError:
321 self.profile = self.default_config.Global.profile
322 try:
323 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
324 self.ipython_dir, self.profile)
325 except ClusterDirError:
326 pass
327 else:
328 self.log.info('Using existing cluster dir: %s' % \
329 self.cluster_dir_obj.location
330 )
331 self.finish_cluster_dir()
332 return
333
334 if self.auto_create_cluster_dir:
335 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
336 self.ipython_dir, self.profile
337 )
338 self.log.info('Creating new cluster dir: %s' % \
339 self.cluster_dir_obj.location
340 )
341 self.finish_cluster_dir()
342 else:
343 raise ClusterDirError('Could not find a valid cluster directory.')
344
345 def finish_cluster_dir(self):
346 # Set the cluster directory
347 self.cluster_dir = self.cluster_dir_obj.location
348
349 # These have to be set because they could be different from the one
350 # that we just computed. Because command line has the highest
351 # priority, this will always end up in the master_config.
352 self.default_config.Global.cluster_dir = self.cluster_dir
353 self.command_line_config.Global.cluster_dir = self.cluster_dir
354
355 # Set the search path to the cluster directory
356 self.config_file_paths = (self.cluster_dir,)
357
358 def find_config_file_name(self):
359 """Find the config file name for this application."""
360 # For this type of Application it should be set as a class attribute.
361 if not hasattr(self, 'config_file_name'):
362 self.log.critical("No config filename found")
363
364 def find_config_file_paths(self):
365 # Set the search path to the cluster directory
366 self.config_file_paths = (self.cluster_dir,)
367
368 def pre_construct(self):
369 # The log and security dirs were set earlier, but here we put them
370 # into the config and log them.
371 config = self.master_config
372 sdir = self.cluster_dir_obj.security_dir
373 self.security_dir = config.Global.security_dir = sdir
374 ldir = self.cluster_dir_obj.log_dir
375 self.log_dir = config.Global.log_dir = ldir
376 pdir = self.cluster_dir_obj.pid_dir
377 self.pid_dir = config.Global.pid_dir = pdir
378 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
379 config.Global.work_dir = unicode(genutils.expand_path(config.Global.work_dir))
380 # Change to the working directory. We do this just before construct
381 # is called so all the components there have the right working dir.
382 self.to_work_dir()
383
384 def to_work_dir(self):
385 wd = self.master_config.Global.work_dir
386 if unicode(wd) != unicode(os.getcwd()):
387 os.chdir(wd)
388 self.log.info("Changing to working dir: %s" % wd)
389
390 def start_logging(self):
391 # Remove old log files
392 if self.master_config.Global.clean_logs:
393 log_dir = self.master_config.Global.log_dir
394 for f in os.listdir(log_dir):
395 if f.startswith(self.name + u'-') and f.endswith('.log'):
396 os.remove(os.path.join(log_dir, f))
397 # Start logging to the new log file
398 if self.master_config.Global.log_to_file:
399 log_filename = self.name + u'-' + str(os.getpid()) + u'.log'
400 logfile = os.path.join(self.log_dir, log_filename)
401 open_log_file = open(logfile, 'w')
402 else:
403 open_log_file = sys.stdout
404 log.startLogging(open_log_file)
405
406 def write_pid_file(self, overwrite=False):
407 """Create a .pid file in the pid_dir with my pid.
408
409 This must be called after pre_construct, which sets `self.pid_dir`.
410 This raises :exc:`PIDFileError` if the pid file exists already.
411 """
412 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
413 if os.path.isfile(pid_file):
414 pid = self.get_pid_from_file()
415 if not overwrite:
416 raise PIDFileError(
417 'The pid file [%s] already exists. \nThis could mean that this '
418 'server is already running with [pid=%s].' % (pid_file, pid)
419 )
420 with open(pid_file, 'w') as f:
421 self.log.info("Creating pid file: %s" % pid_file)
422 f.write(repr(os.getpid())+'\n')
423
424 def remove_pid_file(self):
425 """Remove the pid file.
426
427 This should be called at shutdown by registering a callback with
428 :func:`reactor.addSystemEventTrigger`. This needs to return
429 ``None``.
430 """
431 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
432 if os.path.isfile(pid_file):
433 try:
434 self.log.info("Removing pid file: %s" % pid_file)
435 os.remove(pid_file)
436 except:
437 self.log.warn("Error removing the pid file: %s" % pid_file)
438
439 def get_pid_from_file(self):
440 """Get the pid from the pid file.
441
442 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
443 """
444 pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
445 if os.path.isfile(pid_file):
446 with open(pid_file, 'r') as f:
447 pid = int(f.read().strip())
448 return pid
449 else:
450 raise PIDFileError('pid file not found: %s' % pid_file)
@@ -0,0 +1,79 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 A class for creating a Twisted service that is configured using IPython's
5 configuration system.
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2009 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import zope.interface as zi
20
21 from IPython.core.component import Component
22
23 #-----------------------------------------------------------------------------
24 # Code
25 #-----------------------------------------------------------------------------
26
27
28 class IConfiguredObjectFactory(zi.Interface):
29 """I am a component that creates a configured object.
30
31 This class is useful if you want to configure a class that is not a
32 subclass of :class:`IPython.core.component.Component`.
33 """
34
35 def __init__(config):
36 """Get ready to configure the object using config."""
37
38 def create():
39 """Return an instance of the configured object."""
40
41
42 class ConfiguredObjectFactory(Component):
43
44 zi.implements(IConfiguredObjectFactory)
45
46 def __init__(self, config):
47 super(ConfiguredObjectFactory, self).__init__(None, config=config)
48
49 def create(self):
50 raise NotImplementedError('create must be implemented in a subclass')
51
52
53 class IAdaptedConfiguredObjectFactory(zi.Interface):
54 """I am a component that adapts and configures an object.
55
56 This class is useful if you have the adapt an instance and configure it.
57 """
58
59 def __init__(config, adaptee=None):
60 """Get ready to adapt adaptee and then configure it using config."""
61
62 def create():
63 """Return an instance of the adapted and configured object."""
64
65
66 class AdaptedConfiguredObjectFactory(Component):
67
68 # zi.implements(IAdaptedConfiguredObjectFactory)
69
70 def __init__(self, config, adaptee):
71 # print
72 # print "config pre:", config
73 super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config)
74 # print
75 # print "config post:", config
76 self.adaptee = adaptee
77
78 def create(self):
79 raise NotImplementedError('create must be implemented in a subclass') No newline at end of file
@@ -0,0 +1,460 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 The ipcluster application.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import logging
19 import os
20 import signal
21
22 if os.name=='posix':
23 from twisted.scripts._twistd_unix import daemonize
24
25 from IPython.core import release
26 from IPython.external.argparse import ArgumentParser
27 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
28 from IPython.utils.importstring import import_item
29
30 from IPython.kernel.clusterdir import (
31 ApplicationWithClusterDir, ClusterDirError, PIDFileError
32 )
33
34 from twisted.internet import reactor, defer
35 from twisted.python import log, failure
36
37
38 #-----------------------------------------------------------------------------
39 # The ipcluster application
40 #-----------------------------------------------------------------------------
41
42
43 # Exit codes for ipcluster
44
45 # This will be the exit code if the ipcluster appears to be running because
46 # a .pid file exists
47 ALREADY_STARTED = 10
48
49 # This will be the exit code if ipcluster stop is run, but there is not .pid
50 # file to be found.
51 ALREADY_STOPPED = 11
52
53
54 class IPClusterCLLoader(ArgParseConfigLoader):
55
56 def _add_other_arguments(self):
57 # This has all the common options that all subcommands use
58 parent_parser1 = ArgumentParser(add_help=False,
59 argument_default=NoConfigDefault)
60 parent_parser1.add_argument('--ipython-dir',
61 dest='Global.ipython_dir',type=unicode,
62 help='Set to override default location of Global.ipython_dir.',
63 metavar='Global.ipython_dir')
64 parent_parser1.add_argument('--log-level',
65 dest="Global.log_level",type=int,
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
67 metavar='Global.log_level')
68
69 # This has all the common options that other subcommands use
70 parent_parser2 = ArgumentParser(add_help=False,
71 argument_default=NoConfigDefault)
72 parent_parser2.add_argument('-p','--profile',
73 dest='Global.profile',type=unicode,
74 help='The string name of the profile to be used. This determines '
75 'the name of the cluster dir as: cluster_<profile>. The default profile '
76 'is named "default". The cluster directory is resolve this way '
77 'if the --cluster-dir option is not used.',
78 metavar='Global.profile')
79 parent_parser2.add_argument('--cluster-dir',
80 dest='Global.cluster_dir',type=unicode,
81 help='Set the cluster dir. This overrides the logic used by the '
82 '--profile option.',
83 metavar='Global.cluster_dir'),
84 parent_parser2.add_argument('--work-dir',
85 dest='Global.work_dir',type=unicode,
86 help='Set the working dir for the process.',
87 metavar='Global.work_dir')
88 parent_parser2.add_argument('--log-to-file',
89 action='store_true', dest='Global.log_to_file',
90 help='Log to a file in the log directory (default is stdout)'
91 )
92
93 subparsers = self.parser.add_subparsers(
94 dest='Global.subcommand',
95 title='ipcluster subcommands',
96 description='ipcluster has a variety of subcommands. '
97 'The general way of running ipcluster is "ipcluster <cmd> '
98 ' [options]""',
99 help='For more help, type "ipcluster <cmd> -h"')
100
101 parser_list = subparsers.add_parser(
102 'list',
103 help='List all clusters in cwd and ipython_dir.',
104 parents=[parent_parser1]
105 )
106
107 parser_create = subparsers.add_parser(
108 'create',
109 help='Create a new cluster directory.',
110 parents=[parent_parser1, parent_parser2]
111 )
112 parser_create.add_argument(
113 '--reset-config',
114 dest='Global.reset_config', action='store_true',
115 default=NoConfigDefault,
116 help='Recopy the default config files to the cluster directory. '
117 'You will loose any modifications you have made to these files.'
118 )
119
120 parser_start = subparsers.add_parser(
121 'start',
122 help='Start a cluster.',
123 parents=[parent_parser1, parent_parser2]
124 )
125 parser_start.add_argument(
126 '-n', '--number',
127 type=int, dest='Global.n',
128 help='The number of engines to start.',
129 metavar='Global.n'
130 )
131 parser_start.add_argument('--clean-logs',
132 dest='Global.clean_logs', action='store_true',
133 help='Delete old log flies before starting.',
134 )
135 parser_start.add_argument('--no-clean-logs',
136 dest='Global.clean_logs', action='store_false',
137 help="Don't delete old log flies before starting.",
138 )
139 parser_start.add_argument('--daemon',
140 dest='Global.daemonize', action='store_true',
141 help='Daemonize the ipcluster program. This implies --log-to-file',
142 )
143 parser_start.add_argument('--no-daemon',
144 dest='Global.daemonize', action='store_false',
145 help="Dont't daemonize the ipcluster program.",
146 )
147
148 parser_start = subparsers.add_parser(
149 'stop',
150 help='Stop a cluster.',
151 parents=[parent_parser1, parent_parser2]
152 )
153 parser_start.add_argument('--signal',
154 dest='Global.signal', type=int,
155 help="The signal number to use in stopping the cluster (default=2).",
156 metavar="Global.signal",
157 )
158
159
160 default_config_file_name = u'ipcluster_config.py'
161
162
163 _description = """Start an IPython cluster for parallel computing.\n\n
164
165 An IPython cluster consists of 1 controller and 1 or more engines.
166 This command automates the startup of these processes using a wide
167 range of startup methods (SSH, local processes, PBS, mpiexec,
168 Windows HPC Server 2008). To start a cluster with 4 engines on your
169 local host simply do "ipcluster start -n 4". For more complex usage
170 you will typically do "ipcluster create -p mycluster", then edit
171 configuration files, followed by "ipcluster start -p mycluster -n 4".
172 """
173
174
175 class IPClusterApp(ApplicationWithClusterDir):
176
177 name = u'ipcluster'
178 description = _description
179 config_file_name = default_config_file_name
180 default_log_level = logging.INFO
181 auto_create_cluster_dir = False
182
183 def create_default_config(self):
184 super(IPClusterApp, self).create_default_config()
185 self.default_config.Global.controller_launcher = \
186 'IPython.kernel.launcher.LocalControllerLauncher'
187 self.default_config.Global.engine_launcher = \
188 'IPython.kernel.launcher.LocalEngineSetLauncher'
189 self.default_config.Global.n = 2
190 self.default_config.Global.reset_config = False
191 self.default_config.Global.clean_logs = True
192 self.default_config.Global.signal = 2
193 self.default_config.Global.daemonize = False
194
195 def create_command_line_config(self):
196 """Create and return a command line config loader."""
197 return IPClusterCLLoader(
198 description=self.description,
199 version=release.version
200 )
201
202 def find_resources(self):
203 subcommand = self.command_line_config.Global.subcommand
204 if subcommand=='list':
205 self.list_cluster_dirs()
206 # Exit immediately because there is nothing left to do.
207 self.exit()
208 elif subcommand=='create':
209 self.auto_create_cluster_dir = True
210 super(IPClusterApp, self).find_resources()
211 elif subcommand=='start' or subcommand=='stop':
212 self.auto_create_cluster_dir = True
213 try:
214 super(IPClusterApp, self).find_resources()
215 except ClusterDirError:
216 raise ClusterDirError(
217 "Could not find a cluster directory. A cluster dir must "
218 "be created before running 'ipcluster start'. Do "
219 "'ipcluster create -h' or 'ipcluster list -h' for more "
220 "information about creating and listing cluster dirs."
221 )
222
223 def list_cluster_dirs(self):
224 # Find the search paths
225 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
226 if cluster_dir_paths:
227 cluster_dir_paths = cluster_dir_paths.split(':')
228 else:
229 cluster_dir_paths = []
230 try:
231 ipython_dir = self.command_line_config.Global.ipython_dir
232 except AttributeError:
233 ipython_dir = self.default_config.Global.ipython_dir
234 paths = [os.getcwd(), ipython_dir] + \
235 cluster_dir_paths
236 paths = list(set(paths))
237
238 self.log.info('Searching for cluster dirs in paths: %r' % paths)
239 for path in paths:
240 files = os.listdir(path)
241 for f in files:
242 full_path = os.path.join(path, f)
243 if os.path.isdir(full_path) and f.startswith('cluster_'):
244 profile = full_path.split('_')[-1]
245 start_cmd = 'ipcluster start -p %s -n 4' % profile
246 print start_cmd + " ==> " + full_path
247
248 def pre_construct(self):
249 # IPClusterApp.pre_construct() is where we cd to the working directory.
250 super(IPClusterApp, self).pre_construct()
251 config = self.master_config
252 try:
253 daemon = config.Global.daemonize
254 if daemon:
255 config.Global.log_to_file = True
256 except AttributeError:
257 pass
258
259 def construct(self):
260 config = self.master_config
261 subcmd = config.Global.subcommand
262 reset = config.Global.reset_config
263 if subcmd == 'list':
264 return
265 if subcmd == 'create':
266 self.log.info('Copying default config files to cluster directory '
267 '[overwrite=%r]' % (reset,))
268 self.cluster_dir_obj.copy_all_config_files(overwrite=reset)
269 if subcmd =='start':
270 self.cluster_dir_obj.copy_all_config_files(overwrite=False)
271 self.start_logging()
272 reactor.callWhenRunning(self.start_launchers)
273
274 def start_launchers(self):
275 config = self.master_config
276
277 # Create the launchers. In both bases, we set the work_dir of
278 # the launcher to the cluster_dir. This is where the launcher's
279 # subprocesses will be launched. It is not where the controller
280 # and engine will be launched.
281 el_class = import_item(config.Global.engine_launcher)
282 self.engine_launcher = el_class(
283 work_dir=self.cluster_dir, config=config
284 )
285 cl_class = import_item(config.Global.controller_launcher)
286 self.controller_launcher = cl_class(
287 work_dir=self.cluster_dir, config=config
288 )
289
290 # Setup signals
291 signal.signal(signal.SIGINT, self.sigint_handler)
292
293 # Setup the observing of stopping. If the controller dies, shut
294 # everything down as that will be completely fatal for the engines.
295 d1 = self.controller_launcher.observe_stop()
296 d1.addCallback(self.stop_launchers)
297 # But, we don't monitor the stopping of engines. An engine dying
298 # is just fine and in principle a user could start a new engine.
299 # Also, if we did monitor engine stopping, it is difficult to
300 # know what to do when only some engines die. Currently, the
301 # observing of engine stopping is inconsistent. Some launchers
302 # might trigger on a single engine stopping, other wait until
303 # all stop. TODO: think more about how to handle this.
304
305 # Start the controller and engines
306 self._stopping = False # Make sure stop_launchers is not called 2x.
307 d = self.start_controller()
308 d.addCallback(self.start_engines)
309 d.addCallback(self.startup_message)
310 # If the controller or engines fail to start, stop everything
311 d.addErrback(self.stop_launchers)
312 return d
313
314 def startup_message(self, r=None):
315 log.msg("IPython cluster: started")
316 return r
317
318 def start_controller(self, r=None):
319 # log.msg("In start_controller")
320 config = self.master_config
321 d = self.controller_launcher.start(
322 cluster_dir=config.Global.cluster_dir
323 )
324 return d
325
326 def start_engines(self, r=None):
327 # log.msg("In start_engines")
328 config = self.master_config
329 d = self.engine_launcher.start(
330 config.Global.n,
331 cluster_dir=config.Global.cluster_dir
332 )
333 return d
334
335 def stop_controller(self, r=None):
336 # log.msg("In stop_controller")
337 if self.controller_launcher.running:
338 d = self.controller_launcher.stop()
339 d.addErrback(self.log_err)
340 return d
341 else:
342 return defer.succeed(None)
343
344 def stop_engines(self, r=None):
345 # log.msg("In stop_engines")
346 if self.engine_launcher.running:
347 d = self.engine_launcher.stop()
348 d.addErrback(self.log_err)
349 return d
350 else:
351 return defer.succeed(None)
352
353 def log_err(self, f):
354 log.msg(f.getTraceback())
355 return None
356
357 def stop_launchers(self, r=None):
358 if not self._stopping:
359 self._stopping = True
360 if isinstance(r, failure.Failure):
361 log.msg('Unexpected error in ipcluster:')
362 log.msg(r.getTraceback())
363 log.msg("IPython cluster: stopping")
364 self.stop_engines()
365 self.stop_controller()
366 # Wait a few seconds to let things shut down.
367 reactor.callLater(4.0, reactor.stop)
368
369 def sigint_handler(self, signum, frame):
370 self.stop_launchers()
371
372 def start_logging(self):
373 # Remove old log files of the controller and engine
374 if self.master_config.Global.clean_logs:
375 log_dir = self.master_config.Global.log_dir
376 for f in os.listdir(log_dir):
377 if f.startswith('ipengine' + '-'):
378 if f.endswith('.log') or f.endswith('.out') or f.endswith('.err'):
379 os.remove(os.path.join(log_dir, f))
380 if f.startswith('ipcontroller' + '-'):
381 if f.endswith('.log') or f.endswith('.out') or f.endswith('.err'):
382 os.remove(os.path.join(log_dir, f))
383 # This will remote old log files for ipcluster itself
384 super(IPClusterApp, self).start_logging()
385
386 def start_app(self):
387 """Start the application, depending on what subcommand is used."""
388 subcmd = self.master_config.Global.subcommand
389 if subcmd=='create' or subcmd=='list':
390 return
391 elif subcmd=='start':
392 self.start_app_start()
393 elif subcmd=='stop':
394 self.start_app_stop()
395
396 def start_app_start(self):
397 """Start the app for the start subcommand."""
398 config = self.master_config
399 # First see if the cluster is already running
400 try:
401 pid = self.get_pid_from_file()
402 except PIDFileError:
403 pass
404 else:
405 self.log.critical(
406 'Cluster is already running with [pid=%s]. '
407 'use "ipcluster stop" to stop the cluster.' % pid
408 )
409 # Here I exit with a unusual exit status that other processes
410 # can watch for to learn how I existed.
411 self.exit(ALREADY_STARTED)
412
413 # Now log and daemonize
414 self.log.info(
415 'Starting ipcluster with [daemon=%r]' % config.Global.daemonize
416 )
417 # TODO: Get daemonize working on Windows or as a Windows Server.
418 if config.Global.daemonize:
419 if os.name=='posix':
420 daemonize()
421
422 # Now write the new pid file AFTER our new forked pid is active.
423 self.write_pid_file()
424 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
425 reactor.run()
426
427 def start_app_stop(self):
428 """Start the app for the stop subcommand."""
429 config = self.master_config
430 try:
431 pid = self.get_pid_from_file()
432 except PIDFileError:
433 self.log.critical(
434 'Problem reading pid file, cluster is probably not running.'
435 )
436 # Here I exit with a unusual exit status that other processes
437 # can watch for to learn how I existed.
438 self.exit(ALREADY_STOPPED)
439 else:
440 if os.name=='posix':
441 sig = config.Global.signal
442 self.log.info(
443 "Stopping cluster [pid=%r] with [signal=%r]" % (pid, sig)
444 )
445 os.kill(pid, sig)
446 elif os.name=='nt':
447 # As of right now, we don't support daemonize on Windows, so
448 # stop will not do anything. Minimally, it should clean up the
449 # old .pid files.
450 self.remove_pid_file()
451
452 def launch_new_instance():
453 """Create and run the IPython cluster."""
454 app = IPClusterApp()
455 app.start()
456
457
458 if __name__ == '__main__':
459 launch_new_instance()
460
@@ -0,0 +1,255 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 The IPython controller application.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 from __future__ import with_statement
19
20 import copy
21 import os
22 import sys
23
24 from twisted.application import service
25 from twisted.internet import reactor
26 from twisted.python import log
27
28 from IPython.config.loader import Config, NoConfigDefault
29 from IPython.core import release
30 from IPython.core.application import Application
31 from IPython.kernel import controllerservice
32 from IPython.kernel.clusterdir import ApplicationWithClusterDir
33 from IPython.kernel.fcutil import FCServiceFactory
34 from IPython.utils.traitlets import Str, Instance, Unicode
35
36 #-----------------------------------------------------------------------------
37 # Default interfaces
38 #-----------------------------------------------------------------------------
39
40 # The default client interfaces for FCClientServiceFactory.interfaces
41 default_client_interfaces = Config()
42 default_client_interfaces.Task.interface_chain = [
43 'IPython.kernel.task.ITaskController',
44 'IPython.kernel.taskfc.IFCTaskController'
45 ]
46
47 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
48
49 default_client_interfaces.MultiEngine.interface_chain = [
50 'IPython.kernel.multiengine.IMultiEngine',
51 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
52 ]
53
54 default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
55
56 # Make this a dict we can pass to Config.__init__ for the default
57 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
58
59
60
61 # The default engine interfaces for FCEngineServiceFactory.interfaces
62 default_engine_interfaces = Config()
63 default_engine_interfaces.Default.interface_chain = [
64 'IPython.kernel.enginefc.IFCControllerBase'
65 ]
66
67 default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
68
69 # Make this a dict we can pass to Config.__init__ for the default
70 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
71
72
73 #-----------------------------------------------------------------------------
74 # Service factories
75 #-----------------------------------------------------------------------------
76
77
78 class FCClientServiceFactory(FCServiceFactory):
79 """A Foolscap implementation of the client services."""
80
81 cert_file = Unicode(u'ipcontroller-client.pem', config=True)
82 interfaces = Instance(klass=Config, kw=default_client_interfaces,
83 allow_none=False, config=True)
84
85
86 class FCEngineServiceFactory(FCServiceFactory):
87 """A Foolscap implementation of the engine services."""
88
89 cert_file = Unicode(u'ipcontroller-engine.pem', config=True)
90 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
91 allow_none=False, config=True)
92
93
94 #-----------------------------------------------------------------------------
95 # The main application
96 #-----------------------------------------------------------------------------
97
98
99 cl_args = (
100 # Client config
101 (('--client-ip',), dict(
102 type=str, dest='FCClientServiceFactory.ip',
103 help='The IP address or hostname the controller will listen on for '
104 'client connections.',
105 metavar='FCClientServiceFactory.ip')
106 ),
107 (('--client-port',), dict(
108 type=int, dest='FCClientServiceFactory.port',
109 help='The port the controller will listen on for client connections. '
110 'The default is to use 0, which will autoselect an open port.',
111 metavar='FCClientServiceFactory.port')
112 ),
113 (('--client-location',), dict(
114 type=str, dest='FCClientServiceFactory.location',
115 help='The hostname or IP that clients should connect to. This does '
116 'not control which interface the controller listens on. Instead, this '
117 'determines the hostname/IP that is listed in the FURL, which is how '
118 'clients know where to connect. Useful if the controller is listening '
119 'on multiple interfaces.',
120 metavar='FCClientServiceFactory.location')
121 ),
122 # Engine config
123 (('--engine-ip',), dict(
124 type=str, dest='FCEngineServiceFactory.ip',
125 help='The IP address or hostname the controller will listen on for '
126 'engine connections.',
127 metavar='FCEngineServiceFactory.ip')
128 ),
129 (('--engine-port',), dict(
130 type=int, dest='FCEngineServiceFactory.port',
131 help='The port the controller will listen on for engine connections. '
132 'The default is to use 0, which will autoselect an open port.',
133 metavar='FCEngineServiceFactory.port')
134 ),
135 (('--engine-location',), dict(
136 type=str, dest='FCEngineServiceFactory.location',
137 help='The hostname or IP that engines should connect to. This does '
138 'not control which interface the controller listens on. Instead, this '
139 'determines the hostname/IP that is listed in the FURL, which is how '
140 'engines know where to connect. Useful if the controller is listening '
141 'on multiple interfaces.',
142 metavar='FCEngineServiceFactory.location')
143 ),
144 # Global config
145 (('--log-to-file',), dict(
146 action='store_true', dest='Global.log_to_file',
147 help='Log to a file in the log directory (default is stdout)')
148 ),
149 (('-r','--reuse-furls'), dict(
150 action='store_true', dest='Global.reuse_furls',
151 help='Try to reuse all FURL files. If this is not set all FURL files '
152 'are deleted before the controller starts. This must be set if '
153 'specific ports are specified by --engine-port or --client-port.')
154 ),
155 (('--no-secure',), dict(
156 action='store_false', dest='Global.secure',
157 help='Turn off SSL encryption for all connections.')
158 ),
159 (('--secure',), dict(
160 action='store_true', dest='Global.secure',
161 help='Turn off SSL encryption for all connections.')
162 )
163 )
164
165
166 _description = """Start the IPython controller for parallel computing.
167
168 The IPython controller provides a gateway between the IPython engines and
169 clients. The controller needs to be started before the engines and can be
170 configured using command line options or using a cluster directory. Cluster
171 directories contain config, log and security files and are usually located in
172 your .ipython directory and named as "cluster_<profile>". See the --profile
173 and --cluster-dir options for details.
174 """
175
176 default_config_file_name = u'ipcontroller_config.py'
177
178
179 class IPControllerApp(ApplicationWithClusterDir):
180
181 name = u'ipcontroller'
182 description = _description
183 config_file_name = default_config_file_name
184 auto_create_cluster_dir = True
185 cl_arguments = Application.cl_arguments + cl_args
186
187 def create_default_config(self):
188 super(IPControllerApp, self).create_default_config()
189 self.default_config.Global.reuse_furls = False
190 self.default_config.Global.secure = True
191 self.default_config.Global.import_statements = []
192 self.default_config.Global.clean_logs = True
193
194 def post_load_command_line_config(self):
195 # Now setup reuse_furls
196 c = self.command_line_config
197 if hasattr(c.Global, 'reuse_furls'):
198 c.FCClientServiceFactory.reuse_furls = c.Global.reuse_furls
199 c.FCEngineServiceFactory.reuse_furls = c.Global.reuse_furls
200 del c.Global.reuse_furls
201 if hasattr(c.Global, 'secure'):
202 c.FCClientServiceFactory.secure = c.Global.secure
203 c.FCEngineServiceFactory.secure = c.Global.secure
204 del c.Global.secure
205
206 def construct(self):
207 # This is the working dir by now.
208 sys.path.insert(0, '')
209
210 self.start_logging()
211 self.import_statements()
212
213 # Create the service hierarchy
214 self.main_service = service.MultiService()
215 # The controller service
216 controller_service = controllerservice.ControllerService()
217 controller_service.setServiceParent(self.main_service)
218 # The client tub and all its refereceables
219 csfactory = FCClientServiceFactory(self.master_config, controller_service)
220 client_service = csfactory.create()
221 client_service.setServiceParent(self.main_service)
222 # The engine tub
223 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
224 engine_service = esfactory.create()
225 engine_service.setServiceParent(self.main_service)
226
227 def import_statements(self):
228 statements = self.master_config.Global.import_statements
229 for s in statements:
230 try:
231 log.msg("Executing statement: '%s'" % s)
232 exec s in globals(), locals()
233 except:
234 log.msg("Error running statement: %s" % s)
235
236 def start_app(self):
237 # Start the controller service.
238 self.main_service.startService()
239 # Write the .pid file overwriting old ones. This allow multiple
240 # controllers to clober each other. But Windows is not cleaning
241 # these up properly.
242 self.write_pid_file(overwrite=True)
243 # Add a trigger to delete the .pid file upon shutting down.
244 reactor.addSystemEventTrigger('during','shutdown', self.remove_pid_file)
245 reactor.run()
246
247
248 def launch_new_instance():
249 """Create and run the IPython controller"""
250 app = IPControllerApp()
251 app.start()
252
253
254 if __name__ == '__main__':
255 launch_new_instance()
@@ -0,0 +1,229 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 The IPython controller application
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import os
19 import sys
20
21 from twisted.application import service
22 from twisted.internet import reactor
23 from twisted.python import log
24
25 from IPython.core.application import Application
26 from IPython.kernel.clusterdir import ApplicationWithClusterDir
27 from IPython.kernel.engineconnector import EngineConnector
28 from IPython.kernel.engineservice import EngineService
29 from IPython.kernel.fcutil import Tub
30 from IPython.utils.importstring import import_item
31
32 #-----------------------------------------------------------------------------
33 # The main application
34 #-----------------------------------------------------------------------------
35
36 cl_args = (
37 # Controller config
38 (('--furl-file',), dict(
39 type=unicode, dest='Global.furl_file',
40 help='The full location of the file containing the FURL of the '
41 'controller. If this is not given, the FURL file must be in the '
42 'security directory of the cluster directory. This location is '
43 'resolved using the --profile and --app-dir options.',
44 metavar='Global.furl_file')
45 ),
46 # MPI
47 (('--mpi',), dict(
48 type=str, dest='MPI.use',
49 help='How to enable MPI (mpi4py, pytrilinos, or empty string to disable).',
50 metavar='MPI.use')
51 ),
52 # Global config
53 (('--log-to-file',), dict(
54 action='store_true', dest='Global.log_to_file',
55 help='Log to a file in the log directory (default is stdout)')
56 )
57 )
58
59
60 mpi4py_init = """from mpi4py import MPI as mpi
61 mpi.size = mpi.COMM_WORLD.Get_size()
62 mpi.rank = mpi.COMM_WORLD.Get_rank()
63 """
64
65 pytrilinos_init = """from PyTrilinos import Epetra
66 class SimpleStruct:
67 pass
68 mpi = SimpleStruct()
69 mpi.rank = 0
70 mpi.size = 0
71 """
72
73
74 default_config_file_name = u'ipengine_config.py'
75
76
77 _description = """Start an IPython engine for parallel computing.\n\n
78
79 IPython engines run in parallel and perform computations on behalf of a client
80 and controller. A controller needs to be started before the engines. The
81 engine can be configured using command line options or using a cluster
82 directory. Cluster directories contain config, log and security files and are
83 usually located in your .ipython directory and named as "cluster_<profile>".
84 See the --profile and --cluster-dir options for details.
85 """
86
87
88 class IPEngineApp(ApplicationWithClusterDir):
89
90 name = u'ipengine'
91 description = _description
92 config_file_name = default_config_file_name
93 auto_create_cluster_dir = True
94 cl_arguments = Application.cl_arguments + cl_args
95
96 def create_default_config(self):
97 super(IPEngineApp, self).create_default_config()
98
99 # The engine should not clean logs as we don't want to remove the
100 # active log files of other running engines.
101 self.default_config.Global.clean_logs = False
102
103 # Global config attributes
104 self.default_config.Global.exec_lines = []
105 self.default_config.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
106
107 # Configuration related to the controller
108 # This must match the filename (path not included) that the controller
109 # used for the FURL file.
110 self.default_config.Global.furl_file_name = u'ipcontroller-engine.furl'
111 # If given, this is the actual location of the controller's FURL file.
112 # If not, this is computed using the profile, app_dir and furl_file_name
113 self.default_config.Global.furl_file = u''
114
115 # The max number of connection attemps and the initial delay between
116 # those attemps.
117 self.default_config.Global.connect_delay = 0.1
118 self.default_config.Global.connect_max_tries = 15
119
120 # MPI related config attributes
121 self.default_config.MPI.use = ''
122 self.default_config.MPI.mpi4py = mpi4py_init
123 self.default_config.MPI.pytrilinos = pytrilinos_init
124
125 def post_load_command_line_config(self):
126 pass
127
128 def pre_construct(self):
129 super(IPEngineApp, self).pre_construct()
130 self.find_cont_furl_file()
131
132 def find_cont_furl_file(self):
133 """Set the furl file.
134
135 Here we don't try to actually see if it exists for is valid as that
136 is hadled by the connection logic.
137 """
138 config = self.master_config
139 # Find the actual controller FURL file
140 if not config.Global.furl_file:
141 try_this = os.path.join(
142 config.Global.cluster_dir,
143 config.Global.security_dir,
144 config.Global.furl_file_name
145 )
146 config.Global.furl_file = try_this
147
148 def construct(self):
149 # This is the working dir by now.
150 sys.path.insert(0, '')
151
152 self.start_mpi()
153 self.start_logging()
154
155 # Create the underlying shell class and EngineService
156 shell_class = import_item(self.master_config.Global.shell_class)
157 self.engine_service = EngineService(shell_class, mpi=mpi)
158
159 self.exec_lines()
160
161 # Create the service hierarchy
162 self.main_service = service.MultiService()
163 self.engine_service.setServiceParent(self.main_service)
164 self.tub_service = Tub()
165 self.tub_service.setServiceParent(self.main_service)
166 # This needs to be called before the connection is initiated
167 self.main_service.startService()
168
169 # This initiates the connection to the controller and calls
170 # register_engine to tell the controller we are ready to do work
171 self.engine_connector = EngineConnector(self.tub_service)
172
173 log.msg("Using furl file: %s" % self.master_config.Global.furl_file)
174
175 reactor.callWhenRunning(self.call_connect)
176
177 def call_connect(self):
178 d = self.engine_connector.connect_to_controller(
179 self.engine_service,
180 self.master_config.Global.furl_file,
181 self.master_config.Global.connect_delay,
182 self.master_config.Global.connect_max_tries
183 )
184
185 def handle_error(f):
186 log.msg('Error connecting to controller. This usually means that '
187 'i) the controller was not started, ii) a firewall was blocking '
188 'the engine from connecting to the controller or iii) the engine '
189 ' was not pointed at the right FURL file:')
190 log.msg(f.getErrorMessage())
191 reactor.callLater(0.1, reactor.stop)
192
193 d.addErrback(handle_error)
194
195 def start_mpi(self):
196 global mpi
197 mpikey = self.master_config.MPI.use
198 mpi_import_statement = self.master_config.MPI.get(mpikey, None)
199 if mpi_import_statement is not None:
200 try:
201 self.log.info("Initializing MPI:")
202 self.log.info(mpi_import_statement)
203 exec mpi_import_statement in globals()
204 except:
205 mpi = None
206 else:
207 mpi = None
208
209 def exec_lines(self):
210 for line in self.master_config.Global.exec_lines:
211 try:
212 log.msg("Executing statement: '%s'" % line)
213 self.engine_service.execute(line)
214 except:
215 log.msg("Error executing statement: %s" % line)
216
217 def start_app(self):
218 reactor.run()
219
220
221 def launch_new_instance():
222 """Create and run the IPython controller"""
223 app = IPEngineApp()
224 app.start()
225
226
227 if __name__ == '__main__':
228 launch_new_instance()
229
This diff has been collapsed as it changes many lines, (869 lines changed) Show them Hide them
@@ -0,0 +1,869 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Facilities for launching IPython processes asynchronously.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import os
19 import re
20 import sys
21
22 from IPython.core.component import Component
23 from IPython.external import Itpl
24 from IPython.utils.traitlets import Str, Int, List, Unicode, Enum
25 from IPython.utils.platutils import find_cmd
26 from IPython.kernel.twistedutil import gatherBoth, make_deferred, sleep_deferred
27 from IPython.kernel.winhpcjob import (
28 WinHPCJob, WinHPCTask,
29 IPControllerTask, IPEngineTask,
30 IPControllerJob, IPEngineSetJob
31 )
32
33 from twisted.internet import reactor, defer
34 from twisted.internet.defer import inlineCallbacks
35 from twisted.internet.protocol import ProcessProtocol
36 from twisted.internet.utils import getProcessOutput
37 from twisted.internet.error import ProcessDone, ProcessTerminated
38 from twisted.python import log
39 from twisted.python.failure import Failure
40
41 #-----------------------------------------------------------------------------
42 # Utilities
43 #-----------------------------------------------------------------------------
44
45
46 def find_controller_cmd():
47 """Find the command line ipcontroller program in a cross platform way."""
48 if sys.platform == 'win32':
49 # This logic is needed because the ipcontroller script doesn't
50 # always get installed in the same way or in the same location.
51 from IPython.kernel import ipcontrollerapp
52 script_location = ipcontrollerapp.__file__.replace('.pyc', '.py')
53 # The -u option here turns on unbuffered output, which is required
54 # on Win32 to prevent wierd conflict and problems with Twisted.
55 # Also, use sys.executable to make sure we are picking up the
56 # right python exe.
57 cmd = [sys.executable, '-u', script_location]
58 else:
59 # ipcontroller has to be on the PATH in this case.
60 cmd = ['ipcontroller']
61 return cmd
62
63
64 def find_engine_cmd():
65 """Find the command line ipengine program in a cross platform way."""
66 if sys.platform == 'win32':
67 # This logic is needed because the ipengine script doesn't
68 # always get installed in the same way or in the same location.
69 from IPython.kernel import ipengineapp
70 script_location = ipengineapp.__file__.replace('.pyc', '.py')
71 # The -u option here turns on unbuffered output, which is required
72 # on Win32 to prevent wierd conflict and problems with Twisted.
73 # Also, use sys.executable to make sure we are picking up the
74 # right python exe.
75 cmd = [sys.executable, '-u', script_location]
76 else:
77 # ipcontroller has to be on the PATH in this case.
78 cmd = ['ipengine']
79 return cmd
80
81
82 #-----------------------------------------------------------------------------
83 # Base launchers and errors
84 #-----------------------------------------------------------------------------
85
86
87 class LauncherError(Exception):
88 pass
89
90
91 class ProcessStateError(LauncherError):
92 pass
93
94
95 class UnknownStatus(LauncherError):
96 pass
97
98
99 class BaseLauncher(Component):
100 """An asbtraction for starting, stopping and signaling a process."""
101
102 # In all of the launchers, the work_dir is where child processes will be
103 # run. This will usually be the cluster_dir, but may not be. any work_dir
104 # passed into the __init__ method will override the config value.
105 # This should not be used to set the work_dir for the actual engine
106 # and controller. Instead, use their own config files or the
107 # controller_args, engine_args attributes of the launchers to add
108 # the --work-dir option.
109 work_dir = Unicode(u'')
110
111 def __init__(self, work_dir, parent=None, name=None, config=None):
112 super(BaseLauncher, self).__init__(parent, name, config)
113 self.work_dir = work_dir
114 self.state = 'before' # can be before, running, after
115 self.stop_deferreds = []
116 self.start_data = None
117 self.stop_data = None
118
119 @property
120 def args(self):
121 """A list of cmd and args that will be used to start the process.
122
123 This is what is passed to :func:`spawnProcess` and the first element
124 will be the process name.
125 """
126 return self.find_args()
127
128 def find_args(self):
129 """The ``.args`` property calls this to find the args list.
130
131 Subcommand should implement this to construct the cmd and args.
132 """
133 raise NotImplementedError('find_args must be implemented in a subclass')
134
135 @property
136 def arg_str(self):
137 """The string form of the program arguments."""
138 return ' '.join(self.args)
139
140 @property
141 def running(self):
142 """Am I running."""
143 if self.state == 'running':
144 return True
145 else:
146 return False
147
148 def start(self):
149 """Start the process.
150
151 This must return a deferred that fires with information about the
152 process starting (like a pid, job id, etc.).
153 """
154 return defer.fail(
155 Failure(NotImplementedError(
156 'start must be implemented in a subclass')
157 )
158 )
159
160 def stop(self):
161 """Stop the process and notify observers of stopping.
162
163 This must return a deferred that fires with information about the
164 processing stopping, like errors that occur while the process is
165 attempting to be shut down. This deferred won't fire when the process
166 actually stops. To observe the actual process stopping, see
167 :func:`observe_stop`.
168 """
169 return defer.fail(
170 Failure(NotImplementedError(
171 'stop must be implemented in a subclass')
172 )
173 )
174
175 def observe_stop(self):
176 """Get a deferred that will fire when the process stops.
177
178 The deferred will fire with data that contains information about
179 the exit status of the process.
180 """
181 if self.state=='after':
182 return defer.succeed(self.stop_data)
183 else:
184 d = defer.Deferred()
185 self.stop_deferreds.append(d)
186 return d
187
188 def notify_start(self, data):
189 """Call this to trigger startup actions.
190
191 This logs the process startup and sets the state to 'running'. It is
192 a pass-through so it can be used as a callback.
193 """
194
195 log.msg('Process %r started: %r' % (self.args[0], data))
196 self.start_data = data
197 self.state = 'running'
198 return data
199
200 def notify_stop(self, data):
201 """Call this to trigger process stop actions.
202
203 This logs the process stopping and sets the state to 'after'. Call
204 this to trigger all the deferreds from :func:`observe_stop`."""
205
206 log.msg('Process %r stopped: %r' % (self.args[0], data))
207 self.stop_data = data
208 self.state = 'after'
209 for i in range(len(self.stop_deferreds)):
210 d = self.stop_deferreds.pop()
211 d.callback(data)
212 return data
213
214 def signal(self, sig):
215 """Signal the process.
216
217 Return a semi-meaningless deferred after signaling the process.
218
219 Parameters
220 ----------
221 sig : str or int
222 'KILL', 'INT', etc., or any signal number
223 """
224 return defer.fail(
225 Failure(NotImplementedError(
226 'signal must be implemented in a subclass')
227 )
228 )
229
230
231 #-----------------------------------------------------------------------------
232 # Local process launchers
233 #-----------------------------------------------------------------------------
234
235
236 class LocalProcessLauncherProtocol(ProcessProtocol):
237 """A ProcessProtocol to go with the LocalProcessLauncher."""
238
239 def __init__(self, process_launcher):
240 self.process_launcher = process_launcher
241 self.pid = None
242
243 def connectionMade(self):
244 self.pid = self.transport.pid
245 self.process_launcher.notify_start(self.transport.pid)
246
247 def processEnded(self, status):
248 value = status.value
249 if isinstance(value, ProcessDone):
250 self.process_launcher.notify_stop(
251 {'exit_code':0,
252 'signal':None,
253 'status':None,
254 'pid':self.pid
255 }
256 )
257 elif isinstance(value, ProcessTerminated):
258 self.process_launcher.notify_stop(
259 {'exit_code':value.exitCode,
260 'signal':value.signal,
261 'status':value.status,
262 'pid':self.pid
263 }
264 )
265 else:
266 raise UnknownStatus("Unknown exit status, this is probably a "
267 "bug in Twisted")
268
269 def outReceived(self, data):
270 log.msg(data)
271
272 def errReceived(self, data):
273 log.err(data)
274
275
276 class LocalProcessLauncher(BaseLauncher):
277 """Start and stop an external process in an asynchronous manner.
278
279 This will launch the external process with a working directory of
280 ``self.work_dir``.
281 """
282
283 # This is used to to construct self.args, which is passed to
284 # spawnProcess.
285 cmd_and_args = List([])
286
287 def __init__(self, work_dir, parent=None, name=None, config=None):
288 super(LocalProcessLauncher, self).__init__(
289 work_dir, parent, name, config
290 )
291 self.process_protocol = None
292 self.start_deferred = None
293
294 def find_args(self):
295 return self.cmd_and_args
296
297 def start(self):
298 if self.state == 'before':
299 self.process_protocol = LocalProcessLauncherProtocol(self)
300 self.start_deferred = defer.Deferred()
301 self.process_transport = reactor.spawnProcess(
302 self.process_protocol,
303 str(self.args[0]), # twisted expects these to be str, not unicode
304 [str(a) for a in self.args], # str expected, not unicode
305 env=os.environ,
306 path=self.work_dir # start in the work_dir
307 )
308 return self.start_deferred
309 else:
310 s = 'The process was already started and has state: %r' % self.state
311 return defer.fail(ProcessStateError(s))
312
313 def notify_start(self, data):
314 super(LocalProcessLauncher, self).notify_start(data)
315 self.start_deferred.callback(data)
316
317 def stop(self):
318 return self.interrupt_then_kill()
319
320 @make_deferred
321 def signal(self, sig):
322 if self.state == 'running':
323 self.process_transport.signalProcess(sig)
324
325 @inlineCallbacks
326 def interrupt_then_kill(self, delay=2.0):
327 """Send INT, wait a delay and then send KILL."""
328 yield self.signal('INT')
329 yield sleep_deferred(delay)
330 yield self.signal('KILL')
331
332
333 class LocalControllerLauncher(LocalProcessLauncher):
334 """Launch a controller as a regular external process."""
335
336 controller_cmd = List(find_controller_cmd(), config=True)
337 # Command line arguments to ipcontroller.
338 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
339
340 def find_args(self):
341 return self.controller_cmd + self.controller_args
342
343 def start(self, cluster_dir):
344 """Start the controller by cluster_dir."""
345 self.controller_args.extend(['--cluster-dir', cluster_dir])
346 self.cluster_dir = unicode(cluster_dir)
347 log.msg("Starting LocalControllerLauncher: %r" % self.args)
348 return super(LocalControllerLauncher, self).start()
349
350
351 class LocalEngineLauncher(LocalProcessLauncher):
352 """Launch a single engine as a regular externall process."""
353
354 engine_cmd = List(find_engine_cmd(), config=True)
355 # Command line arguments for ipengine.
356 engine_args = List(
357 ['--log-to-file','--log-level', '40'], config=True
358 )
359
360 def find_args(self):
361 return self.engine_cmd + self.engine_args
362
363 def start(self, cluster_dir):
364 """Start the engine by cluster_dir."""
365 self.engine_args.extend(['--cluster-dir', cluster_dir])
366 self.cluster_dir = unicode(cluster_dir)
367 return super(LocalEngineLauncher, self).start()
368
369
370 class LocalEngineSetLauncher(BaseLauncher):
371 """Launch a set of engines as regular external processes."""
372
373 # Command line arguments for ipengine.
374 engine_args = List(
375 ['--log-to-file','--log-level', '40'], config=True
376 )
377
378 def __init__(self, work_dir, parent=None, name=None, config=None):
379 super(LocalEngineSetLauncher, self).__init__(
380 work_dir, parent, name, config
381 )
382 self.launchers = []
383
384 def start(self, n, cluster_dir):
385 """Start n engines by profile or cluster_dir."""
386 self.cluster_dir = unicode(cluster_dir)
387 dlist = []
388 for i in range(n):
389 el = LocalEngineLauncher(self.work_dir, self)
390 # Copy the engine args over to each engine launcher.
391 import copy
392 el.engine_args = copy.deepcopy(self.engine_args)
393 d = el.start(cluster_dir)
394 if i==0:
395 log.msg("Starting LocalEngineSetLauncher: %r" % el.args)
396 self.launchers.append(el)
397 dlist.append(d)
398 # The consumeErrors here could be dangerous
399 dfinal = gatherBoth(dlist, consumeErrors=True)
400 dfinal.addCallback(self.notify_start)
401 return dfinal
402
403 def find_args(self):
404 return ['engine set']
405
406 def signal(self, sig):
407 dlist = []
408 for el in self.launchers:
409 d = el.signal(sig)
410 dlist.append(d)
411 dfinal = gatherBoth(dlist, consumeErrors=True)
412 return dfinal
413
414 def interrupt_then_kill(self, delay=1.0):
415 dlist = []
416 for el in self.launchers:
417 d = el.interrupt_then_kill(delay)
418 dlist.append(d)
419 dfinal = gatherBoth(dlist, consumeErrors=True)
420 return dfinal
421
422 def stop(self):
423 return self.interrupt_then_kill()
424
425 def observe_stop(self):
426 dlist = [el.observe_stop() for el in self.launchers]
427 dfinal = gatherBoth(dlist, consumeErrors=False)
428 dfinal.addCallback(self.notify_stop)
429 return dfinal
430
431
432 #-----------------------------------------------------------------------------
433 # MPIExec launchers
434 #-----------------------------------------------------------------------------
435
436
437 class MPIExecLauncher(LocalProcessLauncher):
438 """Launch an external process using mpiexec."""
439
440 # The mpiexec command to use in starting the process.
441 mpi_cmd = List(['mpiexec'], config=True)
442 # The command line arguments to pass to mpiexec.
443 mpi_args = List([], config=True)
444 # The program to start using mpiexec.
445 program = List(['date'], config=True)
446 # The command line argument to the program.
447 program_args = List([], config=True)
448 # The number of instances of the program to start.
449 n = Int(1, config=True)
450
451 def find_args(self):
452 """Build self.args using all the fields."""
453 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
454 self.program + self.program_args
455
456 def start(self, n):
457 """Start n instances of the program using mpiexec."""
458 self.n = n
459 return super(MPIExecLauncher, self).start()
460
461
462 class MPIExecControllerLauncher(MPIExecLauncher):
463 """Launch a controller using mpiexec."""
464
465 controller_cmd = List(find_controller_cmd(), config=True)
466 # Command line arguments to ipcontroller.
467 controller_args = List(['--log-to-file','--log-level', '40'], config=True)
468 n = Int(1, config=False)
469
470 def start(self, cluster_dir):
471 """Start the controller by cluster_dir."""
472 self.controller_args.extend(['--cluster-dir', cluster_dir])
473 self.cluster_dir = unicode(cluster_dir)
474 log.msg("Starting MPIExecControllerLauncher: %r" % self.args)
475 return super(MPIExecControllerLauncher, self).start(1)
476
477 def find_args(self):
478 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
479 self.controller_cmd + self.controller_args
480
481
482 class MPIExecEngineSetLauncher(MPIExecLauncher):
483
484 engine_cmd = List(find_engine_cmd(), config=True)
485 # Command line arguments for ipengine.
486 engine_args = List(
487 ['--log-to-file','--log-level', '40'], config=True
488 )
489 n = Int(1, config=True)
490
491 def start(self, n, cluster_dir):
492 """Start n engines by profile or cluster_dir."""
493 self.engine_args.extend(['--cluster-dir', cluster_dir])
494 self.cluster_dir = unicode(cluster_dir)
495 self.n = n
496 log.msg('Starting MPIExecEngineSetLauncher: %r' % self.args)
497 return super(MPIExecEngineSetLauncher, self).start(n)
498
499 def find_args(self):
500 return self.mpi_cmd + ['-n', self.n] + self.mpi_args + \
501 self.engine_cmd + self.engine_args
502
503
504 #-----------------------------------------------------------------------------
505 # SSH launchers
506 #-----------------------------------------------------------------------------
507
508 # TODO: Get SSH Launcher working again.
509
510 class SSHLauncher(BaseLauncher):
511 """A minimal launcher for ssh.
512
513 To be useful this will probably have to be extended to use the ``sshx``
514 idea for environment variables. There could be other things this needs
515 as well.
516 """
517
518 ssh_cmd = List(['ssh'], config=True)
519 ssh_args = List([], config=True)
520 program = List(['date'], config=True)
521 program_args = List([], config=True)
522 hostname = Str('', config=True)
523 user = Str('', config=True)
524 location = Str('')
525
526 def _hostname_changed(self, name, old, new):
527 self.location = '%s@%s' % (self.user, new)
528
529 def _user_changed(self, name, old, new):
530 self.location = '%s@%s' % (new, self.hostname)
531
532 def find_args(self):
533 return self.ssh_cmd + self.ssh_args + [self.location] + \
534 self.program + self.program_args
535
536 def start(self, n, hostname=None, user=None):
537 if hostname is not None:
538 self.hostname = hostname
539 if user is not None:
540 self.user = user
541 return super(SSHLauncher, self).start()
542
543
544 class SSHControllerLauncher(SSHLauncher):
545 pass
546
547
548 class SSHEngineSetLauncher(BaseLauncher):
549 pass
550
551
552 #-----------------------------------------------------------------------------
553 # Windows HPC Server 2008 scheduler launchers
554 #-----------------------------------------------------------------------------
555
556
557 # This is only used on Windows.
558 def find_job_cmd():
559 if os.name=='nt':
560 return find_cmd('job')
561 else:
562 return 'job'
563
564
565 class WindowsHPCLauncher(BaseLauncher):
566
567 # A regular expression used to get the job id from the output of the
568 # submit_command.
569 job_id_regexp = Str(r'\d+', config=True)
570 # The filename of the instantiated job script.
571 job_file_name = Unicode(u'ipython_job.xml', config=True)
572 # The full path to the instantiated job script. This gets made dynamically
573 # by combining the work_dir with the job_file_name.
574 job_file = Unicode(u'')
575 # The hostname of the scheduler to submit the job to
576 scheduler = Str('', config=True)
577 job_cmd = Str(find_job_cmd(), config=True)
578
579 def __init__(self, work_dir, parent=None, name=None, config=None):
580 super(WindowsHPCLauncher, self).__init__(
581 work_dir, parent, name, config
582 )
583
584 @property
585 def job_file(self):
586 return os.path.join(self.work_dir, self.job_file_name)
587
588 def write_job_file(self, n):
589 raise NotImplementedError("Implement write_job_file in a subclass.")
590
591 def find_args(self):
592 return ['job.exe']
593
594 def parse_job_id(self, output):
595 """Take the output of the submit command and return the job id."""
596 m = re.search(self.job_id_regexp, output)
597 if m is not None:
598 job_id = m.group()
599 else:
600 raise LauncherError("Job id couldn't be determined: %s" % output)
601 self.job_id = job_id
602 log.msg('Job started with job id: %r' % job_id)
603 return job_id
604
605 @inlineCallbacks
606 def start(self, n):
607 """Start n copies of the process using the Win HPC job scheduler."""
608 self.write_job_file(n)
609 args = [
610 'submit',
611 '/jobfile:%s' % self.job_file,
612 '/scheduler:%s' % self.scheduler
613 ]
614 log.msg("Starting Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
615 # Twisted will raise DeprecationWarnings if we try to pass unicode to this
616 output = yield getProcessOutput(str(self.job_cmd),
617 [str(a) for a in args],
618 env=dict((str(k),str(v)) for k,v in os.environ.items()),
619 path=self.work_dir
620 )
621 job_id = self.parse_job_id(output)
622 self.notify_start(job_id)
623 defer.returnValue(job_id)
624
625 @inlineCallbacks
626 def stop(self):
627 args = [
628 'cancel',
629 self.job_id,
630 '/scheduler:%s' % self.scheduler
631 ]
632 log.msg("Stopping Win HPC Job: %s" % (self.job_cmd + ' ' + ' '.join(args),))
633 try:
634 # Twisted will raise DeprecationWarnings if we try to pass unicode to this
635 output = yield getProcessOutput(str(self.job_cmd),
636 [str(a) for a in args],
637 env=dict((str(k),str(v)) for k,v in os.environ.items()),
638 path=self.work_dir
639 )
640 except:
641 output = 'The job already appears to be stoppped: %r' % self.job_id
642 self.notify_stop(output) # Pass the output of the kill cmd
643 defer.returnValue(output)
644
645
646 class WindowsHPCControllerLauncher(WindowsHPCLauncher):
647
648 job_file_name = Unicode(u'ipcontroller_job.xml', config=True)
649 extra_args = List([], config=False)
650
651 def write_job_file(self, n):
652 job = IPControllerJob(self)
653
654 t = IPControllerTask(self)
655 # The tasks work directory is *not* the actual work directory of
656 # the controller. It is used as the base path for the stdout/stderr
657 # files that the scheduler redirects to.
658 t.work_directory = self.cluster_dir
659 # Add the --cluster-dir and from self.start().
660 t.controller_args.extend(self.extra_args)
661 job.add_task(t)
662
663 log.msg("Writing job description file: %s" % self.job_file)
664 job.write(self.job_file)
665
666 @property
667 def job_file(self):
668 return os.path.join(self.cluster_dir, self.job_file_name)
669
670 def start(self, cluster_dir):
671 """Start the controller by cluster_dir."""
672 self.extra_args = ['--cluster-dir', cluster_dir]
673 self.cluster_dir = unicode(cluster_dir)
674 return super(WindowsHPCControllerLauncher, self).start(1)
675
676
677 class WindowsHPCEngineSetLauncher(WindowsHPCLauncher):
678
679 job_file_name = Unicode(u'ipengineset_job.xml', config=True)
680 extra_args = List([], config=False)
681
682 def write_job_file(self, n):
683 job = IPEngineSetJob(self)
684
685 for i in range(n):
686 t = IPEngineTask(self)
687 # The tasks work directory is *not* the actual work directory of
688 # the engine. It is used as the base path for the stdout/stderr
689 # files that the scheduler redirects to.
690 t.work_directory = self.cluster_dir
691 # Add the --cluster-dir and from self.start().
692 t.engine_args.extend(self.extra_args)
693 job.add_task(t)
694
695 log.msg("Writing job description file: %s" % self.job_file)
696 job.write(self.job_file)
697
698 @property
699 def job_file(self):
700 return os.path.join(self.cluster_dir, self.job_file_name)
701
702 def start(self, n, cluster_dir):
703 """Start the controller by cluster_dir."""
704 self.extra_args = ['--cluster-dir', cluster_dir]
705 self.cluster_dir = unicode(cluster_dir)
706 return super(WindowsHPCEngineSetLauncher, self).start(n)
707
708
709 #-----------------------------------------------------------------------------
710 # Batch (PBS) system launchers
711 #-----------------------------------------------------------------------------
712
713 # TODO: Get PBS launcher working again.
714
715 class BatchSystemLauncher(BaseLauncher):
716 """Launch an external process using a batch system.
717
718 This class is designed to work with UNIX batch systems like PBS, LSF,
719 GridEngine, etc. The overall model is that there are different commands
720 like qsub, qdel, etc. that handle the starting and stopping of the process.
721
722 This class also has the notion of a batch script. The ``batch_template``
723 attribute can be set to a string that is a template for the batch script.
724 This template is instantiated using Itpl. Thus the template can use
725 ${n} fot the number of instances. Subclasses can add additional variables
726 to the template dict.
727 """
728
729 # Subclasses must fill these in. See PBSEngineSet
730 # The name of the command line program used to submit jobs.
731 submit_command = Str('', config=True)
732 # The name of the command line program used to delete jobs.
733 delete_command = Str('', config=True)
734 # A regular expression used to get the job id from the output of the
735 # submit_command.
736 job_id_regexp = Str('', config=True)
737 # The string that is the batch script template itself.
738 batch_template = Str('', config=True)
739 # The filename of the instantiated batch script.
740 batch_file_name = Unicode(u'batch_script', config=True)
741 # The full path to the instantiated batch script.
742 batch_file = Unicode(u'')
743
744 def __init__(self, work_dir, parent=None, name=None, config=None):
745 super(BatchSystemLauncher, self).__init__(
746 work_dir, parent, name, config
747 )
748 self.batch_file = os.path.join(self.work_dir, self.batch_file_name)
749 self.context = {}
750
751 def parse_job_id(self, output):
752 """Take the output of the submit command and return the job id."""
753 m = re.match(self.job_id_regexp, output)
754 if m is not None:
755 job_id = m.group()
756 else:
757 raise LauncherError("Job id couldn't be determined: %s" % output)
758 self.job_id = job_id
759 log.msg('Job started with job id: %r' % job_id)
760 return job_id
761
762 def write_batch_script(self, n):
763 """Instantiate and write the batch script to the work_dir."""
764 self.context['n'] = n
765 script_as_string = Itpl.itplns(self.batch_template, self.context)
766 log.msg('Writing instantiated batch script: %s' % self.batch_file)
767 f = open(self.batch_file, 'w')
768 f.write(script_as_string)
769 f.close()
770
771 @inlineCallbacks
772 def start(self, n):
773 """Start n copies of the process using a batch system."""
774 self.write_batch_script(n)
775 output = yield getProcessOutput(self.submit_command,
776 [self.batch_file], env=os.environ)
777 job_id = self.parse_job_id(output)
778 self.notify_start(job_id)
779 defer.returnValue(job_id)
780
781 @inlineCallbacks
782 def stop(self):
783 output = yield getProcessOutput(self.delete_command,
784 [self.job_id], env=os.environ
785 )
786 self.notify_stop(output) # Pass the output of the kill cmd
787 defer.returnValue(output)
788
789
790 class PBSLauncher(BatchSystemLauncher):
791 """A BatchSystemLauncher subclass for PBS."""
792
793 submit_command = Str('qsub', config=True)
794 delete_command = Str('qdel', config=True)
795 job_id_regexp = Str(r'\d+', config=True)
796 batch_template = Str('', config=True)
797 batch_file_name = Unicode(u'pbs_batch_script', config=True)
798 batch_file = Unicode(u'')
799
800
801 class PBSControllerLauncher(PBSLauncher):
802 """Launch a controller using PBS."""
803
804 batch_file_name = Unicode(u'pbs_batch_script_controller', config=True)
805
806 def start(self, cluster_dir):
807 """Start the controller by profile or cluster_dir."""
808 # Here we save profile and cluster_dir in the context so they
809 # can be used in the batch script template as ${profile} and
810 # ${cluster_dir}
811 self.context['cluster_dir'] = cluster_dir
812 self.cluster_dir = unicode(cluster_dir)
813 log.msg("Starting PBSControllerLauncher: %r" % self.args)
814 return super(PBSControllerLauncher, self).start(1)
815
816
817 class PBSEngineSetLauncher(PBSLauncher):
818
819 batch_file_name = Unicode(u'pbs_batch_script_engines', config=True)
820
821 def start(self, n, cluster_dir):
822 """Start n engines by profile or cluster_dir."""
823 self.program_args.extend(['--cluster-dir', cluster_dir])
824 self.cluster_dir = unicode(cluster_dir)
825 log.msg('Starting PBSEngineSetLauncher: %r' % self.args)
826 return super(PBSEngineSetLauncher, self).start(n)
827
828
829 #-----------------------------------------------------------------------------
830 # A launcher for ipcluster itself!
831 #-----------------------------------------------------------------------------
832
833
834 def find_ipcluster_cmd():
835 """Find the command line ipcluster program in a cross platform way."""
836 if sys.platform == 'win32':
837 # This logic is needed because the ipcluster script doesn't
838 # always get installed in the same way or in the same location.
839 from IPython.kernel import ipclusterapp
840 script_location = ipclusterapp.__file__.replace('.pyc', '.py')
841 # The -u option here turns on unbuffered output, which is required
842 # on Win32 to prevent wierd conflict and problems with Twisted.
843 # Also, use sys.executable to make sure we are picking up the
844 # right python exe.
845 cmd = [sys.executable, '-u', script_location]
846 else:
847 # ipcontroller has to be on the PATH in this case.
848 cmd = ['ipcluster']
849 return cmd
850
851
852 class IPClusterLauncher(LocalProcessLauncher):
853 """Launch the ipcluster program in an external process."""
854
855 ipcluster_cmd = List(find_ipcluster_cmd(), config=True)
856 # Command line arguments to pass to ipcluster.
857 ipcluster_args = List(
858 ['--clean-logs', '--log-to-file', '--log-level', '40'], config=True)
859 ipcluster_subcommand = Str('start')
860 ipcluster_n = Int(2)
861
862 def find_args(self):
863 return self.ipcluster_cmd + [self.ipcluster_subcommand] + \
864 ['-n', repr(self.ipcluster_n)] + self.ipcluster_args
865
866 def start(self):
867 log.msg("Starting ipcluster: %r" % self.args)
868 return super(IPClusterLauncher, self).start()
869
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -16,18 +16,17 b' IPython is a set of tools for interactive and exploratory computing in Python.'
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 from __future__ import absolute_import
19
20
20 import os
21 import os
21 import sys
22 import sys
22 from IPython.core import release
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Setup everything
25 # Setup everything
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28
28 if sys.version[0:3] < '2.5':
29 if sys.version[0:3] < '2.4':
29 raise ImportError('Python Version 2.5 or above is required for IPython.')
30 raise ImportError('Python Version 2.4 or above is required for IPython.')
31
30
32
31
33 # Make it easy to import extensions - they are always directly on pythonpath.
32 # Make it easy to import extensions - they are always directly on pythonpath.
@@ -39,11 +38,16 b' sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))'
39 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
40
39
41 # In some cases, these are causing circular imports.
40 # In some cases, these are causing circular imports.
42 from IPython.core.iplib import InteractiveShell
41 from .config.loader import Config
43 from IPython.core.embed import embed
42 from .core import release
44 from IPython.core.error import TryNext
43 from .core.application import Application
44 from .core.ipapp import IPythonApp
45 from .core.embed import embed
46 from .core.error import TryNext
47 from .core.iplib import InteractiveShell
48 from .testing import test
45
49
46 from IPython.lib import (
50 from .lib import (
47 enable_wx, disable_wx,
51 enable_wx, disable_wx,
48 enable_gtk, disable_gtk,
52 enable_gtk, disable_gtk,
49 enable_qt4, disable_qt4,
53 enable_qt4, disable_qt4,
@@ -61,4 +65,3 b' for author, email in release.authors.values():'
61 __license__ = release.license
65 __license__ = release.license
62 __version__ = release.version
66 __version__ = release.version
63 __revision__ = release.revision
67 __revision__ = release.revision
64
@@ -13,7 +13,7 b' c = get_config()'
13
13
14 # Set this to determine the detail of what is logged at startup.
14 # Set this to determine the detail of what is logged at startup.
15 # The default is 30 and possible values are 0,10,20,30,40,50.
15 # The default is 30 and possible values are 0,10,20,30,40,50.
16 c.Global.log_level = 20
16 # c.Global.log_level = 20
17
17
18 # This should be a list of importable Python modules that have an
18 # This should be a list of importable Python modules that have an
19 # load_in_ipython(ip) method. This method gets called when the extension
19 # load_in_ipython(ip) method. This method gets called when the extension
@@ -35,7 +35,7 b' c.Global.log_level = 20'
35 # These files are run in IPython in the user's namespace. Files with a .py
35 # These files are run in IPython in the user's namespace. Files with a .py
36 # extension need to be pure Python. Files with a .ipy extension can have
36 # extension need to be pure Python. Files with a .ipy extension can have
37 # custom IPython syntax (like magics, etc.).
37 # custom IPython syntax (like magics, etc.).
38 # These files need to be in the cwd, the ipythondir or be absolute paths.
38 # These files need to be in the cwd, the ipython_dir or be absolute paths.
39 # c.Global.exec_files = [
39 # c.Global.exec_files = [
40 # 'mycode.py',
40 # 'mycode.py',
41 # 'fancy.ipy'
41 # 'fancy.ipy'
@@ -71,9 +71,9 b' c.Global.log_level = 20'
71
71
72 # c.InteractiveShell.logstart = True
72 # c.InteractiveShell.logstart = True
73
73
74 # c.InteractiveShell.logfile = 'ipython_log.py'
74 # c.InteractiveShell.logfile = u'ipython_log.py'
75
75
76 # c.InteractiveShell.logappend = 'mylog.py'
76 # c.InteractiveShell.logappend = u'mylog.py'
77
77
78 # c.InteractiveShell.object_info_string_level = 0
78 # c.InteractiveShell.object_info_string_level = 0
79
79
@@ -1,10 +1,10 b''
1 #!/usr/bin/env python
1 # coding: utf-8
2 # encoding: utf-8
3 """A simple configuration system.
2 """A simple configuration system.
4
3
5 Authors:
4 Authors
6
5 -------
7 * Brian Granger
6 * Brian Granger
7 * Fernando Perez
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
@@ -37,7 +37,25 b' class ConfigError(Exception):'
37 class ConfigLoaderError(ConfigError):
37 class ConfigLoaderError(ConfigError):
38 pass
38 pass
39
39
40
40 #-----------------------------------------------------------------------------
41 # Argparse fix
42 #-----------------------------------------------------------------------------
43 # Unfortunately argparse by default prints help messages to stderr instead of
44 # stdout. This makes it annoying to capture long help screens at the command
45 # line, since one must know how to pipe stderr, which many users don't know how
46 # to do. So we override the print_help method with one that defaults to
47 # stdout and use our class instead.
48
49 class ArgumentParser(argparse.ArgumentParser):
50 """Simple argparse subclass that prints help to stdout by default."""
51
52 def print_help(self, file=None):
53 if file is None:
54 file = sys.stdout
55 return super(ArgumentParser, self).print_help(file)
56
57 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
58
41 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
42 # Config class for holding config information
60 # Config class for holding config information
43 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
@@ -244,8 +262,14 b' class PyFileConfigLoader(FileConfigLoader):'
244 # with the parents.
262 # with the parents.
245 def load_subconfig(fname):
263 def load_subconfig(fname):
246 loader = PyFileConfigLoader(fname, self.path)
264 loader = PyFileConfigLoader(fname, self.path)
247 sub_config = loader.load_config()
265 try:
248 self.config._merge(sub_config)
266 sub_config = loader.load_config()
267 except IOError:
268 # Pass silently if the sub config is not there. This happens
269 # when a user us using a profile, but not the default config.
270 pass
271 else:
272 self.config._merge(sub_config)
249
273
250 # Again, this needs to be a closure and should be used in config
274 # Again, this needs to be a closure and should be used in config
251 # files to get the config being loaded.
275 # files to get the config being loaded.
@@ -268,26 +292,55 b' class CommandLineConfigLoader(ConfigLoader):'
268 """
292 """
269
293
270
294
271 class NoConfigDefault(object): pass
295 class __NoConfigDefault(object): pass
272 NoConfigDefault = NoConfigDefault()
296 NoConfigDefault = __NoConfigDefault()
297
273
298
274 class ArgParseConfigLoader(CommandLineConfigLoader):
299 class ArgParseConfigLoader(CommandLineConfigLoader):
300 #: Global default for arguments (see argparse docs for details)
301 argument_default = NoConfigDefault
275
302
276 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
303 def __init__(self, argv=None, arguments=(), *args, **kw):
277 arguments = ()
278
279 def __init__(self, *args, **kw):
280 """Create a config loader for use with argparse.
304 """Create a config loader for use with argparse.
281
305
282 The args and kwargs arguments here are passed onto the constructor
306 With the exception of ``argv`` and ``arguments``, other args and kwargs
283 of :class:`argparse.ArgumentParser`.
307 arguments here are passed onto the constructor of
308 :class:`argparse.ArgumentParser`.
309
310 Parameters
311 ----------
312
313 argv : optional, list
314 If given, used to read command-line arguments from, otherwise
315 sys.argv[1:] is used.
316
317 arguments : optional, tuple
318 Description of valid command-line arguments, to be called in sequence
319 with parser.add_argument() to configure the parser.
284 """
320 """
285 super(CommandLineConfigLoader, self).__init__()
321 super(CommandLineConfigLoader, self).__init__()
322 if argv == None:
323 argv = sys.argv[1:]
324 self.argv = argv
325 self.arguments = arguments
286 self.args = args
326 self.args = args
287 self.kw = kw
327 kwargs = dict(argument_default=self.argument_default)
328 kwargs.update(kw)
329 self.kw = kwargs
288
330
289 def load_config(self, args=None):
331 def load_config(self, args=None):
290 """Parse command line arguments and return as a Struct."""
332 """Parse command line arguments and return as a Struct.
333
334 Parameters
335 ----------
336
337 args : optional, list
338 If given, a list with the structure of sys.argv[1:] to parse arguments
339 from. If not given, the instance's self.argv attribute (given at
340 construction time) is used."""
341
342 if args is None:
343 args = self.argv
291 self._create_parser()
344 self._create_parser()
292 self._parse_args(args)
345 self._parse_args(args)
293 self._convert_to_config()
346 self._convert_to_config()
@@ -300,25 +353,21 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
300 return []
353 return []
301
354
302 def _create_parser(self):
355 def _create_parser(self):
303 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
356 self.parser = ArgumentParser(*self.args, **self.kw)
304 self._add_arguments()
357 self._add_arguments()
305 self._add_other_arguments()
358 self._add_other_arguments()
306
359
307 def _add_other_arguments(self):
308 pass
309
310 def _add_arguments(self):
360 def _add_arguments(self):
311 for argument in self.arguments:
361 for argument in self.arguments:
312 if not argument[1].has_key('default'):
313 argument[1]['default'] = NoConfigDefault
314 self.parser.add_argument(*argument[0],**argument[1])
362 self.parser.add_argument(*argument[0],**argument[1])
315
363
316 def _parse_args(self, args=None):
364 def _add_other_arguments(self):
317 """self.parser->self.parsed_data"""
365 """Meant for subclasses to add their own arguments."""
318 if args is None:
366 pass
319 self.parsed_data, self.extra_args = self.parser.parse_known_args()
367
320 else:
368 def _parse_args(self, args):
321 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
369 """self.parser->self.parsed_data"""
370 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
322
371
323 def _convert_to_config(self):
372 def _convert_to_config(self):
324 """self.parsed_data->self.config"""
373 """self.parsed_data->self.config"""
@@ -326,4 +375,3 b' class ArgParseConfigLoader(CommandLineConfigLoader):'
326 if v is not NoConfigDefault:
375 if v is not NoConfigDefault:
327 exec_str = 'self.config.' + k + '= v'
376 exec_str = 'self.config.' + k + '= v'
328 exec exec_str in locals(), globals()
377 exec exec_str in locals(), globals()
329
1 NO CONTENT: file renamed from IPython/config/profile/__init_.py to IPython/config/profile/__init__.py
NO CONTENT: file renamed from IPython/config/profile/__init_.py to IPython/config/profile/__init__.py
@@ -37,17 +37,18 b' from IPython.config.loader import ('
37
37
38
38
39 pyfile = """
39 pyfile = """
40 a = 10
40 c = get_config()
41 b = 20
41 c.a = 10
42 Foo.Bar.value = 10
42 c.b = 20
43 Foo.Bam.value = range(10)
43 c.Foo.Bar.value = 10
44 D.C.value = 'hi there'
44 c.Foo.Bam.value = range(10)
45 c.D.C.value = 'hi there'
45 """
46 """
46
47
47 class TestPyFileCL(TestCase):
48 class TestPyFileCL(TestCase):
48
49
49 def test_basic(self):
50 def test_basic(self):
50 fd, fname = mkstemp()
51 fd, fname = mkstemp('.py')
51 f = os.fdopen(fd, 'w')
52 f = os.fdopen(fd, 'w')
52 f.write(pyfile)
53 f.write(pyfile)
53 f.close()
54 f.close()
@@ -65,15 +66,13 b' class TestArgParseCL(TestCase):'
65
66
66 def test_basic(self):
67 def test_basic(self):
67
68
68 class MyLoader(ArgParseConfigLoader):
69 arguments = (
69 arguments = (
70 (('-f','--foo'), dict(dest='Global.foo', type=str)),
70 (('-f','--foo'), dict(dest='Global.foo', type=str)),
71 (('-b',), dict(dest='MyClass.bar', type=int)),
71 (('-b',), dict(dest='MyClass.bar', type=int)),
72 (('-n',), dict(dest='n', action='store_true')),
72 (('-n',), dict(dest='n', action='store_true')),
73 (('Global.bam',), dict(type=str))
73 (('Global.bam',), dict(type=str))
74 )
74 )
75
75 cl = ArgParseConfigLoader(arguments=arguments)
76 cl = MyLoader()
77 config = cl.load_config('-f hi -b 10 -n wow'.split())
76 config = cl.load_config('-f hi -b 10 -n wow'.split())
78 self.assertEquals(config.Global.foo, 'hi')
77 self.assertEquals(config.Global.foo, 'hi')
79 self.assertEquals(config.MyClass.bar, 10)
78 self.assertEquals(config.MyClass.bar, 10)
@@ -1,7 +1,12 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
1 # encoding: utf-8
3 """
2 """
4 An application for IPython
3 An application for IPython.
4
5 All top-level applications should use the classes in this module for
6 handling configuration and creating componenets.
7
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.
5
10
6 Authors:
11 Authors:
7
12
@@ -26,62 +31,131 b' Notes'
26 import logging
31 import logging
27 import os
32 import os
28 import sys
33 import sys
29 import traceback
30 from copy import deepcopy
31
34
32 from IPython.utils.genutils import get_ipython_dir, filefind
35 from IPython.core import release, crashhandler
36 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
33 from IPython.config.loader import (
37 from IPython.config.loader import (
34 PyFileConfigLoader,
38 PyFileConfigLoader,
35 ArgParseConfigLoader,
39 ArgParseConfigLoader,
36 Config,
40 Config,
37 NoConfigDefault
38 )
41 )
39
42
40 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
41 # Classes and functions
44 # Classes and functions
42 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
43
46
44
45 class IPythonArgParseConfigLoader(ArgParseConfigLoader):
46 """Default command line options for IPython based applications."""
47
48 def _add_other_arguments(self):
49 self.parser.add_argument('-ipythondir',dest='Global.ipythondir',type=str,
50 help='Set to override default location of Global.ipythondir.',
51 default=NoConfigDefault,
52 metavar='Global.ipythondir')
53 self.parser.add_argument('-p','-profile',dest='Global.profile',type=str,
54 help='The string name of the ipython profile to be used.',
55 default=NoConfigDefault,
56 metavar='Global.profile')
57 self.parser.add_argument('-log_level',dest="Global.log_level",type=int,
58 help='Set the log level (0,10,20,30,40,50). Default is 30.',
59 default=NoConfigDefault)
60 self.parser.add_argument('-config_file',dest='Global.config_file',type=str,
61 help='Set the config file name to override default.',
62 default=NoConfigDefault,
63 metavar='Global.config_file')
64
65
66 class ApplicationError(Exception):
47 class ApplicationError(Exception):
67 pass
48 pass
68
49
69
50
51 app_cl_args = (
52 (('--ipython-dir', ), dict(
53 dest='Global.ipython_dir',type=unicode,
54 help=
55 """Set to override default location of the IPython directory
56 IPYTHON_DIR, stored as Global.ipython_dir. This can also be specified
57 through the environment variable IPYTHON_DIR.""",
58 metavar='Global.ipython_dir') ),
59 (('-p', '--profile',), dict(
60 dest='Global.profile',type=unicode,
61 help=
62 """The string name of the ipython profile to be used. Assume that your
63 config file is ipython_config-<name>.py (looks in current dir first,
64 then in IPYTHON_DIR). This is a quick way to keep and load multiple
65 config files for different tasks, especially if include your basic one
66 in your more specialized ones. You can keep a basic
67 IPYTHON_DIR/ipython_config.py file and then have other 'profiles' which
68 include this one and load extra things for particular tasks.""",
69 metavar='Global.profile') ),
70 (('--log-level',), dict(
71 dest="Global.log_level",type=int,
72 help='Set the log level (0,10,20,30,40,50). Default is 30.',
73 metavar='Global.log_level')),
74 (('--config-file',), dict(
75 dest='Global.config_file',type=unicode,
76 help=
77 """Set the config file name to override default. Normally IPython
78 loads ipython_config.py (from current directory) or
79 IPYTHON_DIR/ipython_config.py. If the loading of your config file
80 fails, IPython starts with a bare bones configuration (no modules
81 loaded at all).""",
82 metavar='Global.config_file')),
83 )
84
70 class Application(object):
85 class Application(object):
71 """Load a config, construct an app and run it.
86 """Load a config, construct components and set them running.
87
88 The configuration of an application can be done via four different Config
89 objects, which are loaded and ultimately merged into a single one used from
90 that point on by the app. These are:
91
92 1. default_config: internal defaults, implemented in code.
93 2. file_config: read from the filesystem.
94 3. command_line_config: read from the system's command line flags.
95 4. constructor_config: passed parametrically to the constructor.
96
97 During initialization, 3 is actually read before 2, since at the
98 command-line one may override the location of the file to be read. But the
99 above is the order in which the merge is made.
100
101 There is a final config object can be created and passed to the
102 constructor: override_config. If it exists, this completely overrides the
103 configs 2-4 above (the default is still used to ensure that all needed
104 fields at least are created). This makes it easier to create
105 parametrically (e.g. in testing or sphinx plugins) objects with a known
106 configuration, that are unaffected by whatever arguments may be present in
107 sys.argv or files in the user's various directories.
72 """
108 """
73
109
74 config_file_name = 'ipython_config.py'
110 name = u'ipython'
75 name = 'ipython'
111 description = 'IPython: an enhanced interactive Python shell.'
76
112 #: usage message printed by argparse. If None, auto-generate
77 def __init__(self):
113 usage = None
114 config_file_name = u'ipython_config.py'
115 #: Track the default and actual separately because some messages are
116 #: only printed if we aren't using the default.
117 default_config_file_name = config_file_name
118 default_log_level = logging.WARN
119 #: Set by --profile option
120 profile_name = None
121 #: User's ipython directory, typically ~/.ipython/
122 ipython_dir = None
123 #: internal defaults, implemented in code.
124 default_config = None
125 #: read from the filesystem
126 file_config = None
127 #: read from the system's command line flags
128 command_line_config = None
129 #: passed parametrically to the constructor.
130 constructor_config = None
131 #: final override, if given supercedes file/command/constructor configs
132 override_config = None
133 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
134 argv = None
135 #: Default command line arguments. Subclasses should create a new tuple
136 #: that *includes* these.
137 cl_arguments = app_cl_args
138
139 #: extra arguments computed by the command-line loader
140 extra_args = None
141
142 # Private attributes
143 _exiting = False
144 _initialized = False
145
146 # Class choices for things that will be instantiated at runtime.
147 _CrashHandler = crashhandler.CrashHandler
148
149 def __init__(self, argv=None, constructor_config=None, override_config=None):
150 self.argv = sys.argv[1:] if argv is None else argv
151 self.constructor_config = constructor_config
152 self.override_config = override_config
78 self.init_logger()
153 self.init_logger()
79 self.default_config_file_name = self.config_file_name
80
154
81 def init_logger(self):
155 def init_logger(self):
82 self.log = logging.getLogger(self.__class__.__name__)
156 self.log = logging.getLogger(self.__class__.__name__)
83 # This is used as the default until the command line arguments are read.
157 # This is used as the default until the command line arguments are read.
84 self.log.setLevel(logging.WARN)
158 self.log.setLevel(self.default_log_level)
85 self._log_handler = logging.StreamHandler()
159 self._log_handler = logging.StreamHandler()
86 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
160 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
87 self._log_handler.setFormatter(self._log_formatter)
161 self._log_handler.setFormatter(self._log_formatter)
@@ -95,28 +169,80 b' class Application(object):'
95
169
96 log_level = property(_get_log_level, _set_log_level)
170 log_level = property(_get_log_level, _set_log_level)
97
171
172 def initialize(self):
173 """Initialize the application.
174
175 Loads all configuration information and sets all application state, but
176 does not start any relevant processing (typically some kind of event
177 loop).
178
179 Once this method has been called, the application is flagged as
180 initialized and the method becomes a no-op."""
181
182 if self._initialized:
183 return
184
185 # The first part is protected with an 'attempt' wrapper, that will log
186 # failures with the basic system traceback machinery. Once our crash
187 # handler is in place, we can let any subsequent exception propagate,
188 # as our handler will log it with much better detail than the default.
189 self.attempt(self.create_crash_handler)
190
191 # Configuration phase
192 # Default config (internally hardwired in application code)
193 self.create_default_config()
194 self.log_default_config()
195 self.set_default_config_log_level()
196
197 if self.override_config is None:
198 # Command-line config
199 self.pre_load_command_line_config()
200 self.load_command_line_config()
201 self.set_command_line_config_log_level()
202 self.post_load_command_line_config()
203 self.log_command_line_config()
204
205 # Find resources needed for filesystem access, using information from
206 # the above two
207 self.find_ipython_dir()
208 self.find_resources()
209 self.find_config_file_name()
210 self.find_config_file_paths()
211
212 if self.override_config is None:
213 # File-based config
214 self.pre_load_file_config()
215 self.load_file_config()
216 self.set_file_config_log_level()
217 self.post_load_file_config()
218 self.log_file_config()
219
220 # Merge all config objects into a single one the app can then use
221 self.merge_configs()
222 self.log_master_config()
223
224 # Construction phase
225 self.pre_construct()
226 self.construct()
227 self.post_construct()
228
229 # Done, flag as such and
230 self._initialized = True
231
98 def start(self):
232 def start(self):
99 """Start the application."""
233 """Start the application."""
100 self.attempt(self.create_default_config)
234 self.initialize()
101 self.attempt(self.pre_load_command_line_config)
235 self.start_app()
102 self.attempt(self.load_command_line_config, action='abort')
103 self.attempt(self.post_load_command_line_config)
104 self.attempt(self.find_ipythondir)
105 self.attempt(self.find_config_file_name)
106 self.attempt(self.find_config_file_paths)
107 self.attempt(self.pre_load_file_config)
108 self.attempt(self.load_file_config)
109 self.attempt(self.post_load_file_config)
110 self.attempt(self.merge_configs)
111 self.attempt(self.pre_construct)
112 self.attempt(self.construct)
113 self.attempt(self.post_construct)
114 self.attempt(self.start_app)
115
236
116 #-------------------------------------------------------------------------
237 #-------------------------------------------------------------------------
117 # Various stages of Application creation
238 # Various stages of Application creation
118 #-------------------------------------------------------------------------
239 #-------------------------------------------------------------------------
119
240
241 def create_crash_handler(self):
242 """Create a crash handler, typically setting sys.excepthook to it."""
243 self.crash_handler = self._CrashHandler(self, self.name)
244 sys.excepthook = self.crash_handler
245
120 def create_default_config(self):
246 def create_default_config(self):
121 """Create defaults that can't be set elsewhere.
247 """Create defaults that can't be set elsewhere.
122
248
@@ -126,66 +252,91 b' class Application(object):'
126 we set them here. The Global section is for variables like this that
252 we set them here. The Global section is for variables like this that
127 don't belong to a particular component.
253 don't belong to a particular component.
128 """
254 """
129 self.default_config = Config()
255 c = Config()
130 self.default_config.Global.ipythondir = get_ipython_dir()
256 c.Global.ipython_dir = get_ipython_dir()
257 c.Global.log_level = self.log_level
258 self.default_config = c
259
260 def log_default_config(self):
131 self.log.debug('Default config loaded:')
261 self.log.debug('Default config loaded:')
132 self.log.debug(repr(self.default_config))
262 self.log.debug(repr(self.default_config))
133
263
264 def set_default_config_log_level(self):
265 try:
266 self.log_level = self.default_config.Global.log_level
267 except AttributeError:
268 # Fallback to the default_log_level class attribute
269 pass
270
134 def create_command_line_config(self):
271 def create_command_line_config(self):
135 """Create and return a command line config loader."""
272 """Create and return a command line config loader."""
136 return IPythonArgParseConfigLoader(description=self.name)
273 return ArgParseConfigLoader(self.argv, self.cl_arguments,
274 description=self.description,
275 version=release.version,
276 usage=self.usage,
277 )
137
278
138 def pre_load_command_line_config(self):
279 def pre_load_command_line_config(self):
139 """Do actions just before loading the command line config."""
280 """Do actions just before loading the command line config."""
140 pass
281 pass
141
282
142 def load_command_line_config(self):
283 def load_command_line_config(self):
143 """Load the command line config.
284 """Load the command line config."""
144
145 This method also sets ``self.debug``.
146 """
147
148 loader = self.create_command_line_config()
285 loader = self.create_command_line_config()
149 self.command_line_config = loader.load_config()
286 self.command_line_config = loader.load_config()
150 self.extra_args = loader.get_extra_args()
287 self.extra_args = loader.get_extra_args()
151
288
289 def set_command_line_config_log_level(self):
152 try:
290 try:
153 self.log_level = self.command_line_config.Global.log_level
291 self.log_level = self.command_line_config.Global.log_level
154 except AttributeError:
292 except AttributeError:
155 pass # Use existing value which is set in Application.init_logger.
293 pass
156 self.log.debug("Command line config loaded:")
157 self.log.debug(repr(self.command_line_config))
158
294
159 def post_load_command_line_config(self):
295 def post_load_command_line_config(self):
160 """Do actions just after loading the command line config."""
296 """Do actions just after loading the command line config."""
161 pass
297 pass
162
298
163 def find_ipythondir(self):
299 def log_command_line_config(self):
300 self.log.debug("Command line config loaded:")
301 self.log.debug(repr(self.command_line_config))
302
303 def find_ipython_dir(self):
164 """Set the IPython directory.
304 """Set the IPython directory.
165
305
166 This sets ``self.ipythondir``, but the actual value that is passed
306 This sets ``self.ipython_dir``, but the actual value that is passed to
167 to the application is kept in either ``self.default_config`` or
307 the application is kept in either ``self.default_config`` or
168 ``self.command_line_config``. This also added ``self.ipythondir`` to
308 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
169 ``sys.path`` so config files there can be references by other config
309 ``sys.path`` so config files there can be referenced by other config
170 files.
310 files.
171 """
311 """
172
312
173 try:
313 try:
174 self.ipythondir = self.command_line_config.Global.ipythondir
314 self.ipython_dir = self.command_line_config.Global.ipython_dir
175 except AttributeError:
315 except AttributeError:
176 self.ipythondir = self.default_config.Global.ipythondir
316 self.ipython_dir = self.default_config.Global.ipython_dir
177 sys.path.append(os.path.abspath(self.ipythondir))
317 sys.path.append(os.path.abspath(self.ipython_dir))
178 if not os.path.isdir(self.ipythondir):
318 if not os.path.isdir(self.ipython_dir):
179 os.makedirs(self.ipythondir, mode = 0777)
319 os.makedirs(self.ipython_dir, mode=0777)
180 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
320 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
321
322 def find_resources(self):
323 """Find other resources that need to be in place.
324
325 Things like cluster directories need to be in place to find the
326 config file. These happen right after the IPython directory has
327 been set.
328 """
329 pass
181
330
182 def find_config_file_name(self):
331 def find_config_file_name(self):
183 """Find the config file name for this application.
332 """Find the config file name for this application.
184
333
185 If a profile has been set at the command line, this will resolve
334 This must set ``self.config_file_name`` to the filename of the
186 it. The search paths for the config file are set in
335 config file to use (just the filename). The search paths for the
187 :meth:`find_config_file_paths` and then passed to the config file
336 config file are set in :meth:`find_config_file_paths` and then passed
188 loader where they are resolved to an absolute path.
337 to the config file loader where they are resolved to an absolute path.
338
339 If a profile has been set at the command line, this will resolve it.
189 """
340 """
190
341
191 try:
342 try:
@@ -195,15 +346,24 b' class Application(object):'
195
346
196 try:
347 try:
197 self.profile_name = self.command_line_config.Global.profile
348 self.profile_name = self.command_line_config.Global.profile
198 name_parts = self.config_file_name.split('.')
199 name_parts.insert(1, '_' + self.profile_name + '.')
200 self.config_file_name = ''.join(name_parts)
201 except AttributeError:
349 except AttributeError:
202 pass
350 pass
351 else:
352 name_parts = self.config_file_name.split('.')
353 name_parts.insert(1, u'_' + self.profile_name + u'.')
354 self.config_file_name = ''.join(name_parts)
203
355
204 def find_config_file_paths(self):
356 def find_config_file_paths(self):
205 """Set the search paths for resolving the config file."""
357 """Set the search paths for resolving the config file.
206 self.config_file_paths = (os.getcwd(), self.ipythondir)
358
359 This must set ``self.config_file_paths`` to a sequence of search
360 paths to pass to the config file loader.
361 """
362 # Include our own profiles directory last, so that users can still find
363 # our shipped copies of builtin profiles even if they don't have them
364 # in their local ipython directory.
365 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
366 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
207
367
208 def pre_load_file_config(self):
368 def pre_load_file_config(self):
209 """Do actions before the config file is loaded."""
369 """Do actions before the config file is loaded."""
@@ -216,7 +376,8 b' class Application(object):'
216 ``CONFIG_FILE`` config variable is set to the resolved config file
376 ``CONFIG_FILE`` config variable is set to the resolved config file
217 location. If not successful, an empty config is used.
377 location. If not successful, an empty config is used.
218 """
378 """
219 self.log.debug("Attempting to load config file: <%s>" % self.config_file_name)
379 self.log.debug("Attempting to load config file: %s" %
380 self.config_file_name)
220 loader = PyFileConfigLoader(self.config_file_name,
381 loader = PyFileConfigLoader(self.config_file_name,
221 path=self.config_file_paths)
382 path=self.config_file_paths)
222 try:
383 try:
@@ -225,19 +386,18 b' class Application(object):'
225 except IOError:
386 except IOError:
226 # Only warn if the default config file was NOT being used.
387 # Only warn if the default config file was NOT being used.
227 if not self.config_file_name==self.default_config_file_name:
388 if not self.config_file_name==self.default_config_file_name:
228 self.log.warn("Config file not found, skipping: <%s>" % \
389 self.log.warn("Config file not found, skipping: %s" %
229 self.config_file_name, exc_info=True)
390 self.config_file_name, exc_info=True)
230 self.file_config = Config()
391 self.file_config = Config()
231 except:
392 except:
232 self.log.warn("Error loading config file: <%s>" % \
393 self.log.warn("Error loading config file: %s" %
233 self.config_file_name, exc_info=True)
394 self.config_file_name, exc_info=True)
234 self.file_config = Config()
395 self.file_config = Config()
235 else:
396
236 self.log.debug("Config file loaded: <%s>" % loader.full_filename)
397 def set_file_config_log_level(self):
237 self.log.debug(repr(self.file_config))
238 # We need to keeep self.log_level updated. But we only use the value
398 # We need to keeep self.log_level updated. But we only use the value
239 # of the file_config if a value was not specified at the command
399 # of the file_config if a value was not specified at the command
240 # line.
400 # line, because the command line overrides everything.
241 if not hasattr(self.command_line_config.Global, 'log_level'):
401 if not hasattr(self.command_line_config.Global, 'log_level'):
242 try:
402 try:
243 self.log_level = self.file_config.Global.log_level
403 self.log_level = self.file_config.Global.log_level
@@ -248,13 +408,31 b' class Application(object):'
248 """Do actions after the config file is loaded."""
408 """Do actions after the config file is loaded."""
249 pass
409 pass
250
410
411 def log_file_config(self):
412 if hasattr(self.file_config.Global, 'config_file'):
413 self.log.debug("Config file loaded: %s" %
414 self.file_config.Global.config_file)
415 self.log.debug(repr(self.file_config))
416
251 def merge_configs(self):
417 def merge_configs(self):
252 """Merge the default, command line and file config objects."""
418 """Merge the default, command line and file config objects."""
253 config = Config()
419 config = Config()
254 config._merge(self.default_config)
420 config._merge(self.default_config)
255 config._merge(self.file_config)
421 if self.override_config is None:
256 config._merge(self.command_line_config)
422 config._merge(self.file_config)
423 config._merge(self.command_line_config)
424 if self.constructor_config is not None:
425 config._merge(self.constructor_config)
426 else:
427 config._merge(self.override_config)
428 # XXX fperez - propose to Brian we rename master_config to simply
429 # config, I think this is going to be heavily used in examples and
430 # application code and the name is shorter/easier to find/remember.
431 # For now, just alias it...
257 self.master_config = config
432 self.master_config = config
433 self.config = config
434
435 def log_master_config(self):
258 self.log.debug("Master config created:")
436 self.log.debug("Master config created:")
259 self.log.debug(repr(self.master_config))
437 self.log.debug(repr(self.master_config))
260
438
@@ -280,21 +458,31 b' class Application(object):'
280
458
281 def abort(self):
459 def abort(self):
282 """Abort the starting of the application."""
460 """Abort the starting of the application."""
283 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
461 if self._exiting:
284 sys.exit(1)
462 pass
463 else:
464 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
465 self._exiting = True
466 sys.exit(1)
285
467
286 def exit(self):
468 def exit(self, exit_status=0):
287 self.log.critical("Aborting application: %s" % self.name)
469 if self._exiting:
288 sys.exit(1)
470 pass
471 else:
472 self.log.debug("Exiting application: %s" % self.name)
473 self._exiting = True
474 sys.exit(exit_status)
289
475
290 def attempt(self, func, action='abort'):
476 def attempt(self, func, action='abort'):
291 try:
477 try:
292 func()
478 func()
293 except SystemExit:
479 except SystemExit:
294 self.exit()
480 raise
295 except:
481 except:
296 if action == 'abort':
482 if action == 'abort':
483 self.log.critical("Aborting application: %s" % self.name,
484 exc_info=True)
297 self.abort()
485 self.abort()
486 raise
298 elif action == 'exit':
487 elif action == 'exit':
299 self.exit()
488 self.exit(0)
300
@@ -31,8 +31,8 b' from IPython.utils.autoattr import auto_attr'
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33
33
34 class BuiltinUndefined(object): pass
34 class __BuiltinUndefined(object): pass
35 BuiltinUndefined = BuiltinUndefined()
35 BuiltinUndefined = __BuiltinUndefined()
36
36
37
37
38 class BuiltinTrap(Component):
38 class BuiltinTrap(Component):
@@ -86,6 +86,7 b' class BuiltinTrap(Component):'
86 """Store ipython references in the __builtin__ namespace."""
86 """Store ipython references in the __builtin__ namespace."""
87 self.add_builtin('exit', Quitter(self.shell, 'exit'))
87 self.add_builtin('exit', Quitter(self.shell, 'exit'))
88 self.add_builtin('quit', Quitter(self.shell, 'quit'))
88 self.add_builtin('quit', Quitter(self.shell, 'quit'))
89 self.add_builtin('get_ipython', self.shell.get_ipython)
89
90
90 # Recursive reload function
91 # Recursive reload function
91 try:
92 try:
@@ -44,7 +44,6 b' its input.'
44
44
45 - When the original stdin is not a tty device, GNU readline is never
45 - When the original stdin is not a tty device, GNU readline is never
46 used, and this module (and the readline module) are silently inactive.
46 used, and this module (and the readline module) are silently inactive.
47
48 """
47 """
49
48
50 #*****************************************************************************
49 #*****************************************************************************
@@ -54,14 +53,19 b' used, and this module (and the readline module) are silently inactive.'
54 # proper procedure is to maintain its copyright as belonging to the Python
53 # proper procedure is to maintain its copyright as belonging to the Python
55 # Software Foundation (in addition to my own, for all new code).
54 # Software Foundation (in addition to my own, for all new code).
56 #
55 #
56 # Copyright (C) 2008-2010 IPython Development Team
57 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
57 # Copyright (C) 2001 Python Software Foundation, www.python.org
58 # Copyright (C) 2001 Python Software Foundation, www.python.org
58 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
59 #
59 #
60 # Distributed under the terms of the BSD License. The full license is in
60 # Distributed under the terms of the BSD License. The full license is in
61 # the file COPYING, distributed as part of this software.
61 # the file COPYING, distributed as part of this software.
62 #
62 #
63 #*****************************************************************************
63 #*****************************************************************************
64
64
65 #-----------------------------------------------------------------------------
66 # Imports
67 #-----------------------------------------------------------------------------
68
65 import __builtin__
69 import __builtin__
66 import __main__
70 import __main__
67 import glob
71 import glob
@@ -73,23 +77,57 b' import shlex'
73 import sys
77 import sys
74 import types
78 import types
75
79
80 import IPython.utils.rlineimpl as readline
76 from IPython.core.error import TryNext
81 from IPython.core.error import TryNext
77 from IPython.core.prefilter import ESC_MAGIC
82 from IPython.core.prefilter import ESC_MAGIC
78
79 import IPython.utils.rlineimpl as readline
80 from IPython.utils.ipstruct import Struct
81 from IPython.utils import generics
83 from IPython.utils import generics
82
83 # Python 2.4 offers sets as a builtin
84 try:
85 set()
86 except NameError:
87 from sets import Set as set
88
89 from IPython.utils.genutils import debugx, dir2
84 from IPython.utils.genutils import debugx, dir2
90
85
86 #-----------------------------------------------------------------------------
87 # Globals
88 #-----------------------------------------------------------------------------
89
90 # Public API
91 __all__ = ['Completer','IPCompleter']
91 __all__ = ['Completer','IPCompleter']
92
92
93 if sys.platform == 'win32':
94 PROTECTABLES = ' '
95 else:
96 PROTECTABLES = ' ()'
97
98 #-----------------------------------------------------------------------------
99 # Main functions and classes
100 #-----------------------------------------------------------------------------
101
102 def protect_filename(s):
103 """Escape a string to protect certain characters."""
104
105 return "".join([(ch in PROTECTABLES and '\\' + ch or ch)
106 for ch in s])
107
108
109 def single_dir_expand(matches):
110 "Recursively expand match lists containing a single dir."
111
112 if len(matches) == 1 and os.path.isdir(matches[0]):
113 # Takes care of links to directories also. Use '/'
114 # explicitly, even under Windows, so that name completions
115 # don't end up escaped.
116 d = matches[0]
117 if d[-1] in ['/','\\']:
118 d = d[:-1]
119
120 subdirs = os.listdir(d)
121 if subdirs:
122 matches = [ (d + '/' + p) for p in subdirs]
123 return single_dir_expand(matches)
124 else:
125 return matches
126 else:
127 return matches
128
129 class Bunch: pass
130
93 class Completer:
131 class Completer:
94 def __init__(self,namespace=None,global_namespace=None):
132 def __init__(self,namespace=None,global_namespace=None):
95 """Create a new completer for the command line.
133 """Create a new completer for the command line.
@@ -152,6 +190,7 b' class Completer:'
152 defined in self.namespace or self.global_namespace that match.
190 defined in self.namespace or self.global_namespace that match.
153
191
154 """
192 """
193 #print 'Completer->global_matches, txt=%r' % text # dbg
155 matches = []
194 matches = []
156 match_append = matches.append
195 match_append = matches.append
157 n = len(text)
196 n = len(text)
@@ -179,6 +218,7 b' class Completer:'
179 """
218 """
180 import re
219 import re
181
220
221 #print 'Completer->attr_matches, txt=%r' % text # dbg
182 # Another option, seems to work great. Catches things like ''.<tab>
222 # Another option, seems to work great. Catches things like ''.<tab>
183 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
223 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
184
224
@@ -205,6 +245,7 b' class Completer:'
205 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
245 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
206 return res
246 return res
207
247
248
208 class IPCompleter(Completer):
249 class IPCompleter(Completer):
209 """Extension of the completer class with IPython-specific features"""
250 """Extension of the completer class with IPython-specific features"""
210
251
@@ -235,7 +276,7 b' class IPCompleter(Completer):'
235 to complete. """
276 to complete. """
236
277
237 Completer.__init__(self,namespace,global_namespace)
278 Completer.__init__(self,namespace,global_namespace)
238 self.magic_prefix = shell.name+'.magic_'
279
239 self.magic_escape = ESC_MAGIC
280 self.magic_escape = ESC_MAGIC
240 self.readline = readline
281 self.readline = readline
241 delims = self.readline.get_completer_delims()
282 delims = self.readline.get_completer_delims()
@@ -244,7 +285,8 b' class IPCompleter(Completer):'
244 self.get_line_buffer = self.readline.get_line_buffer
285 self.get_line_buffer = self.readline.get_line_buffer
245 self.get_endidx = self.readline.get_endidx
286 self.get_endidx = self.readline.get_endidx
246 self.omit__names = omit__names
287 self.omit__names = omit__names
247 self.merge_completions = shell.readline_merge_completions
288 self.merge_completions = shell.readline_merge_completions
289 self.shell = shell.shell
248 if alias_table is None:
290 if alias_table is None:
249 alias_table = {}
291 alias_table = {}
250 self.alias_table = alias_table
292 self.alias_table = alias_table
@@ -263,11 +305,13 b' class IPCompleter(Completer):'
263 self.clean_glob = self._clean_glob_win32
305 self.clean_glob = self._clean_glob_win32
264 else:
306 else:
265 self.clean_glob = self._clean_glob
307 self.clean_glob = self._clean_glob
308
309 # All active matcher routines for completion
266 self.matchers = [self.python_matches,
310 self.matchers = [self.python_matches,
267 self.file_matches,
311 self.file_matches,
312 self.magic_matches,
268 self.alias_matches,
313 self.alias_matches,
269 self.python_func_kw_matches]
314 self.python_func_kw_matches]
270
271
315
272 # Code contributed by Alex Schmolck, for ipython/emacs integration
316 # Code contributed by Alex Schmolck, for ipython/emacs integration
273 def all_completions(self, text):
317 def all_completions(self, text):
@@ -278,9 +322,8 b' class IPCompleter(Completer):'
278 try:
322 try:
279 for i in xrange(sys.maxint):
323 for i in xrange(sys.maxint):
280 res = self.complete(text, i)
324 res = self.complete(text, i)
281
325 if not res:
282 if not res: break
326 break
283
284 comp_append(res)
327 comp_append(res)
285 #XXX workaround for ``notDefined.<tab>``
328 #XXX workaround for ``notDefined.<tab>``
286 except NameError:
329 except NameError:
@@ -316,41 +359,12 b' class IPCompleter(Completer):'
316 # don't want to treat as delimiters in filename matching
359 # don't want to treat as delimiters in filename matching
317 # when escaped with backslash
360 # when escaped with backslash
318
361
319 if sys.platform == 'win32':
320 protectables = ' '
321 else:
322 protectables = ' ()'
323
324 if text.startswith('!'):
362 if text.startswith('!'):
325 text = text[1:]
363 text = text[1:]
326 text_prefix = '!'
364 text_prefix = '!'
327 else:
365 else:
328 text_prefix = ''
366 text_prefix = ''
329
367
330 def protect_filename(s):
331 return "".join([(ch in protectables and '\\' + ch or ch)
332 for ch in s])
333
334 def single_dir_expand(matches):
335 "Recursively expand match lists containing a single dir."
336
337 if len(matches) == 1 and os.path.isdir(matches[0]):
338 # Takes care of links to directories also. Use '/'
339 # explicitly, even under Windows, so that name completions
340 # don't end up escaped.
341 d = matches[0]
342 if d[-1] in ['/','\\']:
343 d = d[:-1]
344
345 subdirs = os.listdir(d)
346 if subdirs:
347 matches = [ (d + '/' + p) for p in subdirs]
348 return single_dir_expand(matches)
349 else:
350 return matches
351 else:
352 return matches
353
354 lbuf = self.lbuf
368 lbuf = self.lbuf
355 open_quotes = 0 # track strings with open quotes
369 open_quotes = 0 # track strings with open quotes
356 try:
370 try:
@@ -402,13 +416,24 b' class IPCompleter(Completer):'
402 #print 'mm',matches # dbg
416 #print 'mm',matches # dbg
403 return single_dir_expand(matches)
417 return single_dir_expand(matches)
404
418
419 def magic_matches(self, text):
420 """Match magics"""
421 #print 'Completer->magic_matches:',text,'lb',self.lbuf # dbg
422 # Get all shell magics now rather than statically, so magics loaded at
423 # runtime show up too
424 magics = self.shell.lsmagic()
425 pre = self.magic_escape
426 baretext = text.lstrip(pre)
427 return [ pre+m for m in magics if m.startswith(baretext)]
428
405 def alias_matches(self, text):
429 def alias_matches(self, text):
406 """Match internal system aliases"""
430 """Match internal system aliases"""
407 #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg
431 #print 'Completer->alias_matches:',text,'lb',self.lbuf # dbg
408
432
409 # if we are not in the first 'item', alias matching
433 # if we are not in the first 'item', alias matching
410 # doesn't make sense - unless we are starting with 'sudo' command.
434 # doesn't make sense - unless we are starting with 'sudo' command.
411 if ' ' in self.lbuf.lstrip() and not self.lbuf.lstrip().startswith('sudo'):
435 if ' ' in self.lbuf.lstrip() and \
436 not self.lbuf.lstrip().startswith('sudo'):
412 return []
437 return []
413 text = os.path.expanduser(text)
438 text = os.path.expanduser(text)
414 aliases = self.alias_table.keys()
439 aliases = self.alias_table.keys()
@@ -420,7 +445,7 b' class IPCompleter(Completer):'
420 def python_matches(self,text):
445 def python_matches(self,text):
421 """Match attributes or global python names"""
446 """Match attributes or global python names"""
422
447
423 #print 'Completer->python_matches, txt=<%s>' % text # dbg
448 #print 'Completer->python_matches, txt=%r' % text # dbg
424 if "." in text:
449 if "." in text:
425 try:
450 try:
426 matches = self.attr_matches(text)
451 matches = self.attr_matches(text)
@@ -439,11 +464,7 b' class IPCompleter(Completer):'
439 matches = []
464 matches = []
440 else:
465 else:
441 matches = self.global_matches(text)
466 matches = self.global_matches(text)
442 # this is so completion finds magics when automagic is on:
467
443 if (matches == [] and
444 not text.startswith(os.sep) and
445 not ' ' in self.lbuf):
446 matches = self.attr_matches(self.magic_prefix+text)
447 return matches
468 return matches
448
469
449 def _default_arguments(self, obj):
470 def _default_arguments(self, obj):
@@ -514,9 +535,11 b' class IPCompleter(Completer):'
514 callableMatches = self.attr_matches('.'.join(ids[::-1]))
535 callableMatches = self.attr_matches('.'.join(ids[::-1]))
515 argMatches = []
536 argMatches = []
516 for callableMatch in callableMatches:
537 for callableMatch in callableMatches:
517 try: namedArgs = self._default_arguments(eval(callableMatch,
538 try:
539 namedArgs = self._default_arguments(eval(callableMatch,
518 self.namespace))
540 self.namespace))
519 except: continue
541 except:
542 continue
520 for namedArg in namedArgs:
543 for namedArg in namedArgs:
521 if namedArg.startswith(text):
544 if namedArg.startswith(text):
522 argMatches.append("%s=" %namedArg)
545 argMatches.append("%s=" %namedArg)
@@ -528,7 +551,7 b' class IPCompleter(Completer):'
528 if not line.strip():
551 if not line.strip():
529 return None
552 return None
530
553
531 event = Struct()
554 event = Bunch()
532 event.line = line
555 event.line = line
533 event.symbol = text
556 event.symbol = text
534 cmd = line.split(None,1)[0]
557 cmd = line.split(None,1)[0]
@@ -540,11 +563,9 b' class IPCompleter(Completer):'
540 try_magic = self.custom_completers.s_matches(
563 try_magic = self.custom_completers.s_matches(
541 self.magic_escape + cmd)
564 self.magic_escape + cmd)
542 else:
565 else:
543 try_magic = []
566 try_magic = []
544
545
567
546 for c in itertools.chain(
568 for c in itertools.chain(self.custom_completers.s_matches(cmd),
547 self.custom_completers.s_matches(cmd),
548 try_magic,
569 try_magic,
549 self.custom_completers.flat_matches(self.lbuf)):
570 self.custom_completers.flat_matches(self.lbuf)):
550 #print "try",c # dbg
571 #print "try",c # dbg
@@ -555,7 +576,8 b' class IPCompleter(Completer):'
555 if withcase:
576 if withcase:
556 return withcase
577 return withcase
557 # if none, then case insensitive ones are ok too
578 # if none, then case insensitive ones are ok too
558 return [r for r in res if r.lower().startswith(text.lower())]
579 text_low = text.lower()
580 return [r for r in res if r.lower().startswith(text_low)]
559 except TryNext:
581 except TryNext:
560 pass
582 pass
561
583
@@ -598,14 +620,11 b' class IPCompleter(Completer):'
598 return None
620 return None
599
621
600 magic_escape = self.magic_escape
622 magic_escape = self.magic_escape
601 magic_prefix = self.magic_prefix
602
623
603 self.lbuf = self.full_lbuf[:self.get_endidx()]
624 self.lbuf = self.full_lbuf[:self.get_endidx()]
604
625
605 try:
626 try:
606 if text.startswith(magic_escape):
627 if text.startswith('~'):
607 text = text.replace(magic_escape,magic_prefix)
608 elif text.startswith('~'):
609 text = os.path.expanduser(text)
628 text = os.path.expanduser(text)
610 if state == 0:
629 if state == 0:
611 custom_res = self.dispatch_custom_completer(text)
630 custom_res = self.dispatch_custom_completer(text)
@@ -625,13 +644,10 b' class IPCompleter(Completer):'
625 self.matches = matcher(text)
644 self.matches = matcher(text)
626 if self.matches:
645 if self.matches:
627 break
646 break
628 def uniq(alist):
647 self.matches = list(set(self.matches))
629 set = {}
630 return [set.setdefault(e,e) for e in alist if e not in set]
631 self.matches = uniq(self.matches)
632 try:
648 try:
633 ret = self.matches[state].replace(magic_prefix,magic_escape)
649 #print "MATCH: %r" % self.matches[state] # dbg
634 return ret
650 return self.matches[state]
635 except IndexError:
651 except IndexError:
636 return None
652 return None
637 except:
653 except:
@@ -157,7 +157,7 b' def masquerade_as(instance, cls):'
157 cls.register_instance(instance)
157 cls.register_instance(instance)
158
158
159
159
160 class ComponentNameGenerator(object):
160 class __ComponentNameGenerator(object):
161 """A Singleton to generate unique component names."""
161 """A Singleton to generate unique component names."""
162
162
163 def __init__(self, prefix):
163 def __init__(self, prefix):
@@ -170,7 +170,7 b' class ComponentNameGenerator(object):'
170 return "%s%s" % (self.prefix, count)
170 return "%s%s" % (self.prefix, count)
171
171
172
172
173 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
173 ComponentNameGenerator = __ComponentNameGenerator('ipython.component')
174
174
175
175
176 class MetaComponent(MetaHasTraits, MetaComponentTracker):
176 class MetaComponent(MetaHasTraits, MetaComponentTracker):
@@ -237,14 +237,20 b' class Component(HasTraits):'
237 self.config = config
237 self.config = config
238 # We used to deepcopy, but for now we are trying to just save
238 # We used to deepcopy, but for now we are trying to just save
239 # by reference. This *could* have side effects as all components
239 # by reference. This *could* have side effects as all components
240 # will share config.
240 # will share config. In fact, I did find such a side effect in
241 # _config_changed below. If a config attribute value was a mutable type
242 # all instances of a component were getting the same copy, effectively
243 # making that a class attribute.
241 # self.config = deepcopy(config)
244 # self.config = deepcopy(config)
242 else:
245 else:
243 if self.parent is not None:
246 if self.parent is not None:
244 self.config = self.parent.config
247 self.config = self.parent.config
245 # We used to deepcopy, but for now we are trying to just save
248 # We used to deepcopy, but for now we are trying to just save
246 # by reference. This *could* have side effects as all components
249 # by reference. This *could* have side effects as all components
247 # will share config.
250 # will share config. In fact, I did find such a side effect in
251 # _config_changed below. If a config attribute value was a mutable type
252 # all instances of a component were getting the same copy, effectively
253 # making that a class attribute.
248 # self.config = deepcopy(self.parent.config)
254 # self.config = deepcopy(self.parent.config)
249
255
250 self.created = datetime.datetime.now()
256 self.created = datetime.datetime.now()
@@ -296,14 +302,29 b' class Component(HasTraits):'
296 if new._has_section(sname):
302 if new._has_section(sname):
297 my_config = new[sname]
303 my_config = new[sname]
298 for k, v in traits.items():
304 for k, v in traits.items():
305 # Don't allow traitlets with config=True to start with
306 # uppercase. Otherwise, they are confused with Config
307 # subsections. But, developers shouldn't have uppercase
308 # attributes anyways! (PEP 6)
309 if k[0].upper()==k[0] and not k.startswith('_'):
310 raise ComponentError('Component traitlets with '
311 'config=True must start with a lowercase so they are '
312 'not confused with Config subsections: %s.%s' % \
313 (self.__class__.__name__, k))
299 try:
314 try:
315 # Here we grab the value from the config
316 # If k has the naming convention of a config
317 # section, it will be auto created.
300 config_value = my_config[k]
318 config_value = my_config[k]
301 except KeyError:
319 except KeyError:
302 pass
320 pass
303 else:
321 else:
304 # print "Setting %s.%s from %s.%s=%r" % \
322 # print "Setting %s.%s from %s.%s=%r" % \
305 # (self.__class__.__name__,k,sname,k,config_value)
323 # (self.__class__.__name__,k,sname,k,config_value)
306 setattr(self, k, config_value)
324 # We have to do a deepcopy here if we don't deepcopy the entire
325 # config object. If we don't, a mutable config_value will be
326 # shared by all instances, effectively making it a class attribute.
327 setattr(self, k, deepcopy(config_value))
307
328
308 @property
329 @property
309 def children(self):
330 def children(self):
@@ -28,10 +28,8 b' from IPython.core import release'
28 from IPython.core import ultratb
28 from IPython.core import ultratb
29 from IPython.external.Itpl import itpl
29 from IPython.external.Itpl import itpl
30
30
31 from IPython.utils.genutils import *
32
33 #****************************************************************************
31 #****************************************************************************
34 class CrashHandler:
32 class CrashHandler(object):
35 """Customizable crash handlers for IPython-based systems.
33 """Customizable crash handlers for IPython-based systems.
36
34
37 Instances of this class provide a __call__ method which can be used as a
35 Instances of this class provide a __call__ method which can be used as a
@@ -41,15 +39,15 b' class CrashHandler:'
41
39
42 """
40 """
43
41
44 def __init__(self,IP,app_name,contact_name,contact_email,
42 def __init__(self,app, app_name, contact_name=None, contact_email=None,
45 bug_tracker,crash_report_fname,
43 bug_tracker=None, crash_report_fname='CrashReport.txt',
46 show_crash_traceback=True):
44 show_crash_traceback=True, call_pdb=False):
47 """New crash handler.
45 """New crash handler.
48
46
49 Inputs:
47 Inputs:
50
48
51 - IP: a running IPython instance, which will be queried at crash time
49 - app: a running application instance, which will be queried at crash
52 for internal information.
50 time for internal information.
53
51
54 - app_name: a string containing the name of your application.
52 - app_name: a string containing the name of your application.
55
53
@@ -77,13 +75,16 b' class CrashHandler:'
77 """
75 """
78
76
79 # apply args into instance
77 # apply args into instance
80 self.IP = IP # IPython instance
78 self.app = app
81 self.app_name = app_name
79 self.app_name = app_name
82 self.contact_name = contact_name
80 self.contact_name = contact_name
83 self.contact_email = contact_email
81 self.contact_email = contact_email
84 self.bug_tracker = bug_tracker
82 self.bug_tracker = bug_tracker
85 self.crash_report_fname = crash_report_fname
83 self.crash_report_fname = crash_report_fname
86 self.show_crash_traceback = show_crash_traceback
84 self.show_crash_traceback = show_crash_traceback
85 self.section_sep = '\n\n'+'*'*75+'\n\n'
86 self.call_pdb = call_pdb
87 #self.call_pdb = True # dbg
87
88
88 # Hardcoded defaults, which can be overridden either by subclasses or
89 # Hardcoded defaults, which can be overridden either by subclasses or
89 # at runtime for the instance.
90 # at runtime for the instance.
@@ -124,7 +125,7 b' $self.bug_tracker'
124 #color_scheme = 'Linux' # dbg
125 #color_scheme = 'Linux' # dbg
125
126
126 try:
127 try:
127 rptdir = self.IP.config.IPYTHONDIR
128 rptdir = self.app.ipython_dir
128 except:
129 except:
129 rptdir = os.getcwd()
130 rptdir = os.getcwd()
130 if not os.path.isdir(rptdir):
131 if not os.path.isdir(rptdir):
@@ -134,8 +135,14 b' $self.bug_tracker'
134 # properly expanded out in the user message template
135 # properly expanded out in the user message template
135 self.crash_report_fname = report_name
136 self.crash_report_fname = report_name
136 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
137 TBhandler = ultratb.VerboseTB(color_scheme=color_scheme,
137 long_header=1)
138 long_header=1,
138 traceback = TBhandler.text(etype,evalue,etb,context=31)
139 call_pdb=self.call_pdb,
140 )
141 if self.call_pdb:
142 TBhandler(etype,evalue,etb)
143 return
144 else:
145 traceback = TBhandler.text(etype,evalue,etb,context=31)
139
146
140 # print traceback to screen
147 # print traceback to screen
141 if self.show_crash_traceback:
148 if self.show_crash_traceback:
@@ -155,74 +162,66 b' $self.bug_tracker'
155 # Construct report on disk
162 # Construct report on disk
156 report.write(self.make_report(traceback))
163 report.write(self.make_report(traceback))
157 report.close()
164 report.close()
158 raw_input("Press enter to exit:")
165 raw_input("Hit <Enter> to quit this message (your terminal may close):")
159
166
160 def make_report(self,traceback):
167 def make_report(self,traceback):
161 """Return a string containing a crash report."""
168 """Return a string containing a crash report."""
162
169 import platform
163 sec_sep = '\n\n'+'*'*75+'\n\n'
170
164
171 sec_sep = self.section_sep
172
165 report = []
173 report = []
166 rpt_add = report.append
174 rpt_add = report.append
167
175
168 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
176 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
169 rpt_add('IPython version: %s \n\n' % release.version)
177 rpt_add('IPython version: %s \n' % release.version)
170 rpt_add('BZR revision : %s \n\n' % release.revision)
178 rpt_add('BZR revision : %s \n' % release.revision)
171 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
179 rpt_add('Platform info : os.name -> %s, sys.platform -> %s\n' %
172 (os.name,sys.platform) )
180 (os.name,sys.platform) )
173 rpt_add(sec_sep+'Current user configuration structure:\n\n')
181 rpt_add(' : %s\n' % platform.platform())
174 rpt_add(pformat(self.IP.dict()))
182 rpt_add('Python info : %s\n' % sys.version)
175 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
183
176 try:
184 try:
177 rpt_add(sec_sep+"History of session input:")
185 config = pformat(self.app.config)
178 for line in self.IP.user_ns['_ih']:
186 rpt_add(sec_sep+'Current user configuration structure:\n\n')
179 rpt_add(line)
187 rpt_add(config)
180 rpt_add('\n*** Last line of input (may not be in above history):\n')
181 rpt_add(self.IP._last_input_line+'\n')
182 except:
188 except:
183 pass
189 pass
190 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
184
191
185 return ''.join(report)
192 return ''.join(report)
186
193
194
187 class IPythonCrashHandler(CrashHandler):
195 class IPythonCrashHandler(CrashHandler):
188 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
196 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
189
197
190 def __init__(self,IP):
198 def __init__(self, app, app_name='IPython'):
191
199
192 # Set here which of the IPython authors should be listed as contact
200 # Set here which of the IPython authors should be listed as contact
193 AUTHOR_CONTACT = 'Fernando'
201 AUTHOR_CONTACT = 'Fernando'
194
202
195 # Set argument defaults
203 # Set argument defaults
196 app_name = 'IPython'
197 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
204 bug_tracker = 'https://bugs.launchpad.net/ipython/+filebug'
198 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
205 contact_name,contact_email = release.authors[AUTHOR_CONTACT][:2]
199 crash_report_fname = 'IPython_crash_report.txt'
206 crash_report_fname = 'IPython_crash_report.txt'
200 # Call parent constructor
207 # Call parent constructor
201 CrashHandler.__init__(self,IP,app_name,contact_name,contact_email,
208 CrashHandler.__init__(self,app,app_name,contact_name,contact_email,
202 bug_tracker,crash_report_fname)
209 bug_tracker,crash_report_fname)
203
210
204 def make_report(self,traceback):
211 def make_report(self,traceback):
205 """Return a string containing a crash report."""
212 """Return a string containing a crash report."""
206
213
207 sec_sep = '\n\n'+'*'*75+'\n\n'
214 sec_sep = self.section_sep
208
215 # Start with parent report
209 report = []
216 report = [super(IPythonCrashHandler, self).make_report(traceback)]
217 # Add interactive-specific info we may have
210 rpt_add = report.append
218 rpt_add = report.append
211
212 rpt_add('*'*75+'\n\n'+'IPython post-mortem report\n\n')
213 rpt_add('IPython version: %s \n\n' % release.version)
214 rpt_add('BZR revision : %s \n\n' % release.revision)
215 rpt_add('Platform info : os.name -> %s, sys.platform -> %s' %
216 (os.name,sys.platform) )
217 rpt_add(sec_sep+'Current user configuration structure:\n\n')
218 # rpt_add(pformat(self.IP.dict()))
219 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
220 try:
219 try:
221 rpt_add(sec_sep+"History of session input:")
220 rpt_add(sec_sep+"History of session input:")
222 for line in self.IP.user_ns['_ih']:
221 for line in self.app.shell.user_ns['_ih']:
223 rpt_add(line)
222 rpt_add(line)
224 rpt_add('\n*** Last line of input (may not be in above history):\n')
223 rpt_add('\n*** Last line of input (may not be in above history):\n')
225 rpt_add(self.IP._last_input_line+'\n')
224 rpt_add(self.app.shell._last_input_line+'\n')
226 except:
225 except:
227 pass
226 pass
228
227
@@ -70,6 +70,7 b' def BdbQuit_excepthook(et,ev,tb):'
70 def BdbQuit_IPython_excepthook(self,et,ev,tb):
70 def BdbQuit_IPython_excepthook(self,et,ev,tb):
71 print 'Exiting Debugger.'
71 print 'Exiting Debugger.'
72
72
73
73 class Tracer(object):
74 class Tracer(object):
74 """Class for local debugging, similar to pdb.set_trace.
75 """Class for local debugging, similar to pdb.set_trace.
75
76
@@ -105,12 +106,10 b' class Tracer(object):'
105 from the Python standard library for usage details.
106 from the Python standard library for usage details.
106 """
107 """
107
108
108 global __IPYTHON__
109 try:
109 try:
110 __IPYTHON__
110 ip = ipapi.get()
111 except NameError:
111 except:
112 # Outside of ipython, we set our own exception hook manually
112 # Outside of ipython, we set our own exception hook manually
113 __IPYTHON__ = ipapi.get()
114 BdbQuit_excepthook.excepthook_ori = sys.excepthook
113 BdbQuit_excepthook.excepthook_ori = sys.excepthook
115 sys.excepthook = BdbQuit_excepthook
114 sys.excepthook = BdbQuit_excepthook
116 def_colors = 'NoColor'
115 def_colors = 'NoColor'
@@ -122,9 +121,8 b' class Tracer(object):'
122 pass
121 pass
123 else:
122 else:
124 # In ipython, we use its custom exception handler mechanism
123 # In ipython, we use its custom exception handler mechanism
125 ip = ipapi.get()
126 def_colors = ip.colors
124 def_colors = ip.colors
127 ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
125 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
128
126
129 if colors is None:
127 if colors is None:
130 colors = def_colors
128 colors = def_colors
@@ -138,6 +136,7 b' class Tracer(object):'
138
136
139 self.debugger.set_trace(sys._getframe().f_back)
137 self.debugger.set_trace(sys._getframe().f_back)
140
138
139
141 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
140 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
142 """Make new_fn have old_fn's doc string. This is particularly useful
141 """Make new_fn have old_fn's doc string. This is particularly useful
143 for the do_... commands that hook into the help system.
142 for the do_... commands that hook into the help system.
@@ -149,6 +148,7 b' def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):'
149 wrapper.__doc__ = old_fn.__doc__ + additional_text
148 wrapper.__doc__ = old_fn.__doc__ + additional_text
150 return wrapper
149 return wrapper
151
150
151
152 def _file_lines(fname):
152 def _file_lines(fname):
153 """Return the contents of a named file as a list of lines.
153 """Return the contents of a named file as a list of lines.
154
154
@@ -164,143 +164,98 b' def _file_lines(fname):'
164 outfile.close()
164 outfile.close()
165 return out
165 return out
166
166
167
167 class Pdb(OldPdb):
168 class Pdb(OldPdb):
168 """Modified Pdb class, does not load readline."""
169 """Modified Pdb class, does not load readline."""
169
170
170 if sys.version[:3] >= '2.5' or has_pydb:
171 def __init__(self,color_scheme='NoColor',completekey=None,
171 def __init__(self,color_scheme='NoColor',completekey=None,
172 stdin=None, stdout=None):
172 stdin=None, stdout=None):
173
173
174 # Parent constructor:
174 # Parent constructor:
175 if has_pydb and completekey is None:
175 if has_pydb and completekey is None:
176 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
176 OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
177 else:
177 else:
178 OldPdb.__init__(self,completekey,stdin,stdout)
178 OldPdb.__init__(self,completekey,stdin,stdout)
179
180 self.prompt = prompt # The default prompt is '(Pdb)'
181
179
182 # IPython changes...
180 self.prompt = prompt # The default prompt is '(Pdb)'
183 self.is_pydb = has_pydb
181
184
182 # IPython changes...
185 if self.is_pydb:
183 self.is_pydb = has_pydb
186
187 # iplib.py's ipalias seems to want pdb's checkline
188 # which located in pydb.fn
189 import pydb.fns
190 self.checkline = lambda filename, lineno: \
191 pydb.fns.checkline(self, filename, lineno)
192
193 self.curframe = None
194 self.do_restart = self.new_do_restart
195
196 self.old_all_completions = __IPYTHON__.Completer.all_completions
197 __IPYTHON__.Completer.all_completions=self.all_completions
198
199 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
200 OldPdb.do_list)
201 self.do_l = self.do_list
202 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
203 OldPdb.do_frame)
204
205 self.aliases = {}
206
207 # Create color table: we copy the default one from the traceback
208 # module and add a few attributes needed for debugging
209 self.color_scheme_table = exception_colors()
210
184
211 # shorthands
185 self.shell = ipapi.get()
212 C = coloransi.TermColors
213 cst = self.color_scheme_table
214
186
215 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
187 if self.is_pydb:
216 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
217
188
218 cst['Linux'].colors.breakpoint_enabled = C.LightRed
189 # iplib.py's ipalias seems to want pdb's checkline
219 cst['Linux'].colors.breakpoint_disabled = C.Red
190 # which located in pydb.fn
191 import pydb.fns
192 self.checkline = lambda filename, lineno: \
193 pydb.fns.checkline(self, filename, lineno)
220
194
221 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
195 self.curframe = None
222 cst['LightBG'].colors.breakpoint_disabled = C.Red
196 self.do_restart = self.new_do_restart
223
197
224 self.set_colors(color_scheme)
198 self.old_all_completions = self.shell.Completer.all_completions
199 self.shell.Completer.all_completions=self.all_completions
225
200
226 # Add a python parser so we can syntax highlight source while
201 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
227 # debugging.
202 OldPdb.do_list)
228 self.parser = PyColorize.Parser()
203 self.do_l = self.do_list
204 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
205 OldPdb.do_frame)
229
206
207 self.aliases = {}
230
208
231 else:
209 # Create color table: we copy the default one from the traceback
232 # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
210 # module and add a few attributes needed for debugging
233 # because it binds readline and breaks tab-completion. This means we
211 self.color_scheme_table = exception_colors()
234 # have to COPY the constructor here.
235 def __init__(self,color_scheme='NoColor'):
236 bdb.Bdb.__init__(self)
237 cmd.Cmd.__init__(self,completekey=None) # don't load readline
238 self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
239 self.aliases = {}
240
241 # These two lines are part of the py2.4 constructor, let's put them
242 # unconditionally here as they won't cause any problems in 2.3.
243 self.mainpyfile = ''
244 self._wait_for_mainpyfile = 0
245
246 # Read $HOME/.pdbrc and ./.pdbrc
247 try:
248 self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
249 ".pdbrc"))
250 except KeyError:
251 self.rcLines = []
252 self.rcLines.extend(_file_lines(".pdbrc"))
253
212
254 # Create color table: we copy the default one from the traceback
213 # shorthands
255 # module and add a few attributes needed for debugging
214 C = coloransi.TermColors
256 self.color_scheme_table = exception_colors()
215 cst = self.color_scheme_table
257
216
258 # shorthands
217 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
259 C = coloransi.TermColors
218 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
260 cst = self.color_scheme_table
261
219
262 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
220 cst['Linux'].colors.breakpoint_enabled = C.LightRed
263 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
221 cst['Linux'].colors.breakpoint_disabled = C.Red
264
222
265 cst['Linux'].colors.breakpoint_enabled = C.LightRed
223 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['Linux'].colors.breakpoint_disabled = C.Red
224 cst['LightBG'].colors.breakpoint_disabled = C.Red
267
225
268 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
226 self.set_colors(color_scheme)
269 cst['LightBG'].colors.breakpoint_disabled = C.Red
270
227
271 self.set_colors(color_scheme)
228 # Add a python parser so we can syntax highlight source while
229 # debugging.
230 self.parser = PyColorize.Parser()
272
231
273 # Add a python parser so we can syntax highlight source while
274 # debugging.
275 self.parser = PyColorize.Parser()
276
277 def set_colors(self, scheme):
232 def set_colors(self, scheme):
278 """Shorthand access to the color table scheme selector method."""
233 """Shorthand access to the color table scheme selector method."""
279 self.color_scheme_table.set_active_scheme(scheme)
234 self.color_scheme_table.set_active_scheme(scheme)
280
235
281 def interaction(self, frame, traceback):
236 def interaction(self, frame, traceback):
282 __IPYTHON__.set_completer_frame(frame)
237 self.shell.set_completer_frame(frame)
283 OldPdb.interaction(self, frame, traceback)
238 OldPdb.interaction(self, frame, traceback)
284
239
285 def new_do_up(self, arg):
240 def new_do_up(self, arg):
286 OldPdb.do_up(self, arg)
241 OldPdb.do_up(self, arg)
287 __IPYTHON__.set_completer_frame(self.curframe)
242 self.shell.set_completer_frame(self.curframe)
288 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
243 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
289
244
290 def new_do_down(self, arg):
245 def new_do_down(self, arg):
291 OldPdb.do_down(self, arg)
246 OldPdb.do_down(self, arg)
292 __IPYTHON__.set_completer_frame(self.curframe)
247 self.shell.set_completer_frame(self.curframe)
293
248
294 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
249 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
295
250
296 def new_do_frame(self, arg):
251 def new_do_frame(self, arg):
297 OldPdb.do_frame(self, arg)
252 OldPdb.do_frame(self, arg)
298 __IPYTHON__.set_completer_frame(self.curframe)
253 self.shell.set_completer_frame(self.curframe)
299
254
300 def new_do_quit(self, arg):
255 def new_do_quit(self, arg):
301
256
302 if hasattr(self, 'old_all_completions'):
257 if hasattr(self, 'old_all_completions'):
303 __IPYTHON__.Completer.all_completions=self.old_all_completions
258 self.shell.Completer.all_completions=self.old_all_completions
304
259
305
260
306 return OldPdb.do_quit(self, arg)
261 return OldPdb.do_quit(self, arg)
@@ -314,7 +269,7 b' class Pdb(OldPdb):'
314 return self.do_quit(arg)
269 return self.do_quit(arg)
315
270
316 def postloop(self):
271 def postloop(self):
317 __IPYTHON__.set_completer_frame(None)
272 self.shell.set_completer_frame(None)
318
273
319 def print_stack_trace(self):
274 def print_stack_trace(self):
320 try:
275 try:
@@ -331,7 +286,7 b' class Pdb(OldPdb):'
331 # vds: >>
286 # vds: >>
332 frame, lineno = frame_lineno
287 frame, lineno = frame_lineno
333 filename = frame.f_code.co_filename
288 filename = frame.f_code.co_filename
334 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
289 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
335 # vds: <<
290 # vds: <<
336
291
337 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
292 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
@@ -500,7 +455,7 b' class Pdb(OldPdb):'
500 # vds: >>
455 # vds: >>
501 lineno = first
456 lineno = first
502 filename = self.curframe.f_code.co_filename
457 filename = self.curframe.f_code.co_filename
503 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
458 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
504 # vds: <<
459 # vds: <<
505
460
506 do_l = do_list
461 do_l = do_list
@@ -509,16 +464,49 b' class Pdb(OldPdb):'
509 """The debugger interface to magic_pdef"""
464 """The debugger interface to magic_pdef"""
510 namespaces = [('Locals', self.curframe.f_locals),
465 namespaces = [('Locals', self.curframe.f_locals),
511 ('Globals', self.curframe.f_globals)]
466 ('Globals', self.curframe.f_globals)]
512 __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
467 self.shell.magic_pdef(arg, namespaces=namespaces)
513
468
514 def do_pdoc(self, arg):
469 def do_pdoc(self, arg):
515 """The debugger interface to magic_pdoc"""
470 """The debugger interface to magic_pdoc"""
516 namespaces = [('Locals', self.curframe.f_locals),
471 namespaces = [('Locals', self.curframe.f_locals),
517 ('Globals', self.curframe.f_globals)]
472 ('Globals', self.curframe.f_globals)]
518 __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
473 self.shell.magic_pdoc(arg, namespaces=namespaces)
519
474
520 def do_pinfo(self, arg):
475 def do_pinfo(self, arg):
521 """The debugger equivalant of ?obj"""
476 """The debugger equivalant of ?obj"""
522 namespaces = [('Locals', self.curframe.f_locals),
477 namespaces = [('Locals', self.curframe.f_locals),
523 ('Globals', self.curframe.f_globals)]
478 ('Globals', self.curframe.f_globals)]
524 __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
479 self.shell.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
480
481 def checkline(self, filename, lineno):
482 """Check whether specified line seems to be executable.
483
484 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
485 line or EOF). Warning: testing is not comprehensive.
486 """
487 #######################################################################
488 # XXX Hack! Use python-2.5 compatible code for this call, because with
489 # all of our changes, we've drifted from the pdb api in 2.6. For now,
490 # changing:
491 #
492 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
493 # to:
494 #
495 line = linecache.getline(filename, lineno)
496 #
497 # does the trick. But in reality, we need to fix this by reconciling
498 # our updates with the new Pdb APIs in Python 2.6.
499 #
500 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
501 #######################################################################
502
503 if not line:
504 print >>self.stdout, 'End of file'
505 return 0
506 line = line.strip()
507 # Don't allow setting breakpoint at a blank line
508 if (not line or (line[0] == '#') or
509 (line[:3] == '"""') or line[:3] == "'''"):
510 print >>self.stdout, '*** Blank or comment'
511 return 0
512 return lineno
@@ -46,11 +46,11 b' class DisplayTrap(Component):'
46 # 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.
47 self._nested_level = 0
47 self._nested_level = 0
48
48
49 @auto_attr
49 # @auto_attr
50 def shell(self):
50 # def shell(self):
51 return Component.get_instances(
51 # return Component.get_instances(
52 root=self.root,
52 # root=self.root,
53 klass='IPython.core.iplib.InteractiveShell')[0]
53 # klass='IPython.core.iplib.InteractiveShell')[0]
54
54
55 def __enter__(self):
55 def __enter__(self):
56 if self._nested_level == 0:
56 if self._nested_level == 0:
@@ -68,7 +68,7 b' class InteractiveShellEmbed(InteractiveShell):'
68 # is True by default.
68 # is True by default.
69 display_banner = CBool(True)
69 display_banner = CBool(True)
70
70
71 def __init__(self, parent=None, config=None, ipythondir=None, usage=None,
71 def __init__(self, parent=None, config=None, ipython_dir=None, usage=None,
72 user_ns=None, user_global_ns=None,
72 user_ns=None, user_global_ns=None,
73 banner1=None, banner2=None, display_banner=None,
73 banner1=None, banner2=None, display_banner=None,
74 custom_exceptions=((),None), exit_msg=''):
74 custom_exceptions=((),None), exit_msg=''):
@@ -76,7 +76,7 b' class InteractiveShellEmbed(InteractiveShell):'
76 self.save_sys_ipcompleter()
76 self.save_sys_ipcompleter()
77
77
78 super(InteractiveShellEmbed,self).__init__(
78 super(InteractiveShellEmbed,self).__init__(
79 parent=parent, config=config, ipythondir=ipythondir, usage=usage,
79 parent=parent, config=config, ipython_dir=ipython_dir, usage=usage,
80 user_ns=user_ns, user_global_ns=user_global_ns,
80 user_ns=user_ns, user_global_ns=user_global_ns,
81 banner1=banner1, banner2=banner2, display_banner=display_banner,
81 banner1=banner1, banner2=banner2, display_banner=display_banner,
82 custom_exceptions=custom_exceptions)
82 custom_exceptions=custom_exceptions)
@@ -233,14 +233,6 b' class InteractiveShellEmbed(InteractiveShell):'
233 for var in local_varnames:
233 for var in local_varnames:
234 delvar(var,None)
234 delvar(var,None)
235
235
236 def set_completer_frame(self, frame=None):
237 if frame:
238 self.Completer.namespace = frame.f_locals
239 self.Completer.global_namespace = frame.f_globals
240 else:
241 self.Completer.namespace = self.user_ns
242 self.Completer.global_namespace = self.user_global_ns
243
244
236
245 _embedded_shell = None
237 _embedded_shell = None
246
238
@@ -14,20 +14,25 b" def magic_history(self, parameter_s = ''):"
14 %history -> print at most 40 inputs (some may be multi-line)\\
14 %history -> print at most 40 inputs (some may be multi-line)\\
15 %history n -> print at most n inputs\\
15 %history n -> print at most n inputs\\
16 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
16 %history n1 n2 -> print inputs between n1 and n2 (n2 not included)\\
17
18 Each input's number <n> is shown, and is accessible as the
19 automatically generated variable _i<n>. Multi-line statements are
20 printed starting at a new line for easy copy/paste.
21
22
17
23 Options:
18 By default, input history is printed without line numbers so it can be
19 directly pasted into an editor.
24
20
25 -n: do NOT print line numbers. This is useful if you want to get a
21 With -n, each input's number <n> is shown, and is accessible as the
26 printout of many lines which can be directly pasted into a text
22 automatically generated variable _i<n> as well as In[<n>]. Multi-line
27 editor.
23 statements are printed starting at a new line for easy copy/paste.
24
25 Options:
28
26
27 -n: print line numbers for each input.
29 This feature is only available if numbered prompts are in use.
28 This feature is only available if numbered prompts are in use.
30
29
30 -o: also print outputs for each input.
31
32 -p: print classic '>>>' python prompts before each input. This is useful
33 for making documentation, and in conjunction with -o, for producing
34 doctest-ready output.
35
31 -t: (default) print the 'translated' history, as IPython understands it.
36 -t: (default) print the 'translated' history, as IPython understands it.
32 IPython filters your input and converts it all into valid Python source
37 IPython filters your input and converts it all into valid Python source
33 before executing it (things like magics or aliases are turned into
38 before executing it (things like magics or aliases are turned into
@@ -50,7 +55,7 b" def magic_history(self, parameter_s = ''):"
50 if not self.outputcache.do_full_cache:
55 if not self.outputcache.do_full_cache:
51 print 'This feature is only available if numbered prompts are in use.'
56 print 'This feature is only available if numbered prompts are in use.'
52 return
57 return
53 opts,args = self.parse_options(parameter_s,'gntsrf:',mode='list')
58 opts,args = self.parse_options(parameter_s,'gnoptsrf:',mode='list')
54
59
55 # Check if output to specific file was requested.
60 # Check if output to specific file was requested.
56 try:
61 try:
@@ -97,9 +102,12 b" def magic_history(self, parameter_s = ''):"
97 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
102 warn('%hist takes 0, 1 or 2 arguments separated by spaces.')
98 print self.magic_hist.__doc__
103 print self.magic_hist.__doc__
99 return
104 return
105
100 width = len(str(final))
106 width = len(str(final))
101 line_sep = ['','\n']
107 line_sep = ['','\n']
102 print_nums = not opts.has_key('n')
108 print_nums = 'n' in opts
109 print_outputs = 'o' in opts
110 pyprompts = 'p' in opts
103
111
104 found = False
112 found = False
105 if pattern is not None:
113 if pattern is not None:
@@ -123,7 +131,19 b" def magic_history(self, parameter_s = ''):"
123 if print_nums:
131 if print_nums:
124 print >> outfile, \
132 print >> outfile, \
125 '%s:%s' % (str(in_num).ljust(width),line_sep[multiline]),
133 '%s:%s' % (str(in_num).ljust(width),line_sep[multiline]),
126 print >> outfile, inline,
134 if pyprompts:
135 print >> outfile, '>>>',
136 if multiline:
137 lines = inline.splitlines()
138 print >> outfile, '\n... '.join(lines)
139 print >> outfile, '... '
140 else:
141 print >> outfile, inline,
142 else:
143 print >> outfile, inline,
144 output = self.shell.user_ns['Out'].get(in_num)
145 if output is not None:
146 print repr(output)
127
147
128 if close_at_end:
148 if close_at_end:
129 outfile.close()
149 outfile.close()
@@ -245,10 +265,10 b' class ShadowHist(object):'
245
265
246
266
247 def init_ipython(ip):
267 def init_ipython(ip):
248 import ipy_completers
249
250 ip.define_magic("rep",rep_f)
268 ip.define_magic("rep",rep_f)
251 ip.define_magic("hist",magic_hist)
269 ip.define_magic("hist",magic_hist)
252 ip.define_magic("history",magic_history)
270 ip.define_magic("history",magic_history)
253
271
254 ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
272 # XXX - ipy_completers are in quarantine, need to be updated to new apis
273 #import ipy_completers
274 #ipy_completers.quick_completer('%hist' ,'-g -t -r -n')
@@ -137,8 +137,7 b' class CommandChainDispatcher:'
137 for prio,cmd in self.chain:
137 for prio,cmd in self.chain:
138 #print "prio",prio,"cmd",cmd #dbg
138 #print "prio",prio,"cmd",cmd #dbg
139 try:
139 try:
140 ret = cmd(*args, **kw)
140 return cmd(*args, **kw)
141 return ret
142 except TryNext, exc:
141 except TryNext, exc:
143 if exc.args or exc.kwargs:
142 if exc.args or exc.kwargs:
144 args = exc.args
143 args = exc.args
@@ -18,16 +18,19 b' has been made into a component, this module will be sent to deathrow.'
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 from IPython.core.error import TryNext, UsageError
21 from IPython.core.error import TryNext, UsageError, IPythonCoreError
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Classes and functions
24 # Classes and functions
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27 def get():
28 def get():
28 """Get the most recently created InteractiveShell instance."""
29 """Get the most recently created InteractiveShell instance."""
29 from IPython.core.iplib import InteractiveShell
30 from IPython.core.iplib import InteractiveShell
30 insts = InteractiveShell.get_instances()
31 insts = InteractiveShell.get_instances()
32 if len(insts)==0:
33 return None
31 most_recent = insts[0]
34 most_recent = insts[0]
32 for inst in insts[1:]:
35 for inst in insts[1:]:
33 if inst.created > most_recent.created:
36 if inst.created > most_recent.created:
This diff has been collapsed as it changes many lines, (579 lines changed) Show them Hide them
@@ -1,19 +1,18 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The main IPython application object
4 The :class:`~IPython.core.application.Application` object for the command
5 line :command:`ipython` program.
5
6
6 Authors:
7 Authors
8 -------
7
9
8 * Brian Granger
10 * Brian Granger
9 * Fernando Perez
11 * Fernando Perez
10
11 Notes
12 -----
13 """
12 """
14
13
15 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2009 The IPython Development Team
15 # Copyright (C) 2008-2010 The IPython Development Team
17 #
16 #
18 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
@@ -22,321 +21,405 b' Notes'
22 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
23 # Imports
22 # Imports
24 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 from __future__ import absolute_import
25
25
26 import logging
26 import logging
27 import os
27 import os
28 import sys
28 import sys
29 import warnings
30
29
31 from IPython.core.application import Application, IPythonArgParseConfigLoader
30 from IPython.core import crashhandler
32 from IPython.core import release
31 from IPython.core.application import Application
33 from IPython.core.iplib import InteractiveShell
32 from IPython.core.iplib import InteractiveShell
34 from IPython.config.loader import (
33 from IPython.config.loader import (
35 NoConfigDefault,
36 Config,
34 Config,
37 ConfigError,
35 PyFileConfigLoader,
38 PyFileConfigLoader
36 # NoConfigDefault,
39 )
37 )
40
41 from IPython.lib import inputhook
38 from IPython.lib import inputhook
42
43 from IPython.utils.ipstruct import Struct
44 from IPython.utils.genutils import filefind, get_ipython_dir
39 from IPython.utils.genutils import filefind, get_ipython_dir
40 from . import usage
45
41
46 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
47 # Utilities and helpers
43 # Globals, utilities and helpers
48 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
49
45
50
46 default_config_file_name = u'ipython_config.py'
51 ipython_desc = """
52 A Python shell with automatic history (input and output), dynamic object
53 introspection, easier configuration, command completion, access to the system
54 shell and more.
55 """
56
57 def pylab_warning():
58 msg = """
59
60 IPython's -pylab mode has been disabled until matplotlib supports this version
61 of IPython. This version of IPython has greatly improved GUI integration that
62 matplotlib will soon be able to take advantage of. This will eventually
63 result in greater stability and a richer API for matplotlib under IPython.
64 However during this transition, you will either need to use an older version
65 of IPython, or do the following to use matplotlib interactively::
66
67 import matplotlib
68 matplotlib.interactive(True)
69 matplotlib.use('wxagg') # adjust for your backend
70 %gui -a wx # adjust for your GUI
71 from matplotlib import pyplot as plt
72
73 See the %gui magic for information on the new interface.
74 """
75 warnings.warn(msg, category=DeprecationWarning, stacklevel=1)
76
77
78 #-----------------------------------------------------------------------------
79 # Main classes and functions
80 #-----------------------------------------------------------------------------
81
47
82 cl_args = (
48 cl_args = (
83 (('-autocall',), dict(
49 (('--autocall',), dict(
84 type=int, dest='InteractiveShell.autocall', default=NoConfigDefault,
50 type=int, dest='InteractiveShell.autocall',
85 help='Set the autocall value (0,1,2).',
51 help=
52 """Make IPython automatically call any callable object even if you
53 didn't type explicit parentheses. For example, 'str 43' becomes
54 'str(43)' automatically. The value can be '0' to disable the feature,
55 '1' for 'smart' autocall, where it is not applied if there are no more
56 arguments on the line, and '2' for 'full' autocall, where all callable
57 objects are automatically called (even if no arguments are present).
58 The default is '1'.""",
86 metavar='InteractiveShell.autocall')
59 metavar='InteractiveShell.autocall')
87 ),
60 ),
88 (('-autoindent',), dict(
61 (('--autoindent',), dict(
89 action='store_true', dest='InteractiveShell.autoindent', default=NoConfigDefault,
62 action='store_true', dest='InteractiveShell.autoindent',
90 help='Turn on autoindenting.')
63 help='Turn on autoindenting.')
91 ),
64 ),
92 (('-noautoindent',), dict(
65 (('--no-autoindent',), dict(
93 action='store_false', dest='InteractiveShell.autoindent', default=NoConfigDefault,
66 action='store_false', dest='InteractiveShell.autoindent',
94 help='Turn off autoindenting.')
67 help='Turn off autoindenting.')
95 ),
68 ),
96 (('-automagic',), dict(
69 (('--automagic',), dict(
97 action='store_true', dest='InteractiveShell.automagic', default=NoConfigDefault,
70 action='store_true', dest='InteractiveShell.automagic',
98 help='Turn on the auto calling of magic commands.')
71 help='Turn on the auto calling of magic commands.'
99 ),
72 'Type %%magic at the IPython prompt for more information.')
100 (('-noautomagic',), dict(
73 ),
101 action='store_false', dest='InteractiveShell.automagic', default=NoConfigDefault,
74 (('--no-automagic',), dict(
75 action='store_false', dest='InteractiveShell.automagic',
102 help='Turn off the auto calling of magic commands.')
76 help='Turn off the auto calling of magic commands.')
103 ),
77 ),
104 (('-autoedit_syntax',), dict(
78 (('--autoedit-syntax',), dict(
105 action='store_true', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
79 action='store_true', dest='InteractiveShell.autoedit_syntax',
106 help='Turn on auto editing of files with syntax errors.')
80 help='Turn on auto editing of files with syntax errors.')
107 ),
81 ),
108 (('-noautoedit_syntax',), dict(
82 (('--no-autoedit-syntax',), dict(
109 action='store_false', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
83 action='store_false', dest='InteractiveShell.autoedit_syntax',
110 help='Turn off auto editing of files with syntax errors.')
84 help='Turn off auto editing of files with syntax errors.')
111 ),
85 ),
112 (('-banner',), dict(
86 (('--banner',), dict(
113 action='store_true', dest='Global.display_banner', default=NoConfigDefault,
87 action='store_true', dest='Global.display_banner',
114 help='Display a banner upon starting IPython.')
88 help='Display a banner upon starting IPython.')
115 ),
89 ),
116 (('-nobanner',), dict(
90 (('--no-banner',), dict(
117 action='store_false', dest='Global.display_banner', default=NoConfigDefault,
91 action='store_false', dest='Global.display_banner',
118 help="Don't display a banner upon starting IPython.")
92 help="Don't display a banner upon starting IPython.")
119 ),
93 ),
120 (('-cache_size',), dict(
94 (('--cache-size',), dict(
121 type=int, dest='InteractiveShell.cache_size', default=NoConfigDefault,
95 type=int, dest='InteractiveShell.cache_size',
122 help="Set the size of the output cache.",
96 help=
97 """Set the size of the output cache. The default is 1000, you can
98 change it permanently in your config file. Setting it to 0 completely
99 disables the caching system, and the minimum value accepted is 20 (if
100 you provide a value less than 20, it is reset to 0 and a warning is
101 issued). This limit is defined because otherwise you'll spend more
102 time re-flushing a too small cache than working.
103 """,
123 metavar='InteractiveShell.cache_size')
104 metavar='InteractiveShell.cache_size')
124 ),
105 ),
125 (('-classic',), dict(
106 (('--classic',), dict(
126 action='store_true', dest='Global.classic', default=NoConfigDefault,
107 action='store_true', dest='Global.classic',
127 help="Gives IPython a similar feel to the classic Python prompt.")
108 help="Gives IPython a similar feel to the classic Python prompt.")
128 ),
109 ),
129 (('-colors',), dict(
110 (('--colors',), dict(
130 type=str, dest='InteractiveShell.colors', default=NoConfigDefault,
111 type=str, dest='InteractiveShell.colors',
131 help="Set the color scheme (NoColor, Linux, and LightBG).",
112 help="Set the color scheme (NoColor, Linux, and LightBG).",
132 metavar='InteractiveShell.colors')
113 metavar='InteractiveShell.colors')
133 ),
114 ),
134 (('-color_info',), dict(
115 (('--color-info',), dict(
135 action='store_true', dest='InteractiveShell.color_info', default=NoConfigDefault,
116 action='store_true', dest='InteractiveShell.color_info',
136 help="Enable using colors for info related things.")
117 help=
118 """IPython can display information about objects via a set of func-
119 tions, and optionally can use colors for this, syntax highlighting
120 source code and various other elements. However, because this
121 information is passed through a pager (like 'less') and many pagers get
122 confused with color codes, this option is off by default. You can test
123 it and turn it on permanently in your ipython_config.py file if it
124 works for you. Test it and turn it on permanently if it works with
125 your system. The magic function %%color_info allows you to toggle this
126 inter- actively for testing."""
127 )
137 ),
128 ),
138 (('-nocolor_info',), dict(
129 (('--no-color-info',), dict(
139 action='store_false', dest='InteractiveShell.color_info', default=NoConfigDefault,
130 action='store_false', dest='InteractiveShell.color_info',
140 help="Disable using colors for info related things.")
131 help="Disable using colors for info related things.")
141 ),
132 ),
142 (('-confirm_exit',), dict(
133 (('--confirm-exit',), dict(
143 action='store_true', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
134 action='store_true', dest='InteractiveShell.confirm_exit',
144 help="Prompt the user when existing.")
135 help=
145 ),
136 """Set to confirm when you try to exit IPython with an EOF (Control-D
146 (('-noconfirm_exit',), dict(
137 in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or
147 action='store_false', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
138 '%%Exit', you can force a direct exit without any confirmation.
148 help="Don't prompt the user when existing.")
139 """
149 ),
140 )
150 (('-deep_reload',), dict(
151 action='store_true', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
152 help="Enable deep (recursive) reloading by default.")
153 ),
141 ),
154 (('-nodeep_reload',), dict(
142 (('--no-confirm-exit',), dict(
155 action='store_false', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
143 action='store_false', dest='InteractiveShell.confirm_exit',
144 help="Don't prompt the user when exiting.")
145 ),
146 (('--deep-reload',), dict(
147 action='store_true', dest='InteractiveShell.deep_reload',
148 help=
149 """Enable deep (recursive) reloading by default. IPython can use the
150 deep_reload module which reloads changes in modules recursively (it
151 replaces the reload() function, so you don't need to change anything to
152 use it). deep_reload() forces a full reload of modules whose code may
153 have changed, which the default reload() function does not. When
154 deep_reload is off, IPython will use the normal reload(), but
155 deep_reload will still be available as dreload(). This fea- ture is off
156 by default [which means that you have both normal reload() and
157 dreload()].""")
158 ),
159 (('--no-deep-reload',), dict(
160 action='store_false', dest='InteractiveShell.deep_reload',
156 help="Disable deep (recursive) reloading by default.")
161 help="Disable deep (recursive) reloading by default.")
157 ),
162 ),
158 (('-editor',), dict(
163 (('--editor',), dict(
159 type=str, dest='InteractiveShell.editor', default=NoConfigDefault,
164 type=str, dest='InteractiveShell.editor',
160 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
165 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
161 metavar='InteractiveShell.editor')
166 metavar='InteractiveShell.editor')
162 ),
167 ),
163 (('-log','-l'), dict(
168 (('--log','-l'), dict(
164 action='store_true', dest='InteractiveShell.logstart', default=NoConfigDefault,
169 action='store_true', dest='InteractiveShell.logstart',
165 help="Start logging to the default file (./ipython_log.py).")
170 help="Start logging to the default log file (./ipython_log.py).")
166 ),
171 ),
167 (('-logfile','-lf'), dict(
172 (('--logfile','-lf'), dict(
168 type=str, dest='InteractiveShell.logfile', default=NoConfigDefault,
173 type=unicode, dest='InteractiveShell.logfile',
169 help="Start logging to logfile.",
174 help="Start logging to logfile with this name.",
170 metavar='InteractiveShell.logfile')
175 metavar='InteractiveShell.logfile')
171 ),
176 ),
172 (('-logappend','-la'), dict(
177 (('--log-append','-la'), dict(
173 type=str, dest='InteractiveShell.logappend', default=NoConfigDefault,
178 type=unicode, dest='InteractiveShell.logappend',
174 help="Start logging to logappend in append mode.",
179 help="Start logging to the given file in append mode.",
175 metavar='InteractiveShell.logfile')
180 metavar='InteractiveShell.logfile')
176 ),
181 ),
177 (('-pdb',), dict(
182 (('--pdb',), dict(
178 action='store_true', dest='InteractiveShell.pdb', default=NoConfigDefault,
183 action='store_true', dest='InteractiveShell.pdb',
179 help="Enable auto calling the pdb debugger after every exception.")
184 help="Enable auto calling the pdb debugger after every exception.")
180 ),
185 ),
181 (('-nopdb',), dict(
186 (('--no-pdb',), dict(
182 action='store_false', dest='InteractiveShell.pdb', default=NoConfigDefault,
187 action='store_false', dest='InteractiveShell.pdb',
183 help="Disable auto calling the pdb debugger after every exception.")
188 help="Disable auto calling the pdb debugger after every exception.")
184 ),
189 ),
185 (('-pprint',), dict(
190 (('--pprint',), dict(
186 action='store_true', dest='InteractiveShell.pprint', default=NoConfigDefault,
191 action='store_true', dest='InteractiveShell.pprint',
187 help="Enable auto pretty printing of results.")
192 help="Enable auto pretty printing of results.")
188 ),
193 ),
189 (('-nopprint',), dict(
194 (('--no-pprint',), dict(
190 action='store_false', dest='InteractiveShell.pprint', default=NoConfigDefault,
195 action='store_false', dest='InteractiveShell.pprint',
191 help="Disable auto auto pretty printing of results.")
196 help="Disable auto auto pretty printing of results.")
192 ),
197 ),
193 (('-prompt_in1','-pi1'), dict(
198 (('--prompt-in1','-pi1'), dict(
194 type=str, dest='InteractiveShell.prompt_in1', default=NoConfigDefault,
199 type=str, dest='InteractiveShell.prompt_in1',
195 help="Set the main input prompt ('In [\#]: ')",
200 help=
201 """Set the main input prompt ('In [\#]: '). Note that if you are using
202 numbered prompts, the number is represented with a '\#' in the string.
203 Don't forget to quote strings with spaces embedded in them. Most
204 bash-like escapes can be used to customize IPython's prompts, as well
205 as a few additional ones which are IPython-spe- cific. All valid
206 prompt escapes are described in detail in the Customization section of
207 the IPython manual.""",
196 metavar='InteractiveShell.prompt_in1')
208 metavar='InteractiveShell.prompt_in1')
197 ),
209 ),
198 (('-prompt_in2','-pi2'), dict(
210 (('--prompt-in2','-pi2'), dict(
199 type=str, dest='InteractiveShell.prompt_in2', default=NoConfigDefault,
211 type=str, dest='InteractiveShell.prompt_in2',
200 help="Set the secondary input prompt (' .\D.: ')",
212 help=
213 """Set the secondary input prompt (' .\D.: '). Similar to the previous
214 option, but used for the continuation prompts. The special sequence
215 '\D' is similar to '\#', but with all digits replaced by dots (so you
216 can have your continuation prompt aligned with your input prompt).
217 Default: ' .\D.: ' (note three spaces at the start for alignment with
218 'In [\#]')""",
201 metavar='InteractiveShell.prompt_in2')
219 metavar='InteractiveShell.prompt_in2')
202 ),
220 ),
203 (('-prompt_out','-po'), dict(
221 (('--prompt-out','-po'), dict(
204 type=str, dest='InteractiveShell.prompt_out', default=NoConfigDefault,
222 type=str, dest='InteractiveShell.prompt_out',
205 help="Set the output prompt ('Out[\#]:')",
223 help="Set the output prompt ('Out[\#]:')",
206 metavar='InteractiveShell.prompt_out')
224 metavar='InteractiveShell.prompt_out')
207 ),
225 ),
208 (('-quick',), dict(
226 (('--quick',), dict(
209 action='store_true', dest='Global.quick', default=NoConfigDefault,
227 action='store_true', dest='Global.quick',
210 help="Enable quick startup with no config files.")
228 help="Enable quick startup with no config files.")
211 ),
229 ),
212 (('-readline',), dict(
230 (('--readline',), dict(
213 action='store_true', dest='InteractiveShell.readline_use', default=NoConfigDefault,
231 action='store_true', dest='InteractiveShell.readline_use',
214 help="Enable readline for command line usage.")
232 help="Enable readline for command line usage.")
215 ),
233 ),
216 (('-noreadline',), dict(
234 (('--no-readline',), dict(
217 action='store_false', dest='InteractiveShell.readline_use', default=NoConfigDefault,
235 action='store_false', dest='InteractiveShell.readline_use',
218 help="Disable readline for command line usage.")
236 help="Disable readline for command line usage.")
219 ),
237 ),
220 (('-screen_length','-sl'), dict(
238 (('--screen-length','-sl'), dict(
221 type=int, dest='InteractiveShell.screen_length', default=NoConfigDefault,
239 type=int, dest='InteractiveShell.screen_length',
222 help='Number of lines on screen, used to control printing of long strings.',
240 help=
241 """Number of lines of your screen, used to control printing of very
242 long strings. Strings longer than this number of lines will be sent
243 through a pager instead of directly printed. The default value for
244 this is 0, which means IPython will auto-detect your screen size every
245 time it needs to print certain potentially long strings (this doesn't
246 change the behavior of the 'print' keyword, it's only triggered
247 internally). If for some reason this isn't working well (it needs
248 curses support), specify it yourself. Otherwise don't change the
249 default.""",
223 metavar='InteractiveShell.screen_length')
250 metavar='InteractiveShell.screen_length')
224 ),
251 ),
225 (('-separate_in','-si'), dict(
252 (('--separate-in','-si'), dict(
226 type=str, dest='InteractiveShell.separate_in', default=NoConfigDefault,
253 type=str, dest='InteractiveShell.separate_in',
227 help="Separator before input prompts. Default '\n'.",
254 help="Separator before input prompts. Default '\\n'.",
228 metavar='InteractiveShell.separate_in')
255 metavar='InteractiveShell.separate_in')
229 ),
256 ),
230 (('-separate_out','-so'), dict(
257 (('--separate-out','-so'), dict(
231 type=str, dest='InteractiveShell.separate_out', default=NoConfigDefault,
258 type=str, dest='InteractiveShell.separate_out',
232 help="Separator before output prompts. Default 0 (nothing).",
259 help="Separator before output prompts. Default 0 (nothing).",
233 metavar='InteractiveShell.separate_out')
260 metavar='InteractiveShell.separate_out')
234 ),
261 ),
235 (('-separate_out2','-so2'), dict(
262 (('--separate-out2','-so2'), dict(
236 type=str, dest='InteractiveShell.separate_out2', default=NoConfigDefault,
263 type=str, dest='InteractiveShell.separate_out2',
237 help="Separator after output prompts. Default 0 (nonight).",
264 help="Separator after output prompts. Default 0 (nonight).",
238 metavar='InteractiveShell.separate_out2')
265 metavar='InteractiveShell.separate_out2')
239 ),
266 ),
240 (('-nosep',), dict(
267 (('-no-sep',), dict(
241 action='store_true', dest='Global.nosep', default=NoConfigDefault,
268 action='store_true', dest='Global.nosep',
242 help="Eliminate all spacing between prompts.")
269 help="Eliminate all spacing between prompts.")
243 ),
270 ),
244 (('-term_title',), dict(
271 (('--term-title',), dict(
245 action='store_true', dest='InteractiveShell.term_title', default=NoConfigDefault,
272 action='store_true', dest='InteractiveShell.term_title',
246 help="Enable auto setting the terminal title.")
273 help="Enable auto setting the terminal title.")
247 ),
274 ),
248 (('-noterm_title',), dict(
275 (('--no-term-title',), dict(
249 action='store_false', dest='InteractiveShell.term_title', default=NoConfigDefault,
276 action='store_false', dest='InteractiveShell.term_title',
250 help="Disable auto setting the terminal title.")
277 help="Disable auto setting the terminal title.")
251 ),
278 ),
252 (('-xmode',), dict(
279 (('--xmode',), dict(
253 type=str, dest='InteractiveShell.xmode', default=NoConfigDefault,
280 type=str, dest='InteractiveShell.xmode',
254 help="Exception mode ('Plain','Context','Verbose')",
281 help=
282 """Exception reporting mode ('Plain','Context','Verbose'). Plain:
283 similar to python's normal traceback printing. Context: prints 5 lines
284 of context source code around each line in the traceback. Verbose:
285 similar to Context, but additionally prints the variables currently
286 visible where the exception happened (shortening their strings if too
287 long). This can potentially be very slow, if you happen to have a huge
288 data structure whose string representation is complex to compute.
289 Your computer may appear to freeze for a while with cpu usage at 100%%.
290 If this occurs, you can cancel the traceback with Ctrl-C (maybe hitting
291 it more than once).
292 """,
255 metavar='InteractiveShell.xmode')
293 metavar='InteractiveShell.xmode')
256 ),
294 ),
257 (('-ext',), dict(
295 (('--ext',), dict(
258 type=str, dest='Global.extra_extension', default=NoConfigDefault,
296 type=str, dest='Global.extra_extension',
259 help="The dotted module name of an IPython extension to load.",
297 help="The dotted module name of an IPython extension to load.",
260 metavar='Global.extra_extension')
298 metavar='Global.extra_extension')
261 ),
299 ),
262 (('-c',), dict(
300 (('-c',), dict(
263 type=str, dest='Global.code_to_run', default=NoConfigDefault,
301 type=str, dest='Global.code_to_run',
264 help="Execute the given command string.",
302 help="Execute the given command string.",
265 metavar='Global.code_to_run')
303 metavar='Global.code_to_run')
266 ),
304 ),
267 (('-i',), dict(
305 (('-i',), dict(
268 action='store_true', dest='Global.force_interact', default=NoConfigDefault,
306 action='store_true', dest='Global.force_interact',
269 help="If running code from the command line, become interactive afterwards.")
307 help=
270 ),
308 "If running code from the command line, become interactive afterwards."
271 (('-wthread',), dict(
309 )
272 action='store_true', dest='Global.wthread', default=NoConfigDefault,
310 ),
273 help="Enable wxPython event loop integration.")
274 ),
275 (('-q4thread','-qthread'), dict(
276 action='store_true', dest='Global.q4thread', default=NoConfigDefault,
277 help="Enable Qt4 event loop integration. Qt3 is no longer supported.")
278 ),
279 (('-gthread',), dict(
280 action='store_true', dest='Global.gthread', default=NoConfigDefault,
281 help="Enable GTK event loop integration.")
282 ),
283 # # These are only here to get the proper deprecation warnings
284 (('-pylab',), dict(
285 action='store_true', dest='Global.pylab', default=NoConfigDefault,
286 help="Disabled. Pylab has been disabled until matplotlib supports this version of IPython.")
287 )
288 )
289
290
291 class IPythonAppCLConfigLoader(IPythonArgParseConfigLoader):
292
311
293 arguments = cl_args
312 # Options to start with GUI control enabled from the beginning
313 (('--gui',), dict(
314 type=str, dest='Global.gui',
315 help="Enable GUI event loop integration ('qt', 'wx', 'gtk').",
316 metavar='gui-mode')
317 ),
294
318
319 (('--pylab','-pylab'), dict(
320 type=str, dest='Global.pylab',
321 nargs='?', const='auto', metavar='gui-mode',
322 help="Pre-load matplotlib and numpy for interactive use. "+
323 "If no value is given, the gui backend is matplotlib's, else use "+
324 "one of: ['tk', 'qt', 'wx', 'gtk'].")
325 ),
326
327 # Legacy GUI options. Leave them in for backwards compatibility, but the
328 # 'thread' names are really a misnomer now.
329 (('--wthread','-wthread'), dict(
330 action='store_true', dest='Global.wthread',
331 help="Enable wxPython event loop integration "+
332 "(DEPRECATED, use --gui wx)")
333 ),
334 (('--q4thread','--qthread','-q4thread','-qthread'), dict(
335 action='store_true', dest='Global.q4thread',
336 help="Enable Qt4 event loop integration. Qt3 is no longer supported. "+
337 "(DEPRECATED, use --gui qt)")
338 ),
339 (('--gthread','-gthread'), dict(
340 action='store_true', dest='Global.gthread',
341 help="Enable GTK event loop integration. "+
342 "(DEPRECATED, use --gui gtk)")
343 ),
344 )
295
345
296 _default_config_file_name = 'ipython_config.py'
346 #-----------------------------------------------------------------------------
347 # Main classes and functions
348 #-----------------------------------------------------------------------------
297
349
298 class IPythonApp(Application):
350 class IPythonApp(Application):
299 name = 'ipython'
351 name = u'ipython'
300 config_file_name = _default_config_file_name
352 #: argparse formats better the 'usage' than the 'description' field
353 description = None
354 #: usage message printed by argparse. If None, auto-generate
355 usage = usage.cl_usage
356
357 config_file_name = default_config_file_name
358
359 cl_arguments = Application.cl_arguments + cl_args
360
361 # Private and configuration attributes
362 _CrashHandler = crashhandler.IPythonCrashHandler
363
364 def __init__(self, argv=None,
365 constructor_config=None, override_config=None,
366 **shell_params):
367 """Create a new IPythonApp.
368
369 See the parent class for details on how configuration is handled.
370
371 Parameters
372 ----------
373 argv : optional, list
374 If given, used as the command-line argv environment to read arguments
375 from.
376
377 constructor_config : optional, Config
378 If given, additional config that is merged last, after internal
379 defaults, command-line and file-based configs.
380
381 override_config : optional, Config
382 If given, config that overrides all others unconditionally (except
383 for internal defaults, which ensure that all parameters exist).
384
385 shell_params : optional, dict
386 All other keywords are passed to the :class:`iplib.InteractiveShell`
387 constructor.
388 """
389 super(IPythonApp, self).__init__(argv, constructor_config,
390 override_config)
391 self.shell_params = shell_params
301
392
302 def create_default_config(self):
393 def create_default_config(self):
303 super(IPythonApp, self).create_default_config()
394 super(IPythonApp, self).create_default_config()
304 self.default_config.Global.display_banner = True
395 # Eliminate multiple lookups
396 Global = self.default_config.Global
397
398 # Set all default values
399 Global.display_banner = True
305
400
306 # If the -c flag is given or a file is given to run at the cmd line
401 # If the -c flag is given or a file is given to run at the cmd line
307 # like "ipython foo.py", normally we exit without starting the main
402 # like "ipython foo.py", normally we exit without starting the main
308 # loop. The force_interact config variable allows a user to override
403 # loop. The force_interact config variable allows a user to override
309 # this and interact. It is also set by the -i cmd line flag, just
404 # this and interact. It is also set by the -i cmd line flag, just
310 # like Python.
405 # like Python.
311 self.default_config.Global.force_interact = False
406 Global.force_interact = False
312
407
313 # By default always interact by starting the IPython mainloop.
408 # By default always interact by starting the IPython mainloop.
314 self.default_config.Global.interact = True
409 Global.interact = True
315
316 # Let the parent class set the default, but each time log_level
317 # changes from config, we need to update self.log_level as that is
318 # what updates the actual log level in self.log.
319 self.default_config.Global.log_level = self.log_level
320
410
321 # No GUI integration by default
411 # No GUI integration by default
322 self.default_config.Global.wthread = False
412 Global.gui = False
323 self.default_config.Global.q4thread = False
413 # Pylab off by default
324 self.default_config.Global.gthread = False
414 Global.pylab = False
325
415
326 def create_command_line_config(self):
416 # Deprecated versions of gui support that used threading, we support
327 """Create and return a command line config loader."""
417 # them just for bacwards compatibility as an alternate spelling for
328 return IPythonAppCLConfigLoader(
418 # '--gui X'
329 description=ipython_desc,
419 Global.qthread = False
330 version=release.version)
420 Global.q4thread = False
331
421 Global.wthread = False
332 def post_load_command_line_config(self):
422 Global.gthread = False
333 """Do actions after loading cl config."""
334 clc = self.command_line_config
335
336 # Display the deprecation warnings about threaded shells
337 if hasattr(clc.Global, 'pylab'):
338 pylab_warning()
339 del clc.Global['pylab']
340
423
341 def load_file_config(self):
424 def load_file_config(self):
342 if hasattr(self.command_line_config.Global, 'quick'):
425 if hasattr(self.command_line_config.Global, 'quick'):
@@ -379,8 +462,7 b' class IPythonApp(Application):'
379 # unless the -i flag (Global.force_interact) is true.
462 # unless the -i flag (Global.force_interact) is true.
380 code_to_run = config.Global.get('code_to_run','')
463 code_to_run = config.Global.get('code_to_run','')
381 file_to_run = False
464 file_to_run = False
382 if len(self.extra_args)>=1:
465 if self.extra_args and self.extra_args[0]:
383 if self.extra_args[0]:
384 file_to_run = True
466 file_to_run = True
385 if file_to_run or code_to_run:
467 if file_to_run or code_to_run:
386 if not config.Global.force_interact:
468 if not config.Global.force_interact:
@@ -392,10 +474,8 b' class IPythonApp(Application):'
392 sys.path.insert(0, '')
474 sys.path.insert(0, '')
393
475
394 # Create an InteractiveShell instance
476 # Create an InteractiveShell instance
395 self.shell = InteractiveShell(
477 self.shell = InteractiveShell(None, self.master_config,
396 parent=None,
478 **self.shell_params )
397 config=self.master_config
398 )
399
479
400 def post_construct(self):
480 def post_construct(self):
401 """Do actions after construct, but before starting the app."""
481 """Do actions after construct, but before starting the app."""
@@ -414,29 +494,52 b' class IPythonApp(Application):'
414 if self.log_level <= logging.INFO: print
494 if self.log_level <= logging.INFO: print
415
495
416 # Now a variety of things that happen after the banner is printed.
496 # Now a variety of things that happen after the banner is printed.
417 self._enable_gui()
497 self._enable_gui_pylab()
418 self._load_extensions()
498 self._load_extensions()
419 self._run_exec_lines()
499 self._run_exec_lines()
420 self._run_exec_files()
500 self._run_exec_files()
421 self._run_cmd_line_code()
501 self._run_cmd_line_code()
502 self._configure_xmode()
503
504 def _enable_gui_pylab(self):
505 """Enable GUI event loop integration, taking pylab into account."""
506 Global = self.master_config.Global
507
508 # Select which gui to use
509 if Global.gui:
510 gui = Global.gui
511 # The following are deprecated, but there's likely to be a lot of use
512 # of this form out there, so we might as well support it for now. But
513 # the --gui option above takes precedence.
514 elif Global.wthread:
515 gui = inputhook.GUI_WX
516 elif Global.qthread:
517 gui = inputhook.GUI_QT
518 elif Global.gthread:
519 gui = inputhook.GUI_GTK
520 else:
521 gui = None
522
523 # Using --pylab will also require gui activation, though which toolkit
524 # to use may be chosen automatically based on mpl configuration.
525 if Global.pylab:
526 activate = self.shell.enable_pylab
527 if Global.pylab == 'auto':
528 gui = None
529 else:
530 gui = Global.pylab
531 else:
532 # Enable only GUI integration, no pylab
533 activate = inputhook.enable_gui
422
534
423 def _enable_gui(self):
535 if gui or Global.pylab:
424 """Enable GUI event loop integration."""
536 try:
425 config = self.master_config
537 self.log.info("Enabling GUI event loop integration, "
426 try:
538 "toolkit=%s, pylab=%s" % (gui, Global.pylab) )
427 # Enable GUI integration
539 activate(gui)
428 if config.Global.wthread:
540 except:
429 self.log.info("Enabling wx GUI event loop integration")
541 self.log.warn("Error in enabling GUI event loop integration:")
430 inputhook.enable_wx(app=True)
542 self.shell.showtraceback()
431 elif config.Global.q4thread:
432 self.log.info("Enabling Qt4 GUI event loop integration")
433 inputhook.enable_qt4(app=True)
434 elif config.Global.gthread:
435 self.log.info("Enabling GTK GUI event loop integration")
436 inputhook.enable_gtk(app=True)
437 except:
438 self.log.warn("Error in enabling GUI event loop integration:")
439 self.shell.showtraceback()
440
543
441 def _load_extensions(self):
544 def _load_extensions(self):
442 """Load all IPython extensions in Global.extensions.
545 """Load all IPython extensions in Global.extensions.
@@ -477,9 +580,9 b' class IPythonApp(Application):'
477 self.shell.showtraceback()
580 self.shell.showtraceback()
478
581
479 def _exec_file(self, fname):
582 def _exec_file(self, fname):
480 full_filename = filefind(fname, ['.', self.ipythondir])
583 full_filename = filefind(fname, [u'.', self.ipython_dir])
481 if os.path.isfile(full_filename):
584 if os.path.isfile(full_filename):
482 if full_filename.endswith('.py'):
585 if full_filename.endswith(u'.py'):
483 self.log.info("Running file in user namespace: %s" % full_filename)
586 self.log.info("Running file in user namespace: %s" % full_filename)
484 self.shell.safe_execfile(full_filename, self.shell.user_ns)
587 self.shell.safe_execfile(full_filename, self.shell.user_ns)
485 elif full_filename.endswith('.ipy'):
588 elif full_filename.endswith('.ipy'):
@@ -521,26 +624,32 b' class IPythonApp(Application):'
521 self.log.warn("Error in executing file in user namespace: %s" % fname)
624 self.log.warn("Error in executing file in user namespace: %s" % fname)
522 self.shell.showtraceback()
625 self.shell.showtraceback()
523
626
627 def _configure_xmode(self):
628 # XXX - shouldn't this be read from the config? I'm still a little
629 # lost with all the details of handling the new config guys...
630 self.shell.InteractiveTB.set_mode(mode=self.shell.xmode)
631
524 def start_app(self):
632 def start_app(self):
525 if self.master_config.Global.interact:
633 if self.master_config.Global.interact:
526 self.log.debug("Starting IPython's mainloop...")
634 self.log.debug("Starting IPython's mainloop...")
527 self.shell.mainloop()
635 self.shell.mainloop()
636 else:
637 self.log.debug("IPython not interactive, start_app is no-op...")
528
638
529
639
530 def load_default_config(ipythondir=None):
640 def load_default_config(ipython_dir=None):
531 """Load the default config file from the default ipythondir.
641 """Load the default config file from the default ipython_dir.
532
642
533 This is useful for embedded shells.
643 This is useful for embedded shells.
534 """
644 """
535 if ipythondir is None:
645 if ipython_dir is None:
536 ipythondir = get_ipython_dir()
646 ipython_dir = get_ipython_dir()
537 cl = PyFileConfigLoader(_default_config_file_name, ipythondir)
647 cl = PyFileConfigLoader(default_config_file_name, ipython_dir)
538 config = cl.load_config()
648 config = cl.load_config()
539 return config
649 return config
540
650
541
651
542 def launch_new_instance():
652 def launch_new_instance():
543 """Create a run a full blown IPython instance"""
653 """Create and run a full blown IPython instance"""
544 app = IPythonApp()
654 app = IPythonApp()
545 app.start()
655 app.start()
546
@@ -17,6 +17,7 b' Main IPython Component'
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from __future__ import with_statement
19 from __future__ import with_statement
20 from __future__ import absolute_import
20
21
21 import __builtin__
22 import __builtin__
22 import StringIO
23 import StringIO
@@ -31,34 +32,37 b' import sys'
31 import tempfile
32 import tempfile
32 from contextlib import nested
33 from contextlib import nested
33
34
34 from IPython.core import ultratb
35 from IPython.core import debugger, oinspect
35 from IPython.core import debugger, oinspect
36 from IPython.core import shadowns
37 from IPython.core import history as ipcorehist
36 from IPython.core import history as ipcorehist
38 from IPython.core import prefilter
37 from IPython.core import prefilter
38 from IPython.core import shadowns
39 from IPython.core import ultratb
39 from IPython.core.alias import AliasManager
40 from IPython.core.alias import AliasManager
40 from IPython.core.builtin_trap import BuiltinTrap
41 from IPython.core.builtin_trap import BuiltinTrap
42 from IPython.core.component import Component
41 from IPython.core.display_trap import DisplayTrap
43 from IPython.core.display_trap import DisplayTrap
44 from IPython.core.error import TryNext, UsageError
42 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
45 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
43 from IPython.core.logger import Logger
46 from IPython.core.logger import Logger
44 from IPython.core.magic import Magic
47 from IPython.core.magic import Magic
45 from IPython.core.prompts import CachedOutput
46 from IPython.core.prefilter import PrefilterManager
48 from IPython.core.prefilter import PrefilterManager
47 from IPython.core.component import Component
49 from IPython.core.prompts import CachedOutput
50 from IPython.core.pylabtools import pylab_activate
48 from IPython.core.usage import interactive_usage, default_banner
51 from IPython.core.usage import interactive_usage, default_banner
49 from IPython.core.error import TryNext, UsageError
50
51 from IPython.utils import pickleshare
52 from IPython.external.Itpl import ItplNS
52 from IPython.external.Itpl import ItplNS
53 from IPython.lib.inputhook import enable_gui
53 from IPython.lib.backgroundjobs import BackgroundJobManager
54 from IPython.lib.backgroundjobs import BackgroundJobManager
54 from IPython.utils.ipstruct import Struct
55 from IPython.utils import PyColorize
55 from IPython.utils import PyColorize
56 from IPython.utils.genutils import *
56 from IPython.utils import pickleshare
57 from IPython.utils.genutils import get_ipython_dir
57 from IPython.utils.genutils import get_ipython_dir
58 from IPython.utils.ipstruct import Struct
58 from IPython.utils.platutils import toggle_set_term_title, set_term_title
59 from IPython.utils.platutils import toggle_set_term_title, set_term_title
59 from IPython.utils.strdispatch import StrDispatch
60 from IPython.utils.strdispatch import StrDispatch
60 from IPython.utils.syspathcontext import prepended_to_syspath
61 from IPython.utils.syspathcontext import prepended_to_syspath
61
62
63 # XXX - need to clean up this import * line
64 from IPython.utils.genutils import *
65
62 # from IPython.utils import growl
66 # from IPython.utils import growl
63 # growl.start("IPython")
67 # growl.start("IPython")
64
68
@@ -70,7 +74,6 b' from IPython.utils.traitlets import ('
70 # Globals
74 # Globals
71 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
72
76
73
74 # store the builtin raw_input globally, and use this always, in case user code
77 # store the builtin raw_input globally, and use this always, in case user code
75 # overwrites it (like wx.py.PyShell does)
78 # overwrites it (like wx.py.PyShell does)
76 raw_input_original = raw_input
79 raw_input_original = raw_input
@@ -78,12 +81,10 b' raw_input_original = raw_input'
78 # compiled regexps for autoindent management
81 # compiled regexps for autoindent management
79 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
82 dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass')
80
83
81
82 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
83 # Utilities
85 # Utilities
84 #-----------------------------------------------------------------------------
86 #-----------------------------------------------------------------------------
85
87
86
87 ini_spaces_re = re.compile(r'^(\s+)')
88 ini_spaces_re = re.compile(r'^(\s+)')
88
89
89
90
@@ -113,6 +114,8 b' def softspace(file, newvalue):'
113 return oldvalue
114 return oldvalue
114
115
115
116
117 def no_op(*a, **kw): pass
118
116 class SpaceInInput(exceptions.Exception): pass
119 class SpaceInInput(exceptions.Exception): pass
117
120
118 class Bunch: pass
121 class Bunch: pass
@@ -162,6 +165,15 b' def get_default_editor():'
162 return ed
165 return ed
163
166
164
167
168 def get_default_colors():
169 if sys.platform=='darwin':
170 return "LightBG"
171 elif os.name=='nt':
172 return 'Linux'
173 else:
174 return 'Linux'
175
176
165 class SeparateStr(Str):
177 class SeparateStr(Str):
166 """A Str subclass to validate separate_in, separate_out, etc.
178 """A Str subclass to validate separate_in, separate_out, etc.
167
179
@@ -174,6 +186,57 b' class SeparateStr(Str):'
174 return super(SeparateStr, self).validate(obj, value)
186 return super(SeparateStr, self).validate(obj, value)
175
187
176
188
189 def make_user_namespaces(user_ns=None, user_global_ns=None):
190 """Return a valid local and global user interactive namespaces.
191
192 This builds a dict with the minimal information needed to operate as a
193 valid IPython user namespace, which you can pass to the various
194 embedding classes in ipython. The default implementation returns the
195 same dict for both the locals and the globals to allow functions to
196 refer to variables in the namespace. Customized implementations can
197 return different dicts. The locals dictionary can actually be anything
198 following the basic mapping protocol of a dict, but the globals dict
199 must be a true dict, not even a subclass. It is recommended that any
200 custom object for the locals namespace synchronize with the globals
201 dict somehow.
202
203 Raises TypeError if the provided globals namespace is not a true dict.
204
205 Parameters
206 ----------
207 user_ns : dict-like, optional
208 The current user namespace. The items in this namespace should
209 be included in the output. If None, an appropriate blank
210 namespace should be created.
211 user_global_ns : dict, optional
212 The current user global namespace. The items in this namespace
213 should be included in the output. If None, an appropriate
214 blank namespace should be created.
215
216 Returns
217 -------
218 A pair of dictionary-like object to be used as the local namespace
219 of the interpreter and a dict to be used as the global namespace.
220 """
221
222 if user_ns is None:
223 # Set __name__ to __main__ to better match the behavior of the
224 # normal interpreter.
225 user_ns = {'__name__' :'__main__',
226 '__builtins__' : __builtin__,
227 }
228 else:
229 user_ns.setdefault('__name__','__main__')
230 user_ns.setdefault('__builtins__',__builtin__)
231
232 if user_global_ns is None:
233 user_global_ns = user_ns
234 if type(user_global_ns) is not dict:
235 raise TypeError("user_global_ns must be a true dict; got %r"
236 % type(user_global_ns))
237
238 return user_ns, user_global_ns
239
177 #-----------------------------------------------------------------------------
240 #-----------------------------------------------------------------------------
178 # Main IPython class
241 # Main IPython class
179 #-----------------------------------------------------------------------------
242 #-----------------------------------------------------------------------------
@@ -182,7 +245,7 b' class SeparateStr(Str):'
182 class InteractiveShell(Component, Magic):
245 class InteractiveShell(Component, Magic):
183 """An enhanced, interactive shell for Python."""
246 """An enhanced, interactive shell for Python."""
184
247
185 autocall = Enum((0,1,2), config=True)
248 autocall = Enum((0,1,2), default_value=1, config=True)
186 autoedit_syntax = CBool(False, config=True)
249 autoedit_syntax = CBool(False, config=True)
187 autoindent = CBool(True, config=True)
250 autoindent = CBool(True, config=True)
188 automagic = CBool(True, config=True)
251 automagic = CBool(True, config=True)
@@ -192,7 +255,7 b' class InteractiveShell(Component, Magic):'
192 cache_size = Int(1000, config=True)
255 cache_size = Int(1000, config=True)
193 color_info = CBool(True, config=True)
256 color_info = CBool(True, config=True)
194 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
257 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
195 default_value='LightBG', config=True)
258 default_value=get_default_colors(), config=True)
196 confirm_exit = CBool(True, config=True)
259 confirm_exit = CBool(True, config=True)
197 debug = CBool(False, config=True)
260 debug = CBool(False, config=True)
198 deep_reload = CBool(False, config=True)
261 deep_reload = CBool(False, config=True)
@@ -206,7 +269,7 b' class InteractiveShell(Component, Magic):'
206 embedded_active = CBool(False)
269 embedded_active = CBool(False)
207 editor = Str(get_default_editor(), config=True)
270 editor = Str(get_default_editor(), config=True)
208 filename = Str("<ipython console>")
271 filename = Str("<ipython console>")
209 ipythondir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
272 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
210 logstart = CBool(False, config=True)
273 logstart = CBool(False, config=True)
211 logfile = Str('', config=True)
274 logfile = Str('', config=True)
212 logappend = Str('', config=True)
275 logappend = Str('', config=True)
@@ -264,7 +327,7 b' class InteractiveShell(Component, Magic):'
264 # Subclasses with thread support should override this as needed.
327 # Subclasses with thread support should override this as needed.
265 isthreaded = False
328 isthreaded = False
266
329
267 def __init__(self, parent=None, config=None, ipythondir=None, usage=None,
330 def __init__(self, parent=None, config=None, ipython_dir=None, usage=None,
268 user_ns=None, user_global_ns=None,
331 user_ns=None, user_global_ns=None,
269 banner1=None, banner2=None, display_banner=None,
332 banner1=None, banner2=None, display_banner=None,
270 custom_exceptions=((),None)):
333 custom_exceptions=((),None)):
@@ -274,7 +337,7 b' class InteractiveShell(Component, Magic):'
274 super(InteractiveShell, self).__init__(parent, config=config)
337 super(InteractiveShell, self).__init__(parent, config=config)
275
338
276 # These are relatively independent and stateless
339 # These are relatively independent and stateless
277 self.init_ipythondir(ipythondir)
340 self.init_ipython_dir(ipython_dir)
278 self.init_instance_attrs()
341 self.init_instance_attrs()
279 self.init_term_title()
342 self.init_term_title()
280 self.init_usage(usage)
343 self.init_usage(usage)
@@ -320,6 +383,7 b' class InteractiveShell(Component, Magic):'
320 self.hooks.late_startup_hook()
383 self.hooks.late_startup_hook()
321
384
322 def get_ipython(self):
385 def get_ipython(self):
386 """Return the currently running IPython instance."""
323 return self
387 return self
324
388
325 #-------------------------------------------------------------------------
389 #-------------------------------------------------------------------------
@@ -332,7 +396,7 b' class InteractiveShell(Component, Magic):'
332 def _banner2_changed(self):
396 def _banner2_changed(self):
333 self.compute_banner()
397 self.compute_banner()
334
398
335 def _ipythondir_changed(self, name, new):
399 def _ipython_dir_changed(self, name, new):
336 if not os.path.isdir(new):
400 if not os.path.isdir(new):
337 os.makedirs(new, mode = 0777)
401 os.makedirs(new, mode = 0777)
338 if not os.path.isdir(self.ipython_extension_dir):
402 if not os.path.isdir(self.ipython_extension_dir):
@@ -340,7 +404,7 b' class InteractiveShell(Component, Magic):'
340
404
341 @property
405 @property
342 def ipython_extension_dir(self):
406 def ipython_extension_dir(self):
343 return os.path.join(self.ipythondir, 'extensions')
407 return os.path.join(self.ipython_dir, 'extensions')
344
408
345 @property
409 @property
346 def usable_screen_length(self):
410 def usable_screen_length(self):
@@ -372,19 +436,19 b' class InteractiveShell(Component, Magic):'
372 # init_* methods called by __init__
436 # init_* methods called by __init__
373 #-------------------------------------------------------------------------
437 #-------------------------------------------------------------------------
374
438
375 def init_ipythondir(self, ipythondir):
439 def init_ipython_dir(self, ipython_dir):
376 if ipythondir is not None:
440 if ipython_dir is not None:
377 self.ipythondir = ipythondir
441 self.ipython_dir = ipython_dir
378 self.config.Global.ipythondir = self.ipythondir
442 self.config.Global.ipython_dir = self.ipython_dir
379 return
443 return
380
444
381 if hasattr(self.config.Global, 'ipythondir'):
445 if hasattr(self.config.Global, 'ipython_dir'):
382 self.ipythondir = self.config.Global.ipythondir
446 self.ipython_dir = self.config.Global.ipython_dir
383 else:
447 else:
384 self.ipythondir = get_ipython_dir()
448 self.ipython_dir = get_ipython_dir()
385
449
386 # All children can just read this
450 # All children can just read this
387 self.config.Global.ipythondir = self.ipythondir
451 self.config.Global.ipython_dir = self.ipython_dir
388
452
389 def init_instance_attrs(self):
453 def init_instance_attrs(self):
390 self.jobs = BackgroundJobManager()
454 self.jobs = BackgroundJobManager()
@@ -805,8 +869,7 b' class InteractiveShell(Component, Magic):'
805 # These routines return properly built dicts as needed by the rest of
869 # These routines return properly built dicts as needed by the rest of
806 # the code, and can also be used by extension writers to generate
870 # the code, and can also be used by extension writers to generate
807 # properly initialized namespaces.
871 # properly initialized namespaces.
808 user_ns, user_global_ns = self.make_user_namespaces(user_ns,
872 user_ns, user_global_ns = make_user_namespaces(user_ns, user_global_ns)
809 user_global_ns)
810
873
811 # Assign namespaces
874 # Assign namespaces
812 # This is the namespace where all normal user variables live
875 # This is the namespace where all normal user variables live
@@ -816,7 +879,7 b' class InteractiveShell(Component, Magic):'
816 # An auxiliary namespace that checks what parts of the user_ns were
879 # An auxiliary namespace that checks what parts of the user_ns were
817 # loaded at startup, so we can list later only variables defined in
880 # loaded at startup, so we can list later only variables defined in
818 # actual interactive use. Since it is always a subset of user_ns, it
881 # actual interactive use. Since it is always a subset of user_ns, it
819 # doesn't need to be seaparately tracked in the ns_table
882 # doesn't need to be separately tracked in the ns_table.
820 self.user_config_ns = {}
883 self.user_config_ns = {}
821
884
822 # A namespace to keep track of internal data structures to prevent
885 # A namespace to keep track of internal data structures to prevent
@@ -891,55 +954,6 b' class InteractiveShell(Component, Magic):'
891 else:
954 else:
892 sys.modules[main_name] = FakeModule(self.user_ns)
955 sys.modules[main_name] = FakeModule(self.user_ns)
893
956
894 def make_user_namespaces(self, user_ns=None, user_global_ns=None):
895 """Return a valid local and global user interactive namespaces.
896
897 This builds a dict with the minimal information needed to operate as a
898 valid IPython user namespace, which you can pass to the various
899 embedding classes in ipython. The default implementation returns the
900 same dict for both the locals and the globals to allow functions to
901 refer to variables in the namespace. Customized implementations can
902 return different dicts. The locals dictionary can actually be anything
903 following the basic mapping protocol of a dict, but the globals dict
904 must be a true dict, not even a subclass. It is recommended that any
905 custom object for the locals namespace synchronize with the globals
906 dict somehow.
907
908 Raises TypeError if the provided globals namespace is not a true dict.
909
910 :Parameters:
911 user_ns : dict-like, optional
912 The current user namespace. The items in this namespace should
913 be included in the output. If None, an appropriate blank
914 namespace should be created.
915 user_global_ns : dict, optional
916 The current user global namespace. The items in this namespace
917 should be included in the output. If None, an appropriate
918 blank namespace should be created.
919
920 :Returns:
921 A tuple pair of dictionary-like object to be used as the local namespace
922 of the interpreter and a dict to be used as the global namespace.
923 """
924
925 if user_ns is None:
926 # Set __name__ to __main__ to better match the behavior of the
927 # normal interpreter.
928 user_ns = {'__name__' :'__main__',
929 '__builtins__' : __builtin__,
930 }
931 else:
932 user_ns.setdefault('__name__','__main__')
933 user_ns.setdefault('__builtins__',__builtin__)
934
935 if user_global_ns is None:
936 user_global_ns = user_ns
937 if type(user_global_ns) is not dict:
938 raise TypeError("user_global_ns must be a true dict; got %r"
939 % type(user_global_ns))
940
941 return user_ns, user_global_ns
942
943 def init_user_ns(self):
957 def init_user_ns(self):
944 """Initialize all user-visible namespaces to their minimum defaults.
958 """Initialize all user-visible namespaces to their minimum defaults.
945
959
@@ -952,26 +966,43 b' class InteractiveShell(Component, Magic):'
952 method. If they were not empty before, data will simply be added to
966 method. If they were not empty before, data will simply be added to
953 therm.
967 therm.
954 """
968 """
955 # Store myself as the public api!!!
969 # This function works in two parts: first we put a few things in
956 self.user_ns['get_ipython'] = self.get_ipython
970 # user_ns, and we sync that contents into user_config_ns so that these
971 # initial variables aren't shown by %who. After the sync, we add the
972 # rest of what we *do* want the user to see with %who even on a new
973 # session.
974 ns = {}
975
976 # Put 'help' in the user namespace
977 try:
978 from site import _Helper
979 ns['help'] = _Helper()
980 except ImportError:
981 warn('help() not available - check site.py')
957
982
958 # make global variables for user access to the histories
983 # make global variables for user access to the histories
959 self.user_ns['_ih'] = self.input_hist
984 ns['_ih'] = self.input_hist
960 self.user_ns['_oh'] = self.output_hist
985 ns['_oh'] = self.output_hist
961 self.user_ns['_dh'] = self.dir_hist
986 ns['_dh'] = self.dir_hist
987
988 ns['_sh'] = shadowns
989
990 # Sync what we've added so far to user_config_ns so these aren't seen
991 # by %who
992 self.user_config_ns.update(ns)
993
994 # Now, continue adding more contents
962
995
963 # user aliases to input and output histories
996 # user aliases to input and output histories
964 self.user_ns['In'] = self.input_hist
997 ns['In'] = self.input_hist
965 self.user_ns['Out'] = self.output_hist
998 ns['Out'] = self.output_hist
966
999
967 self.user_ns['_sh'] = shadowns
1000 # Store myself as the public api!!!
1001 ns['get_ipython'] = self.get_ipython
1002
1003 # And update the real user's namespace
1004 self.user_ns.update(ns)
968
1005
969 # Put 'help' in the user namespace
970 try:
971 from site import _Helper
972 self.user_ns['help'] = _Helper()
973 except ImportError:
974 warn('help() not available - check site.py')
975
1006
976 def reset(self):
1007 def reset(self):
977 """Clear all internal namespaces.
1008 """Clear all internal namespaces.
@@ -1070,7 +1101,7 b' class InteractiveShell(Component, Magic):'
1070 histfname = 'history-%s' % self.profile
1101 histfname = 'history-%s' % self.profile
1071 else:
1102 else:
1072 histfname = 'history'
1103 histfname = 'history'
1073 self.histfile = os.path.join(self.ipythondir, histfname)
1104 self.histfile = os.path.join(self.ipython_dir, histfname)
1074
1105
1075 # Fill the history zero entry, user counter starts at 1
1106 # Fill the history zero entry, user counter starts at 1
1076 self.input_hist.append('\n')
1107 self.input_hist.append('\n')
@@ -1078,21 +1109,18 b' class InteractiveShell(Component, Magic):'
1078
1109
1079 def init_shadow_hist(self):
1110 def init_shadow_hist(self):
1080 try:
1111 try:
1081 self.db = pickleshare.PickleShareDB(self.ipythondir + "/db")
1112 self.db = pickleshare.PickleShareDB(self.ipython_dir + "/db")
1082 except exceptions.UnicodeDecodeError:
1113 except exceptions.UnicodeDecodeError:
1083 print "Your ipythondir can't be decoded to unicode!"
1114 print "Your ipython_dir can't be decoded to unicode!"
1084 print "Please set HOME environment variable to something that"
1115 print "Please set HOME environment variable to something that"
1085 print r"only has ASCII characters, e.g. c:\home"
1116 print r"only has ASCII characters, e.g. c:\home"
1086 print "Now it is", self.ipythondir
1117 print "Now it is", self.ipython_dir
1087 sys.exit()
1118 sys.exit()
1088 self.shadowhist = ipcorehist.ShadowHist(self.db)
1119 self.shadowhist = ipcorehist.ShadowHist(self.db)
1089
1120
1090 def savehist(self):
1121 def savehist(self):
1091 """Save input history to a file (via readline library)."""
1122 """Save input history to a file (via readline library)."""
1092
1123
1093 if not self.has_readline:
1094 return
1095
1096 try:
1124 try:
1097 self.readline.write_history_file(self.histfile)
1125 self.readline.write_history_file(self.histfile)
1098 except:
1126 except:
@@ -1102,12 +1130,11 b' class InteractiveShell(Component, Magic):'
1102 def reloadhist(self):
1130 def reloadhist(self):
1103 """Reload the input history from disk file."""
1131 """Reload the input history from disk file."""
1104
1132
1105 if self.has_readline:
1133 try:
1106 try:
1134 self.readline.clear_history()
1107 self.readline.clear_history()
1135 self.readline.read_history_file(self.shell.histfile)
1108 self.readline.read_history_file(self.shell.histfile)
1136 except AttributeError:
1109 except AttributeError:
1137 pass
1110 pass
1111
1138
1112 def history_saving_wrapper(self, func):
1139 def history_saving_wrapper(self, func):
1113 """ Wrap func for readline history saving
1140 """ Wrap func for readline history saving
@@ -1141,37 +1168,14 b' class InteractiveShell(Component, Magic):'
1141 color_scheme='NoColor',
1168 color_scheme='NoColor',
1142 tb_offset = 1)
1169 tb_offset = 1)
1143
1170
1144 # IPython itself shouldn't crash. This will produce a detailed
1171 # The instance will store a pointer to the system-wide exception hook,
1145 # post-mortem if it does. But we only install the crash handler for
1172 # so that runtime code (such as magics) can access it. This is because
1146 # non-threaded shells, the threaded ones use a normal verbose reporter
1173 # during the read-eval loop, it may get temporarily overwritten.
1147 # and lose the crash handler. This is because exceptions in the main
1174 self.sys_excepthook = sys.excepthook
1148 # thread (such as in GUI code) propagate directly to sys.excepthook,
1149 # and there's no point in printing crash dumps for every user exception.
1150 if self.isthreaded:
1151 ipCrashHandler = ultratb.FormattedTB()
1152 else:
1153 from IPython.core import crashhandler
1154 ipCrashHandler = crashhandler.IPythonCrashHandler(self)
1155 self.set_crash_handler(ipCrashHandler)
1156
1175
1157 # and add any custom exception handlers the user may have specified
1176 # and add any custom exception handlers the user may have specified
1158 self.set_custom_exc(*custom_exceptions)
1177 self.set_custom_exc(*custom_exceptions)
1159
1178
1160 def set_crash_handler(self, crashHandler):
1161 """Set the IPython crash handler.
1162
1163 This must be a callable with a signature suitable for use as
1164 sys.excepthook."""
1165
1166 # Install the given crash handler as the Python exception hook
1167 sys.excepthook = crashHandler
1168
1169 # The instance will store a pointer to this, so that runtime code
1170 # (such as magics) can access it. This is because during the
1171 # read-eval loop, it gets temporarily overwritten (to deal with GUI
1172 # frameworks).
1173 self.sys_excepthook = sys.excepthook
1174
1175 def set_custom_exc(self,exc_tuple,handler):
1179 def set_custom_exc(self,exc_tuple,handler):
1176 """set_custom_exc(exc_tuple,handler)
1180 """set_custom_exc(exc_tuple,handler)
1177
1181
@@ -1239,7 +1243,8 b' class InteractiveShell(Component, Magic):'
1239 """
1243 """
1240 self.showtraceback((etype,value,tb),tb_offset=0)
1244 self.showtraceback((etype,value,tb),tb_offset=0)
1241
1245
1242 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None):
1246 def showtraceback(self,exc_tuple = None,filename=None,tb_offset=None,
1247 exception_only=False):
1243 """Display the exception that just occurred.
1248 """Display the exception that just occurred.
1244
1249
1245 If nothing is known about the exception, this is the method which
1250 If nothing is known about the exception, this is the method which
@@ -1250,18 +1255,24 b' class InteractiveShell(Component, Magic):'
1250 care of calling it if needed, so unless you are explicitly catching a
1255 care of calling it if needed, so unless you are explicitly catching a
1251 SyntaxError exception, don't try to analyze the stack manually and
1256 SyntaxError exception, don't try to analyze the stack manually and
1252 simply call this method."""
1257 simply call this method."""
1253
1254
1255 # Though this won't be called by syntax errors in the input line,
1256 # there may be SyntaxError cases whith imported code.
1257
1258
1258 try:
1259 try:
1259 if exc_tuple is None:
1260 if exc_tuple is None:
1260 etype, value, tb = sys.exc_info()
1261 etype, value, tb = sys.exc_info()
1261 else:
1262 else:
1262 etype, value, tb = exc_tuple
1263 etype, value, tb = exc_tuple
1264
1265 if etype is None:
1266 if hasattr(sys, 'last_type'):
1267 etype, value, tb = sys.last_type, sys.last_value, \
1268 sys.last_traceback
1269 else:
1270 self.write('No traceback available to show.\n')
1271 return
1263
1272
1264 if etype is SyntaxError:
1273 if etype is SyntaxError:
1274 # Though this won't be called by syntax errors in the input
1275 # line, there may be SyntaxError cases whith imported code.
1265 self.showsyntaxerror(filename)
1276 self.showsyntaxerror(filename)
1266 elif etype is UsageError:
1277 elif etype is UsageError:
1267 print "UsageError:", value
1278 print "UsageError:", value
@@ -1277,12 +1288,20 b' class InteractiveShell(Component, Magic):'
1277 if etype in self.custom_exceptions:
1288 if etype in self.custom_exceptions:
1278 self.CustomTB(etype,value,tb)
1289 self.CustomTB(etype,value,tb)
1279 else:
1290 else:
1280 self.InteractiveTB(etype,value,tb,tb_offset=tb_offset)
1291 if exception_only:
1281 if self.InteractiveTB.call_pdb and self.has_readline:
1292 m = ('An exception has occurred, use %tb to see the '
1282 # pdb mucks up readline, fix it back
1293 'full traceback.')
1283 self.set_completer()
1294 print m
1295 self.InteractiveTB.show_exception_only(etype, value)
1296 else:
1297 self.InteractiveTB(etype,value,tb,tb_offset=tb_offset)
1298 if self.InteractiveTB.call_pdb:
1299 # pdb mucks up readline, fix it back
1300 self.set_completer()
1301
1284 except KeyboardInterrupt:
1302 except KeyboardInterrupt:
1285 self.write("\nKeyboardInterrupt\n")
1303 self.write("\nKeyboardInterrupt\n")
1304
1286
1305
1287 def showsyntaxerror(self, filename=None):
1306 def showsyntaxerror(self, filename=None):
1288 """Display the syntax error that just occurred.
1307 """Display the syntax error that just occurred.
@@ -1295,7 +1314,7 b' class InteractiveShell(Component, Magic):'
1295 """
1314 """
1296 etype, value, last_traceback = sys.exc_info()
1315 etype, value, last_traceback = sys.exc_info()
1297
1316
1298 # See note about these variables in showtraceback() below
1317 # See note about these variables in showtraceback() above
1299 sys.last_type = etype
1318 sys.last_type = etype
1300 sys.last_value = value
1319 sys.last_value = value
1301 sys.last_traceback = last_traceback
1320 sys.last_traceback = last_traceback
@@ -1426,9 +1445,7 b' class InteractiveShell(Component, Magic):'
1426 return outcomps
1445 return outcomps
1427
1446
1428 def set_custom_completer(self,completer,pos=0):
1447 def set_custom_completer(self,completer,pos=0):
1429 """set_custom_completer(completer,pos=0)
1448 """Adds a new custom completer function.
1430
1431 Adds a new custom completer function.
1432
1449
1433 The position argument (defaults to 0) is the index in the completers
1450 The position argument (defaults to 0) is the index in the completers
1434 list where you want the completer to be inserted."""
1451 list where you want the completer to be inserted."""
@@ -1438,9 +1455,18 b' class InteractiveShell(Component, Magic):'
1438 self.Completer.matchers.insert(pos,newcomp)
1455 self.Completer.matchers.insert(pos,newcomp)
1439
1456
1440 def set_completer(self):
1457 def set_completer(self):
1441 """reset readline's completer to be our own."""
1458 """Reset readline's completer to be our own."""
1442 self.readline.set_completer(self.Completer.complete)
1459 self.readline.set_completer(self.Completer.complete)
1443
1460
1461 def set_completer_frame(self, frame=None):
1462 """Set the frame of the completer."""
1463 if frame:
1464 self.Completer.namespace = frame.f_locals
1465 self.Completer.global_namespace = frame.f_globals
1466 else:
1467 self.Completer.namespace = self.user_ns
1468 self.Completer.global_namespace = self.user_global_ns
1469
1444 #-------------------------------------------------------------------------
1470 #-------------------------------------------------------------------------
1445 # Things related to readline
1471 # Things related to readline
1446 #-------------------------------------------------------------------------
1472 #-------------------------------------------------------------------------
@@ -1448,20 +1474,25 b' class InteractiveShell(Component, Magic):'
1448 def init_readline(self):
1474 def init_readline(self):
1449 """Command history completion/saving/reloading."""
1475 """Command history completion/saving/reloading."""
1450
1476
1477 if self.readline_use:
1478 import IPython.utils.rlineimpl as readline
1479
1451 self.rl_next_input = None
1480 self.rl_next_input = None
1452 self.rl_do_indent = False
1481 self.rl_do_indent = False
1453
1482
1454 if not self.readline_use:
1483 if not self.readline_use or not readline.have_readline:
1455 return
1484 self.has_readline = False
1456
1457 import IPython.utils.rlineimpl as readline
1458
1459 if not readline.have_readline:
1460 self.has_readline = 0
1461 self.readline = None
1485 self.readline = None
1462 # no point in bugging windows users with this every time:
1486 # Set a number of methods that depend on readline to be no-op
1463 warn('Readline services not available on this platform.')
1487 self.savehist = no_op
1488 self.reloadhist = no_op
1489 self.set_completer = no_op
1490 self.set_custom_completer = no_op
1491 self.set_completer_frame = no_op
1492 warn('Readline services not available or not loaded.')
1464 else:
1493 else:
1494 self.has_readline = True
1495 self.readline = readline
1465 sys.modules['readline'] = readline
1496 sys.modules['readline'] = readline
1466 import atexit
1497 import atexit
1467 from IPython.core.completer import IPCompleter
1498 from IPython.core.completer import IPCompleter
@@ -1496,8 +1527,6 b' class InteractiveShell(Component, Magic):'
1496 warn('Problems reading readline initialization file <%s>'
1527 warn('Problems reading readline initialization file <%s>'
1497 % inputrc_name)
1528 % inputrc_name)
1498
1529
1499 self.has_readline = 1
1500 self.readline = readline
1501 # save this in sys so embedded copies can restore it properly
1530 # save this in sys so embedded copies can restore it properly
1502 sys.ipcompleter = self.Completer.complete
1531 sys.ipcompleter = self.Completer.complete
1503 self.set_completer()
1532 self.set_completer()
@@ -1569,6 +1598,9 b' class InteractiveShell(Component, Magic):'
1569 # Set user colors (don't do it in the constructor above so that it
1598 # Set user colors (don't do it in the constructor above so that it
1570 # doesn't crash if colors option is invalid)
1599 # doesn't crash if colors option is invalid)
1571 self.magic_colors(self.colors)
1600 self.magic_colors(self.colors)
1601 # History was moved to a separate module
1602 from . import history
1603 history.init_ipython(self)
1572
1604
1573 def magic(self,arg_s):
1605 def magic(self,arg_s):
1574 """Call a magic function by name.
1606 """Call a magic function by name.
@@ -1587,7 +1619,6 b' class InteractiveShell(Component, Magic):'
1587 valid Python code you can type at the interpreter, including loops and
1619 valid Python code you can type at the interpreter, including loops and
1588 compound statements.
1620 compound statements.
1589 """
1621 """
1590
1591 args = arg_s.split(' ',1)
1622 args = arg_s.split(' ',1)
1592 magic_name = args[0]
1623 magic_name = args[0]
1593 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
1624 magic_name = magic_name.lstrip(prefilter.ESC_MAGIC)
@@ -1826,7 +1857,8 b' class InteractiveShell(Component, Magic):'
1826 except EOFError:
1857 except EOFError:
1827 if self.autoindent:
1858 if self.autoindent:
1828 self.rl_do_indent = False
1859 self.rl_do_indent = False
1829 self.readline_startup_hook(None)
1860 if self.has_readline:
1861 self.readline_startup_hook(None)
1830 self.write('\n')
1862 self.write('\n')
1831 self.exit()
1863 self.exit()
1832 except bdb.BdbQuit:
1864 except bdb.BdbQuit:
@@ -1843,10 +1875,13 b' class InteractiveShell(Component, Magic):'
1843 if (self.SyntaxTB.last_syntax_error and
1875 if (self.SyntaxTB.last_syntax_error and
1844 self.autoedit_syntax):
1876 self.autoedit_syntax):
1845 self.edit_syntax_error()
1877 self.edit_syntax_error()
1846
1878
1847 # We are off again...
1879 # We are off again...
1848 __builtin__.__dict__['__IPYTHON__active'] -= 1
1880 __builtin__.__dict__['__IPYTHON__active'] -= 1
1849
1881
1882 # Turn off the exit flag, so the mainloop can be restarted if desired
1883 self.exit_now = False
1884
1850 def safe_execfile(self, fname, *where, **kw):
1885 def safe_execfile(self, fname, *where, **kw):
1851 """A safe version of the builtin execfile().
1886 """A safe version of the builtin execfile().
1852
1887
@@ -1862,7 +1897,8 b' class InteractiveShell(Component, Magic):'
1862 One or two namespaces, passed to execfile() as (globals,locals).
1897 One or two namespaces, passed to execfile() as (globals,locals).
1863 If only one is given, it is passed as both.
1898 If only one is given, it is passed as both.
1864 exit_ignore : bool (False)
1899 exit_ignore : bool (False)
1865 If True, then don't print errors for non-zero exit statuses.
1900 If True, then silence SystemExit for non-zero status (it is always
1901 silenced for zero status, as it is so common).
1866 """
1902 """
1867 kw.setdefault('exit_ignore', False)
1903 kw.setdefault('exit_ignore', False)
1868
1904
@@ -1887,40 +1923,21 b' class InteractiveShell(Component, Magic):'
1887
1923
1888 with prepended_to_syspath(dname):
1924 with prepended_to_syspath(dname):
1889 try:
1925 try:
1890 if sys.platform == 'win32' and sys.version_info < (2,5,1):
1926 execfile(fname,*where)
1891 # Work around a bug in Python for Windows. The bug was
1892 # fixed in in Python 2.5 r54159 and 54158, but that's still
1893 # SVN Python as of March/07. For details, see:
1894 # http://projects.scipy.org/ipython/ipython/ticket/123
1895 try:
1896 globs,locs = where[0:2]
1897 except:
1898 try:
1899 globs = locs = where[0]
1900 except:
1901 globs = locs = globals()
1902 exec file(fname) in globs,locs
1903 else:
1904 execfile(fname,*where)
1905 except SyntaxError:
1906 self.showsyntaxerror()
1907 warn('Failure executing file: <%s>' % fname)
1908 except SystemExit, status:
1927 except SystemExit, status:
1909 # Code that correctly sets the exit status flag to success (0)
1928 # If the call was made with 0 or None exit status (sys.exit(0)
1910 # shouldn't be bothered with a traceback. Note that a plain
1929 # or sys.exit() ), don't bother showing a traceback, as both of
1911 # sys.exit() does NOT set the message to 0 (it's empty) so that
1930 # these are considered normal by the OS:
1912 # will still get a traceback. Note that the structure of the
1931 # > python -c'import sys;sys.exit(0)'; echo $?
1913 # SystemExit exception changed between Python 2.4 and 2.5, so
1932 # 0
1914 # the checks must be done in a version-dependent way.
1933 # > python -c'import sys;sys.exit()'; echo $?
1915 show = False
1934 # 0
1916 if status.message!=0 and not kw['exit_ignore']:
1935 # For other exit status, we show the exception unless
1917 show = True
1936 # explicitly silenced, but only in short form.
1918 if show:
1937 if status.code not in (0, None) and not kw['exit_ignore']:
1919 self.showtraceback()
1938 self.showtraceback(exception_only=True)
1920 warn('Failure executing file: <%s>' % fname)
1921 except:
1939 except:
1922 self.showtraceback()
1940 self.showtraceback()
1923 warn('Failure executing file: <%s>' % fname)
1924
1941
1925 def safe_execfile_ipy(self, fname):
1942 def safe_execfile_ipy(self, fname):
1926 """Like safe_execfile, but for .ipy files with IPython syntax.
1943 """Like safe_execfile, but for .ipy files with IPython syntax.
@@ -2134,9 +2151,8 b' class InteractiveShell(Component, Magic):'
2134 sys.excepthook = old_excepthook
2151 sys.excepthook = old_excepthook
2135 except SystemExit:
2152 except SystemExit:
2136 self.resetbuffer()
2153 self.resetbuffer()
2137 self.showtraceback()
2154 self.showtraceback(exception_only=True)
2138 warn("Type %exit or %quit to exit IPython "
2155 warn("To exit: use any of 'exit', 'quit', %Exit or Ctrl-D.", level=1)
2139 "(%Exit or %Quit do so unconditionally).",level=1)
2140 except self.custom_exceptions:
2156 except self.custom_exceptions:
2141 etype,value,tb = sys.exc_info()
2157 etype,value,tb = sys.exc_info()
2142 self.CustomTB(etype,value,tb)
2158 self.CustomTB(etype,value,tb)
@@ -2278,6 +2294,8 b' class InteractiveShell(Component, Magic):'
2278 def get_component(self, name=None, klass=None):
2294 def get_component(self, name=None, klass=None):
2279 """Fetch a component by name and klass in my tree."""
2295 """Fetch a component by name and klass in my tree."""
2280 c = Component.get_instances(root=self, name=name, klass=klass)
2296 c = Component.get_instances(root=self, name=name, klass=klass)
2297 if len(c) == 0:
2298 return None
2281 if len(c) == 1:
2299 if len(c) == 1:
2282 return c[0]
2300 return c[0]
2283 else:
2301 else:
@@ -2309,7 +2327,7 b' class InteractiveShell(Component, Magic):'
2309 You can put your extension modules anywhere you want, as long as
2327 You can put your extension modules anywhere you want, as long as
2310 they can be imported by Python's standard import mechanism. However,
2328 they can be imported by Python's standard import mechanism. However,
2311 to make it easy to write extensions, you can also put your extensions
2329 to make it easy to write extensions, you can also put your extensions
2312 in ``os.path.join(self.ipythondir, 'extensions')``. This directory
2330 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2313 is added to ``sys.path`` automatically.
2331 is added to ``sys.path`` automatically.
2314 """
2332 """
2315 from IPython.utils.syspathcontext import prepended_to_syspath
2333 from IPython.utils.syspathcontext import prepended_to_syspath
@@ -2318,7 +2336,7 b' class InteractiveShell(Component, Magic):'
2318 with prepended_to_syspath(self.ipython_extension_dir):
2336 with prepended_to_syspath(self.ipython_extension_dir):
2319 __import__(module_str)
2337 __import__(module_str)
2320 mod = sys.modules[module_str]
2338 mod = sys.modules[module_str]
2321 self._call_load_ipython_extension(mod)
2339 return self._call_load_ipython_extension(mod)
2322
2340
2323 def unload_extension(self, module_str):
2341 def unload_extension(self, module_str):
2324 """Unload an IPython extension by its module name.
2342 """Unload an IPython extension by its module name.
@@ -2350,11 +2368,11 b' class InteractiveShell(Component, Magic):'
2350
2368
2351 def _call_load_ipython_extension(self, mod):
2369 def _call_load_ipython_extension(self, mod):
2352 if hasattr(mod, 'load_ipython_extension'):
2370 if hasattr(mod, 'load_ipython_extension'):
2353 mod.load_ipython_extension(self)
2371 return mod.load_ipython_extension(self)
2354
2372
2355 def _call_unload_ipython_extension(self, mod):
2373 def _call_unload_ipython_extension(self, mod):
2356 if hasattr(mod, 'unload_ipython_extension'):
2374 if hasattr(mod, 'unload_ipython_extension'):
2357 mod.unload_ipython_extension(self)
2375 return mod.unload_ipython_extension(self)
2358
2376
2359 #-------------------------------------------------------------------------
2377 #-------------------------------------------------------------------------
2360 # Things related to the prefilter
2378 # Things related to the prefilter
@@ -2362,6 +2380,10 b' class InteractiveShell(Component, Magic):'
2362
2380
2363 def init_prefilter(self):
2381 def init_prefilter(self):
2364 self.prefilter_manager = PrefilterManager(self, config=self.config)
2382 self.prefilter_manager = PrefilterManager(self, config=self.config)
2383 # Ultimately this will be refactored in the new interpreter code, but
2384 # for now, we should expose the main prefilter method (there's legacy
2385 # code out there that may rely on this).
2386 self.prefilter = self.prefilter_manager.prefilter_lines
2365
2387
2366 #-------------------------------------------------------------------------
2388 #-------------------------------------------------------------------------
2367 # Utilities
2389 # Utilities
@@ -2427,11 +2449,46 b' class InteractiveShell(Component, Magic):'
2427 return ask_yes_no(prompt,default)
2449 return ask_yes_no(prompt,default)
2428
2450
2429 #-------------------------------------------------------------------------
2451 #-------------------------------------------------------------------------
2452 # Things related to GUI support and pylab
2453 #-------------------------------------------------------------------------
2454
2455 def enable_pylab(self, gui=None):
2456 """Activate pylab support at runtime.
2457
2458 This turns on support for matplotlib, preloads into the interactive
2459 namespace all of numpy and pylab, and configures IPython to correcdtly
2460 interact with the GUI event loop. The GUI backend to be used can be
2461 optionally selected with the optional :param:`gui` argument.
2462
2463 Parameters
2464 ----------
2465 gui : optional, string
2466
2467 If given, dictates the choice of matplotlib GUI backend to use
2468 (should be one of IPython's supported backends, 'tk', 'qt', 'wx' or
2469 'gtk'), otherwise we use the default chosen by matplotlib (as
2470 dictated by the matplotlib build-time options plus the user's
2471 matplotlibrc configuration file).
2472 """
2473 # We want to prevent the loading of pylab to pollute the user's
2474 # namespace as shown by the %who* magics, so we execute the activation
2475 # code in an empty namespace, and we update *both* user_ns and
2476 # user_config_ns with this information.
2477 ns = {}
2478 gui = pylab_activate(ns, gui)
2479 self.user_ns.update(ns)
2480 self.user_config_ns.update(ns)
2481 # Now we must activate the gui pylab wants to use, and fix %run to take
2482 # plot updates into account
2483 enable_gui(gui)
2484 self.magic_run = self._pylab_magic_run
2485
2486 #-------------------------------------------------------------------------
2430 # Things related to IPython exiting
2487 # Things related to IPython exiting
2431 #-------------------------------------------------------------------------
2488 #-------------------------------------------------------------------------
2432
2489
2433 def ask_exit(self):
2490 def ask_exit(self):
2434 """ Call for exiting. Can be overiden and used as a callback. """
2491 """ Ask the shell to exit. Can be overiden and used as a callback. """
2435 self.exit_now = True
2492 self.exit_now = True
2436
2493
2437 def exit(self):
2494 def exit(self):
@@ -21,6 +21,7 b' import os'
21 import pdb
21 import pdb
22 import pydoc
22 import pydoc
23 import sys
23 import sys
24 import shutil
24 import re
25 import re
25 import tempfile
26 import tempfile
26 import time
27 import time
@@ -43,21 +44,26 b' except ImportError:'
43
44
44 # Homebrewed
45 # Homebrewed
45 import IPython
46 import IPython
46 from IPython.utils import wildcard
47 import IPython.utils.generics
48
47 from IPython.core import debugger, oinspect
49 from IPython.core import debugger, oinspect
48 from IPython.core.error import TryNext
50 from IPython.core.error import TryNext
51 from IPython.core.error import UsageError
49 from IPython.core.fakemodule import FakeModule
52 from IPython.core.fakemodule import FakeModule
53 from IPython.core.macro import Macro
54 from IPython.core.page import page
50 from IPython.core.prefilter import ESC_MAGIC
55 from IPython.core.prefilter import ESC_MAGIC
56 from IPython.core.pylabtools import mpl_runner
57 from IPython.lib.inputhook import enable_gui
51 from IPython.external.Itpl import Itpl, itpl, printpl,itplns
58 from IPython.external.Itpl import Itpl, itpl, printpl,itplns
59 from IPython.testing import decorators as testdec
60 from IPython.utils import platutils
61 from IPython.utils import wildcard
52 from IPython.utils.PyColorize import Parser
62 from IPython.utils.PyColorize import Parser
53 from IPython.utils.ipstruct import Struct
63 from IPython.utils.ipstruct import Struct
54 from IPython.core.macro import Macro
64
65 # XXX - We need to switch to explicit imports here with genutils
55 from IPython.utils.genutils import *
66 from IPython.utils.genutils import *
56 from IPython.core.page import page
57 from IPython.utils import platutils
58 import IPython.utils.generics
59 from IPython.core.error import UsageError
60 from IPython.testing import decorators as testdec
61
67
62 #***************************************************************************
68 #***************************************************************************
63 # Utility functions
69 # Utility functions
@@ -79,10 +85,16 b' def compress_dhist(dh):'
79 done.add(h)
85 done.add(h)
80
86
81 return newhead + tail
87 return newhead + tail
82
88
83
89
84 #***************************************************************************
90 #***************************************************************************
85 # Main class implementing Magic functionality
91 # Main class implementing Magic functionality
92
93 # XXX - for some odd reason, if Magic is made a new-style class, we get errors
94 # on construction of the main InteractiveShell object. Something odd is going
95 # on with super() calls, Component and the MRO... For now leave it as-is, but
96 # eventually this needs to be clarified.
97
86 class Magic:
98 class Magic:
87 """Magic functions for InteractiveShell.
99 """Magic functions for InteractiveShell.
88
100
@@ -334,7 +346,7 b' python-profiler package from non-free.""")'
334 raise ValueError,'incorrect mode given: %s' % mode
346 raise ValueError,'incorrect mode given: %s' % mode
335 # Get options
347 # Get options
336 list_all = kw.get('list_all',0)
348 list_all = kw.get('list_all',0)
337 posix = kw.get('posix',True)
349 posix = kw.get('posix', os.name == 'posix')
338
350
339 # Check if we have more than one argument to warrant extra processing:
351 # Check if we have more than one argument to warrant extra processing:
340 odict = {} # Dictionary with options
352 odict = {} # Dictionary with options
@@ -863,7 +875,7 b' Currently the magic system has the following functions:\\n"""'
863 show_all=opt('a'),ignore_case=ignore_case)
875 show_all=opt('a'),ignore_case=ignore_case)
864 except:
876 except:
865 shell.showtraceback()
877 shell.showtraceback()
866
878
867 def magic_who_ls(self, parameter_s=''):
879 def magic_who_ls(self, parameter_s=''):
868 """Return a sorted list of all interactive variables.
880 """Return a sorted list of all interactive variables.
869
881
@@ -873,17 +885,15 b' Currently the magic system has the following functions:\\n"""'
873 user_ns = self.shell.user_ns
885 user_ns = self.shell.user_ns
874 internal_ns = self.shell.internal_ns
886 internal_ns = self.shell.internal_ns
875 user_config_ns = self.shell.user_config_ns
887 user_config_ns = self.shell.user_config_ns
876 out = []
888 out = [ i for i in user_ns
889 if not i.startswith('_') \
890 and not (i in internal_ns or i in user_config_ns) ]
891
877 typelist = parameter_s.split()
892 typelist = parameter_s.split()
893 if typelist:
894 typeset = set(typelist)
895 out = [i for i in out if type(i).__name__ in typeset]
878
896
879 for i in user_ns:
880 if not (i.startswith('_') or i.startswith('_i')) \
881 and not (i in internal_ns or i in user_config_ns):
882 if typelist:
883 if type(user_ns[i]).__name__ in typelist:
884 out.append(i)
885 else:
886 out.append(i)
887 out.sort()
897 out.sort()
888 return out
898 return out
889
899
@@ -1268,7 +1278,6 b' Currently the magic system has the following functions:\\n"""'
1268 If you want IPython to automatically do this on every exception, see
1278 If you want IPython to automatically do this on every exception, see
1269 the %pdb magic for more details.
1279 the %pdb magic for more details.
1270 """
1280 """
1271
1272 self.shell.debugger(force=True)
1281 self.shell.debugger(force=True)
1273
1282
1274 @testdec.skip_doctest
1283 @testdec.skip_doctest
@@ -1571,7 +1580,7 b' Currently the magic system has the following functions:\\n"""'
1571 return
1580 return
1572
1581
1573 if filename.lower().endswith('.ipy'):
1582 if filename.lower().endswith('.ipy'):
1574 self.safe_execfile_ipy(filename)
1583 self.shell.safe_execfile_ipy(filename)
1575 return
1584 return
1576
1585
1577 # Control the response to exit() calls made by the script being run
1586 # Control the response to exit() calls made by the script being run
@@ -2522,20 +2531,7 b' Defaulting color scheme to \'NoColor\'"""'
2522 self.shell.pprint = 1 - self.shell.pprint
2531 self.shell.pprint = 1 - self.shell.pprint
2523 print 'Pretty printing has been turned', \
2532 print 'Pretty printing has been turned', \
2524 ['OFF','ON'][self.shell.pprint]
2533 ['OFF','ON'][self.shell.pprint]
2525
2534
2526 def magic_exit(self, parameter_s=''):
2527 """Exit IPython, confirming if configured to do so.
2528
2529 You can configure whether IPython asks for confirmation upon exit by
2530 setting the confirm_exit flag in the ipythonrc file."""
2531
2532 self.shell.exit()
2533
2534 def magic_quit(self, parameter_s=''):
2535 """Exit IPython, confirming if configured to do so (like %exit)"""
2536
2537 self.shell.exit()
2538
2539 def magic_Exit(self, parameter_s=''):
2535 def magic_Exit(self, parameter_s=''):
2540 """Exit IPython without confirmation."""
2536 """Exit IPython without confirmation."""
2541
2537
@@ -2685,11 +2681,12 b' Defaulting color scheme to \'NoColor\'"""'
2685 else:
2681 else:
2686 syscmdlist.append(ff)
2682 syscmdlist.append(ff)
2687 else:
2683 else:
2684 no_alias = self.shell.alias_manager.no_alias
2688 for pdir in path:
2685 for pdir in path:
2689 os.chdir(pdir)
2686 os.chdir(pdir)
2690 for ff in os.listdir(pdir):
2687 for ff in os.listdir(pdir):
2691 base, ext = os.path.splitext(ff)
2688 base, ext = os.path.splitext(ff)
2692 if isexec(ff) and base.lower() not in self.shell.no_alias:
2689 if isexec(ff) and base.lower() not in no_alias:
2693 if ext.lower() == '.exe':
2690 if ext.lower() == '.exe':
2694 ff = base
2691 ff = base
2695 try:
2692 try:
@@ -3365,7 +3362,7 b' Defaulting color scheme to \'NoColor\'"""'
3365 # By default, echo back to terminal unless quiet mode is requested
3362 # By default, echo back to terminal unless quiet mode is requested
3366 if not opts.has_key('q'):
3363 if not opts.has_key('q'):
3367 write = self.shell.write
3364 write = self.shell.write
3368 write(block)
3365 write(self.shell.pycolorize(block))
3369 if not block.endswith('\n'):
3366 if not block.endswith('\n'):
3370 write('\n')
3367 write('\n')
3371 write("## -- End pasted text --\n")
3368 write("## -- End pasted text --\n")
@@ -3378,34 +3375,6 b' Defaulting color scheme to \'NoColor\'"""'
3378 qr = IPython.core.usage.quick_reference + self.magic_magic('-brief')
3375 qr = IPython.core.usage.quick_reference + self.magic_magic('-brief')
3379
3376
3380 page(qr)
3377 page(qr)
3381
3382 def magic_upgrade(self,arg):
3383 """ Upgrade your IPython installation
3384
3385 This will copy the config files that don't yet exist in your
3386 ipython dir from the system config dir. Use this after upgrading
3387 IPython if you don't wish to delete your .ipython dir.
3388
3389 Call with -nolegacy to get rid of ipythonrc* files (recommended for
3390 new users)
3391
3392 """
3393 ip = self.getapi()
3394 ipinstallation = path(IPython.__file__).dirname()
3395 upgrade_script = '%s "%s"' % (sys.executable,ipinstallation / 'utils' / 'upgradedir.py')
3396 src_config = ipinstallation / 'config' / 'userconfig'
3397 userdir = path(ip.config.IPYTHONDIR)
3398 cmd = '%s "%s" "%s"' % (upgrade_script, src_config, userdir)
3399 print ">",cmd
3400 shell(cmd)
3401 if arg == '-nolegacy':
3402 legacy = userdir.files('ipythonrc*')
3403 print "Nuking legacy files:",legacy
3404
3405 [p.remove() for p in legacy]
3406 suffix = (sys.platform == 'win32' and '.ini' or '')
3407 (userdir / ('ipythonrc' + suffix)).write_text('# Empty, see ipy_user_conf.py\n')
3408
3409
3378
3410 def magic_doctest_mode(self,parameter_s=''):
3379 def magic_doctest_mode(self,parameter_s=''):
3411 """Toggle doctest mode on and off.
3380 """Toggle doctest mode on and off.
@@ -3427,8 +3396,6 b' Defaulting color scheme to \'NoColor\'"""'
3427 your existing IPython session.
3396 your existing IPython session.
3428 """
3397 """
3429
3398
3430 # XXX - Fix this to have cleaner activate/deactivate calls.
3431 from IPython.extensions import InterpreterPasteInput as ipaste
3432 from IPython.utils.ipstruct import Struct
3399 from IPython.utils.ipstruct import Struct
3433
3400
3434 # Shorthands
3401 # Shorthands
@@ -3451,8 +3418,6 b' Defaulting color scheme to \'NoColor\'"""'
3451
3418
3452 if mode == False:
3419 if mode == False:
3453 # turn on
3420 # turn on
3454 ipaste.activate_prefilter()
3455
3456 oc.prompt1.p_template = '>>> '
3421 oc.prompt1.p_template = '>>> '
3457 oc.prompt2.p_template = '... '
3422 oc.prompt2.p_template = '... '
3458 oc.prompt_out.p_template = ''
3423 oc.prompt_out.p_template = ''
@@ -3466,13 +3431,11 b' Defaulting color scheme to \'NoColor\'"""'
3466 oc.prompt_out.pad_left = False
3431 oc.prompt_out.pad_left = False
3467
3432
3468 shell.pprint = False
3433 shell.pprint = False
3469
3434
3470 shell.magic_xmode('Plain')
3435 shell.magic_xmode('Plain')
3471
3436
3472 else:
3437 else:
3473 # turn off
3438 # turn off
3474 ipaste.deactivate_prefilter()
3475
3476 oc.prompt1.p_template = shell.prompt_in1
3439 oc.prompt1.p_template = shell.prompt_in1
3477 oc.prompt2.p_template = shell.prompt_in2
3440 oc.prompt2.p_template = shell.prompt_in2
3478 oc.prompt_out.p_template = shell.prompt_out
3441 oc.prompt_out.p_template = shell.prompt_out
@@ -3485,7 +3448,7 b' Defaulting color scheme to \'NoColor\'"""'
3485 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3448 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3486 oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
3449 oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
3487
3450
3488 rc.pprint = dstore.rc_pprint
3451 shell.pprint = dstore.rc_pprint
3489
3452
3490 shell.magic_xmode(dstore.xmode)
3453 shell.magic_xmode(dstore.xmode)
3491
3454
@@ -3503,7 +3466,7 b' Defaulting color scheme to \'NoColor\'"""'
3503 using the (pylab/wthread/etc.) command line flags. GUI toolkits
3466 using the (pylab/wthread/etc.) command line flags. GUI toolkits
3504 can now be enabled, disabled and swtiched at runtime and keyboard
3467 can now be enabled, disabled and swtiched at runtime and keyboard
3505 interrupts should work without any problems. The following toolkits
3468 interrupts should work without any problems. The following toolkits
3506 are supports: wxPython, PyQt4, PyGTK, and Tk::
3469 are supported: wxPython, PyQt4, PyGTK, and Tk::
3507
3470
3508 %gui wx # enable wxPython event loop integration
3471 %gui wx # enable wxPython event loop integration
3509 %gui qt4|qt # enable PyQt4 event loop integration
3472 %gui qt4|qt # enable PyQt4 event loop integration
@@ -3522,25 +3485,13 b' Defaulting color scheme to \'NoColor\'"""'
3522
3485
3523 This is highly recommended for most users.
3486 This is highly recommended for most users.
3524 """
3487 """
3525 from IPython.lib import inputhook
3488 opts, arg = self.parse_options(parameter_s,'a')
3526 if "-a" in parameter_s:
3489 if arg=='': arg = None
3527 app = True
3490 return enable_gui(arg, 'a' in opts)
3528 else:
3529 app = False
3530 if not parameter_s:
3531 inputhook.clear_inputhook()
3532 elif 'wx' in parameter_s:
3533 return inputhook.enable_wx(app)
3534 elif ('qt4' in parameter_s) or ('qt' in parameter_s):
3535 return inputhook.enable_qt4(app)
3536 elif 'gtk' in parameter_s:
3537 return inputhook.enable_gtk(app)
3538 elif 'tk' in parameter_s:
3539 return inputhook.enable_tk(app)
3540
3491
3541 def magic_load_ext(self, module_str):
3492 def magic_load_ext(self, module_str):
3542 """Load an IPython extension by its module name."""
3493 """Load an IPython extension by its module name."""
3543 self.load_extension(module_str)
3494 return self.load_extension(module_str)
3544
3495
3545 def magic_unload_ext(self, module_str):
3496 def magic_unload_ext(self, module_str):
3546 """Unload an IPython extension by its module name."""
3497 """Unload an IPython extension by its module name."""
@@ -3550,4 +3501,113 b' Defaulting color scheme to \'NoColor\'"""'
3550 """Reload an IPython extension by its module name."""
3501 """Reload an IPython extension by its module name."""
3551 self.reload_extension(module_str)
3502 self.reload_extension(module_str)
3552
3503
3504 @testdec.skip_doctest
3505 def magic_install_profiles(self, s):
3506 """Install the default IPython profiles into the .ipython dir.
3507
3508 If the default profiles have already been installed, they will not
3509 be overwritten. You can force overwriting them by using the ``-o``
3510 option::
3511
3512 In [1]: %install_profiles -o
3513 """
3514 if '-o' in s:
3515 overwrite = True
3516 else:
3517 overwrite = False
3518 from IPython.config import profile
3519 profile_dir = os.path.split(profile.__file__)[0]
3520 ipython_dir = self.ipython_dir
3521 files = os.listdir(profile_dir)
3522
3523 to_install = []
3524 for f in files:
3525 if f.startswith('ipython_config'):
3526 src = os.path.join(profile_dir, f)
3527 dst = os.path.join(ipython_dir, f)
3528 if (not os.path.isfile(dst)) or overwrite:
3529 to_install.append((f, src, dst))
3530 if len(to_install)>0:
3531 print "Installing profiles to: ", ipython_dir
3532 for (f, src, dst) in to_install:
3533 shutil.copy(src, dst)
3534 print " %s" % f
3535
3536 def magic_install_default_config(self, s):
3537 """Install IPython's default config file into the .ipython dir.
3538
3539 If the default config file (:file:`ipython_config.py`) is already
3540 installed, it will not be overwritten. You can force overwriting
3541 by using the ``-o`` option::
3542
3543 In [1]: %install_default_config
3544 """
3545 if '-o' in s:
3546 overwrite = True
3547 else:
3548 overwrite = False
3549 from IPython.config import default
3550 config_dir = os.path.split(default.__file__)[0]
3551 ipython_dir = self.ipython_dir
3552 default_config_file_name = 'ipython_config.py'
3553 src = os.path.join(config_dir, default_config_file_name)
3554 dst = os.path.join(ipython_dir, default_config_file_name)
3555 if (not os.path.isfile(dst)) or overwrite:
3556 shutil.copy(src, dst)
3557 print "Installing default config file: %s" % dst
3558
3559 # Pylab support: simple wrappers that activate pylab, load gui input
3560 # handling and modify slightly %run
3561
3562 @testdec.skip_doctest
3563 def _pylab_magic_run(self, parameter_s=''):
3564 Magic.magic_run(self, parameter_s,
3565 runner=mpl_runner(self.shell.safe_execfile))
3566
3567 _pylab_magic_run.__doc__ = magic_run.__doc__
3568
3569 @testdec.skip_doctest
3570 def magic_pylab(self, s):
3571 """Load numpy and matplotlib to work interactively.
3572
3573 %pylab [GUINAME]
3574
3575 This function lets you activate pylab (matplotlib, numpy and
3576 interactive support) at any point during an IPython session.
3577
3578 It will import at the top level numpy as np, pyplot as plt, matplotlib,
3579 pylab and mlab, as well as all names from numpy and pylab.
3580
3581 Parameters
3582 ----------
3583 guiname : optional
3584 One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk' or
3585 'tk'). If given, the corresponding Matplotlib backend is used,
3586 otherwise matplotlib's default (which you can override in your
3587 matplotlib config file) is used.
3588
3589 Examples
3590 --------
3591 In this case, where the MPL default is TkAgg:
3592 In [2]: %pylab
3593
3594 Welcome to pylab, a matplotlib-based Python environment.
3595 Backend in use: TkAgg
3596 For more information, type 'help(pylab)'.
3597
3598 But you can explicitly request a different backend:
3599 In [3]: %pylab qt
3600
3601 Welcome to pylab, a matplotlib-based Python environment.
3602 Backend in use: Qt4Agg
3603 For more information, type 'help(pylab)'.
3604 """
3605 self.shell.enable_pylab(s)
3606
3607 def magic_tb(self, s):
3608 """Print the last traceback with the currently active exception mode.
3609
3610 See %xmode for changing exception reporting modes."""
3611 self.shell.showtraceback()
3612
3553 # end Magic
3613 # end Magic
@@ -39,7 +39,7 b' from IPython.core.splitinput import split_user_input'
39 from IPython.core.page import page
39 from IPython.core.page import page
40
40
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
41 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
42 from IPython.utils.genutils import make_quoted_expr
42 from IPython.utils.genutils import make_quoted_expr, Term
43 from IPython.utils.autoattr import auto_attr
43 from IPython.utils.autoattr import auto_attr
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
@@ -158,7 +158,7 b' class LineInfo(object):'
158 without worrying about *further* damaging state.
158 without worrying about *further* damaging state.
159 """
159 """
160 if not self._oinfo:
160 if not self._oinfo:
161 self._oinfo = ip._ofind(self.ifun)
161 self._oinfo = ip.shell._ofind(self.ifun)
162 return self._oinfo
162 return self._oinfo
163
163
164 def __str__(self):
164 def __str__(self):
@@ -362,7 +362,7 b' class PrefilterManager(Component):'
362 line = transformer.transform(line, continue_prompt)
362 line = transformer.transform(line, continue_prompt)
363 return line
363 return line
364
364
365 def prefilter_line(self, line, continue_prompt):
365 def prefilter_line(self, line, continue_prompt=False):
366 """Prefilter a single input line as text.
366 """Prefilter a single input line as text.
367
367
368 This method prefilters a single line of text by calling the
368 This method prefilters a single line of text by calling the
@@ -416,7 +416,7 b' class PrefilterManager(Component):'
416 # print "prefiltered line: %r" % prefiltered
416 # print "prefiltered line: %r" % prefiltered
417 return prefiltered
417 return prefiltered
418
418
419 def prefilter_lines(self, lines, continue_prompt):
419 def prefilter_lines(self, lines, continue_prompt=False):
420 """Prefilter multiple input lines of text.
420 """Prefilter multiple input lines of text.
421
421
422 This is the main entry point for prefiltering multiple lines of
422 This is the main entry point for prefiltering multiple lines of
@@ -427,11 +427,19 b' class PrefilterManager(Component):'
427 which is the case when the user goes back to a multiline history
427 which is the case when the user goes back to a multiline history
428 entry and presses enter.
428 entry and presses enter.
429 """
429 """
430 out = []
430 llines = lines.rstrip('\n').split('\n')
431 for line in lines.rstrip('\n').split('\n'):
431 # We can get multiple lines in one shot, where multiline input 'blends'
432 out.append(self.prefilter_line(line, continue_prompt))
432 # into one line, in cases like recalling from the readline history
433 return '\n'.join(out)
433 # buffer. We need to make sure that in such cases, we correctly
434
434 # communicate downstream which line is first and which are continuation
435 # ones.
436 if len(llines) > 1:
437 out = '\n'.join([self.prefilter_line(line, lnum>0)
438 for lnum, line in enumerate(llines) ])
439 else:
440 out = self.prefilter_line(llines[0], continue_prompt)
441
442 return out
435
443
436 #-----------------------------------------------------------------------------
444 #-----------------------------------------------------------------------------
437 # Prefilter transformers
445 # Prefilter transformers
@@ -508,6 +516,47 b' class AssignMagicTransformer(PrefilterTransformer):'
508 return line
516 return line
509
517
510
518
519 _classic_prompt_re = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )')
520
521 class PyPromptTransformer(PrefilterTransformer):
522 """Handle inputs that start with '>>> ' syntax."""
523
524 priority = Int(50, config=True)
525
526 def transform(self, line, continue_prompt):
527
528 if not line or line.isspace() or line.strip() == '...':
529 # This allows us to recognize multiple input prompts separated by
530 # blank lines and pasted in a single chunk, very common when
531 # pasting doctests or long tutorial passages.
532 return ''
533 m = _classic_prompt_re.match(line)
534 if m:
535 return line[len(m.group(0)):]
536 else:
537 return line
538
539
540 _ipy_prompt_re = re.compile(r'(^[ \t]*In \[\d+\]: |^[ \t]*\ \ \ \.\.\.+: )')
541
542 class IPyPromptTransformer(PrefilterTransformer):
543 """Handle inputs that start classic IPython prompt syntax."""
544
545 priority = Int(50, config=True)
546
547 def transform(self, line, continue_prompt):
548
549 if not line or line.isspace() or line.strip() == '...':
550 # This allows us to recognize multiple input prompts separated by
551 # blank lines and pasted in a single chunk, very common when
552 # pasting doctests or long tutorial passages.
553 return ''
554 m = _ipy_prompt_re.match(line)
555 if m:
556 return line[len(m.group(0)):]
557 else:
558 return line
559
511 #-----------------------------------------------------------------------------
560 #-----------------------------------------------------------------------------
512 # Prefilter checkers
561 # Prefilter checkers
513 #-----------------------------------------------------------------------------
562 #-----------------------------------------------------------------------------
@@ -755,9 +804,17 b' class PrefilterHandler(Component):'
755 line = line_info.line
804 line = line_info.line
756 continue_prompt = line_info.continue_prompt
805 continue_prompt = line_info.continue_prompt
757
806
758 if (continue_prompt and self.shell.autoindent and line.isspace() and
807 if (continue_prompt and
759 (0 < abs(len(line) - self.shell.indent_current_nsp) <= 2 or
808 self.shell.autoindent and
760 (self.shell.buffer[-1]).isspace() )):
809 line.isspace() and
810
811 (0 < abs(len(line) - self.shell.indent_current_nsp) <= 2
812 or
813 not self.shell.buffer
814 or
815 (self.shell.buffer[-1]).isspace()
816 )
817 ):
761 line = ''
818 line = ''
762
819
763 self.shell.log(line, line, continue_prompt)
820 self.shell.log(line, line, continue_prompt)
@@ -845,12 +902,11 b' class AutoHandler(PrefilterHandler):'
845 pre = line_info.pre
902 pre = line_info.pre
846 continue_prompt = line_info.continue_prompt
903 continue_prompt = line_info.continue_prompt
847 obj = line_info.ofind(self)['obj']
904 obj = line_info.ofind(self)['obj']
848
849 #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun,the_rest) # dbg
905 #print 'pre <%s> ifun <%s> rest <%s>' % (pre,ifun,the_rest) # dbg
850
906
851 # This should only be active for single-line input!
907 # This should only be active for single-line input!
852 if continue_prompt:
908 if continue_prompt:
853 self.log(line,line,continue_prompt)
909 self.shell.log(line,line,continue_prompt)
854 return line
910 return line
855
911
856 force_auto = isinstance(obj, IPyAutocall)
912 force_auto = isinstance(obj, IPyAutocall)
@@ -967,7 +1023,9 b' class EmacsHandler(PrefilterHandler):'
967
1023
968 _default_transformers = [
1024 _default_transformers = [
969 AssignSystemTransformer,
1025 AssignSystemTransformer,
970 AssignMagicTransformer
1026 AssignMagicTransformer,
1027 PyPromptTransformer,
1028 IPyPromptTransformer,
971 ]
1029 ]
972
1030
973 _default_checkers = [
1031 _default_checkers = [
@@ -992,4 +1050,3 b' _default_handlers = ['
992 HelpHandler,
1050 HelpHandler,
993 EmacsHandler
1051 EmacsHandler
994 ]
1052 ]
995
@@ -549,18 +549,23 b' class CachedOutput:'
549 # print "Got prompt: ", outprompt
549 # print "Got prompt: ", outprompt
550 if self.do_full_cache:
550 if self.do_full_cache:
551 cout_write(outprompt)
551 cout_write(outprompt)
552 else:
553 print "self.do_full_cache = False"
554
552
555 # and now call a possibly user-defined print mechanism
553 # and now call a possibly user-defined print mechanism. Note that
556 manipulated_val = self.display(arg)
554 # self.display typically prints as a side-effect, we don't do any
555 # printing to stdout here.
556 try:
557 manipulated_val = self.display(arg)
558 except TypeError:
559 # If the user's display hook didn't return a string we can
560 # print, we're done. Happens commonly if they return None
561 cout_write('\n')
562 return
557
563
558 # user display hooks can change the variable to be stored in
564 # user display hooks can change the variable to be stored in
559 # output history
565 # output history
560
561 if manipulated_val is not None:
566 if manipulated_val is not None:
562 arg = manipulated_val
567 arg = manipulated_val
563
568
564 # avoid recursive reference when displaying _oh/Out
569 # avoid recursive reference when displaying _oh/Out
565 if arg is not self.user_ns['_oh']:
570 if arg is not self.user_ns['_oh']:
566 self.update(arg)
571 self.update(arg)
@@ -1,10 +1,10 b''
1 #!/usr/bin/env python
1 # coding: utf-8
2 # encoding: utf-8
3 """
2 """
4 A simple class for quitting IPython.
3 A simple class for quitting IPython.
5
4
6 Authors:
5 Authors
7
6 -------
7 * Fernando Perez
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10
10
@@ -19,6 +19,7 b' Authors:'
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import sys
22
23
23 class Quitter(object):
24 class Quitter(object):
24 """Simple class to handle exit, similar to Python 2.5's.
25 """Simple class to handle exit, similar to Python 2.5's.
@@ -30,9 +31,13 b' class Quitter(object):'
30 self.shell = shell
31 self.shell = shell
31 self.name = name
32 self.name = name
32
33
33 def __repr__(self):
34 def __str__(self):
34 return 'Type %s() to exit.' % self.name
35 return 'Type %s() to exit.' % self.name
35 __str__ = __repr__
36
36
37 def __call__(self):
37 def __call__(self):
38 self.shell.exit() No newline at end of file
38 self.shell.ask_exit()
39
40 # Repr MUST return a string, else display like pprint hooks get confused
41 def __repr__(self):
42 self.shell.ask_exit()
43 return 'Bye.'
@@ -23,7 +23,7 b" name = 'ipython'"
23 development = True # change this to False to do a release
23 development = True # change this to False to do a release
24 version_base = '0.11'
24 version_base = '0.11'
25 branch = 'ipython'
25 branch = 'ipython'
26 revision = '1205'
26 revision = '1321'
27
27
28 if development:
28 if development:
29 if branch == 'ipython':
29 if branch == 'ipython':
@@ -1,16 +1,12 b''
1 """Simple script to instantiate a class for testing %run"""
1 """Simple script to be run *twice*, to check reference counting bugs.
2
2
3 import sys
3 See test_run for details."""
4
5 # An external test will check that calls to f() work after %run
6 class foo: pass
7
4
8 def f():
5 import sys
9 return foo()
10
6
11 # We also want to ensure that while objects remain available for immediate
7 # We want to ensure that while objects remain available for immediate access,
12 # access, objects from *previous* runs of the same script get collected, to
8 # objects from *previous* runs of the same script get collected, to avoid
13 # avoid accumulating massive amounts of old references.
9 # accumulating massive amounts of old references.
14 class C(object):
10 class C(object):
15 def __init__(self,name):
11 def __init__(self,name):
16 self.name = name
12 self.name = name
@@ -18,6 +14,7 b' class C(object):'
18 def __del__(self):
14 def __del__(self):
19 print 'tclass.py: deleting object:',self.name
15 print 'tclass.py: deleting object:',self.name
20
16
17
21 try:
18 try:
22 name = sys.argv[1]
19 name = sys.argv[1]
23 except IndexError:
20 except IndexError:
@@ -25,3 +22,9 b' except IndexError:'
25 else:
22 else:
26 if name.startswith('C'):
23 if name.startswith('C'):
27 c = C(name)
24 c = C(name)
25
26 #print >> sys.stderr, "ARGV:", sys.argv # dbg
27
28 # This next print statement is NOT debugging, we're making the check on a
29 # completely separate process so we verify by capturing stdout:
30 print 'ARGV 1-:', sys.argv[1:]
@@ -13,68 +13,233 b' import tempfile'
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 # our own packages
15 # our own packages
16 from IPython.core import iplib
16 from IPython.testing import decorators as dec
17 from IPython.core import ipapi
17 from IPython.testing.globalipapp import get_ipython
18 from IPython.core.oldusersetup import user_setup
19
18
20 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
21 # Globals
20 # Globals
22 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
23
22
24 # Useful global ipapi object and main IPython one. Unfortunately we have a
23 # Get the public instance of IPython
25 # long precedent of carrying the 'ipapi' global object which is injected into
24 ip = get_ipython()
26 # the system namespace as _ip, but that keeps a pointer to the actual IPython
27 # InteractiveShell instance, which is named IP. Since in testing we do need
28 # access to the real thing (we want to probe beyond what ipapi exposes), make
29 # here a global reference to each. In general, things that are exposed by the
30 # ipapi instance should be read from there, but we also will often need to use
31 # the actual IPython one.
32
33 # Get the public instance of IPython, and if it's None, make one so we can use
34 # it for testing
35 ip = ipapi.get()
36 if ip is None:
37 # IPython not running yet, make one from the testing machinery for
38 # consistency when the test suite is being run via iptest
39 from IPython.testing.plugin import ipdoctest
40 ip = ipapi.get()
41
25
42 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
43 # Test functions
27 # Test functions
44 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
45
29
30 @dec.parametric
46 def test_reset():
31 def test_reset():
47 """reset must clear most namespaces."""
32 """reset must clear most namespaces."""
48 ip.reset() # first, it should run without error
33 # The number of variables in the private user_config_ns is not zero, but it
49 # Then, check that most namespaces end up empty
34 # should be constant regardless of what we do
35 nvars_config_ns = len(ip.user_config_ns)
36
37 # Check that reset runs without error
38 ip.reset()
39
40 # Once we've reset it (to clear of any junk that might have been there from
41 # other tests, we can count how many variables are in the user's namespace
42 nvars_user_ns = len(ip.user_ns)
43
44 # Now add a few variables to user_ns, and check that reset clears them
45 ip.user_ns['x'] = 1
46 ip.user_ns['y'] = 1
47 ip.reset()
48
49 # Finally, check that all namespaces have only as many variables as we
50 # expect to find in them:
50 for ns in ip.ns_refs_table:
51 for ns in ip.ns_refs_table:
51 if ns is ip.user_ns:
52 if ns is ip.user_ns:
52 # The user namespace is reset with some data, so we can't check for
53 nvars_expected = nvars_user_ns
53 # it being empty
54 elif ns is ip.user_config_ns:
54 continue
55 nvars_expected = nvars_config_ns
55 nt.assert_equals(len(ns),0)
56 else:
56
57 nvars_expected = 0
57
58
58 # make sure that user_setup can be run re-entrantly in 'install' mode.
59 yield nt.assert_equals(len(ns), nvars_expected)
59 def test_user_setup():
60
60 # use a lambda to pass kwargs to the generator
61
61 user_setup = lambda a,k: user_setup(*a,**k)
62 # Tests for reporting of exceptions in various modes, handling of SystemExit,
62 kw = dict(mode='install', interactive=False)
63 # and %tb functionality. This is really a mix of testing ultraTB and iplib.
63
64
64 # Call the user setup and verify that the directory exists
65 def doctest_tb_plain():
65 yield user_setup, (ip.config.IPYTHONDIR,''), kw
66 """
66 yield os.path.isdir, ip.config.IPYTHONDIR
67 In [18]: xmode plain
67
68 Exception reporting mode: Plain
68 # Now repeat the operation with a non-existent directory. Check both that
69
69 # the call succeeds and that the directory is created.
70 In [19]: run simpleerr.py
70 tmpdir = tempfile.mktemp(prefix='ipython-test-')
71 Traceback (most recent call last):
71 # Use a try with an empty except because try/finally doesn't work with a
72 ...line 32, in <module>
72 # yield in Python 2.4.
73 bar(mode)
73 try:
74 ...line 16, in bar
74 yield user_setup, (tmpdir,''), kw
75 div0()
75 yield os.path.isdir, tmpdir
76 ...line 8, in div0
76 except:
77 x/y
77 pass
78 ZeroDivisionError: integer division or modulo by zero
78 # Clean up the temp dir once done
79 """
79 shutil.rmtree(tmpdir)
80
80 No newline at end of file
81
82 def doctest_tb_context():
83 """
84 In [3]: xmode context
85 Exception reporting mode: Context
86
87 In [4]: run simpleerr.py
88 ---------------------------------------------------------------------------
89 ZeroDivisionError Traceback (most recent call last)
90 <BLANKLINE>
91 ... in <module>()
92 30 mode = 'div'
93 31
94 ---> 32 bar(mode)
95 33
96 34
97 <BLANKLINE>
98 ... in bar(mode)
99 14 "bar"
100 15 if mode=='div':
101 ---> 16 div0()
102 17 elif mode=='exit':
103 18 try:
104 <BLANKLINE>
105 ... in div0()
106 6 x = 1
107 7 y = 0
108 ----> 8 x/y
109 9
110 10 def sysexit(stat, mode):
111 <BLANKLINE>
112 ZeroDivisionError: integer division or modulo by zero
113 """
114
115
116 def doctest_tb_verbose():
117 """
118 In [5]: xmode verbose
119 Exception reporting mode: Verbose
120
121 In [6]: run simpleerr.py
122 ---------------------------------------------------------------------------
123 ZeroDivisionError Traceback (most recent call last)
124 <BLANKLINE>
125 ... in <module>()
126 30 mode = 'div'
127 31
128 ---> 32 bar(mode)
129 global bar = <function bar at ...>
130 global mode = 'div'
131 33
132 34
133 <BLANKLINE>
134 ... in bar(mode='div')
135 14 "bar"
136 15 if mode=='div':
137 ---> 16 div0()
138 global div0 = <function div0 at ...>
139 17 elif mode=='exit':
140 18 try:
141 <BLANKLINE>
142 ... in div0()
143 6 x = 1
144 7 y = 0
145 ----> 8 x/y
146 x = 1
147 y = 0
148 9
149 10 def sysexit(stat, mode):
150 <BLANKLINE>
151 ZeroDivisionError: integer division or modulo by zero
152 """
153
154
155 def doctest_tb_sysexit():
156 """
157 In [17]: %xmode plain
158 Exception reporting mode: Plain
159
160 In [18]: %run simpleerr.py exit
161 An exception has occurred, use %tb to see the full traceback.
162 SystemExit: (1, 'Mode = exit')
163
164 In [19]: %run simpleerr.py exit 2
165 An exception has occurred, use %tb to see the full traceback.
166 SystemExit: (2, 'Mode = exit')
167
168 In [20]: %tb
169 Traceback (most recent call last):
170 File ... in <module>
171 bar(mode)
172 File ... line 22, in bar
173 sysexit(stat, mode)
174 File ... line 11, in sysexit
175 raise SystemExit(stat, 'Mode = %s' % mode)
176 SystemExit: (2, 'Mode = exit')
177
178 In [21]: %xmode context
179 Exception reporting mode: Context
180
181 In [22]: %tb
182 ---------------------------------------------------------------------------
183 SystemExit Traceback (most recent call last)
184 <BLANKLINE>
185 ...<module>()
186 30 mode = 'div'
187 31
188 ---> 32 bar(mode)
189 33
190 34
191 <BLANKLINE>
192 ...bar(mode)
193 20 except:
194 21 stat = 1
195 ---> 22 sysexit(stat, mode)
196 23 else:
197 24 raise ValueError('Unknown mode')
198 <BLANKLINE>
199 ...sysexit(stat, mode)
200 9
201 10 def sysexit(stat, mode):
202 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
203 12
204 13 def bar(mode):
205 <BLANKLINE>
206 SystemExit: (2, 'Mode = exit')
207
208 In [23]: %xmode verbose
209 Exception reporting mode: Verbose
210
211 In [24]: %tb
212 ---------------------------------------------------------------------------
213 SystemExit Traceback (most recent call last)
214 <BLANKLINE>
215 ... in <module>()
216 30 mode = 'div'
217 31
218 ---> 32 bar(mode)
219 global bar = <function bar at ...>
220 global mode = 'exit'
221 33
222 34
223 <BLANKLINE>
224 ... in bar(mode='exit')
225 20 except:
226 21 stat = 1
227 ---> 22 sysexit(stat, mode)
228 global sysexit = <function sysexit at ...>
229 stat = 2
230 mode = 'exit'
231 23 else:
232 24 raise ValueError('Unknown mode')
233 <BLANKLINE>
234 ... in sysexit(stat=2, mode='exit')
235 9
236 10 def sysexit(stat, mode):
237 ---> 11 raise SystemExit(stat, 'Mode = %s' % mode)
238 global SystemExit = undefined
239 stat = 2
240 mode = 'exit'
241 12
242 13 def bar(mode):
243 <BLANKLINE>
244 SystemExit: (2, 'Mode = exit')
245 """
@@ -2,22 +2,31 b''
2
2
3 Needs to be run by nose (to make ipython session available).
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5 from __future__ import absolute_import
5
6
7 #-----------------------------------------------------------------------------
8 # Imports
9 #-----------------------------------------------------------------------------
10
11 # stdlib
6 import os
12 import os
7 import sys
13 import sys
8 import tempfile
14 import tempfile
9 import types
15 import types
10 from cStringIO import StringIO
16 from cStringIO import StringIO
11
17
18 # third-party
12 import nose.tools as nt
19 import nose.tools as nt
13
20
21 # our own
22 from IPython.utils import genutils
14 from IPython.utils.platutils import find_cmd, get_long_path_name
23 from IPython.utils.platutils import find_cmd, get_long_path_name
15 from IPython.testing import decorators as dec
24 from IPython.testing import decorators as dec
16 from IPython.testing import tools as tt
25 from IPython.testing import tools as tt
17
26
18 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
19 # Test functions begin
28 # Test functions begin
20
29 #-----------------------------------------------------------------------------
21 def test_rehashx():
30 def test_rehashx():
22 # clear up everything
31 # clear up everything
23 _ip = get_ipython()
32 _ip = get_ipython()
@@ -37,6 +46,19 b' def test_rehashx():'
37 yield (nt.assert_true, len(scoms) > 10)
46 yield (nt.assert_true, len(scoms) > 10)
38
47
39
48
49 def test_magic_parse_options():
50 """Test that we don't mangle paths when parsing magic options."""
51 ip = get_ipython()
52 path = 'c:\\x'
53 opts = ip.parse_options('-f %s' % path,'f:')[0]
54 # argv splitting is os-dependent
55 if os.name == 'posix':
56 expected = 'c:x'
57 else:
58 expected = path
59 nt.assert_equals(opts['f'], expected)
60
61
40 def doctest_hist_f():
62 def doctest_hist_f():
41 """Test %hist -f with temporary filename.
63 """Test %hist -f with temporary filename.
42
64
@@ -45,35 +67,94 b' def doctest_hist_f():'
45 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
67 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
46
68
47 In [11]: %hist -n -f $tfile 3
69 In [11]: %hist -n -f $tfile 3
70
71 In [13]: import os; os.unlink(tfile)
48 """
72 """
49
73
50
74
51 def doctest_hist_r():
75 def doctest_hist_r():
52 """Test %hist -r
76 """Test %hist -r
53
77
54 XXX - This test is not recording the output correctly. Not sure why...
78 XXX - This test is not recording the output correctly. For some reason, in
79 testing mode the raw history isn't getting populated. No idea why.
80 Disabling the output checking for now, though at least we do run it.
55
81
56 In [20]: 'hist' in _ip.lsmagic()
82 In [1]: 'hist' in _ip.lsmagic()
57 Out[20]: True
83 Out[1]: True
58
84
59 In [6]: x=1
85 In [2]: x=1
60
86
61 In [7]: %hist -n -r 2
87 In [3]: %hist -r 2
62 x=1 # random
88 x=1 # random
63 hist -n -r 2 # random
89 %hist -r 2
64 """
90 """
65
91
66 # This test is known to fail on win32.
92 def doctest_hist_op():
67 # See ticket https://bugs.launchpad.net/bugs/366334
93 """Test %hist -op
68 def test_obj_del():
94
69 _ip = get_ipython()
95 In [1]: class b:
70 """Test that object's __del__ methods are called on exit."""
96 ...: pass
71 test_dir = os.path.dirname(__file__)
97 ...:
72 del_file = os.path.join(test_dir,'obj_del.py')
98
73 ipython_cmd = find_cmd('ipython')
99 In [2]: class s(b):
74 out = _ip.getoutput('%s %s' % (ipython_cmd, del_file))
100 ...: def __str__(self):
75 nt.assert_equals(out,'obj_del.py: object A deleted')
101 ...: return 's'
76
102 ...:
103
104 In [3]:
105
106 In [4]: class r(b):
107 ...: def __repr__(self):
108 ...: return 'r'
109 ...:
110
111 In [5]: class sr(s,r): pass
112 ...:
113
114 In [6]:
115
116 In [7]: bb=b()
117
118 In [8]: ss=s()
119
120 In [9]: rr=r()
121
122 In [10]: ssrr=sr()
123
124 In [11]: bb
125 Out[11]: <...b instance at ...>
126
127 In [12]: ss
128 Out[12]: <...s instance at ...>
129
130 In [13]:
131
132 In [14]: %hist -op
133 >>> class b:
134 ... pass
135 ...
136 >>> class s(b):
137 ... def __str__(self):
138 ... return 's'
139 ...
140 >>>
141 >>> class r(b):
142 ... def __repr__(self):
143 ... return 'r'
144 ...
145 >>> class sr(s,r): pass
146 >>>
147 >>> bb=b()
148 >>> ss=s()
149 >>> rr=r()
150 >>> ssrr=sr()
151 >>> bb
152 <...b instance at ...>
153 >>> ss
154 <...s instance at ...>
155 >>>
156 >>> get_ipython().magic("hist -op")
157 """
77
158
78 def test_shist():
159 def test_shist():
79 # Simple tests of ShadowHist class - test generator.
160 # Simple tests of ShadowHist class - test generator.
@@ -97,8 +178,12 b' def test_shist():'
97 yield nt.assert_equal,s.get(2),'world'
178 yield nt.assert_equal,s.get(2),'world'
98
179
99 shutil.rmtree(tfile)
180 shutil.rmtree(tfile)
181
100
182
101 @dec.skipif_not_numpy
183 # XXX failing for now, until we get clearcmd out of quarantine. But we should
184 # fix this and revert the skip to happen only if numpy is not around.
185 #@dec.skipif_not_numpy
186 @dec.skipknownfailure
102 def test_numpy_clear_array_undec():
187 def test_numpy_clear_array_undec():
103 from IPython.extensions import clearcmd
188 from IPython.extensions import clearcmd
104
189
@@ -109,162 +194,8 b' def test_numpy_clear_array_undec():'
109 yield (nt.assert_false, 'a' in _ip.user_ns)
194 yield (nt.assert_false, 'a' in _ip.user_ns)
110
195
111
196
112 @dec.skip()
113 def test_fail_dec(*a,**k):
114 yield nt.assert_true, False
115
116 @dec.skip('This one shouldn not run')
117 def test_fail_dec2(*a,**k):
118 yield nt.assert_true, False
119
120 @dec.skipknownfailure
121 def test_fail_dec3(*a,**k):
122 yield nt.assert_true, False
123
124
125 def doctest_refbug():
126 """Very nasty problem with references held by multiple runs of a script.
127 See: https://bugs.launchpad.net/ipython/+bug/269966
128
129 In [1]: _ip.clear_main_mod_cache()
130
131 In [2]: run refbug
132
133 In [3]: call_f()
134 lowercased: hello
135
136 In [4]: run refbug
137
138 In [5]: call_f()
139 lowercased: hello
140 lowercased: hello
141 """
142
143 #-----------------------------------------------------------------------------
144 # Tests for %run
145 #-----------------------------------------------------------------------------
146
147 # %run is critical enough that it's a good idea to have a solid collection of
148 # tests for it, some as doctests and some as normal tests.
149
150 def doctest_run_ns():
151 """Classes declared %run scripts must be instantiable afterwards.
152
153 In [11]: run tclass foo
154
155 In [12]: isinstance(f(),foo)
156 Out[12]: True
157 """
158
159
160 def doctest_run_ns2():
161 """Classes declared %run scripts must be instantiable afterwards.
162
163 In [4]: run tclass C-first_pass
164
165 In [5]: run tclass C-second_pass
166 tclass.py: deleting object: C-first_pass
167 """
168
169 def doctest_run_builtins():
170 """Check that %run doesn't damage __builtins__ via a doctest.
171
172 This is similar to the test_run_builtins, but I want *both* forms of the
173 test to catch any possible glitches in our testing machinery, since that
174 modifies %run somewhat. So for this, we have both a normal test (below)
175 and a doctest (this one).
176
177 In [1]: import tempfile
178
179 In [2]: bid1 = id(__builtins__)
180
181 In [3]: fname = tempfile.mkstemp()[1]
182
183 In [3]: f = open(fname,'w')
184
185 In [4]: f.write('pass\\n')
186
187 In [5]: f.flush()
188
189 In [6]: print type(__builtins__)
190 <type 'module'>
191
192 In [7]: %run "$fname"
193
194 In [7]: f.close()
195
196 In [8]: bid2 = id(__builtins__)
197
198 In [9]: print type(__builtins__)
199 <type 'module'>
200
201 In [10]: bid1 == bid2
202 Out[10]: True
203
204 In [12]: try:
205 ....: os.unlink(fname)
206 ....: except:
207 ....: pass
208 ....:
209 """
210
211 # For some tests, it will be handy to organize them in a class with a common
212 # setup that makes a temp file
213
214 class TestMagicRun(object):
215
216 def setup(self):
217 """Make a valid python temp file."""
218 fname = tempfile.mkstemp()[1]
219 f = open(fname,'w')
220 f.write('pass\n')
221 f.flush()
222 self.tmpfile = f
223 self.fname = fname
224
225 def run_tmpfile(self):
226 _ip = get_ipython()
227 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
228 # See below and ticket https://bugs.launchpad.net/bugs/366353
229 _ip.magic('run "%s"' % self.fname)
230
231 def test_builtins_id(self):
232 """Check that %run doesn't damage __builtins__ """
233 _ip = get_ipython()
234 # Test that the id of __builtins__ is not modified by %run
235 bid1 = id(_ip.user_ns['__builtins__'])
236 self.run_tmpfile()
237 bid2 = id(_ip.user_ns['__builtins__'])
238 tt.assert_equals(bid1, bid2)
239
240 def test_builtins_type(self):
241 """Check that the type of __builtins__ doesn't change with %run.
242
243 However, the above could pass if __builtins__ was already modified to
244 be a dict (it should be a module) by a previous use of %run. So we
245 also check explicitly that it really is a module:
246 """
247 _ip = get_ipython()
248 self.run_tmpfile()
249 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
250
251 def test_prompts(self):
252 """Test that prompts correctly generate after %run"""
253 self.run_tmpfile()
254 _ip = get_ipython()
255 p2 = str(_ip.outputcache.prompt2).strip()
256 nt.assert_equals(p2[:3], '...')
257
258 def teardown(self):
259 self.tmpfile.close()
260 try:
261 os.unlink(self.fname)
262 except:
263 # On Windows, even though we close the file, we still can't delete
264 # it. I have no clue why
265 pass
266
267 # Multiple tests for clipboard pasting
197 # Multiple tests for clipboard pasting
198 @dec.parametric
268 def test_paste():
199 def test_paste():
269 _ip = get_ipython()
200 _ip = get_ipython()
270 def paste(txt, flags='-q'):
201 def paste(txt, flags='-q'):
@@ -286,11 +217,11 b' def test_paste():'
286 # Run tests with fake clipboard function
217 # Run tests with fake clipboard function
287 user_ns.pop('x', None)
218 user_ns.pop('x', None)
288 paste('x=1')
219 paste('x=1')
289 yield (nt.assert_equal, user_ns['x'], 1)
220 yield nt.assert_equal(user_ns['x'], 1)
290
221
291 user_ns.pop('x', None)
222 user_ns.pop('x', None)
292 paste('>>> x=2')
223 paste('>>> x=2')
293 yield (nt.assert_equal, user_ns['x'], 2)
224 yield nt.assert_equal(user_ns['x'], 2)
294
225
295 paste("""
226 paste("""
296 >>> x = [1,2,3]
227 >>> x = [1,2,3]
@@ -299,14 +230,14 b' def test_paste():'
299 ... y.append(i**2)
230 ... y.append(i**2)
300 ...
231 ...
301 """)
232 """)
302 yield (nt.assert_equal, user_ns['x'], [1,2,3])
233 yield nt.assert_equal(user_ns['x'], [1,2,3])
303 yield (nt.assert_equal, user_ns['y'], [1,4,9])
234 yield nt.assert_equal(user_ns['y'], [1,4,9])
304
235
305 # Now, test that paste -r works
236 # Now, test that paste -r works
306 user_ns.pop('x', None)
237 user_ns.pop('x', None)
307 yield (nt.assert_false, 'x' in user_ns)
238 yield nt.assert_false('x' in user_ns)
308 _ip.magic('paste -r')
239 _ip.magic('paste -r')
309 yield (nt.assert_equal, user_ns['x'], [1,2,3])
240 yield nt.assert_equal(user_ns['x'], [1,2,3])
310
241
311 # Also test paste echoing, by temporarily faking the writer
242 # Also test paste echoing, by temporarily faking the writer
312 w = StringIO()
243 w = StringIO()
@@ -320,12 +251,29 b' def test_paste():'
320 out = w.getvalue()
251 out = w.getvalue()
321 finally:
252 finally:
322 _ip.write = writer
253 _ip.write = writer
323 yield (nt.assert_equal, user_ns['a'], 100)
254 yield nt.assert_equal(user_ns['a'], 100)
324 yield (nt.assert_equal, user_ns['b'], 200)
255 yield nt.assert_equal(user_ns['b'], 200)
325 yield (nt.assert_equal, out, code+"\n## -- End pasted text --\n")
256 yield nt.assert_equal(out, code+"\n## -- End pasted text --\n")
326
257
327 finally:
258 finally:
328 # This should be in a finally clause, instead of the bare except above.
259 # This should be in a finally clause, instead of the bare except above.
329 # Restore original hook
260 # Restore original hook
330 hooks.clipboard_get = original_clip
261 hooks.clipboard_get = original_clip
331
262
263
264 def test_time():
265 _ip.magic('time None')
266
267
268 def doctest_time():
269 """
270 In [10]: %time None
271 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
272 Wall time: 0.00 s
273 """
274
275 def test_doctest_mode():
276 "Toggle doctest_mode twice, it should be a no-op and run without error"
277 _ip.magic('doctest_mode')
278 _ip.magic('doctest_mode')
279
@@ -88,7 +88,6 b' import types'
88 from inspect import getsourcefile, getfile, getmodule,\
88 from inspect import getsourcefile, getfile, getmodule,\
89 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
89 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
90
90
91
92 # IPython's own modules
91 # IPython's own modules
93 # Modified pdb which doesn't damage IPython's readline handling
92 # Modified pdb which doesn't damage IPython's readline handling
94 from IPython.utils import PyColorize
93 from IPython.utils import PyColorize
@@ -263,7 +262,7 b' def _fixed_getinnerframes(etb, context=1,tb_offset=0):'
263
262
264 _parser = PyColorize.Parser()
263 _parser = PyColorize.Parser()
265
264
266 def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):
265 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
267 numbers_width = INDENT_SIZE - 1
266 numbers_width = INDENT_SIZE - 1
268 res = []
267 res = []
269 i = lnum - index
268 i = lnum - index
@@ -313,6 +312,15 b' def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):'
313 class TBTools:
312 class TBTools:
314 """Basic tools used by all traceback printer classes."""
313 """Basic tools used by all traceback printer classes."""
315
314
315 #: Default output stream, can be overridden at call time. A special value
316 #: of 'stdout' *as a string* can be given to force extraction of sys.stdout
317 #: at runtime. This allows testing exception printing with doctests, that
318 #: swap sys.stdout just at execution time.
319 #: Warning: be VERY careful to set this to one of the Term streams, NEVER
320 #: directly to sys.stdout/err, because under win32 the Term streams come from
321 #: pyreadline and know how to handle color correctly, whie stdout/err don't.
322 out_stream = Term.cerr
323
316 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
324 def __init__(self,color_scheme = 'NoColor',call_pdb=False):
317 # Whether to call the interactive pdb debugger after printing
325 # Whether to call the interactive pdb debugger after printing
318 # tracebacks or not
326 # tracebacks or not
@@ -376,16 +384,31 b' class ListTB(TBTools):'
376
384
377 def __call__(self, etype, value, elist):
385 def __call__(self, etype, value, elist):
378 Term.cout.flush()
386 Term.cout.flush()
379 print >> Term.cerr, self.text(etype,value,elist)
387 Term.cerr.writeln(self.text(etype,value,elist))
380 Term.cerr.flush()
388
389 def text(self, etype, value, elist, context=5):
390 """Return a color formatted string with the traceback info.
391
392 Parameters
393 ----------
394 etype : exception type
395 Type of the exception raised.
381
396
382 def text(self,etype, value, elist,context=5):
397 value : object
383 """Return a color formatted string with the traceback info."""
398 Data stored in the exception
399
400 elist : list
401 List of frames, see class docstring for details.
402
403 Returns
404 -------
405 String with formatted exception.
406 """
384
407
385 Colors = self.Colors
408 Colors = self.Colors
386 out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)]
409 out_string = []
387 if elist:
410 if elist:
388 out_string.append('Traceback %s(most recent call last)%s:' % \
411 out_string.append('Traceback %s(most recent call last)%s:' %
389 (Colors.normalEm, Colors.Normal) + '\n')
412 (Colors.normalEm, Colors.Normal) + '\n')
390 out_string.extend(self._format_list(elist))
413 out_string.extend(self._format_list(elist))
391 lines = self._format_exception_only(etype, value)
414 lines = self._format_exception_only(etype, value)
@@ -492,15 +515,29 b' class ListTB(TBTools):'
492 else:
515 else:
493 list.append('%s\n' % str(stype))
516 list.append('%s\n' % str(stype))
494
517
495 # vds:>>
518 # sync with user hooks
496 if have_filedata:
519 if have_filedata:
497 ipinst = ipapi.get()
520 ipinst = ipapi.get()
498 if ipinst is not None:
521 if ipinst is not None:
499 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
522 ipinst.hooks.synchronize_with_editor(filename, lineno, 0)
500 # vds:<<
501
523
502 return list
524 return list
503
525
526 def show_exception_only(self, etype, value):
527 """Only print the exception type and message, without a traceback.
528
529 Parameters
530 ----------
531 etype : exception type
532 value : exception value
533 """
534 # This method needs to use __call__ from *this* class, not the one from
535 # a subclass whose signature or behavior may be different
536 Term.cout.flush()
537 ostream = sys.stdout if self.out_stream == 'stdout' else Term.cerr
538 ostream.write(ListTB.text(self, etype, value, []))
539 ostream.flush()
540
504 def _some_str(self, value):
541 def _some_str(self, value):
505 # Lifted from traceback.py
542 # Lifted from traceback.py
506 try:
543 try:
@@ -781,8 +818,8 b' class VerboseTB(TBTools):'
781 frames.append(level)
818 frames.append(level)
782 else:
819 else:
783 frames.append('%s%s' % (level,''.join(
820 frames.append('%s%s' % (level,''.join(
784 _formatTracebackLines(lnum,index,lines,Colors,lvals,
821 _format_traceback_lines(lnum,index,lines,Colors,lvals,
785 col_scheme))))
822 col_scheme))))
786
823
787 # Get (safely) a string form of the exception info
824 # Get (safely) a string form of the exception info
788 try:
825 try:
@@ -854,11 +891,11 b' class VerboseTB(TBTools):'
854 with display_trap:
891 with display_trap:
855 self.pdb.reset()
892 self.pdb.reset()
856 # Find the right frame so we don't pop up inside ipython itself
893 # Find the right frame so we don't pop up inside ipython itself
857 if hasattr(self,'tb'):
894 if hasattr(self,'tb') and self.tb is not None:
858 etb = self.tb
895 etb = self.tb
859 else:
896 else:
860 etb = self.tb = sys.last_traceback
897 etb = self.tb = sys.last_traceback
861 while self.tb.tb_next is not None:
898 while self.tb is not None and self.tb.tb_next is not None:
862 self.tb = self.tb.tb_next
899 self.tb = self.tb.tb_next
863 if etb and etb.tb_next:
900 if etb and etb.tb_next:
864 etb = etb.tb_next
901 etb = etb.tb_next
@@ -872,8 +909,7 b' class VerboseTB(TBTools):'
872 (etype, evalue, etb) = info or sys.exc_info()
909 (etype, evalue, etb) = info or sys.exc_info()
873 self.tb = etb
910 self.tb = etb
874 Term.cout.flush()
911 Term.cout.flush()
875 print >> Term.cerr, self.text(etype, evalue, etb)
912 Term.cerr.writeln(self.text(etype, evalue, etb))
876 Term.cerr.flush()
877
913
878 # Changed so an instance can just be called as VerboseTB_inst() and print
914 # Changed so an instance can just be called as VerboseTB_inst() and print
879 # out the right info on its own.
915 # out the right info on its own.
@@ -980,6 +1016,7 b' class AutoFormattedTB(FormattedTB):'
980 except:
1016 except:
981 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1017 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
982 """
1018 """
1019
983 def __call__(self,etype=None,evalue=None,etb=None,
1020 def __call__(self,etype=None,evalue=None,etb=None,
984 out=None,tb_offset=None):
1021 out=None,tb_offset=None):
985 """Print out a formatted exception traceback.
1022 """Print out a formatted exception traceback.
@@ -990,16 +1027,18 b' class AutoFormattedTB(FormattedTB):'
990 - tb_offset: the number of frames to skip over in the stack, on a
1027 - tb_offset: the number of frames to skip over in the stack, on a
991 per-call basis (this overrides temporarily the instance's tb_offset
1028 per-call basis (this overrides temporarily the instance's tb_offset
992 given at initialization time. """
1029 given at initialization time. """
993
1030
994 if out is None:
1031 if out is None:
995 out = Term.cerr
1032 out = sys.stdout if self.out_stream=='stdout' else self.out_stream
996 Term.cout.flush()
1033 Term.cout.flush()
997 if tb_offset is not None:
1034 if tb_offset is not None:
998 tb_offset, self.tb_offset = self.tb_offset, tb_offset
1035 tb_offset, self.tb_offset = self.tb_offset, tb_offset
999 print >> out, self.text(etype, evalue, etb)
1036 out.write(self.text(etype, evalue, etb))
1037 out.write('\n')
1000 self.tb_offset = tb_offset
1038 self.tb_offset = tb_offset
1001 else:
1039 else:
1002 print >> out, self.text(etype, evalue, etb)
1040 out.write(self.text(etype, evalue, etb))
1041 out.write('\n')
1003 out.flush()
1042 out.flush()
1004 try:
1043 try:
1005 self.debugger()
1044 self.debugger()
@@ -1,338 +1,47 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 #*****************************************************************************
2 """Usage information for the main IPython applications.
3 # Copyright (C) 2001-2004 Fernando Perez. <fperez@colorado.edu>
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008-2010 The IPython Development Team
6 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
4 #
7 #
5 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
7 #*****************************************************************************
10 #-----------------------------------------------------------------------------
8
11
9 import sys
12 import sys
10 from IPython.core import release
13 from IPython.core import release
11
14
12 __doc__ = """
15 cl_usage = """\
13 IPython -- An enhanced Interactive Python
16 ipython [options] [files]
14 =========================================
15
17
16 A Python shell with automatic history (input and output), dynamic object
18 IPython: an enhanced interactive Python shell.
17 introspection, easier configuration, command completion, access to the system
19
18 shell and more.
20 A Python shell with automatic history (input and output), dynamic object
19
21 introspection, easier configuration, command completion, access to the
20 IPython can also be embedded in running programs. See EMBEDDING below.
22 system shell and more. IPython can also be embedded in running programs.
21
22
23 USAGE
24 ipython [options] files
25
26 If invoked with no options, it executes all the files listed in
27 sequence and drops you into the interpreter while still acknowledging
28 any options you may have set in your ipythonrc file. This behavior is
29 different from standard Python, which when called as python -i will
30 only execute one file and will ignore your configuration setup.
31
32 Please note that some of the configuration options are not available at
33 the command line, simply because they are not practical here. Look into
34 your ipythonrc configuration file for details on those. This file
35 typically installed in the $HOME/.ipython directory.
36
37 For Windows users, $HOME resolves to C:\\Documents and
38 Settings\\YourUserName in most instances, and _ipython is used instead
39 of .ipython, since some Win32 programs have problems with dotted names
40 in directories.
41
42 In the rest of this text, we will refer to this directory as
43 IPYTHONDIR.
44
45 REGULAR OPTIONS
46 After the above threading options have been given, regular options can
47 follow in any order. All options can be abbreviated to their shortest
48 non-ambiguous form and are case-sensitive. One or two dashes can be
49 used. Some options have an alternate short form, indicated after a |.
50
51 Most options can also be set from your ipythonrc configuration file.
52 See the provided examples for assistance. Options given on the comman-
53 dline override the values set in the ipythonrc file.
54
55 All options with a [no] prepended can be specified in negated form
56 (using -nooption instead of -option) to turn the feature off.
57
58 -h, --help
59 Show summary of options.
60
61 -autocall <val>
62 Make IPython automatically call any callable object even if you
63 didn't type explicit parentheses. For example, 'str 43' becomes
64 'str(43)' automatically. The value can be '0' to disable the
65 feature, '1' for 'smart' autocall, where it is not applied if
66 there are no more arguments on the line, and '2' for 'full'
67 autocall, where all callable objects are automatically called
68 (even if no arguments are present). The default is '1'.
69
70 -[no]autoindent
71 Turn automatic indentation on/off.
72
73 -[no]automagic
74 Make magic commands automatic (without needing their first char-
75 acter to be %). Type %magic at the IPython prompt for more
76 information.
77
78 -[no]autoedit_syntax
79 When a syntax error occurs after editing a file, automatically
80 open the file to the trouble causing line for convenient fixing.
81
82 -[no]banner
83 Print the intial information banner (default on).
84
85 -c <command>
86 Execute the given command string, and set sys.argv to ['c'].
87 This is similar to the -c option in the normal Python inter-
88 preter.
89
90 -cache_size|cs <n>
91 Size of the output cache (maximum number of entries to hold in
92 memory). The default is 1000, you can change it permanently in
93 your config file. Setting it to 0 completely disables the
94 caching system, and the minimum value accepted is 20 (if you
95 provide a value less than 20, it is reset to 0 and a warning is
96 issued). This limit is defined because otherwise you'll spend
97 more time re-flushing a too small cache than working.
98
99 -classic|cl
100 Gives IPython a similar feel to the classic Python prompt.
101
102 -colors <scheme>
103 Color scheme for prompts and exception reporting. Currently
104 implemented: NoColor, Linux, and LightBG.
105
106 -[no]color_info
107 IPython can display information about objects via a set of func-
108 tions, and optionally can use colors for this, syntax highlight-
109 ing source code and various other elements. However, because
110 this information is passed through a pager (like 'less') and
111 many pagers get confused with color codes, this option is off by
112 default. You can test it and turn it on permanently in your
113 ipythonrc file if it works for you. As a reference, the 'less'
114 pager supplied with Mandrake 8.2 works ok, but that in RedHat
115 7.2 doesn't.
116
117 Test it and turn it on permanently if it works with your system.
118 The magic function @color_info allows you to toggle this inter-
119 actively for testing.
120
121 -[no]confirm_exit
122 Set to confirm when you try to exit IPython with an EOF (Con-
123 trol-D in Unix, Control-Z/Enter in Windows). Note that using the
124 magic functions @Exit or @Quit you can force a direct exit,
125 bypassing any confirmation.
126
127 -[no]debug
128 Show information about the loading process. Very useful to pin
129 down problems with your configuration files or to get details
130 about session restores.
131
132 -[no]deep_reload
133 IPython can use the deep_reload module which reloads changes in
134 modules recursively (it replaces the reload() function, so you
135 don't need to change anything to use it). deep_reload() forces a
136 full reload of modules whose code may have changed, which the
137 default reload() function does not.
138
139 When deep_reload is off, IPython will use the normal reload(),
140 but deep_reload will still be available as dreload(). This fea-
141 ture is off by default [which means that you have both normal
142 reload() and dreload()].
143
144 -editor <name>
145 Which editor to use with the @edit command. By default, IPython
146 will honor your EDITOR environment variable (if not set, vi is
147 the Unix default and notepad the Windows one). Since this editor
148 is invoked on the fly by IPython and is meant for editing small
149 code snippets, you may want to use a small, lightweight editor
150 here (in case your default EDITOR is something like Emacs).
151
152 -ipythondir <name>
153 The name of your IPython configuration directory IPYTHONDIR.
154 This can also be specified through the environment variable
155 IPYTHONDIR.
156
157 -log|l Generate a log file of all input. The file is named
158 ipython_log.py in your current directory (which prevents logs
159 from multiple IPython sessions from trampling each other). You
160 can use this to later restore a session by loading your logfile
161 as a file to be executed with option -logplay (see below).
162
163 -logfile|lf
164 Specify the name of your logfile.
165
166 -logplay|lp
167 Replay a previous log. For restoring a session as close as pos-
168 sible to the state you left it in, use this option (don't just
169 run the logfile). With -logplay, IPython will try to reconstruct
170 the previous working environment in full, not just execute the
171 commands in the logfile.
172 When a session is restored, logging is automatically turned on
173 again with the name of the logfile it was invoked with (it is
174 read from the log header). So once you've turned logging on for
175 a session, you can quit IPython and reload it as many times as
176 you want and it will continue to log its history and restore
177 from the beginning every time.
178
179 Caveats: there are limitations in this option. The history vari-
180 ables _i*,_* and _dh don't get restored properly. In the future
181 we will try to implement full session saving by writing and
182 retrieving a failed because of inherent limitations of Python's
183 Pickle module, so this may have to wait.
184
185 -[no]messages
186 Print messages which IPython collects about its startup process
187 (default on).
188
189 -[no]pdb
190 Automatically call the pdb debugger after every uncaught excep-
191 tion. If you are used to debugging using pdb, this puts you
192 automatically inside of it after any call (either in IPython or
193 in code called by it) which triggers an exception which goes
194 uncaught.
195
196 -[no]pprint
197 IPython can optionally use the pprint (pretty printer) module
198 for displaying results. pprint tends to give a nicer display of
199 nested data structures. If you like it, you can turn it on per-
200 manently in your config file (default off).
201
202 -profile|p <name>
203 Assume that your config file is ipythonrc-<name> (looks in cur-
204 rent dir first, then in IPYTHONDIR). This is a quick way to keep
205 and load multiple config files for different tasks, especially
206 if you use the include option of config files. You can keep a
207 basic IPYTHONDIR/ipythonrc file and then have other 'profiles'
208 which include this one and load extra things for particular
209 tasks. For example:
210
211 1) $HOME/.ipython/ipythonrc : load basic things you always want.
212 2) $HOME/.ipython/ipythonrc-math : load (1) and basic math-
213 related modules.
214 3) $HOME/.ipython/ipythonrc-numeric : load (1) and Numeric and
215 plotting modules.
216
217 Since it is possible to create an endless loop by having circu-
218 lar file inclusions, IPython will stop if it reaches 15 recur-
219 sive inclusions.
220
221 -prompt_in1|pi1 <string>
222 Specify the string used for input prompts. Note that if you are
223 using numbered prompts, the number is represented with a '\#' in
224 the string. Don't forget to quote strings with spaces embedded
225 in them. Default: 'In [\#]: '.
226
227 Most bash-like escapes can be used to customize IPython's
228 prompts, as well as a few additional ones which are IPython-spe-
229 cific. All valid prompt escapes are described in detail in the
230 Customization section of the IPython HTML/PDF manual.
231
232 -prompt_in2|pi2 <string>
233 Similar to the previous option, but used for the continuation
234 prompts. The special sequence '\D' is similar to '\#', but with
235 all digits replaced dots (so you can have your continuation
236 prompt aligned with your input prompt). Default: ' .\D.: '
237 (note three spaces at the start for alignment with 'In [\#]').
238
239 -prompt_out|po <string>
240 String used for output prompts, also uses numbers like
241 prompt_in1. Default: 'Out[\#]:'.
242
243 -quick Start in bare bones mode (no config file loaded).
244
245 -rcfile <name>
246 Name of your IPython resource configuration file. normally
247 IPython loads ipythonrc (from current directory) or
248 IPYTHONDIR/ipythonrc. If the loading of your config file fails,
249 IPython starts with a bare bones configuration (no modules
250 loaded at all).
251
252 -[no]readline
253 Use the readline library, which is needed to support name com-
254 pletion and command history, among other things. It is enabled
255 by default, but may cause problems for users of X/Emacs in
256 Python comint or shell buffers.
257
258 Note that emacs 'eterm' buffers (opened with M-x term) support
259 IPython's readline and syntax coloring fine, only 'emacs' (M-x
260 shell and C-c !) buffers do not.
261
262 -screen_length|sl <n>
263 Number of lines of your screen. This is used to control print-
264 ing of very long strings. Strings longer than this number of
265 lines will be sent through a pager instead of directly printed.
266
267 The default value for this is 0, which means IPython will auto-
268 detect your screen size every time it needs to print certain
269 potentially long strings (this doesn't change the behavior of
270 the 'print' keyword, it's only triggered internally). If for
271 some reason this isn't working well (it needs curses support),
272 specify it yourself. Otherwise don't change the default.
273
274 -separate_in|si <string>
275 Separator before input prompts. Default '0.
276
277 -separate_out|so <string>
278 Separator before output prompts. Default: 0 (nothing).
279
280 -separate_out2|so2 <string>
281 Separator after output prompts. Default: 0 (nothing).
282
283 -nosep Shorthand for '-separate_in 0 -separate_out 0 -separate_out2 0'.
284 Simply removes all input/output separators.
285
286 -upgrade
287 Allows you to upgrade your IPYTHONDIR configuration when you
288 install a new version of IPython. Since new versions may
289 include new command lines options or example files, this copies
290 updated ipythonrc-type files. However, it backs up (with a .old
291 extension) all files which it overwrites so that you can merge
292 back any custimizations you might have in your personal files.
293
294 -Version
295 Print version information and exit.
296
297 -wxversion <string>
298 Select a specific version of wxPython (used in conjunction with
299 -wthread). Requires the wxversion module, part of recent
300 wxPython distributions.
301
302 -xmode <modename>
303 Mode for exception reporting. The valid modes are Plain, Con-
304 text, and Verbose.
305
306 - Plain: similar to python's normal traceback printing.
307
308 - Context: prints 5 lines of context source code around each
309 line in the traceback.
310
311 - Verbose: similar to Context, but additionally prints the vari-
312 ables currently visible where the exception happened (shortening
313 their strings if too long). This can potentially be very slow,
314 if you happen to have a huge data structure whose string repre-
315 sentation is complex to compute. Your computer may appear to
316 freeze for a while with cpu usage at 100%. If this occurs, you
317 can cancel the traceback with Ctrl-C (maybe hitting it more than
318 once).
319
320
321 EMBEDDING
322 It is possible to start an IPython instance inside your own Python pro-
323 grams. In the documentation example files there are some illustrations
324 on how to do this.
325
326 This feature allows you to evalutate dynamically the state of your
327 code, operate with your variables, analyze them, etc. Note however
328 that any changes you make to values while in the shell do NOT propagate
329 back to the running code, so it is safe to modify your values because
330 you won't break your code in bizarre ways by doing so.
331 """
332
23
333 cmd_line_usage = __doc__
24 If invoked with no options, it executes all the files listed in sequence
25 and exits, use -i to enter interactive mode after running the files. Files
26 ending in .py will be treated as normal Python, but files ending in .ipy
27 can contain special IPython syntax (magic commands, shell expansions, etc.)
28
29 Please note that some of the configuration options are not available at the
30 command line, simply because they are not practical here. Look into your
31 ipython_config.py configuration file for details on those.
32
33 This file typically installed in the $HOME/.ipython directory. For Windows
34 users, $HOME resolves to C:\\Documents and Settings\\YourUserName in most
35 instances.
36
37 In IPython's documentation, we will refer to this directory as IPYTHON_DIR,
38 you can change its default location by setting any path you want in this
39 environment variable.
40
41 For more information, see the manual available in HTML and PDF in your
42 installation, or online at http://ipython.scipy.org.
43 """
334
44
335 #---------------------------------------------------------------------------
336 interactive_usage = """
45 interactive_usage = """
337 IPython -- An enhanced Interactive Python
46 IPython -- An enhanced Interactive Python
338 =========================================
47 =========================================
@@ -128,13 +128,15 b' class PrettyResultDisplay(Component):'
128 #-----------------------------------------------------------------------------
128 #-----------------------------------------------------------------------------
129
129
130
130
131 def load_ipython_extension(ip):
131 def load_ipython_extension(ip=None):
132 """Load the extension in IPython as a hook."""
132 """Load the extension in IPython as a hook."""
133 if ip is None: ip = get_ipython()
133 global _loaded
134 global _loaded
134 if not _loaded:
135 if not _loaded:
135 prd = PrettyResultDisplay(ip, name='pretty_result_display')
136 prd = PrettyResultDisplay(ip, name='pretty_result_display')
136 ip.set_hook('result_display', prd, priority=99)
137 ip.set_hook('result_display', prd, priority=99)
137 _loaded = True
138 _loaded = True
139 return prd
138
140
139 def unload_ipython_extension(ip):
141 def unload_ipython_extension(ip):
140 """Unload the extension."""
142 """Unload the extension."""
@@ -163,60 +165,3 b' def dtype_pprinter(obj, p, cycle):'
163 p.breakable()
165 p.breakable()
164 p.pretty(field)
166 p.pretty(field)
165 p.end_group(7, '])')
167 p.end_group(7, '])')
166
167
168 #-----------------------------------------------------------------------------
169 # Tests
170 #-----------------------------------------------------------------------------
171
172
173 def test_pretty():
174 """
175 In [1]: from IPython.extensions import ipy_pretty
176
177 In [2]: ipy_pretty.activate()
178
179 In [3]: class A(object):
180 ...: def __repr__(self):
181 ...: return 'A()'
182 ...:
183 ...:
184
185 In [4]: a = A()
186
187 In [5]: a
188 Out[5]: A()
189
190 In [6]: def a_pretty_printer(obj, p, cycle):
191 ...: p.text('<A>')
192 ...:
193 ...:
194
195 In [7]: ipy_pretty.for_type(A, a_pretty_printer)
196
197 In [8]: a
198 Out[8]: <A>
199
200 In [9]: class B(object):
201 ...: def __repr__(self):
202 ...: return 'B()'
203 ...:
204 ...:
205
206 In [10]: B.__module__, B.__name__
207 Out[10]: ('__main__', 'B')
208
209 In [11]: def b_pretty_printer(obj, p, cycle):
210 ....: p.text('<B>')
211 ....:
212 ....:
213
214 In [12]: ipy_pretty.for_type_by_name('__main__', 'B', b_pretty_printer)
215
216 In [13]: b = B()
217
218 In [14]: b
219 Out[14]: <B>
220 """
221 assert False, "This should only be doctested, not run."
222
@@ -15,21 +15,20 b' Simple tests for :mod:`IPython.extensions.pretty`.'
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import sys
19 from unittest import TestCase
18 from unittest import TestCase
20
19
21 from IPython.core.component import Component, masquerade_as
20 from IPython.core.component import Component, masquerade_as
22 from IPython.core.iplib import InteractiveShell
21 from IPython.core.iplib import InteractiveShell
23 from IPython.extensions import pretty as pretty_ext
22 from IPython.extensions import pretty as pretty_ext
24 from IPython.external import pretty
23 from IPython.external import pretty
25
24 from IPython.testing import decorators as dec
25 from IPython.testing import tools as tt
26 from IPython.utils.traitlets import Bool
26 from IPython.utils.traitlets import Bool
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Tests
29 # Tests
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32
33 class InteractiveShellStub(Component):
32 class InteractiveShellStub(Component):
34 pprint = Bool(True)
33 pprint = Bool(True)
35
34
@@ -43,9 +42,11 b' class TestPrettyResultDisplay(TestCase):'
43
42
44 def setUp(self):
43 def setUp(self):
45 self.ip = InteractiveShellStub(None)
44 self.ip = InteractiveShellStub(None)
46 # This allows our stub to be retrieved instead of the real InteractiveShell
45 # This allows our stub to be retrieved instead of the real
46 # InteractiveShell
47 masquerade_as(self.ip, InteractiveShell)
47 masquerade_as(self.ip, InteractiveShell)
48 self.prd = pretty_ext.PrettyResultDisplay(self.ip, name='pretty_result_display')
48 self.prd = pretty_ext.PrettyResultDisplay(self.ip,
49 name='pretty_result_display')
49
50
50 def test_for_type(self):
51 def test_for_type(self):
51 self.prd.for_type(A, a_pprinter)
52 self.prd.for_type(A, a_pprinter)
@@ -53,4 +54,48 b' class TestPrettyResultDisplay(TestCase):'
53 result = pretty.pretty(a)
54 result = pretty.pretty(a)
54 self.assertEquals(result, "<A>")
55 self.assertEquals(result, "<A>")
55
56
57 ipy_src = """
58 class A(object):
59 def __repr__(self):
60 return 'A()'
61
62 class B(object):
63 def __repr__(self):
64 return 'B()'
65
66 a = A()
67 b = B()
68
69 def a_pretty_printer(obj, p, cycle):
70 p.text('<A>')
71
72 def b_pretty_printer(obj, p, cycle):
73 p.text('<B>')
74
75
76 a
77 b
78
79 ip = get_ipython()
80 prd = ip.load_extension('pretty')
81 prd.for_type(A, a_pretty_printer)
82 prd.for_type_by_name(B.__module__, B.__name__, b_pretty_printer)
83
84 a
85 b
86 """
87 ipy_out = """
88 A()
89 B()
90 <A>
91 <B>
92 """
56
93
94 class TestPrettyInteractively(tt.TempFileMixin):
95
96 # XXX Unfortunately, ipexec_validate fails under win32. If someone helps
97 # us write a win32-compatible version, we can reactivate this test.
98 @dec.skip_win32
99 def test_printers(self):
100 self.mktmp(ipy_src, '.ipy')
101 tt.ipexec_validate(self.fname, ipy_out)
@@ -2,25 +2,17 b''
2
2
3 # Copyright © 2006-2009 Steven J. Bethard <steven.bethard@gmail.com>.
3 # Copyright © 2006-2009 Steven J. Bethard <steven.bethard@gmail.com>.
4 #
4 #
5 # Redistribution and use in source and binary forms, with or without
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 # modification, are permitted provided that the following conditions are met:
6 # use this file except in compliance with the License. You may obtain a copy
7 # of the License at
7 #
8 #
8 # * Redistributions of source code must retain the above copyright notice, this
9 # http://www.apache.org/licenses/LICENSE-2.0
9 # list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above copyright notice,
11 # this list of conditions and the following disclaimer in the documentation
12 # and/or other materials provided with the distribution.
13 #
10 #
14 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
11 # Unless required by applicable law or agreed to in writing, software
15 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
14 # License for the specific language governing permissions and limitations
18 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15 # under the License.
19 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
16
25 """Command-line parsing library
17 """Command-line parsing library
26
18
@@ -83,7 +75,7 b' considered public as object names -- the API of the formatter objects is'
83 still considered an implementation detail.)
75 still considered an implementation detail.)
84 """
76 """
85
77
86 __version__ = '1.0.1'
78 __version__ = '1.1a1'
87 __all__ = [
79 __all__ = [
88 'ArgumentParser',
80 'ArgumentParser',
89 'ArgumentError',
81 'ArgumentError',
@@ -92,7 +84,7 b' __all__ = ['
92 'FileType',
84 'FileType',
93 'HelpFormatter',
85 'HelpFormatter',
94 'RawDescriptionHelpFormatter',
86 'RawDescriptionHelpFormatter',
95 'RawTextHelpFormatter'
87 'RawTextHelpFormatter',
96 'ArgumentDefaultsHelpFormatter',
88 'ArgumentDefaultsHelpFormatter',
97 ]
89 ]
98
90
@@ -126,6 +118,10 b' except NameError:'
126 result.reverse()
118 result.reverse()
127 return result
119 return result
128
120
121
122 def _callable(obj):
123 return hasattr(obj, '__call__') or hasattr(obj, '__bases__')
124
129 # silence Python 2.6 buggy warnings about Exception.message
125 # silence Python 2.6 buggy warnings about Exception.message
130 if _sys.version_info[:2] == (2, 6):
126 if _sys.version_info[:2] == (2, 6):
131 import warnings
127 import warnings
@@ -141,7 +137,8 b" SUPPRESS = '==SUPPRESS=='"
141 OPTIONAL = '?'
137 OPTIONAL = '?'
142 ZERO_OR_MORE = '*'
138 ZERO_OR_MORE = '*'
143 ONE_OR_MORE = '+'
139 ONE_OR_MORE = '+'
144 PARSER = '==PARSER=='
140 PARSER = 'A...'
141 REMAINDER = '...'
145
142
146 # =============================
143 # =============================
147 # Utility functions and classes
144 # Utility functions and classes
@@ -508,6 +505,8 b' class HelpFormatter(object):'
508 return text
505 return text
509
506
510 def _format_text(self, text):
507 def _format_text(self, text):
508 if '%(prog)' in text:
509 text = text % dict(prog=self._prog)
511 text_width = self._width - self._current_indent
510 text_width = self._width - self._current_indent
512 indent = ' ' * self._current_indent
511 indent = ' ' * self._current_indent
513 return self._fill_text(text, text_width, indent) + '\n\n'
512 return self._fill_text(text, text_width, indent) + '\n\n'
@@ -608,7 +607,9 b' class HelpFormatter(object):'
608 result = '[%s [%s ...]]' % get_metavar(2)
607 result = '[%s [%s ...]]' % get_metavar(2)
609 elif action.nargs == ONE_OR_MORE:
608 elif action.nargs == ONE_OR_MORE:
610 result = '%s [%s ...]' % get_metavar(2)
609 result = '%s [%s ...]' % get_metavar(2)
611 elif action.nargs is PARSER:
610 elif action.nargs == REMAINDER:
611 result = '...'
612 elif action.nargs == PARSER:
612 result = '%s ...' % get_metavar(1)
613 result = '%s ...' % get_metavar(1)
613 else:
614 else:
614 formats = ['%s' for _ in range(action.nargs)]
615 formats = ['%s' for _ in range(action.nargs)]
@@ -724,6 +725,12 b' class ArgumentError(Exception):'
724 return format % dict(message=self.message,
725 return format % dict(message=self.message,
725 argument_name=self.argument_name)
726 argument_name=self.argument_name)
726
727
728
729 class ArgumentTypeError(Exception):
730 """An error from trying to convert a command line string to a type."""
731 pass
732
733
727 # ==============
734 # ==============
728 # Action classes
735 # Action classes
729 # ==============
736 # ==============
@@ -1018,6 +1025,7 b' class _VersionAction(Action):'
1018
1025
1019 def __init__(self,
1026 def __init__(self,
1020 option_strings,
1027 option_strings,
1028 version=None,
1021 dest=SUPPRESS,
1029 dest=SUPPRESS,
1022 default=SUPPRESS,
1030 default=SUPPRESS,
1023 help=None):
1031 help=None):
@@ -1027,10 +1035,15 b' class _VersionAction(Action):'
1027 default=default,
1035 default=default,
1028 nargs=0,
1036 nargs=0,
1029 help=help)
1037 help=help)
1038 self.version = version
1030
1039
1031 def __call__(self, parser, namespace, values, option_string=None):
1040 def __call__(self, parser, namespace, values, option_string=None):
1032 parser.print_version()
1041 version = self.version
1033 parser.exit()
1042 if version is None:
1043 version = parser.version
1044 formatter = parser._get_formatter()
1045 formatter.add_text(version)
1046 parser.exit(message=formatter.format_help())
1034
1047
1035
1048
1036 class _SubParsersAction(Action):
1049 class _SubParsersAction(Action):
@@ -1156,8 +1169,7 b' class Namespace(_AttributeHolder):'
1156 """
1169 """
1157
1170
1158 def __init__(self, **kwargs):
1171 def __init__(self, **kwargs):
1159 for name in kwargs:
1172 self.__dict__.update(**kwargs)
1160 setattr(self, name, kwargs[name])
1161
1173
1162 def __eq__(self, other):
1174 def __eq__(self, other):
1163 return vars(self) == vars(other)
1175 return vars(self) == vars(other)
@@ -1165,6 +1177,9 b' class Namespace(_AttributeHolder):'
1165 def __ne__(self, other):
1177 def __ne__(self, other):
1166 return not (self == other)
1178 return not (self == other)
1167
1179
1180 def __contains__(self, key):
1181 return key in self.__dict__
1182
1168
1183
1169 class _ActionsContainer(object):
1184 class _ActionsContainer(object):
1170
1185
@@ -1211,7 +1226,7 b' class _ActionsContainer(object):'
1211 self._defaults = {}
1226 self._defaults = {}
1212
1227
1213 # determines whether an "option" looks like a negative number
1228 # determines whether an "option" looks like a negative number
1214 self._negative_number_matcher = _re.compile(r'^-\d+|-\d*.\d+$')
1229 self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$')
1215
1230
1216 # whether or not there are any optionals that look like negative
1231 # whether or not there are any optionals that look like negative
1217 # numbers -- uses a list so it can be shared and edited
1232 # numbers -- uses a list so it can be shared and edited
@@ -1228,7 +1243,7 b' class _ActionsContainer(object):'
1228 return self._registries[registry_name].get(value, default)
1243 return self._registries[registry_name].get(value, default)
1229
1244
1230 # ==================================
1245 # ==================================
1231 # Namespace default settings methods
1246 # Namespace default accessor methods
1232 # ==================================
1247 # ==================================
1233 def set_defaults(self, **kwargs):
1248 def set_defaults(self, **kwargs):
1234 self._defaults.update(kwargs)
1249 self._defaults.update(kwargs)
@@ -1239,6 +1254,13 b' class _ActionsContainer(object):'
1239 if action.dest in kwargs:
1254 if action.dest in kwargs:
1240 action.default = kwargs[action.dest]
1255 action.default = kwargs[action.dest]
1241
1256
1257 def get_default(self, dest):
1258 for action in self._actions:
1259 if action.dest == dest and action.default is not None:
1260 return action.default
1261 return self._defaults.get(dest, None)
1262
1263
1242 # =======================
1264 # =======================
1243 # Adding argument actions
1265 # Adding argument actions
1244 # =======================
1266 # =======================
@@ -1253,6 +1275,8 b' class _ActionsContainer(object):'
1253 # argument
1275 # argument
1254 chars = self.prefix_chars
1276 chars = self.prefix_chars
1255 if not args or len(args) == 1 and args[0][0] not in chars:
1277 if not args or len(args) == 1 and args[0][0] not in chars:
1278 if args and 'dest' in kwargs:
1279 raise ValueError('dest supplied twice for positional argument')
1256 kwargs = self._get_positional_kwargs(*args, **kwargs)
1280 kwargs = self._get_positional_kwargs(*args, **kwargs)
1257
1281
1258 # otherwise, we're adding an optional argument
1282 # otherwise, we're adding an optional argument
@@ -1269,6 +1293,8 b' class _ActionsContainer(object):'
1269
1293
1270 # create the action object, and add it to the parser
1294 # create the action object, and add it to the parser
1271 action_class = self._pop_action_class(kwargs)
1295 action_class = self._pop_action_class(kwargs)
1296 if not _callable(action_class):
1297 raise ValueError('unknown action "%s"' % action_class)
1272 action = action_class(**kwargs)
1298 action = action_class(**kwargs)
1273 return self._add_action(action)
1299 return self._add_action(action)
1274
1300
@@ -1578,6 +1604,7 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
1578 if self.version:
1604 if self.version:
1579 self.add_argument(
1605 self.add_argument(
1580 '-v', '--version', action='version', default=SUPPRESS,
1606 '-v', '--version', action='version', default=SUPPRESS,
1607 version=self.version,
1581 help=_("show program's version number and exit"))
1608 help=_("show program's version number and exit"))
1582
1609
1583 # add parent arguments and defaults
1610 # add parent arguments and defaults
@@ -2011,6 +2038,13 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2011 action = self._option_string_actions[arg_string]
2038 action = self._option_string_actions[arg_string]
2012 return action, arg_string, None
2039 return action, arg_string, None
2013
2040
2041 # if the option string before the "=" is present, return the action
2042 if '=' in arg_string:
2043 option_string, explicit_arg = arg_string.split('=', 1)
2044 if option_string in self._option_string_actions:
2045 action = self._option_string_actions[option_string]
2046 return action, option_string, explicit_arg
2047
2014 # search through all possible prefixes of the option string
2048 # search through all possible prefixes of the option string
2015 # and all actions in the parser for possible interpretations
2049 # and all actions in the parser for possible interpretations
2016 option_tuples = self._get_option_tuples(arg_string)
2050 option_tuples = self._get_option_tuples(arg_string)
@@ -2108,8 +2142,12 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2108 elif nargs == ONE_OR_MORE:
2142 elif nargs == ONE_OR_MORE:
2109 nargs_pattern = '(-*A[A-]*)'
2143 nargs_pattern = '(-*A[A-]*)'
2110
2144
2145 # allow any number of options or arguments
2146 elif nargs == REMAINDER:
2147 nargs_pattern = '([-AO]*)'
2148
2111 # allow one argument followed by any number of options or arguments
2149 # allow one argument followed by any number of options or arguments
2112 elif nargs is PARSER:
2150 elif nargs == PARSER:
2113 nargs_pattern = '(-*A[-AO]*)'
2151 nargs_pattern = '(-*A[-AO]*)'
2114
2152
2115 # all others should be integers
2153 # all others should be integers
@@ -2129,7 +2167,7 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2129 # ========================
2167 # ========================
2130 def _get_values(self, action, arg_strings):
2168 def _get_values(self, action, arg_strings):
2131 # for everything but PARSER args, strip out '--'
2169 # for everything but PARSER args, strip out '--'
2132 if action.nargs is not PARSER:
2170 if action.nargs not in [PARSER, REMAINDER]:
2133 arg_strings = [s for s in arg_strings if s != '--']
2171 arg_strings = [s for s in arg_strings if s != '--']
2134
2172
2135 # optional argument produces a default when not present
2173 # optional argument produces a default when not present
@@ -2158,8 +2196,12 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2158 value = self._get_value(action, arg_string)
2196 value = self._get_value(action, arg_string)
2159 self._check_value(action, value)
2197 self._check_value(action, value)
2160
2198
2199 # REMAINDER arguments convert all values, checking none
2200 elif action.nargs == REMAINDER:
2201 value = [self._get_value(action, v) for v in arg_strings]
2202
2161 # PARSER arguments convert all values, but check only the first
2203 # PARSER arguments convert all values, but check only the first
2162 elif action.nargs is PARSER:
2204 elif action.nargs == PARSER:
2163 value = [self._get_value(action, v) for v in arg_strings]
2205 value = [self._get_value(action, v) for v in arg_strings]
2164 self._check_value(action, value[0])
2206 self._check_value(action, value[0])
2165
2207
@@ -2174,16 +2216,21 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2174
2216
2175 def _get_value(self, action, arg_string):
2217 def _get_value(self, action, arg_string):
2176 type_func = self._registry_get('type', action.type, action.type)
2218 type_func = self._registry_get('type', action.type, action.type)
2177 if not hasattr(type_func, '__call__'):
2219 if not _callable(type_func):
2178 if not hasattr(type_func, '__bases__'): # classic classes
2220 msg = _('%r is not callable')
2179 msg = _('%r is not callable')
2221 raise ArgumentError(action, msg % type_func)
2180 raise ArgumentError(action, msg % type_func)
2181
2222
2182 # convert the value to the appropriate type
2223 # convert the value to the appropriate type
2183 try:
2224 try:
2184 result = type_func(arg_string)
2225 result = type_func(arg_string)
2185
2226
2186 # TypeErrors or ValueErrors indicate errors
2227 # ArgumentTypeErrors indicate errors
2228 except ArgumentTypeError:
2229 name = getattr(action.type, '__name__', repr(action.type))
2230 msg = str(_sys.exc_info()[1])
2231 raise ArgumentError(action, msg)
2232
2233 # TypeErrors or ValueErrors also indicate errors
2187 except (TypeError, ValueError):
2234 except (TypeError, ValueError):
2188 name = getattr(action.type, '__name__', repr(action.type))
2235 name = getattr(action.type, '__name__', repr(action.type))
2189 msg = _('invalid %s value: %r')
2236 msg = _('invalid %s value: %r')
@@ -2243,9 +2290,13 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2243 # Help-printing methods
2290 # Help-printing methods
2244 # =====================
2291 # =====================
2245 def print_usage(self, file=None):
2292 def print_usage(self, file=None):
2293 if file is None:
2294 file = _sys.stdout
2246 self._print_message(self.format_usage(), file)
2295 self._print_message(self.format_usage(), file)
2247
2296
2248 def print_help(self, file=None):
2297 def print_help(self, file=None):
2298 if file is None:
2299 file = _sys.stdout
2249 self._print_message(self.format_help(), file)
2300 self._print_message(self.format_help(), file)
2250
2301
2251 def print_version(self, file=None):
2302 def print_version(self, file=None):
@@ -2262,7 +2313,7 b' class ArgumentParser(_AttributeHolder, _ActionsContainer):'
2262 # ===============
2313 # ===============
2263 def exit(self, status=0, message=None):
2314 def exit(self, status=0, message=None):
2264 if message:
2315 if message:
2265 _sys.stderr.write(message)
2316 self._print_message(message, _sys.stderr)
2266 _sys.exit(status)
2317 _sys.exit(status)
2267
2318
2268 def error(self, message):
2319 def error(self, message):
@@ -9,7 +9,6 b' functionnality is abstracted out of ipython0 in reusable functions and'
9 is added on the interpreter. This class can be a used to guide this
9 is added on the interpreter. This class can be a used to guide this
10 refactoring.
10 refactoring.
11 """
11 """
12 __docformat__ = "restructuredtext en"
13
12
14 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
@@ -27,7 +26,7 b' import os'
27 import re
26 import re
28 import __builtin__
27 import __builtin__
29
28
30 from IPython.core.ipmaker import make_IPython
29 from IPython.core.ipapp import IPythonApp
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
32
31
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
@@ -36,6 +35,9 b' from IPython.utils.genutils import Term'
36
35
37 from linefrontendbase import LineFrontEndBase, common_prefix
36 from linefrontendbase import LineFrontEndBase, common_prefix
38
37
38 #-----------------------------------------------------------------------------
39 # Utility functions
40 #-----------------------------------------------------------------------------
39
41
40 def mk_system_call(system_call_function, command):
42 def mk_system_call(system_call_function, command):
41 """ given a os.system replacement, and a leading string command,
43 """ given a os.system replacement, and a leading string command,
@@ -74,13 +76,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
74 Used as the instance's argv value. If not given, [] is used.
76 Used as the instance's argv value. If not given, [] is used.
75 """
77 """
76 if argv is None:
78 if argv is None:
77 argv = []
79 argv = ['--no-banner']
78 # This is a hack to avoid the IPython exception hook to trigger
79 # on exceptions (https://bugs.launchpad.net/bugs/337105)
80 # XXX: This is horrible: module-leve monkey patching -> side
81 # effects.
82 from IPython.core import iplib
83 iplib.InteractiveShell.isthreaded = True
84
80
85 LineFrontEndBase.__init__(self, *args, **kwargs)
81 LineFrontEndBase.__init__(self, *args, **kwargs)
86 self.shell.output_trap = RedirectorOutputTrap(
82 self.shell.output_trap = RedirectorOutputTrap(
@@ -101,12 +97,15 b' class PrefilterFrontEnd(LineFrontEndBase):'
101 return '\n'
97 return '\n'
102 old_rawinput = __builtin__.raw_input
98 old_rawinput = __builtin__.raw_input
103 __builtin__.raw_input = my_rawinput
99 __builtin__.raw_input = my_rawinput
104 # XXX: argv=[] is a bit bold.
100 ipython0 = IPythonApp(argv=argv,
105 ipython0 = make_IPython(argv=argv,
101 user_ns=self.shell.user_ns,
106 user_ns=self.shell.user_ns,
102 user_global_ns=self.shell.user_global_ns)
107 user_global_ns=self.shell.user_global_ns)
103 ipython0.initialize()
108 __builtin__.raw_input = old_rawinput
104 __builtin__.raw_input = old_rawinput
109 self.ipython0 = ipython0
105 # XXX This will need to be updated as we refactor things, but for now,
106 # the .shell attribute of the ipythonapp instance conforms to the old
107 # api.
108 self.ipython0 = ipython0.shell
110 # Set the pager:
109 # Set the pager:
111 self.ipython0.set_hook('show_in_pager',
110 self.ipython0.set_hook('show_in_pager',
112 lambda s, string: self.write("\n" + string))
111 lambda s, string: self.write("\n" + string))
@@ -125,7 +124,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
125
124
126
125
127 if not 'banner' in kwargs and self.banner is None:
126 if not 'banner' in kwargs and self.banner is None:
128 self.banner = self.ipython0.BANNER
127 self.banner = self.ipython0.banner
129
128
130 # FIXME: __init__ and start should be two different steps
129 # FIXME: __init__ and start should be two different steps
131 self.start()
130 self.start()
@@ -202,8 +201,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
202 if completions:
201 if completions:
203 prefix = common_prefix(completions)
202 prefix = common_prefix(completions)
204 line = line[:-len(word)] + prefix
203 line = line[:-len(word)] + prefix
205 return line, completions
204 return line, completions
206
207
205
208 #--------------------------------------------------------------------------
206 #--------------------------------------------------------------------------
209 # LineFrontEndBase interface
207 # LineFrontEndBase interface
@@ -220,23 +218,11 b' class PrefilterFrontEnd(LineFrontEndBase):'
220 self.capture_output()
218 self.capture_output()
221 self.last_result = dict(number=self.prompt_number)
219 self.last_result = dict(number=self.prompt_number)
222
220
223 ## try:
224 ## for line in input_string.split('\n'):
225 ## filtered_lines.append(
226 ## self.ipython0.prefilter(line, False).rstrip())
227 ## except:
228 ## # XXX: probably not the right thing to do.
229 ## self.ipython0.showsyntaxerror()
230 ## self.after_execute()
231 ## finally:
232 ## self.release_output()
233
234
235 try:
221 try:
236 try:
222 try:
237 for line in input_string.split('\n'):
223 for line in input_string.split('\n'):
238 filtered_lines.append(
224 pf = self.ipython0.prefilter_manager.prefilter_lines
239 self.ipython0.prefilter(line, False).rstrip())
225 filtered_lines.append(pf(line, False).rstrip())
240 except:
226 except:
241 # XXX: probably not the right thing to do.
227 # XXX: probably not the right thing to do.
242 self.ipython0.showsyntaxerror()
228 self.ipython0.showsyntaxerror()
@@ -244,13 +230,10 b' class PrefilterFrontEnd(LineFrontEndBase):'
244 finally:
230 finally:
245 self.release_output()
231 self.release_output()
246
232
247
248
249 # Clean up the trailing whitespace, to avoid indentation errors
233 # Clean up the trailing whitespace, to avoid indentation errors
250 filtered_string = '\n'.join(filtered_lines)
234 filtered_string = '\n'.join(filtered_lines)
251 return filtered_string
235 return filtered_string
252
236
253
254 #--------------------------------------------------------------------------
237 #--------------------------------------------------------------------------
255 # PrefilterFrontEnd interface
238 # PrefilterFrontEnd interface
256 #--------------------------------------------------------------------------
239 #--------------------------------------------------------------------------
@@ -261,13 +244,11 b' class PrefilterFrontEnd(LineFrontEndBase):'
261 """
244 """
262 return os.system(command_string)
245 return os.system(command_string)
263
246
264
265 def do_exit(self):
247 def do_exit(self):
266 """ Exit the shell, cleanup and save the history.
248 """ Exit the shell, cleanup and save the history.
267 """
249 """
268 self.ipython0.atexit_operations()
250 self.ipython0.atexit_operations()
269
251
270
271 def _get_completion_text(self, line):
252 def _get_completion_text(self, line):
272 """ Returns the text to be completed by breaking the line at specified
253 """ Returns the text to be completed by breaking the line at specified
273 delimiters.
254 delimiters.
@@ -281,4 +262,3 b' class PrefilterFrontEnd(LineFrontEndBase):'
281 complete_sep = re.compile(expression)
262 complete_sep = re.compile(expression)
282 text = complete_sep.split(line)[-1]
263 text = complete_sep.split(line)[-1]
283 return text
264 return text
284
@@ -21,8 +21,11 b' from nose.tools import assert_equal'
21
21
22 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
22 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
23 from IPython.core.ipapi import get as get_ipython0
23 from IPython.core.ipapi import get as get_ipython0
24 from IPython.testing.plugin.ipdoctest import default_argv
24 from IPython.testing.tools import default_argv
25
25
26 #-----------------------------------------------------------------------------
27 # Support utilities
28 #-----------------------------------------------------------------------------
26
29
27 class TestPrefilterFrontEnd(PrefilterFrontEnd):
30 class TestPrefilterFrontEnd(PrefilterFrontEnd):
28
31
@@ -85,14 +88,14 b' def isolate_ipython0(func):'
85 del user_ns[k]
88 del user_ns[k]
86 for k in new_globals:
89 for k in new_globals:
87 del user_global_ns[k]
90 del user_global_ns[k]
88 # Undo the hack at creation of PrefilterFrontEnd
89 from IPython.core import iplib
90 iplib.InteractiveShell.isthreaded = False
91 return out
91 return out
92
92
93 my_func.__name__ = func.__name__
93 my_func.__name__ = func.__name__
94 return my_func
94 return my_func
95
95
96 #-----------------------------------------------------------------------------
97 # Tests
98 #-----------------------------------------------------------------------------
96
99
97 @isolate_ipython0
100 @isolate_ipython0
98 def test_execution():
101 def test_execution():
@@ -166,7 +169,7 b' def test_magic():'
166 f.input_buffer += '%who'
169 f.input_buffer += '%who'
167 f._on_enter()
170 f._on_enter()
168 out_value = f.out.getvalue()
171 out_value = f.out.getvalue()
169 assert_equal(out_value, 'Interactive namespace is empty.\n')
172 assert_equal(out_value, 'In\tOut\tget_ipython\t\n')
170
173
171
174
172 @isolate_ipython0
175 @isolate_ipython0
@@ -6,11 +6,10 b' ipython.'
6 try:
6 try:
7 import wx
7 import wx
8 except ImportError, e:
8 except ImportError, e:
9 e.message = """%s
9 e.args[0] = """%s
10 ________________________________________________________________________________
10 ________________________________________________________________________________
11 You need wxPython to run this application.
11 You need wxPython to run this application.
12 """ % e.message
12 """ % e.args[0]
13 e.args = (e.message, ) + e.args[1:]
14 raise e
13 raise e
15
14
16 from wx_frontend import WxController
15 from wx_frontend import WxController
@@ -23,13 +23,9 b' import os'
23 import locale
23 import locale
24 from thread_ex import ThreadEx
24 from thread_ex import ThreadEx
25
25
26 try:
26 import IPython
27 import IPython
27 from IPython.core import iplib, ipapp
28 from IPython.utils import genutils
28 from IPython.utils import genutils
29 from IPython.core import iplib
30 except Exception,e:
31 print "Error importing IPython (%s)" % str(e)
32 raise Exception, e
33
29
34 ##############################################################################
30 ##############################################################################
35 class _Helper(object):
31 class _Helper(object):
@@ -155,12 +151,17 b' class NonBlockingIPShell(object):'
155
151
156 #Hack to save sys.displayhook, because ipython seems to overwrite it...
152 #Hack to save sys.displayhook, because ipython seems to overwrite it...
157 self.sys_displayhook_ori = sys.displayhook
153 self.sys_displayhook_ori = sys.displayhook
154
155 ipython0 = ipapp.IPythonApp(argv,user_ns=user_ns,
156 user_global_ns=user_global_ns)
157 ipython0.initialize()
158 self._IP = ipython0.shell
158
159
159 self._IP = IPython.shell.make_IPython(
160 ## self._IP = IPython.shell.make_IPython(
160 argv,user_ns=user_ns,
161 ## argv,user_ns=user_ns,
161 user_global_ns=user_global_ns,
162 ## user_global_ns=user_global_ns,
162 embedded=True,
163 ## embedded=True,
163 shell_class=IPython.shell.InteractiveShell)
164 ## shell_class=IPython.shell.InteractiveShell)
164
165
165 #we save ipython0 displayhook and we restore sys.displayhook
166 #we save ipython0 displayhook and we restore sys.displayhook
166 self.displayhook = sys.displayhook
167 self.displayhook = sys.displayhook
@@ -273,7 +274,7 b' class NonBlockingIPShell(object):'
273 @return: The banner string.
274 @return: The banner string.
274 @rtype: string
275 @rtype: string
275 """
276 """
276 return self._IP.BANNER
277 return self._IP.banner
277
278
278 def get_prompt_count(self):
279 def get_prompt_count(self):
279 """
280 """
@@ -470,7 +471,7 b' class NonBlockingIPShell(object):'
470 '''
471 '''
471
472
472 orig_stdout = sys.stdout
473 orig_stdout = sys.stdout
473 sys.stdout = IPython.shell.Term.cout
474 sys.stdout = genutils.Term.cout
474 #self.sys_displayhook_ori = sys.displayhook
475 #self.sys_displayhook_ori = sys.displayhook
475 #sys.displayhook = self.displayhook
476 #sys.displayhook = self.displayhook
476
477
@@ -109,7 +109,7 b' class MyFrame(wx.Frame):'
109
109
110 def optionSave(self, name, value):
110 def optionSave(self, name, value):
111 ip = get()
111 ip = get()
112 path = ip.config.IPYTHONDIR
112 path = ip.ipython_dir
113 opt = open(path + '/options.conf','w')
113 opt = open(path + '/options.conf','w')
114
114
115 try:
115 try:
@@ -126,7 +126,7 b' class MyFrame(wx.Frame):'
126 def optionLoad(self):
126 def optionLoad(self):
127 try:
127 try:
128 ip = get()
128 ip = get()
129 path = ip.config.IPYTHONDIR
129 path = ip.ipython_dir
130 opt = open(path + '/options.conf','r')
130 opt = open(path + '/options.conf','r')
131 lines = opt.readlines()
131 lines = opt.readlines()
132 opt.close()
132 opt.close()
@@ -1,3 +1,4 b''
1 #!/usr/bin/env python
1 # encoding: utf-8
2 # encoding: utf-8
2
3
3 """Asynchronous clients for the IPython controller.
4 """Asynchronous clients for the IPython controller.
@@ -9,32 +10,32 b' deferreds to the result.'
9
10
10 The main methods are are `get_*_client` and `get_client`.
11 The main methods are are `get_*_client` and `get_client`.
11 """
12 """
12
13 #-----------------------------------------------------------------------------
13 __docformat__ = "restructuredtext en"
14 # Copyright (C) 2008-2009 The IPython Development Team
14
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
17 #
15 #
18 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
21
19
22 #-------------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
23 # Imports
21 # Imports
24 #-------------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
25
23
26 from IPython.kernel import codeutil
24 from IPython.kernel import codeutil
27 from IPython.kernel.clientconnector import ClientConnector
25 from IPython.kernel.clientconnector import (
26 AsyncClientConnector,
27 AsyncCluster
28 )
28
29
29 # Other things that the user will need
30 # Other things that the user will need
30 from IPython.kernel.task import MapTask, StringTask
31 from IPython.kernel.task import MapTask, StringTask
31 from IPython.kernel.error import CompositeError
32 from IPython.kernel.error import CompositeError
32
33
33 #-------------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Code
35 # Code
35 #-------------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
36
37
37 _client_tub = ClientConnector()
38 _client_tub = AsyncClientConnector()
38 get_multiengine_client = _client_tub.get_multiengine_client
39 get_multiengine_client = _client_tub.get_multiengine_client
39 get_task_client = _client_tub.get_task_client
40 get_task_client = _client_tub.get_task_client
40 get_client = _client_tub.get_client
41 get_client = _client_tub.get_client
@@ -1,3 +1,4 b''
1 #!/usr/bin/env python
1 # encoding: utf-8
2 # encoding: utf-8
2
3
3 """This module contains blocking clients for the controller interfaces.
4 """This module contains blocking clients for the controller interfaces.
@@ -15,33 +16,36 b' The main classes in this module are:'
15 * CompositeError
16 * CompositeError
16 """
17 """
17
18
18 __docformat__ = "restructuredtext en"
19 #-----------------------------------------------------------------------------
19
20 # Copyright (C) 2008-2009 The IPython Development Team
20 #-------------------------------------------------------------------------------
21 # Copyright (C) 2008 The IPython Development Team
22 #
21 #
23 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
24 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
25 #-------------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
26
25
27 #-------------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
28 # Imports
27 # Imports
29 #-------------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
30
29
30 from cStringIO import StringIO
31 import sys
31 import sys
32 import warnings
32
33
33 # from IPython.utils import growl
34 # from IPython.utils import growl
34 # growl.start("IPython1 Client")
35 # growl.start("IPython1 Client")
35
36
36
37
37 from twisted.internet import reactor
38 from twisted.internet import reactor
38 from IPython.kernel.clientconnector import ClientConnector
39 from twisted.internet.error import PotentialZombieWarning
40 from twisted.python import log
41
42 from IPython.kernel.clientconnector import ClientConnector, Cluster
39 from IPython.kernel.twistedutil import ReactorInThread
43 from IPython.kernel.twistedutil import ReactorInThread
40 from IPython.kernel.twistedutil import blockingCallFromThread
44 from IPython.kernel.twistedutil import blockingCallFromThread
41
45
42 # These enable various things
46 # These enable various things
43 from IPython.kernel import codeutil
47 from IPython.kernel import codeutil
44 import IPython.kernel.magic
48 # import IPython.kernel.magic
45
49
46 # Other things that the user will need
50 # Other things that the user will need
47 from IPython.kernel.task import MapTask, StringTask
51 from IPython.kernel.task import MapTask, StringTask
@@ -51,46 +55,34 b' from IPython.kernel.error import CompositeError'
51 # Code
55 # Code
52 #-------------------------------------------------------------------------------
56 #-------------------------------------------------------------------------------
53
57
54 _client_tub = ClientConnector()
58 warnings.simplefilter('ignore', PotentialZombieWarning)
55
56
57 def get_multiengine_client(furl_or_file=''):
58 """Get the blocking MultiEngine client.
59
60 :Parameters:
61 furl_or_file : str
62 A furl or a filename containing a furl. If empty, the
63 default furl_file will be used
64
65 :Returns:
66 The connected MultiEngineClient instance
67 """
68 client = blockingCallFromThread(_client_tub.get_multiengine_client,
69 furl_or_file)
70 return client.adapt_to_blocking_client()
71
72 def get_task_client(furl_or_file=''):
73 """Get the blocking Task client.
74
75 :Parameters:
76 furl_or_file : str
77 A furl or a filename containing a furl. If empty, the
78 default furl_file will be used
79
80 :Returns:
81 The connected TaskClient instance
82 """
83 client = blockingCallFromThread(_client_tub.get_task_client,
84 furl_or_file)
85 return client.adapt_to_blocking_client()
86
59
60 _client_tub = ClientConnector()
87
61
62 get_multiengine_client = _client_tub.get_multiengine_client
63 get_task_client = _client_tub.get_task_client
88 MultiEngineClient = get_multiengine_client
64 MultiEngineClient = get_multiengine_client
89 TaskClient = get_task_client
65 TaskClient = get_task_client
90
66
91
67 # This isn't great. I should probably set this up in the ReactorInThread
68 # class below. But, it does work for now.
69 log.startLogging(sys.stdout, setStdout=0)
92
70
93 # Now we start the reactor in a thread
71 # Now we start the reactor in a thread
94 rit = ReactorInThread()
72 rit = ReactorInThread()
95 rit.setDaemon(True)
73 rit.setDaemon(True)
96 rit.start() No newline at end of file
74 rit.start()
75
76
77
78
79 __all__ = [
80 'MapTask',
81 'StringTask',
82 'MultiEngineClient',
83 'TaskClient',
84 'CompositeError',
85 'get_task_client',
86 'get_multiengine_client',
87 'Cluster'
88 ]
This diff has been collapsed as it changes many lines, (815 lines changed) Show them Hide them
@@ -1,142 +1,268 b''
1 #!/usr/bin/env python
1 # encoding: utf-8
2 # encoding: utf-8
2
3
3 """A class for handling client connections to the controller."""
4 """Facilities for handling client connections to the controller."""
4
5
5 __docformat__ = "restructuredtext en"
6 #-----------------------------------------------------------------------------
6
7 # Copyright (C) 2008-2009 The IPython Development Team
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
8 #
10 # 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
11 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
13
12
14 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
15 # Imports
14 # Imports
16 #-------------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17
18 from twisted.internet import defer
19
16
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
17 from __future__ import with_statement
18 import os
21
19
22 from IPython.kernel.config import config_manager as kernel_config_manager
20 from IPython.kernel.fcutil import (
21 Tub,
22 find_furl,
23 is_valid_furl_or_file,
24 validate_furl_or_file,
25 FURLError
26 )
27 from IPython.kernel.clusterdir import ClusterDir, ClusterDirError
28 from IPython.kernel.launcher import IPClusterLauncher
29 from IPython.kernel.twistedutil import (
30 gatherBoth,
31 make_deferred,
32 blockingCallFromThread,
33 sleep_deferred
34 )
23 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
24 from IPython.kernel.fcutil import find_furl
36 from IPython.utils.genutils import get_ipython_dir
25
37
26 co = kernel_config_manager.get_config_obj()
38 from twisted.internet import defer
27 client_co = co['client']
39 from twisted.internet.defer import inlineCallbacks, returnValue
40 from twisted.python import failure, log
28
41
29 #-------------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
30 # The ClientConnector class
43 # The ClientConnector class
31 #-------------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
32
45
33 class ClientConnector(object):
46 DELAY = 0.2
34 """
47 MAX_TRIES = 9
35 This class gets remote references from furls and returns the wrapped clients.
48
36
49
37 This class is also used in `client.py` and `asyncclient.py` to create
50 class ClientConnectorError(Exception):
38 a single per client-process Tub.
51 pass
52
53
54 class AsyncClientConnector(object):
55 """A class for getting remote references and clients from furls.
56
57 This start a single :class:`Tub` for all remote reference and caches
58 references.
39 """
59 """
40
60
41 def __init__(self):
61 def __init__(self):
42 self._remote_refs = {}
62 self._remote_refs = {}
43 self.tub = Tub()
63 self.tub = Tub()
44 self.tub.startService()
64 self.tub.startService()
45
65
46 def get_reference(self, furl_or_file):
66 def _find_furl(self, profile='default', cluster_dir=None,
67 furl_or_file=None, furl_file_name=None,
68 ipython_dir=None):
69 """Find a FURL file by profile+ipython_dir or cluster dir.
70
71 This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception
72 if a FURL file can't be found.
47 """
73 """
48 Get a remote reference using a furl or a file containing a furl.
74 # Try by furl_or_file
49
75 if furl_or_file is not None:
76 validate_furl_or_file(furl_or_file)
77 return furl_or_file
78
79 if furl_file_name is None:
80 raise FURLError('A furl_file_name must be provided')
81
82 # Try by cluster_dir
83 if cluster_dir is not None:
84 cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
85 sdir = cluster_dir_obj.security_dir
86 furl_file = os.path.join(sdir, furl_file_name)
87 validate_furl_or_file(furl_file)
88 return furl_file
89
90 # Try by profile
91 if ipython_dir is None:
92 ipython_dir = get_ipython_dir()
93 if profile is not None:
94 cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
95 ipython_dir, profile)
96 sdir = cluster_dir_obj.security_dir
97 furl_file = os.path.join(sdir, furl_file_name)
98 validate_furl_or_file(furl_file)
99 return furl_file
100
101 raise FURLError('Could not find a valid FURL file.')
102
103 def get_reference(self, furl_or_file):
104 """Get a remote reference using a furl or a file containing a furl.
105
50 Remote references are cached locally so once a remote reference
106 Remote references are cached locally so once a remote reference
51 has been retrieved for a given furl, the cached version is
107 has been retrieved for a given furl, the cached version is
52 returned.
108 returned.
53
109
54 :Parameters:
110 Parameters
55 furl_or_file : str
111 ----------
56 A furl or a filename containing a furl
112 furl_or_file : str
57
113 A furl or a filename containing a furl. This should already be
58 :Returns:
114 validated, but might not yet exist.
59 A deferred to a remote reference
115
116 Returns
117 -------
118 A deferred to a remote reference
60 """
119 """
61 furl = find_furl(furl_or_file)
120 furl = furl_or_file
62 if furl in self._remote_refs:
121 if furl in self._remote_refs:
63 d = defer.succeed(self._remote_refs[furl])
122 d = defer.succeed(self._remote_refs[furl])
64 else:
123 else:
65 d = self.tub.getReference(furl)
124 d = self.tub.getReference(furl)
66 d.addCallback(self.save_ref, furl)
125 d.addCallback(self._save_ref, furl)
67 return d
126 return d
68
127
69 def save_ref(self, ref, furl):
128 def _save_ref(self, ref, furl):
70 """
129 """Cache a remote reference by its furl."""
71 Cache a remote reference by its furl.
72 """
73 self._remote_refs[furl] = ref
130 self._remote_refs[furl] = ref
74 return ref
131 return ref
75
132
76 def get_task_client(self, furl_or_file=''):
133 def get_task_client(self, profile='default', cluster_dir=None,
77 """
134 furl_or_file=None, ipython_dir=None,
78 Get the task controller client.
135 delay=DELAY, max_tries=MAX_TRIES):
136 """Get the task controller client.
79
137
80 This method is a simple wrapper around `get_client` that allow
138 This method is a simple wrapper around `get_client` that passes in
81 `furl_or_file` to be empty, in which case, the furls is taken
139 the default name of the task client FURL file. Usually only
82 from the default furl file given in the configuration.
140 the ``profile`` option will be needed. If a FURL file can't be
141 found by its profile, use ``cluster_dir`` or ``furl_or_file``.
83
142
84 :Parameters:
143 Parameters
85 furl_or_file : str
144 ----------
86 A furl or a filename containing a furl. If empty, the
145 profile : str
87 default furl_file will be used
146 The name of a cluster directory profile (default="default"). The
88
147 cluster directory "cluster_<profile>" will be searched for
89 :Returns:
148 in ``os.getcwd()``, the ipython_dir and then in the directories
90 A deferred to the actual client class
149 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
91 """
150 cluster_dir : str
92 task_co = client_co['client_interfaces']['task']
151 The full path to a cluster directory. This is useful if profiles
93 if furl_or_file:
152 are not being used.
94 ff = furl_or_file
153 furl_or_file : str
95 else:
154 A furl or a filename containing a FURLK. This is useful if you
96 ff = task_co['furl_file']
155 simply know the location of the FURL file.
97 return self.get_client(ff)
156 ipython_dir : str
157 The location of the ipython_dir if different from the default.
158 This is used if the cluster directory is being found by profile.
159 delay : float
160 The initial delay between re-connection attempts. Susequent delays
161 get longer according to ``delay[i] = 1.5*delay[i-1]``.
162 max_tries : int
163 The max number of re-connection attempts.
98
164
99 def get_multiengine_client(self, furl_or_file=''):
165 Returns
166 -------
167 A deferred to the actual client class.
100 """
168 """
101 Get the multiengine controller client.
169 return self.get_client(
170 profile, cluster_dir, furl_or_file,
171 'ipcontroller-tc.furl', ipython_dir,
172 delay, max_tries
173 )
174
175 def get_multiengine_client(self, profile='default', cluster_dir=None,
176 furl_or_file=None, ipython_dir=None,
177 delay=DELAY, max_tries=MAX_TRIES):
178 """Get the multiengine controller client.
102
179
103 This method is a simple wrapper around `get_client` that allow
180 This method is a simple wrapper around `get_client` that passes in
104 `furl_or_file` to be empty, in which case, the furls is taken
181 the default name of the task client FURL file. Usually only
105 from the default furl file given in the configuration.
182 the ``profile`` option will be needed. If a FURL file can't be
183 found by its profile, use ``cluster_dir`` or ``furl_or_file``.
106
184
107 :Parameters:
185 Parameters
108 furl_or_file : str
186 ----------
109 A furl or a filename containing a furl. If empty, the
187 profile : str
110 default furl_file will be used
188 The name of a cluster directory profile (default="default"). The
111
189 cluster directory "cluster_<profile>" will be searched for
112 :Returns:
190 in ``os.getcwd()``, the ipython_dir and then in the directories
113 A deferred to the actual client class
191 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
192 cluster_dir : str
193 The full path to a cluster directory. This is useful if profiles
194 are not being used.
195 furl_or_file : str
196 A furl or a filename containing a FURLK. This is useful if you
197 simply know the location of the FURL file.
198 ipython_dir : str
199 The location of the ipython_dir if different from the default.
200 This is used if the cluster directory is being found by profile.
201 delay : float
202 The initial delay between re-connection attempts. Susequent delays
203 get longer according to ``delay[i] = 1.5*delay[i-1]``.
204 max_tries : int
205 The max number of re-connection attempts.
206
207 Returns
208 -------
209 A deferred to the actual client class.
114 """
210 """
115 task_co = client_co['client_interfaces']['multiengine']
211 return self.get_client(
116 if furl_or_file:
212 profile, cluster_dir, furl_or_file,
117 ff = furl_or_file
213 'ipcontroller-mec.furl', ipython_dir,
118 else:
214 delay, max_tries
119 ff = task_co['furl_file']
215 )
120 return self.get_client(ff)
121
216
122 def get_client(self, furl_or_file):
217 def get_client(self, profile='default', cluster_dir=None,
123 """
218 furl_or_file=None, furl_file_name=None, ipython_dir=None,
124 Get a remote reference and wrap it in a client by furl.
219 delay=DELAY, max_tries=MAX_TRIES):
125
220 """Get a remote reference and wrap it in a client by furl.
126 This method first gets a remote reference and then calls its
221
127 `get_client_name` method to find the apprpriate client class
222 This method is a simple wrapper around `get_client` that passes in
128 that should be used to wrap the remote reference.
223 the default name of the task client FURL file. Usually only
129
224 the ``profile`` option will be needed. If a FURL file can't be
130 :Parameters:
225 found by its profile, use ``cluster_dir`` or ``furl_or_file``.
131 furl_or_file : str
132 A furl or a filename containing a furl
133
226
134 :Returns:
227 Parameters
135 A deferred to the actual client class
228 ----------
229 profile : str
230 The name of a cluster directory profile (default="default"). The
231 cluster directory "cluster_<profile>" will be searched for
232 in ``os.getcwd()``, the ipython_dir and then in the directories
233 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
234 cluster_dir : str
235 The full path to a cluster directory. This is useful if profiles
236 are not being used.
237 furl_or_file : str
238 A furl or a filename containing a FURL. This is useful if you
239 simply know the location of the FURL file.
240 furl_file_name : str
241 The filename (not the full path) of the FURL. This must be
242 provided if ``furl_or_file`` is not.
243 ipython_dir : str
244 The location of the ipython_dir if different from the default.
245 This is used if the cluster directory is being found by profile.
246 delay : float
247 The initial delay between re-connection attempts. Susequent delays
248 get longer according to ``delay[i] = 1.5*delay[i-1]``.
249 max_tries : int
250 The max number of re-connection attempts.
251
252 Returns
253 -------
254 A deferred to the actual client class. Or a failure to a
255 :exc:`FURLError`.
136 """
256 """
137 furl = find_furl(furl_or_file)
257 try:
138 d = self.get_reference(furl)
258 furl_file = self._find_furl(
139 def wrap_remote_reference(rr):
259 profile, cluster_dir, furl_or_file,
260 furl_file_name, ipython_dir
261 )
262 except FURLError:
263 return defer.fail(failure.Failure())
264
265 def _wrap_remote_reference(rr):
140 d = rr.callRemote('get_client_name')
266 d = rr.callRemote('get_client_name')
141 d.addCallback(lambda name: import_item(name))
267 d.addCallback(lambda name: import_item(name))
142 def adapt(client_interface):
268 def adapt(client_interface):
@@ -146,5 +272,502 b' class ClientConnector(object):'
146 d.addCallback(adapt)
272 d.addCallback(adapt)
147
273
148 return d
274 return d
149 d.addCallback(wrap_remote_reference)
275
276 d = self._try_to_connect(furl_file, delay, max_tries, attempt=0)
277 d.addCallback(_wrap_remote_reference)
278 d.addErrback(self._handle_error, furl_file)
279 return d
280
281 def _handle_error(self, f, furl_file):
282 raise ClientConnectorError('Could not connect to the controller '
283 'using the FURL file. This usually means that i) the controller '
284 'was not started or ii) a firewall was blocking the client from '
285 'connecting to the controller: %s' % furl_file)
286
287 @inlineCallbacks
288 def _try_to_connect(self, furl_or_file, delay, max_tries, attempt):
289 """Try to connect to the controller with retry logic."""
290 if attempt < max_tries:
291 log.msg("Connecting [%r]" % attempt)
292 try:
293 self.furl = find_furl(furl_or_file)
294 # Uncomment this to see the FURL being tried.
295 # log.msg("FURL: %s" % self.furl)
296 rr = yield self.get_reference(self.furl)
297 log.msg("Connected: %s" % furl_or_file)
298 except:
299 if attempt==max_tries-1:
300 # This will propagate the exception all the way to the top
301 # where it can be handled.
302 raise
303 else:
304 yield sleep_deferred(delay)
305 rr = yield self._try_to_connect(
306 furl_or_file, 1.5*delay, max_tries, attempt+1
307 )
308 returnValue(rr)
309 else:
310 returnValue(rr)
311 else:
312 raise ClientConnectorError(
313 'Could not connect to controller, max_tries (%r) exceeded. '
314 'This usually means that i) the controller was not started, '
315 'or ii) a firewall was blocking the client from connecting '
316 'to the controller.' % max_tries
317 )
318
319
320 class ClientConnector(object):
321 """A blocking version of a client connector.
322
323 This class creates a single :class:`Tub` instance and allows remote
324 references and client to be retrieved by their FURLs. Remote references
325 are cached locally and FURL files can be found using profiles and cluster
326 directories.
327 """
328
329 def __init__(self):
330 self.async_cc = AsyncClientConnector()
331
332 def get_task_client(self, profile='default', cluster_dir=None,
333 furl_or_file=None, ipython_dir=None,
334 delay=DELAY, max_tries=MAX_TRIES):
335 """Get the task client.
336
337 Usually only the ``profile`` option will be needed. If a FURL file
338 can't be found by its profile, use ``cluster_dir`` or
339 ``furl_or_file``.
340
341 Parameters
342 ----------
343 profile : str
344 The name of a cluster directory profile (default="default"). The
345 cluster directory "cluster_<profile>" will be searched for
346 in ``os.getcwd()``, the ipython_dir and then in the directories
347 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
348 cluster_dir : str
349 The full path to a cluster directory. This is useful if profiles
350 are not being used.
351 furl_or_file : str
352 A furl or a filename containing a FURLK. This is useful if you
353 simply know the location of the FURL file.
354 ipython_dir : str
355 The location of the ipython_dir if different from the default.
356 This is used if the cluster directory is being found by profile.
357 delay : float
358 The initial delay between re-connection attempts. Susequent delays
359 get longer according to ``delay[i] = 1.5*delay[i-1]``.
360 max_tries : int
361 The max number of re-connection attempts.
362
363 Returns
364 -------
365 The task client instance.
366 """
367 client = blockingCallFromThread(
368 self.async_cc.get_task_client, profile, cluster_dir,
369 furl_or_file, ipython_dir, delay, max_tries
370 )
371 return client.adapt_to_blocking_client()
372
373 def get_multiengine_client(self, profile='default', cluster_dir=None,
374 furl_or_file=None, ipython_dir=None,
375 delay=DELAY, max_tries=MAX_TRIES):
376 """Get the multiengine client.
377
378 Usually only the ``profile`` option will be needed. If a FURL file
379 can't be found by its profile, use ``cluster_dir`` or
380 ``furl_or_file``.
381
382 Parameters
383 ----------
384 profile : str
385 The name of a cluster directory profile (default="default"). The
386 cluster directory "cluster_<profile>" will be searched for
387 in ``os.getcwd()``, the ipython_dir and then in the directories
388 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
389 cluster_dir : str
390 The full path to a cluster directory. This is useful if profiles
391 are not being used.
392 furl_or_file : str
393 A furl or a filename containing a FURLK. This is useful if you
394 simply know the location of the FURL file.
395 ipython_dir : str
396 The location of the ipython_dir if different from the default.
397 This is used if the cluster directory is being found by profile.
398 delay : float
399 The initial delay between re-connection attempts. Susequent delays
400 get longer according to ``delay[i] = 1.5*delay[i-1]``.
401 max_tries : int
402 The max number of re-connection attempts.
403
404 Returns
405 -------
406 The multiengine client instance.
407 """
408 client = blockingCallFromThread(
409 self.async_cc.get_multiengine_client, profile, cluster_dir,
410 furl_or_file, ipython_dir, delay, max_tries
411 )
412 return client.adapt_to_blocking_client()
413
414 def get_client(self, profile='default', cluster_dir=None,
415 furl_or_file=None, ipython_dir=None,
416 delay=DELAY, max_tries=MAX_TRIES):
417 client = blockingCallFromThread(
418 self.async_cc.get_client, profile, cluster_dir,
419 furl_or_file, ipython_dir,
420 delay, max_tries
421 )
422 return client.adapt_to_blocking_client()
423
424
425 class ClusterStateError(Exception):
426 pass
427
428
429 class AsyncCluster(object):
430 """An class that wraps the :command:`ipcluster` script."""
431
432 def __init__(self, profile='default', cluster_dir=None, ipython_dir=None,
433 auto_create=False, auto_stop=True):
434 """Create a class to manage an IPython cluster.
435
436 This class calls the :command:`ipcluster` command with the right
437 options to start an IPython cluster. Typically a cluster directory
438 must be created (:command:`ipcluster create`) and configured before
439 using this class. Configuration is done by editing the
440 configuration files in the top level of the cluster directory.
441
442 Parameters
443 ----------
444 profile : str
445 The name of a cluster directory profile (default="default"). The
446 cluster directory "cluster_<profile>" will be searched for
447 in ``os.getcwd()``, the ipython_dir and then in the directories
448 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
449 cluster_dir : str
450 The full path to a cluster directory. This is useful if profiles
451 are not being used.
452 ipython_dir : str
453 The location of the ipython_dir if different from the default.
454 This is used if the cluster directory is being found by profile.
455 auto_create : bool
456 Automatically create the cluster directory it is dones't exist.
457 This will usually only make sense if using a local cluster
458 (default=False).
459 auto_stop : bool
460 Automatically stop the cluster when this instance is garbage
461 collected (default=True). This is useful if you want the cluster
462 to live beyond your current process. There is also an instance
463 attribute ``auto_stop`` to change this behavior.
464 """
465 self._setup_cluster_dir(profile, cluster_dir, ipython_dir, auto_create)
466 self.state = 'before'
467 self.launcher = None
468 self.client_connector = None
469 self.auto_stop = auto_stop
470
471 def __del__(self):
472 if self.auto_stop and self.state=='running':
473 print "Auto stopping the cluster..."
474 self.stop()
475
476 @property
477 def location(self):
478 if hasattr(self, 'cluster_dir_obj'):
479 return self.cluster_dir_obj.location
480 else:
481 return ''
482
483 @property
484 def running(self):
485 if self.state=='running':
486 return True
487 else:
488 return False
489
490 def _setup_cluster_dir(self, profile, cluster_dir, ipython_dir, auto_create):
491 if ipython_dir is None:
492 ipython_dir = get_ipython_dir()
493 if cluster_dir is not None:
494 try:
495 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
496 except ClusterDirError:
497 pass
498 if profile is not None:
499 try:
500 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
501 ipython_dir, profile)
502 except ClusterDirError:
503 pass
504 if auto_create or profile=='default':
505 # This should call 'ipcluster create --profile default
506 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
507 ipython_dir, profile)
508 else:
509 raise ClusterDirError('Cluster dir not found.')
510
511 @make_deferred
512 def start(self, n=2):
513 """Start the IPython cluster with n engines.
514
515 Parameters
516 ----------
517 n : int
518 The number of engine to start.
519 """
520 # We might want to add logic to test if the cluster has started
521 # by another process....
522 if not self.state=='running':
523 self.launcher = IPClusterLauncher(os.getcwd())
524 self.launcher.ipcluster_n = n
525 self.launcher.ipcluster_subcommand = 'start'
526 d = self.launcher.start()
527 d.addCallback(self._handle_start)
528 return d
529 else:
530 raise ClusterStateError('Cluster is already running')
531
532 @make_deferred
533 def stop(self):
534 """Stop the IPython cluster if it is running."""
535 if self.state=='running':
536 d1 = self.launcher.observe_stop()
537 d1.addCallback(self._handle_stop)
538 d2 = self.launcher.stop()
539 return gatherBoth([d1, d2], consumeErrors=True)
540 else:
541 raise ClusterStateError("Cluster not running")
542
543 def get_multiengine_client(self, delay=DELAY, max_tries=MAX_TRIES):
544 """Get the multiengine client for the running cluster.
545
546 If this fails, it means that the cluster has not finished starting.
547 Usually waiting a few seconds are re-trying will solve this.
548 """
549 if self.client_connector is None:
550 self.client_connector = AsyncClientConnector()
551 return self.client_connector.get_multiengine_client(
552 cluster_dir=self.cluster_dir_obj.location,
553 delay=delay, max_tries=max_tries
554 )
555
556 def get_task_client(self, delay=DELAY, max_tries=MAX_TRIES):
557 """Get the task client for the running cluster.
558
559 If this fails, it means that the cluster has not finished starting.
560 Usually waiting a few seconds are re-trying will solve this.
561 """
562 if self.client_connector is None:
563 self.client_connector = AsyncClientConnector()
564 return self.client_connector.get_task_client(
565 cluster_dir=self.cluster_dir_obj.location,
566 delay=delay, max_tries=max_tries
567 )
568
569 def get_ipengine_logs(self):
570 return self.get_logs_by_name('ipengine')
571
572 def get_ipcontroller_logs(self):
573 return self.get_logs_by_name('ipcontroller')
574
575 def get_ipcluster_logs(self):
576 return self.get_logs_by_name('ipcluster')
577
578 def get_logs_by_name(self, name='ipcluster'):
579 log_dir = self.cluster_dir_obj.log_dir
580 logs = {}
581 for log in os.listdir(log_dir):
582 if log.startswith(name + '-') and log.endswith('.log'):
583 with open(os.path.join(log_dir, log), 'r') as f:
584 logs[log] = f.read()
585 return logs
586
587 def get_logs(self):
588 d = self.get_ipcluster_logs()
589 d.update(self.get_ipengine_logs())
590 d.update(self.get_ipcontroller_logs())
150 return d
591 return d
592
593 def _handle_start(self, r):
594 self.state = 'running'
595
596 def _handle_stop(self, r):
597 self.state = 'after'
598
599
600 class Cluster(object):
601
602
603 def __init__(self, profile='default', cluster_dir=None, ipython_dir=None,
604 auto_create=False, auto_stop=True):
605 """Create a class to manage an IPython cluster.
606
607 This class calls the :command:`ipcluster` command with the right
608 options to start an IPython cluster. Typically a cluster directory
609 must be created (:command:`ipcluster create`) and configured before
610 using this class. Configuration is done by editing the
611 configuration files in the top level of the cluster directory.
612
613 Parameters
614 ----------
615 profile : str
616 The name of a cluster directory profile (default="default"). The
617 cluster directory "cluster_<profile>" will be searched for
618 in ``os.getcwd()``, the ipython_dir and then in the directories
619 listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
620 cluster_dir : str
621 The full path to a cluster directory. This is useful if profiles
622 are not being used.
623 ipython_dir : str
624 The location of the ipython_dir if different from the default.
625 This is used if the cluster directory is being found by profile.
626 auto_create : bool
627 Automatically create the cluster directory it is dones't exist.
628 This will usually only make sense if using a local cluster
629 (default=False).
630 auto_stop : bool
631 Automatically stop the cluster when this instance is garbage
632 collected (default=True). This is useful if you want the cluster
633 to live beyond your current process. There is also an instance
634 attribute ``auto_stop`` to change this behavior.
635 """
636 self.async_cluster = AsyncCluster(
637 profile, cluster_dir, ipython_dir, auto_create, auto_stop
638 )
639 self.cluster_dir_obj = self.async_cluster.cluster_dir_obj
640 self.client_connector = None
641
642 def _set_auto_stop(self, value):
643 self.async_cluster.auto_stop = value
644
645 def _get_auto_stop(self):
646 return self.async_cluster.auto_stop
647
648 auto_stop = property(_get_auto_stop, _set_auto_stop)
649
650 @property
651 def location(self):
652 return self.async_cluster.location
653
654 @property
655 def running(self):
656 return self.async_cluster.running
657
658 def start(self, n=2):
659 """Start the IPython cluster with n engines.
660
661 Parameters
662 ----------
663 n : int
664 The number of engine to start.
665 """
666 return blockingCallFromThread(self.async_cluster.start, n)
667
668 def stop(self):
669 """Stop the IPython cluster if it is running."""
670 return blockingCallFromThread(self.async_cluster.stop)
671
672 def get_multiengine_client(self, delay=DELAY, max_tries=MAX_TRIES):
673 """Get the multiengine client for the running cluster.
674
675 This will try to attempt to the controller multiple times. If this
676 fails altogether, try looking at the following:
677 * Make sure the controller is starting properly by looking at its
678 log files.
679 * Make sure the controller is writing its FURL file in the location
680 expected by the client.
681 * Make sure a firewall on the controller's host is not blocking the
682 client from connecting.
683
684 Parameters
685 ----------
686 delay : float
687 The initial delay between re-connection attempts. Susequent delays
688 get longer according to ``delay[i] = 1.5*delay[i-1]``.
689 max_tries : int
690 The max number of re-connection attempts.
691 """
692 if self.client_connector is None:
693 self.client_connector = ClientConnector()
694 return self.client_connector.get_multiengine_client(
695 cluster_dir=self.cluster_dir_obj.location,
696 delay=delay, max_tries=max_tries
697 )
698
699 def get_task_client(self, delay=DELAY, max_tries=MAX_TRIES):
700 """Get the task client for the running cluster.
701
702 This will try to attempt to the controller multiple times. If this
703 fails altogether, try looking at the following:
704 * Make sure the controller is starting properly by looking at its
705 log files.
706 * Make sure the controller is writing its FURL file in the location
707 expected by the client.
708 * Make sure a firewall on the controller's host is not blocking the
709 client from connecting.
710
711 Parameters
712 ----------
713 delay : float
714 The initial delay between re-connection attempts. Susequent delays
715 get longer according to ``delay[i] = 1.5*delay[i-1]``.
716 max_tries : int
717 The max number of re-connection attempts.
718 """
719 if self.client_connector is None:
720 self.client_connector = ClientConnector()
721 return self.client_connector.get_task_client(
722 cluster_dir=self.cluster_dir_obj.location,
723 delay=delay, max_tries=max_tries
724 )
725
726 def __repr__(self):
727 s = "<Cluster(running=%r, location=%s)" % (self.running, self.location)
728 return s
729
730 def get_logs_by_name(self, name='ipcluter'):
731 """Get a dict of logs by process name (ipcluster, ipengine, etc.)"""
732 return self.async_cluster.get_logs_by_name(name)
733
734 def get_ipengine_logs(self):
735 """Get a dict of logs for all engines in this cluster."""
736 return self.async_cluster.get_ipengine_logs()
737
738 def get_ipcontroller_logs(self):
739 """Get a dict of logs for the controller in this cluster."""
740 return self.async_cluster.get_ipcontroller_logs()
741
742 def get_ipcluster_logs(self):
743 """Get a dict of the ipcluster logs for this cluster."""
744 return self.async_cluster.get_ipcluster_logs()
745
746 def get_logs(self):
747 """Get a dict of all logs for this cluster."""
748 return self.async_cluster.get_logs()
749
750 def _print_logs(self, logs):
751 for k, v in logs.iteritems():
752 print "==================================="
753 print "Logfile: %s" % k
754 print "==================================="
755 print v
756 print
757
758 def print_ipengine_logs(self):
759 """Print the ipengine logs for this cluster to stdout."""
760 self._print_logs(self.get_ipengine_logs())
761
762 def print_ipcontroller_logs(self):
763 """Print the ipcontroller logs for this cluster to stdout."""
764 self._print_logs(self.get_ipcontroller_logs())
765
766 def print_ipcluster_logs(self):
767 """Print the ipcluster logs for this cluster to stdout."""
768 self._print_logs(self.get_ipcluster_logs())
769
770 def print_logs(self):
771 """Print all the logs for this cluster to stdout."""
772 self._print_logs(self.get_logs())
773
@@ -211,7 +211,7 b' class Interpreter(object):'
211
211
212 #### Public 'Interpreter' interface ########################################
212 #### Public 'Interpreter' interface ########################################
213
213
214 def formatTraceback(self, et, ev, tb, message=''):
214 def format_traceback(self, et, ev, tb, message=''):
215 """Put a formatted version of the traceback into value and reraise.
215 """Put a formatted version of the traceback into value and reraise.
216
216
217 When exceptions have to be sent over the network, the traceback
217 When exceptions have to be sent over the network, the traceback
@@ -375,7 +375,6 b' class Interpreter(object):'
375 exec code in self.user_ns
375 exec code in self.user_ns
376 outflag = 0
376 outflag = 0
377 except SystemExit:
377 except SystemExit:
378 self.resetbuffer()
379 self.traceback_trap.args = sys.exc_info()
378 self.traceback_trap.args = sys.exc_info()
380 except:
379 except:
381 self.traceback_trap.args = sys.exc_info()
380 self.traceback_trap.args = sys.exc_info()
@@ -395,7 +394,7 b' class Interpreter(object):'
395 python = self.translator(python)
394 python = self.translator(python)
396 self.execute_python(python)
395 self.execute_python(python)
397
396
398 def getCommand(self, i=None):
397 def get_command(self, i=None):
399 """Gets the ith message in the message_cache.
398 """Gets the ith message in the message_cache.
400
399
401 This is implemented here for compatibility with the old ipython1 shell
400 This is implemented here for compatibility with the old ipython1 shell
@@ -492,7 +491,7 b' class Interpreter(object):'
492 # somehow. In the meantime, we'll just stop if there are two lines
491 # somehow. In the meantime, we'll just stop if there are two lines
493 # of pure whitespace at the end.
492 # of pure whitespace at the end.
494 last_two = source.rsplit('\n',2)[-2:]
493 last_two = source.rsplit('\n',2)[-2:]
495 print 'last two:',last_two # dbg
494 #print 'last two:',last_two # dbg
496 if len(last_two)==2 and all(s.isspace() for s in last_two):
495 if len(last_two)==2 and all(s.isspace() for s in last_two):
497 return COMPLETE_INPUT,False
496 return COMPLETE_INPUT,False
498 else:
497 else:
@@ -18,19 +18,19 b' __test__ = {}'
18
18
19 from cStringIO import StringIO
19 from cStringIO import StringIO
20 import os
20 import os
21 import sys
21
22
22 from twisted.trial import unittest
23 from twisted.trial import unittest
23
24
24 from IPython.testing import decorators_trial as dec
25
26 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
27 # Tests
26 # Tests
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29
28
30
31 class TestRedirector(unittest.TestCase):
29 class TestRedirector(unittest.TestCase):
32
30
33 @dec.skip_win32
31 if sys.platform == 'win32':
32 skip = True
33
34 def test_redirector(self):
34 def test_redirector(self):
35 """Checks that the redirector can be used to do synchronous capture.
35 """Checks that the redirector can be used to do synchronous capture.
36 """
36 """
@@ -51,7 +51,6 b' class TestRedirector(unittest.TestCase):'
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
52 self.assertEquals(result1, result2)
52 self.assertEquals(result1, result2)
53
53
54 @dec.skip_win32
55 def test_redirector_output_trap(self):
54 def test_redirector_output_trap(self):
56 """Check the greedy trapping behavior of the traps.
55 """Check the greedy trapping behavior of the traps.
57
56
@@ -59,7 +58,8 b' class TestRedirector(unittest.TestCase):'
59 trap the output, but also that it does it in a gready way, that
58 trap the output, but also that it does it in a gready way, that
60 is by calling the callback ASAP.
59 is by calling the callback ASAP.
61 """
60 """
62 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
61 from IPython.kernel.core.redirector_output_trap import \
62 RedirectorOutputTrap
63 out = StringIO()
63 out = StringIO()
64 trap = RedirectorOutputTrap(out.write, out.write)
64 trap = RedirectorOutputTrap(out.write, out.write)
65 try:
65 try:
@@ -1,32 +1,38 b''
1 #!/usr/bin/env python
1 # encoding: utf-8
2 # encoding: utf-8
2
3
3 """A class that manages the engines connection to the controller."""
4 """A class that manages the engines connection to the controller."""
4
5
5 __docformat__ = "restructuredtext en"
6 #-----------------------------------------------------------------------------
6
7 # Copyright (C) 2008-2009 The IPython Development Team
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
8 #
10 # 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
11 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
13
12
14 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
15 # Imports
14 # Imports
16 #-------------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17
16
18 import os
17 import os
19 import cPickle as pickle
18 import cPickle as pickle
20
19
21 from twisted.python import log, failure
20 from twisted.python import log, failure
22 from twisted.internet import defer
21 from twisted.internet import defer
22 from twisted.internet.defer import inlineCallbacks, returnValue
23
23
24 from IPython.kernel.fcutil import find_furl
24 from IPython.kernel.fcutil import find_furl, validate_furl_or_file
25 from IPython.kernel.enginefc import IFCEngine
25 from IPython.kernel.enginefc import IFCEngine
26 from IPython.kernel.twistedutil import sleep_deferred, make_deferred
26
27
27 #-------------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
28 # The ClientConnector class
29 # The ClientConnector class
29 #-------------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
32
33 class EngineConnectorError(Exception):
34 pass
35
30
36
31 class EngineConnector(object):
37 class EngineConnector(object):
32 """Manage an engines connection to a controller.
38 """Manage an engines connection to a controller.
@@ -38,8 +44,10 b' class EngineConnector(object):'
38
44
39 def __init__(self, tub):
45 def __init__(self, tub):
40 self.tub = tub
46 self.tub = tub
41
47
42 def connect_to_controller(self, engine_service, furl_or_file):
48 @make_deferred
49 def connect_to_controller(self, engine_service, furl_or_file,
50 delay=0.1, max_tries=10):
43 """
51 """
44 Make a connection to a controller specified by a furl.
52 Make a connection to a controller specified by a furl.
45
53
@@ -48,34 +56,73 b' class EngineConnector(object):'
48 foolscap URL contains all the information needed to connect to the
56 foolscap URL contains all the information needed to connect to the
49 controller, including the ip and port as well as any encryption and
57 controller, including the ip and port as well as any encryption and
50 authentication information needed for the connection.
58 authentication information needed for the connection.
51
59
52 After getting a reference to the controller, this method calls the
60 After getting a reference to the controller, this method calls the
53 `register_engine` method of the controller to actually register the
61 `register_engine` method of the controller to actually register the
54 engine.
62 engine.
55
63
56 :Parameters:
64 This method will try to connect to the controller multiple times with
57 engine_service : IEngineBase
65 a delay in between. Each time the FURL file is read anew.
58 An instance of an `IEngineBase` implementer
66
59 furl_or_file : str
67 Parameters
60 A furl or a filename containing a furl
68 __________
69 engine_service : IEngineBase
70 An instance of an `IEngineBase` implementer
71 furl_or_file : str
72 A furl or a filename containing a furl
73 delay : float
74 The intial time to wait between connection attempts. Subsequent
75 attempts have increasing delays.
76 max_tries : int
77 The maximum number of connection attempts.
78
79 Returns
80 -------
81 A deferred to the registered client or a failure to an error
82 like :exc:`FURLError`.
61 """
83 """
62 if not self.tub.running:
84 if not self.tub.running:
63 self.tub.startService()
85 self.tub.startService()
64 self.engine_service = engine_service
86 self.engine_service = engine_service
65 self.engine_reference = IFCEngine(self.engine_service)
87 self.engine_reference = IFCEngine(self.engine_service)
66 try:
88
67 self.furl = find_furl(furl_or_file)
89 validate_furl_or_file(furl_or_file)
68 except ValueError:
90 d = self._try_to_connect(furl_or_file, delay, max_tries, attempt=0)
69 return defer.fail(failure.Failure())
91 d.addCallback(self._register)
92 return d
93
94 @inlineCallbacks
95 def _try_to_connect(self, furl_or_file, delay, max_tries, attempt):
96 """Try to connect to the controller with retry logic."""
97 if attempt < max_tries:
98 log.msg("Attempting to connect to controller [%r]: %s" % \
99 (attempt, furl_or_file))
100 try:
101 self.furl = find_furl(furl_or_file)
102 # Uncomment this to see the FURL being tried.
103 # log.msg("FURL: %s" % self.furl)
104 rr = yield self.tub.getReference(self.furl)
105 except:
106 if attempt==max_tries-1:
107 # This will propagate the exception all the way to the top
108 # where it can be handled.
109 raise
110 else:
111 yield sleep_deferred(delay)
112 rr = yield self._try_to_connect(
113 furl_or_file, 1.5*delay, max_tries, attempt+1
114 )
115 # rr becomes an int when there is a connection!!!
116 returnValue(rr)
117 else:
118 returnValue(rr)
70 else:
119 else:
71 d = self.tub.getReference(self.furl)
120 raise EngineConnectorError(
72 d.addCallbacks(self._register, self._log_failure)
121 'Could not connect to controller, max_tries (%r) exceeded. '
73 return d
122 'This usually means that i) the controller was not started, '
74
123 'or ii) a firewall was blocking the engine from connecting '
75 def _log_failure(self, reason):
124 'to the controller.' % max_tries
76 log.err('EngineConnector: engine registration failed:')
125 )
77 log.err(reason)
78 return reason
79
126
80 def _register(self, rr):
127 def _register(self, rr):
81 self.remote_ref = rr
128 self.remote_ref = rr
@@ -83,7 +130,7 b' class EngineConnector(object):'
83 desired_id = self.engine_service.id
130 desired_id = self.engine_service.id
84 d = self.remote_ref.callRemote('register_engine', self.engine_reference,
131 d = self.remote_ref.callRemote('register_engine', self.engine_reference,
85 desired_id, os.getpid(), pickle.dumps(self.engine_service.properties,2))
132 desired_id, os.getpid(), pickle.dumps(self.engine_service.properties,2))
86 return d.addCallbacks(self._reference_sent, self._log_failure)
133 return d.addCallback(self._reference_sent)
87
134
88 def _reference_sent(self, registration_dict):
135 def _reference_sent(self, registration_dict):
89 self.engine_service.id = registration_dict['id']
136 self.engine_service.id = registration_dict['id']
@@ -387,7 +387,7 b' class EngineService(object, service.Service):'
387 # tb=traceback object
387 # tb=traceback object
388 et,ev,tb = sys.exc_info()
388 et,ev,tb = sys.exc_info()
389 # This call adds attributes to the exception value
389 # This call adds attributes to the exception value
390 et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
390 et,ev,tb = self.shell.format_traceback(et,ev,tb,msg)
391 # Add another attribute
391 # Add another attribute
392 ev._ipython_engine_info = msg
392 ev._ipython_engine_info = msg
393 f = failure.Failure(ev,et,None)
393 f = failure.Failure(ev,et,None)
@@ -444,7 +444,7 b' class EngineService(object, service.Service):'
444 msg = {'engineid':self.id,
444 msg = {'engineid':self.id,
445 'method':'get_result',
445 'method':'get_result',
446 'args':[repr(i)]}
446 'args':[repr(i)]}
447 d = self.executeAndRaise(msg, self.shell.getCommand, i)
447 d = self.executeAndRaise(msg, self.shell.get_command, i)
448 d.addCallback(self.addIDToResult)
448 d.addCallback(self.addIDToResult)
449 return d
449 return d
450
450
@@ -877,7 +877,7 b' class ThreadedEngineService(EngineService):'
877 # tb=traceback object
877 # tb=traceback object
878 et,ev,tb = sys.exc_info()
878 et,ev,tb = sys.exc_info()
879 # This call adds attributes to the exception value
879 # This call adds attributes to the exception value
880 et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
880 et,ev,tb = self.shell.format_traceback(et,ev,tb,msg)
881 # Add another attribute
881 # Add another attribute
882
882
883 # Create a new exception with the new attributes
883 # Create a new exception with the new attributes
@@ -127,9 +127,11 b' class TaskRejectError(KernelError):'
127 class CompositeError(KernelError):
127 class CompositeError(KernelError):
128 def __init__(self, message, elist):
128 def __init__(self, message, elist):
129 Exception.__init__(self, *(message, elist))
129 Exception.__init__(self, *(message, elist))
130 self.message = message
130 # Don't use pack_exception because it will conflict with the .message
131 # attribute that is being deprecated in 2.6 and beyond.
132 self.msg = message
131 self.elist = elist
133 self.elist = elist
132
134
133 def _get_engine_str(self, ev):
135 def _get_engine_str(self, ev):
134 try:
136 try:
135 ei = ev._ipython_engine_info
137 ei = ev._ipython_engine_info
@@ -137,7 +139,7 b' class CompositeError(KernelError):'
137 return '[Engine Exception]'
139 return '[Engine Exception]'
138 else:
140 else:
139 return '[%i:%s]: ' % (ei['engineid'], ei['method'])
141 return '[%i:%s]: ' % (ei['engineid'], ei['method'])
140
142
141 def _get_traceback(self, ev):
143 def _get_traceback(self, ev):
142 try:
144 try:
143 tb = ev._ipython_traceback_text
145 tb = ev._ipython_traceback_text
@@ -145,14 +147,14 b' class CompositeError(KernelError):'
145 return 'No traceback available'
147 return 'No traceback available'
146 else:
148 else:
147 return tb
149 return tb
148
150
149 def __str__(self):
151 def __str__(self):
150 s = str(self.message)
152 s = str(self.msg)
151 for et, ev, etb in self.elist:
153 for et, ev, etb in self.elist:
152 engine_str = self._get_engine_str(ev)
154 engine_str = self._get_engine_str(ev)
153 s = s + '\n' + engine_str + str(et.__name__) + ': ' + str(ev)
155 s = s + '\n' + engine_str + str(et.__name__) + ': ' + str(ev)
154 return s
156 return s
155
157
156 def print_tracebacks(self, excid=None):
158 def print_tracebacks(self, excid=None):
157 if excid is None:
159 if excid is None:
158 for (et,ev,etb) in self.elist:
160 for (et,ev,etb) in self.elist:
@@ -1,27 +1,62 b''
1 #!/usr/bin/env python
1 # encoding: utf-8
2 # encoding: utf-8
3 """
4 Foolscap related utilities.
5 """
2
6
3 """Foolscap related utilities."""
7 #-----------------------------------------------------------------------------
4
8 # Copyright (C) 2008-2009 The IPython Development Team
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
18 from __future__ import with_statement
17
19
18 import os
20 import os
21 import tempfile
22
23 from twisted.internet import reactor, defer
24 from twisted.python import log
19
25
20 from foolscap import Tub, UnauthenticatedTub
26 from foolscap import Tub, UnauthenticatedTub
21
27
28 from IPython.config.loader import Config
29
30 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
31
32 from IPython.kernel.error import SecurityError
33
34 from IPython.utils.traitlets import Int, Str, Bool, Instance
35 from IPython.utils.importstring import import_item
36
37 #-----------------------------------------------------------------------------
38 # Code
39 #-----------------------------------------------------------------------------
40
41
42 # We do this so if a user doesn't have OpenSSL installed, it will try to use
43 # an UnauthenticatedTub. But, they will still run into problems if they
44 # try to use encrypted furls.
45 try:
46 import OpenSSL
47 except:
48 Tub = UnauthenticatedTub
49 have_crypto = False
50 else:
51 have_crypto = True
52
53
54 class FURLError(Exception):
55 pass
56
57
22 def check_furl_file_security(furl_file, secure):
58 def check_furl_file_security(furl_file, secure):
23 """Remove the old furl_file if changing security modes."""
59 """Remove the old furl_file if changing security modes."""
24
25 if os.path.isfile(furl_file):
60 if os.path.isfile(furl_file):
26 f = open(furl_file, 'r')
61 f = open(furl_file, 'r')
27 oldfurl = f.read().strip()
62 oldfurl = f.read().strip()
@@ -29,41 +64,210 b' def check_furl_file_security(furl_file, secure):'
29 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
64 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
30 os.remove(furl_file)
65 os.remove(furl_file)
31
66
67
32 def is_secure(furl):
68 def is_secure(furl):
69 """Is the given FURL secure or not."""
33 if is_valid(furl):
70 if is_valid(furl):
34 if furl.startswith("pb://"):
71 if furl.startswith("pb://"):
35 return True
72 return True
36 elif furl.startswith("pbu://"):
73 elif furl.startswith("pbu://"):
37 return False
74 return False
38 else:
75 else:
39 raise ValueError("invalid furl: %s" % furl)
76 raise FURLError("invalid FURL: %s" % furl)
77
40
78
41 def is_valid(furl):
79 def is_valid(furl):
80 """Is the str a valid FURL or not."""
42 if isinstance(furl, str):
81 if isinstance(furl, str):
43 if furl.startswith("pb://") or furl.startswith("pbu://"):
82 if furl.startswith("pb://") or furl.startswith("pbu://"):
44 return True
83 return True
45 else:
84 else:
46 return False
85 return False
47
86
87
48 def find_furl(furl_or_file):
88 def find_furl(furl_or_file):
89 """Find, validate and return a FURL in a string or file."""
49 if isinstance(furl_or_file, str):
90 if isinstance(furl_or_file, str):
50 if is_valid(furl_or_file):
91 if is_valid(furl_or_file):
51 return furl_or_file
92 return furl_or_file
52 if os.path.isfile(furl_or_file):
93 if os.path.isfile(furl_or_file):
53 furl = open(furl_or_file, 'r').read().strip()
94 with open(furl_or_file, 'r') as f:
95 furl = f.read().strip()
54 if is_valid(furl):
96 if is_valid(furl):
55 return furl
97 return furl
56 raise ValueError("not a furl or a file containing a furl: %s" % furl_or_file)
98 raise FURLError("Not a valid FURL or FURL file: %s" % furl_or_file)
57
99
58 # We do this so if a user doesn't have OpenSSL installed, it will try to use
59 # an UnauthenticatedTub. But, they will still run into problems if they
60 # try to use encrypted furls.
61 try:
62 import OpenSSL
63 except:
64 Tub = UnauthenticatedTub
65 have_crypto = False
66 else:
67 have_crypto = True
68
100
101 def is_valid_furl_or_file(furl_or_file):
102 """Validate a FURL or a FURL file.
103
104 If ``furl_or_file`` looks like a file, we simply make sure its directory
105 exists and that it has a ``.furl`` file extension. We don't try to see
106 if the FURL file exists or to read its contents. This is useful for
107 cases where auto re-connection is being used.
108 """
109 if isinstance(furl_or_file, str):
110 if is_valid(furl_or_file):
111 return True
112 if isinstance(furl_or_file, (str, unicode)):
113 path, furl_filename = os.path.split(furl_or_file)
114 if os.path.isdir(path) and furl_filename.endswith('.furl'):
115 return True
116 return False
117
118
119 def validate_furl_or_file(furl_or_file):
120 if not is_valid_furl_or_file(furl_or_file):
121 raise FURLError('Not a valid FURL or FURL file: %r' % furl_or_file)
122
123
124 def get_temp_furlfile(filename):
125 """Return a temporary FURL file."""
126 return tempfile.mktemp(dir=os.path.dirname(filename),
127 prefix=os.path.basename(filename))
128
129
130 def make_tub(ip, port, secure, cert_file):
131 """Create a listening tub given an ip, port, and cert_file location.
132
133 Parameters
134 ----------
135 ip : str
136 The ip address or hostname that the tub should listen on.
137 Empty means all interfaces.
138 port : int
139 The port that the tub should listen on. A value of 0 means
140 pick a random port
141 secure: bool
142 Will the connection be secure (in the Foolscap sense).
143 cert_file: str
144 A filename of a file to be used for theSSL certificate.
145
146 Returns
147 -------
148 A tub, listener tuple.
149 """
150 if secure:
151 if have_crypto:
152 tub = Tub(certFile=cert_file)
153 else:
154 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
155 "can't run in secure mode. Try running without "
156 "security using 'ipcontroller -xy'.")
157 else:
158 tub = UnauthenticatedTub()
159
160 # Set the strport based on the ip and port and start listening
161 if ip == '':
162 strport = "tcp:%i" % port
163 else:
164 strport = "tcp:%i:interface=%s" % (port, ip)
165 log.msg("Starting listener with [secure=%r] on: %s" % (secure, strport))
166 listener = tub.listenOn(strport)
167
168 return tub, listener
169
170
171 class FCServiceFactory(AdaptedConfiguredObjectFactory):
172 """This class creates a tub with various services running in it.
173
174 The basic idea is that :meth:`create` returns a running :class:`Tub`
175 instance that has a number of Foolscap references registered in it.
176 This class is a subclass of :class:`IPython.core.component.Component`
177 so the IPython configuration and component system are used.
178
179 Attributes
180 ----------
181 interfaces : Config
182 A Config instance whose values are sub-Config objects having two
183 keys: furl_file and interface_chain.
184
185 The other attributes are the standard ones for Foolscap.
186 """
187
188 ip = Str('', config=True)
189 port = Int(0, config=True)
190 secure = Bool(True, config=True)
191 cert_file = Str('', config=True)
192 location = Str('', config=True)
193 reuse_furls = Bool(False, config=True)
194 interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
195
196 def __init__(self, config, adaptee):
197 super(FCServiceFactory, self).__init__(config, adaptee)
198 self._check_reuse_furls()
199
200 def _ip_changed(self, name, old, new):
201 if new == 'localhost' or new == '127.0.0.1':
202 self.location = '127.0.0.1'
203
204 def _check_reuse_furls(self):
205 furl_files = [i.furl_file for i in self.interfaces.values()]
206 for ff in furl_files:
207 fullfile = self._get_security_file(ff)
208 if self.reuse_furls:
209 if self.port==0:
210 raise FURLError("You are trying to reuse the FURL file "
211 "for this connection, but the port for this connection "
212 "is set to 0 (autoselect). To reuse the FURL file "
213 "you need to specify specific port to listen on."
214 )
215 else:
216 log.msg("Reusing FURL file: %s" % fullfile)
217 else:
218 if os.path.isfile(fullfile):
219 log.msg("Removing old FURL file: %s" % fullfile)
220 os.remove(fullfile)
221
222 def _get_security_file(self, filename):
223 return os.path.join(self.config.Global.security_dir, filename)
224
225 def create(self):
226 """Create and return the Foolscap tub with everything running."""
227
228 self.tub, self.listener = make_tub(
229 self.ip, self.port, self.secure,
230 self._get_security_file(self.cert_file)
231 )
232 # log.msg("Interfaces to register [%r]: %r" % \
233 # (self.__class__, self.interfaces))
234 if not self.secure:
235 log.msg("WARNING: running with no security: %s" % \
236 self.__class__.__name__)
237 reactor.callWhenRunning(self.set_location_and_register)
238 return self.tub
239
240 def set_location_and_register(self):
241 """Set the location for the tub and return a deferred."""
242
243 if self.location == '':
244 d = self.tub.setLocationAutomatically()
245 else:
246 d = defer.maybeDeferred(self.tub.setLocation,
247 "%s:%i" % (self.location, self.listener.getPortnum()))
248 self.adapt_to_interfaces(d)
249
250 def adapt_to_interfaces(self, d):
251 """Run through the interfaces, adapt and register."""
252
253 for ifname, ifconfig in self.interfaces.iteritems():
254 ff = self._get_security_file(ifconfig.furl_file)
255 log.msg("Adapting [%s] to interface: %s" % \
256 (self.adaptee.__class__.__name__, ifname))
257 log.msg("Saving FURL for interface [%s] to file: %s" % (ifname, ff))
258 check_furl_file_security(ff, self.secure)
259 adaptee = self.adaptee
260 for i in ifconfig.interface_chain:
261 adaptee = import_item(i)(adaptee)
262 d.addCallback(self.register, adaptee, furl_file=ff)
263
264 def register(self, empty, ref, furl_file):
265 """Register the reference with the FURL file.
266
267 The FURL file is created and then moved to make sure that when the
268 file appears, the buffer has been flushed and the file closed.
269 """
270 temp_furl_file = get_temp_furlfile(furl_file)
271 self.tub.registerReference(ref, furlFile=temp_furl_file)
272 os.rename(temp_furl_file, furl_file)
69
273
@@ -262,9 +262,8 b' class MultiEngine(ControllerAdapterBase):'
262 elif targets == 'all':
262 elif targets == 'all':
263 eList = self.engines.values()
263 eList = self.engines.values()
264 if len(eList) == 0:
264 if len(eList) == 0:
265 msg = """There are no engines registered.
265 raise error.NoEnginesRegistered("There are no engines registered. "
266 Check the logs in ~/.ipython/log if you think there should have been."""
266 "Check the logs if you think there should have been.")
267 raise error.NoEnginesRegistered(msg)
268 else:
267 else:
269 return eList
268 return eList
270 else:
269 else:
@@ -263,10 +263,18 b' class InteractiveMultiEngineClient(object):'
263 """
263 """
264
264
265 try:
265 try:
266 __IPYTHON__.activeController = self
266 # This is injected into __builtins__.
267 ip = get_ipython()
267 except NameError:
268 except NameError:
268 print "The IPython Controller magics only work within IPython."
269 print "The IPython parallel magics (%result, %px, %autopx) only work within IPython."
269
270 else:
271 pmagic = ip.get_component('parallel_magic')
272 if pmagic is not None:
273 pmagic.active_multiengine_client = self
274 else:
275 print "You must first load the parallelmagic extension " \
276 "by doing '%load_ext parallelmagic'"
277
270 def __setitem__(self, key, value):
278 def __setitem__(self, key, value):
271 """Add a dictionary interface for pushing/pulling.
279 """Add a dictionary interface for pushing/pulling.
272
280
@@ -1,22 +1,18 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """ipcluster script"""
4 #-----------------------------------------------------------------------------
5
5 # Copyright (C) 2008-2009 The IPython Development Team
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
6 #
11 # Distributed under the terms of the BSD License. The full license is in
7 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
8 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
14
10
15 #-------------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
16 # Imports
12 # Imports
17 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
18
15
19 if __name__ == '__main__':
16 from IPython.kernel.ipclusterapp import launch_new_instance
20 from IPython.kernel.scripts import ipcluster
21 ipcluster.main()
22
17
18 launch_new_instance()
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from IPython/kernel/core/notification.py to IPython/utils/notification.py
NO CONTENT: file renamed from IPython/kernel/core/notification.py to IPython/utils/notification.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from IPython/kernel/core/tests/test_notification.py to IPython/utils/tests/test_notification.py
NO CONTENT: file renamed from IPython/kernel/core/tests/test_notification.py to IPython/utils/tests/test_notification.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
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
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now