Merge lp:~ipython-dev/ipython/kernel-config into lp:ipython/0.11

Proposed by Brian Granger
Status: Merged
Merged at revision: not available
Proposed branch: lp:~ipython-dev/ipython/kernel-config
Merge into: lp:ipython/0.11
Diff against target: 11644 lines (+6503/-3129)
79 files modified
IPython/config/api.py (+0/-102)
IPython/config/default/ipcluster_config.py (+184/-0)
IPython/config/default/ipcontroller_config.py (+136/-0)
IPython/config/default/ipengine_config.py (+90/-0)
IPython/config/default/ipython_config.py (+4/-4)
IPython/config/loader.py (+9/-2)
IPython/config/profile/ipython_config_cluster.py (+24/-0)
IPython/core/application.py (+125/-61)
IPython/core/builtin_trap.py (+1/-0)
IPython/core/component.py (+24/-3)
IPython/core/crashhandler.py (+1/-1)
IPython/core/debugger.py (+76/-121)
IPython/core/display_trap.py (+5/-5)
IPython/core/embed.py (+2/-10)
IPython/core/ipapi.py (+4/-1)
IPython/core/ipapp.py (+70/-72)
IPython/core/iplib.py (+43/-25)
IPython/core/magic.py (+56/-29)
IPython/core/oldusersetup.py (+0/-219)
IPython/core/prefilter.py (+1/-1)
IPython/core/tests/test_iplib.py (+1/-24)
IPython/core/usage.py (+7/-7)
IPython/extensions/parallelmagic.py (+181/-147)
IPython/frontend/wx/ipythonx.py (+2/-3)
IPython/gui/wx/wxIPython.py (+2/-2)
IPython/kernel/asyncclient.py (+13/-12)
IPython/kernel/client.py (+35/-43)
IPython/kernel/clientconnector.py (+735/-112)
IPython/kernel/clusterdir.py (+475/-0)
IPython/kernel/config/__init__.py (+0/-126)
IPython/kernel/configobjfactory.py (+79/-0)
IPython/kernel/core/config/__init__.py (+0/-25)
IPython/kernel/engineconnector.py (+79/-32)
IPython/kernel/error.py (+8/-6)
IPython/kernel/fcutil.py (+230/-26)
IPython/kernel/ipclusterapp.py (+471/-0)
IPython/kernel/ipcontrollerapp.py (+275/-0)
IPython/kernel/ipengineapp.py (+248/-0)
IPython/kernel/launcher.py (+869/-0)
IPython/kernel/multiengine.py (+2/-3)
IPython/kernel/multiengineclient.py (+11/-3)
IPython/kernel/scripts/ipcluster (+10/-14)
IPython/kernel/scripts/ipcluster.py (+0/-813)
IPython/kernel/scripts/ipcontroller (+10/-12)
IPython/kernel/scripts/ipcontroller.py (+0/-416)
IPython/kernel/scripts/ipengine (+11/-11)
IPython/kernel/scripts/ipengine.py (+0/-193)
IPython/kernel/twistedutil.py (+34/-9)
IPython/kernel/winhpcjob.py (+318/-0)
IPython/lib/inputhookwx.py (+73/-62)
IPython/lib/irunner.py (+3/-3)
IPython/quarantine/InterpreterExec.py (+1/-1)
IPython/quarantine/ipy_fsops.py (+2/-2)
IPython/utils/genutils.py (+95/-86)
IPython/utils/notification.py (+86/-68)
IPython/utils/tests/test_notification.py (+72/-79)
IPython/utils/traitlets.py (+34/-4)
docs/Makefile (+2/-0)
docs/autogen_api.py (+1/-0)
docs/examples/core/new-embed.py (+17/-0)
docs/examples/kernel/fractal.py (+90/-0)
docs/examples/kernel/mcdriver.py (+54/-54)
docs/examples/kernel/mcpricer.py (+42/-40)
docs/examples/kernel/parallelpi.py (+54/-0)
docs/examples/kernel/pidigits.py (+144/-0)
docs/examples/kernel/wordfreq.py (+48/-5)
docs/man/ipcluster.1 (+1/-1)
docs/man/ipython.1 (+6/-6)
docs/source/conf.py (+8/-5)
docs/source/config/overview.txt (+4/-4)
docs/source/development/index.txt (+1/-0)
docs/source/development/ipgraph.txt (+59/-0)
docs/source/interactive/reference.txt (+9/-9)
docs/source/parallel/index.txt (+2/-0)
docs/source/parallel/parallel_demos.txt (+282/-0)
docs/source/parallel/parallel_winhpc.txt (+333/-0)
docs/source/parallel/winhpc_index.txt (+14/-0)
setup.py (+3/-3)
setupbase.py (+2/-2)
To merge this branch: bzr merge lp:~ipython-dev/ipython/kernel-config
Reviewer Review Type Date Requested Status
Fernando Perez Approve
Vishal Vatsa Pending
Review via email: mp+14900@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Brian Granger (ellisonbg) wrote :

This is the result of a massive effort to refactor IPython.kernel to use the new config system. This also includes an improved ipcluster/ipcontroller/ipengine command line programs. Here are the main files that should be reviewed:

IPython.kernel:

* clusterdir.py
* ipcontrollerapp.py
* ipengineapp.py
* launcher.py
* ipclusterapp.py

IPython.config.default:

* ipcontroller_config.py
* ipengine_config.py
* ipcluster_config.py

I have also fixed a number of bugs and problems in IPython itself:

* The command line options use a new style.
* %debug is fixed.
* New magics %load_ext/%unload_ext/%reload_ext/%install_profiles/%install_default_config.

Enjoy!

1269. By Brian Granger

Added better documentation to command line programs.

1270. By Brian Granger

Initial draft of Windows HPC documentation.

1271. By Brian Granger

Work in the documentation.

1272. By bgranger <bgranger@red>

Adding figures for options pricing example.

1273. By Brian Granger

Final work on the Win HPC whitepaper.

Revision history for this message
Fernando Perez (fdo.perez) wrote :

Summary: Excellent work! Many thanks for all of this.

I'm not merging it now just in case you want to do it yourself tomorrow (12/30) first thing or have some other little change you'd like to put in. But if you don't merge it I'll go ahead and do it, and will begin adding code I have (like testing stuff) so we can push towards 0.11. We still have cleanups and things to do, but all of that is best done on top of a more functional trunk, which this branch helps LOT with.

Per-commit comments:

-r1264: ok

-r1265: in kernel/ipclusterapp.py, should we output something visible to the user indicating the 4s pause at shutdown? I won't add it myself, as I'm not sure this should go in; most of our output is now via logging, right? Putting this in the log wouldn't be too useful, it would be strictly a convenience for the user.

-r1266: ok, just upstream

-r1267: great, more examples!

-r1268: good, these are changes we'd discussed over the phone.

-r1269: better docs, excellent...

-r1270, 71, 72, 73: this document is stellar, we'd reviewed it already.

review: Approve
Revision history for this message
Brian Granger (ellisonbg) wrote :

> I'm not merging it now just in case you want to do it yourself tomorrow (12/30) first thing or have some other little change you'd like to put in.  But if you don't merge it I'll go ahead and do it, and will begin adding code I have (like testing stuff) so we can push towards 0.11.  We still have cleanups and things to do, but all of that is best done on top of a more functional trunk, which this branch helps LOT with.

Thanks for the review. Go ahead and do the merge, I am working on
other things right now.

> Per-commit comments:
>
> -r1264: ok
>
> -r1265: in kernel/ipclusterapp.py, should we output something visible to the user indicating the 4s pause at shutdown?  I won't add it myself, as I'm not sure this should go in; most of our output is now via logging, right?  Putting this in the log wouldn't be too useful, it would be strictly a convenience for the user.

Yes, I can add a message about this to the logs.

> -r1266: ok, just upstream
>
> -r1267: great, more examples!
>
> -r1268: good, these are changes we'd discussed over the phone.
>
> -r1269: better docs, excellent...
>
> -r1270, 71, 72, 73: this document is stellar, we'd reviewed it already.
>
> --
> https://code.launchpad.net/~ipython-dev/ipython/kernel-config/+merge/14900
> You proposed lp:~ipython-dev/ipython/kernel-config for merging.
>

--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>

Revision history for this message
Fernando Perez (fdo.perez) wrote :

On Wed, Dec 30, 2009 at 8:39 AM, Brian Granger <email address hidden> wrote:
>
> Thanks for the review.  Go ahead and do the merge, I am working on
> other things right now

Done, merged and pushed with an extra tiny fix. Thanks again, great job!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'IPython/config/api.py'
2--- IPython/config/api.py 2009-07-02 02:49:32 +0000
3+++ IPython/config/api.py 1970-01-01 00:00:00 +0000
4@@ -1,102 +0,0 @@
5-# encoding: utf-8
6-
7-"""This is the official entry point to IPython's configuration system. """
8-
9-__docformat__ = "restructuredtext en"
10-
11-#-------------------------------------------------------------------------------
12-# Copyright (C) 2008 The IPython Development Team
13-#
14-# Distributed under the terms of the BSD License. The full license is in
15-# the file COPYING, distributed as part of this software.
16-#-------------------------------------------------------------------------------
17-
18-#-------------------------------------------------------------------------------
19-# Imports
20-#-------------------------------------------------------------------------------
21-
22-import os
23-from os.path import join as pjoin
24-
25-from IPython.utils.genutils import get_home_dir, get_ipython_dir
26-from IPython.external.configobj import ConfigObj
27-
28-
29-class ConfigObjManager(object):
30-
31- def __init__(self, configObj, filename):
32- self.current = configObj
33- self.current.indent_type = ' '
34- self.filename = filename
35- # self.write_default_config_file()
36-
37- def get_config_obj(self):
38- return self.current
39-
40- def update_config_obj(self, newConfig):
41- self.current.merge(newConfig)
42-
43- def update_config_obj_from_file(self, filename):
44- newConfig = ConfigObj(filename, file_error=False)
45- self.current.merge(newConfig)
46-
47- def update_config_obj_from_default_file(self, ipythondir=None):
48- fname = self.resolve_file_path(self.filename, ipythondir)
49- self.update_config_obj_from_file(fname)
50-
51- def write_config_obj_to_file(self, filename):
52- f = open(filename, 'w')
53- self.current.write(f)
54- f.close()
55-
56- def write_default_config_file(self):
57- ipdir = get_ipython_dir()
58- fname = pjoin(ipdir, self.filename)
59- if not os.path.isfile(fname):
60- print "Writing the configuration file to: " + fname
61- self.write_config_obj_to_file(fname)
62-
63- def _import(self, key):
64- package = '.'.join(key.split('.')[0:-1])
65- obj = key.split('.')[-1]
66- execString = 'from %s import %s' % (package, obj)
67- exec execString
68- exec 'temp = %s' % obj
69- return temp
70-
71- def resolve_file_path(self, filename, ipythondir = None):
72- """Resolve filenames into absolute paths.
73-
74- This function looks in the following directories in order:
75-
76- 1. In the current working directory or by absolute path with ~ expanded
77- 2. In ipythondir if that is set
78- 3. In the IPYTHONDIR environment variable if it exists
79- 4. In the ~/.ipython directory
80-
81- Note: The IPYTHONDIR is also used by the trunk version of IPython so
82- changing it will also affect it was well.
83- """
84-
85- # In cwd or by absolute path with ~ expanded
86- trythis = os.path.expanduser(filename)
87- if os.path.isfile(trythis):
88- return trythis
89-
90- # In ipythondir if it is set
91- if ipythondir is not None:
92- trythis = pjoin(ipythondir, filename)
93- if os.path.isfile(trythis):
94- return trythis
95-
96- trythis = pjoin(get_ipython_dir(), filename)
97- if os.path.isfile(trythis):
98- return trythis
99-
100- return None
101-
102-
103-
104-
105-
106-
107
108=== added file 'IPython/config/default/ipcluster_config.py'
109--- IPython/config/default/ipcluster_config.py 1970-01-01 00:00:00 +0000
110+++ IPython/config/default/ipcluster_config.py 2009-11-21 00:11:10 +0000
111@@ -0,0 +1,184 @@
112+import os
113+
114+c = get_config()
115+
116+#-----------------------------------------------------------------------------
117+# Select which launchers to use
118+#-----------------------------------------------------------------------------
119+
120+# This allows you to control what method is used to start the controller
121+# and engines. The following methods are currently supported:
122+# - Start as a regular process on localhost.
123+# - Start using mpiexec.
124+# - Start using the Windows HPC Server 2008 scheduler
125+# - Start using PBS
126+# - Start using SSH (currently broken)
127+
128+
129+# The selected launchers can be configured below.
130+
131+# Options are:
132+# - LocalControllerLauncher
133+# - MPIExecControllerLauncher
134+# - PBSControllerLauncher
135+# - WindowsHPCControllerLauncher
136+# c.Global.controller_launcher = 'IPython.kernel.launcher.LocalControllerLauncher'
137+
138+# Options are:
139+# - LocalEngineSetLauncher
140+# - MPIExecEngineSetLauncher
141+# - PBSEngineSetLauncher
142+# - WindowsHPCEngineSetLauncher
143+# c.Global.engine_launcher = 'IPython.kernel.launcher.LocalEngineSetLauncher'
144+
145+#-----------------------------------------------------------------------------
146+# Global configuration
147+#-----------------------------------------------------------------------------
148+
149+# The default number of engines that will be started. This is overridden by
150+# the -n command line option: "ipcluster start -n 4"
151+# c.Global.n = 2
152+
153+# Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
154+# c.Global.log_to_file = False
155+
156+# Remove old logs from cluster_dir/log before starting.
157+# c.Global.clean_logs = True
158+
159+# The working directory for the process. The application will use os.chdir
160+# to change to this directory before starting.
161+# c.Global.work_dir = os.getcwd()
162+
163+
164+#-----------------------------------------------------------------------------
165+# Local process launchers
166+#-----------------------------------------------------------------------------
167+
168+# The command line arguments to call the controller with.
169+# c.LocalControllerLauncher.controller_args = \
170+# ['--log-to-file','--log-level', '40']
171+
172+# The working directory for the controller
173+# c.LocalEngineSetLauncher.work_dir = u''
174+
175+# Command line argument passed to the engines.
176+# c.LocalEngineSetLauncher.engine_args = ['--log-to-file','--log-level', '40']
177+
178+#-----------------------------------------------------------------------------
179+# MPIExec launchers
180+#-----------------------------------------------------------------------------
181+
182+# The mpiexec/mpirun command to use in started the controller.
183+# c.MPIExecControllerLauncher.mpi_cmd = ['mpiexec']
184+
185+# Additional arguments to pass to the actual mpiexec command.
186+# c.MPIExecControllerLauncher.mpi_args = []
187+
188+# The command line argument to call the controller with.
189+# c.MPIExecControllerLauncher.controller_args = \
190+# ['--log-to-file','--log-level', '40']
191+
192+
193+# The mpiexec/mpirun command to use in started the controller.
194+# c.MPIExecEngineSetLauncher.mpi_cmd = ['mpiexec']
195+
196+# Additional arguments to pass to the actual mpiexec command.
197+# c.MPIExecEngineSetLauncher.mpi_args = []
198+
199+# Command line argument passed to the engines.
200+# c.MPIExecEngineSetLauncher.engine_args = ['--log-to-file','--log-level', '40']
201+
202+# The default number of engines to start if not given elsewhere.
203+# c.MPIExecEngineSetLauncher.n = 1
204+
205+#-----------------------------------------------------------------------------
206+# SSH launchers
207+#-----------------------------------------------------------------------------
208+
209+# Todo
210+
211+
212+#-----------------------------------------------------------------------------
213+# Unix batch (PBS) schedulers launchers
214+#-----------------------------------------------------------------------------
215+
216+# The command line program to use to submit a PBS job.
217+# c.PBSControllerLauncher.submit_command = 'qsub'
218+
219+# The command line program to use to delete a PBS job.
220+# c.PBSControllerLauncher.delete_command = 'qdel'
221+
222+# A regular expression that takes the output of qsub and find the job id.
223+# c.PBSControllerLauncher.job_id_regexp = r'\d+'
224+
225+# The batch submission script used to start the controller. This is where
226+# environment variables would be setup, etc. This string is interpolated using
227+# the Itpl module in IPython.external. Basically, you can use ${n} for the
228+# number of engine and ${cluster_dir} for the cluster_dir.
229+# c.PBSControllerLauncher.batch_template = """"""
230+
231+# The name of the instantiated batch script that will actually be used to
232+# submit the job. This will be written to the cluster directory.
233+# c.PBSControllerLauncher.batch_file_name = u'pbs_batch_script_controller'
234+
235+
236+# The command line program to use to submit a PBS job.
237+# c.PBSEngineSetLauncher.submit_command = 'qsub'
238+
239+# The command line program to use to delete a PBS job.
240+# c.PBSEngineSetLauncher.delete_command = 'qdel'
241+
242+# A regular expression that takes the output of qsub and find the job id.
243+# c.PBSEngineSetLauncher.job_id_regexp = r'\d+'
244+
245+# The batch submission script used to start the engines. This is where
246+# environment variables would be setup, etc. This string is interpolated using
247+# the Itpl module in IPython.external. Basically, you can use ${n} for the
248+# number of engine and ${cluster_dir} for the cluster_dir.
249+# c.PBSEngineSetLauncher.batch_template = """"""
250+
251+# The name of the instantiated batch script that will actually be used to
252+# submit the job. This will be written to the cluster directory.
253+# c.PBSEngineSetLauncher.batch_file_name = u'pbs_batch_script_engines'
254+
255+#-----------------------------------------------------------------------------
256+# Windows HPC Server 2008 launcher configuration
257+#-----------------------------------------------------------------------------
258+
259+# c.IPControllerJob.job_name = 'IPController'
260+# c.IPControllerJob.is_exclusive = False
261+# c.IPControllerJob.username = r'USERDOMAIN\USERNAME'
262+# c.IPControllerJob.priority = 'Highest'
263+# c.IPControllerJob.requested_nodes = ''
264+# c.IPControllerJob.project = 'MyProject'
265+
266+# c.IPControllerTask.task_name = 'IPController'
267+# c.IPControllerTask.controller_cmd = [u'ipcontroller.exe']
268+# c.IPControllerTask.controller_args = ['--log-to-file', '--log-level', '40']
269+# c.IPControllerTask.environment_variables = {}
270+
271+# c.WindowsHPCControllerLauncher.scheduler = 'HEADNODE'
272+# c.WindowsHPCControllerLauncher.job_file_name = u'ipcontroller_job.xml'
273+
274+
275+# c.IPEngineSetJob.job_name = 'IPEngineSet'
276+# c.IPEngineSetJob.is_exclusive = False
277+# c.IPEngineSetJob.username = r'USERDOMAIN\USERNAME'
278+# c.IPEngineSetJob.priority = 'Highest'
279+# c.IPEngineSetJob.requested_nodes = ''
280+# c.IPEngineSetJob.project = 'MyProject'
281+
282+# c.IPEngineTask.task_name = 'IPEngine'
283+# c.IPEngineTask.engine_cmd = [u'ipengine.exe']
284+# c.IPEngineTask.engine_args = ['--log-to-file', '--log-level', '40']
285+# c.IPEngineTask.environment_variables = {}
286+
287+# c.WindowsHPCEngineSetLauncher.scheduler = 'HEADNODE'
288+# c.WindowsHPCEngineSetLauncher.job_file_name = u'ipengineset_job.xml'
289+
290+
291+
292+
293+
294+
295+
296
297=== added file 'IPython/config/default/ipcontroller_config.py'
298--- IPython/config/default/ipcontroller_config.py 1970-01-01 00:00:00 +0000
299+++ IPython/config/default/ipcontroller_config.py 2009-11-21 00:11:10 +0000
300@@ -0,0 +1,136 @@
301+from IPython.config.loader import Config
302+
303+c = get_config()
304+
305+#-----------------------------------------------------------------------------
306+# Global configuration
307+#-----------------------------------------------------------------------------
308+
309+# Basic Global config attributes
310+
311+# Start up messages are logged to stdout using the logging module.
312+# These all happen before the twisted reactor is started and are
313+# useful for debugging purposes. Can be (10=DEBUG,20=INFO,30=WARN,40=CRITICAL)
314+# and smaller is more verbose.
315+# c.Global.log_level = 20
316+
317+# Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
318+# c.Global.log_to_file = False
319+
320+# Remove old logs from cluster_dir/log before starting.
321+# c.Global.clean_logs = True
322+
323+# A list of Python statements that will be run before starting the
324+# controller. This is provided because occasionally certain things need to
325+# be imported in the controller for pickling to work.
326+# c.Global.import_statements = ['import math']
327+
328+# Reuse the controller's FURL files. If False, FURL files are regenerated
329+# each time the controller is run. If True, they will be reused, *but*, you
330+# also must set the network ports by hand. If set, this will override the
331+# values set for the client and engine connections below.
332+# c.Global.reuse_furls = True
333+
334+# Enable SSL encryption on all connections to the controller. If set, this
335+# will override the values set for the client and engine connections below.
336+# c.Global.secure = True
337+
338+# The working directory for the process. The application will use os.chdir
339+# to change to this directory before starting.
340+# c.Global.work_dir = os.getcwd()
341+
342+#-----------------------------------------------------------------------------
343+# Configure the client services
344+#-----------------------------------------------------------------------------
345+
346+# Basic client service config attributes
347+
348+# The network interface the controller will listen on for client connections.
349+# This should be an IP address or hostname of the controller's host. The empty
350+# string means listen on all interfaces.
351+# c.FCClientServiceFactory.ip = ''
352+
353+# The TCP/IP port the controller will listen on for client connections. If 0
354+# a random port will be used. If the controller's host has a firewall running
355+# it must allow incoming traffic on this port.
356+# c.FCClientServiceFactory.port = 0
357+
358+# The client learns how to connect to the controller by looking at the
359+# location field embedded in the FURL. If this field is empty, all network
360+# interfaces that the controller is listening on will be listed. To have the
361+# client connect on a particular interface, list it here.
362+# c.FCClientServiceFactory.location = ''
363+
364+# Use SSL encryption for the client connection.
365+# c.FCClientServiceFactory.secure = True
366+
367+# Reuse the client FURL each time the controller is started. If set, you must
368+# also pick a specific network port above (FCClientServiceFactory.port).
369+# c.FCClientServiceFactory.reuse_furls = False
370+
371+#-----------------------------------------------------------------------------
372+# Configure the engine services
373+#-----------------------------------------------------------------------------
374+
375+# Basic config attributes for the engine services.
376+
377+# The network interface the controller will listen on for engine connections.
378+# This should be an IP address or hostname of the controller's host. The empty
379+# string means listen on all interfaces.
380+# c.FCEngineServiceFactory.ip = ''
381+
382+# The TCP/IP port the controller will listen on for engine connections. If 0
383+# a random port will be used. If the controller's host has a firewall running
384+# it must allow incoming traffic on this port.
385+# c.FCEngineServiceFactory.port = 0
386+
387+# The engine learns how to connect to the controller by looking at the
388+# location field embedded in the FURL. If this field is empty, all network
389+# interfaces that the controller is listening on will be listed. To have the
390+# client connect on a particular interface, list it here.
391+# c.FCEngineServiceFactory.location = ''
392+
393+# Use SSL encryption for the engine connection.
394+# c.FCEngineServiceFactory.secure = True
395+
396+# Reuse the client FURL each time the controller is started. If set, you must
397+# also pick a specific network port above (FCClientServiceFactory.port).
398+# c.FCEngineServiceFactory.reuse_furls = False
399+
400+#-----------------------------------------------------------------------------
401+# Developer level configuration attributes
402+#-----------------------------------------------------------------------------
403+
404+# You shouldn't have to modify anything in this section. These attributes
405+# are more for developers who want to change the behavior of the controller
406+# at a fundamental level.
407+
408+# c.FCClientServiceFactory.cert_file = u'ipcontroller-client.pem'
409+
410+# default_client_interfaces = Config()
411+# default_client_interfaces.Task.interface_chain = [
412+# 'IPython.kernel.task.ITaskController',
413+# 'IPython.kernel.taskfc.IFCTaskController'
414+# ]
415+#
416+# default_client_interfaces.Task.furl_file = u'ipcontroller-tc.furl'
417+#
418+# default_client_interfaces.MultiEngine.interface_chain = [
419+# 'IPython.kernel.multiengine.IMultiEngine',
420+# 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
421+# ]
422+#
423+# default_client_interfaces.MultiEngine.furl_file = u'ipcontroller-mec.furl'
424+#
425+# c.FCEngineServiceFactory.interfaces = default_client_interfaces
426+
427+# c.FCEngineServiceFactory.cert_file = u'ipcontroller-engine.pem'
428+
429+# default_engine_interfaces = Config()
430+# default_engine_interfaces.Default.interface_chain = [
431+# 'IPython.kernel.enginefc.IFCControllerBase'
432+# ]
433+#
434+# default_engine_interfaces.Default.furl_file = u'ipcontroller-engine.furl'
435+#
436+# c.FCEngineServiceFactory.interfaces = default_engine_interfaces
437
438=== added file 'IPython/config/default/ipengine_config.py'
439--- IPython/config/default/ipengine_config.py 1970-01-01 00:00:00 +0000
440+++ IPython/config/default/ipengine_config.py 2009-11-21 00:11:10 +0000
441@@ -0,0 +1,90 @@
442+c = get_config()
443+
444+#-----------------------------------------------------------------------------
445+# Global configuration
446+#-----------------------------------------------------------------------------
447+
448+# Start up messages are logged to stdout using the logging module.
449+# These all happen before the twisted reactor is started and are
450+# useful for debugging purposes. Can be (10=DEBUG,20=INFO,30=WARN,40=CRITICAL)
451+# and smaller is more verbose.
452+# c.Global.log_level = 20
453+
454+# Log to a file in cluster_dir/log, otherwise just log to sys.stdout.
455+# c.Global.log_to_file = False
456+
457+# Remove old logs from cluster_dir/log before starting.
458+# c.Global.clean_logs = True
459+
460+# A list of strings that will be executed in the users namespace on the engine
461+# before it connects to the controller.
462+# c.Global.exec_lines = ['import numpy']
463+
464+# The engine will try to connect to the controller multiple times, to allow
465+# the controller time to startup and write its FURL file. These parameters
466+# control the number of retries (connect_max_tries) and the initial delay
467+# (connect_delay) between attemps. The actual delay between attempts gets
468+# longer each time by a factor of 1.5 (delay[i] = 1.5*delay[i-1])
469+# those attemps.
470+# c.Global.connect_delay = 0.1
471+# c.Global.connect_max_tries = 15
472+
473+# By default, the engine will look for the controller's FURL file in its own
474+# cluster directory. Sometimes, the FURL file will be elsewhere and this
475+# attribute can be set to the full path of the FURL file.
476+# c.Global.furl_file = u''
477+
478+# The working directory for the process. The application will use os.chdir
479+# to change to this directory before starting.
480+# c.Global.work_dir = os.getcwd()
481+
482+#-----------------------------------------------------------------------------
483+# MPI configuration
484+#-----------------------------------------------------------------------------
485+
486+# Upon starting the engine can be configured to call MPI_Init. This section
487+# configures that.
488+
489+# Select which MPI section to execute to setup MPI. The value of this
490+# attribute must match the name of another attribute in the MPI config
491+# section (mpi4py, pytrilinos, etc.). This can also be set by the --mpi
492+# command line option.
493+# c.MPI.use = ''
494+
495+# Initialize MPI using mpi4py. To use this, set c.MPI.use = 'mpi4py' to use
496+# --mpi=mpi4py at the command line.
497+# c.MPI.mpi4py = """from mpi4py import MPI as mpi
498+# mpi.size = mpi.COMM_WORLD.Get_size()
499+# mpi.rank = mpi.COMM_WORLD.Get_rank()
500+# """
501+
502+# Initialize MPI using pytrilinos. To use this, set c.MPI.use = 'pytrilinos'
503+# to use --mpi=pytrilinos at the command line.
504+# c.MPI.pytrilinos = """from PyTrilinos import Epetra
505+# class SimpleStruct:
506+# pass
507+# mpi = SimpleStruct()
508+# mpi.rank = 0
509+# mpi.size = 0
510+# """
511+
512+#-----------------------------------------------------------------------------
513+# Developer level configuration attributes
514+#-----------------------------------------------------------------------------
515+
516+# You shouldn't have to modify anything in this section. These attributes
517+# are more for developers who want to change the behavior of the controller
518+# at a fundamental level.
519+
520+# You should not have to change these attributes.
521+
522+# c.Global.shell_class = 'IPython.kernel.core.interpreter.Interpreter'
523+
524+# c.Global.furl_file_name = u'ipcontroller-engine.furl'
525+
526+
527+
528+
529+
530+
531+
532
533=== modified file 'IPython/config/default/ipython_config.py'
534--- IPython/config/default/ipython_config.py 2009-09-28 05:57:44 +0000
535+++ IPython/config/default/ipython_config.py 2009-11-21 00:11:10 +0000
536@@ -13,7 +13,7 @@
537
538 # Set this to determine the detail of what is logged at startup.
539 # The default is 30 and possible values are 0,10,20,30,40,50.
540-c.Global.log_level = 20
541+# c.Global.log_level = 20
542
543 # This should be a list of importable Python modules that have an
544 # load_in_ipython(ip) method. This method gets called when the extension
545@@ -35,7 +35,7 @@
546 # These files are run in IPython in the user's namespace. Files with a .py
547 # extension need to be pure Python. Files with a .ipy extension can have
548 # custom IPython syntax (like magics, etc.).
549-# These files need to be in the cwd, the ipythondir or be absolute paths.
550+# These files need to be in the cwd, the ipython_dir or be absolute paths.
551 # c.Global.exec_files = [
552 # 'mycode.py',
553 # 'fancy.ipy'
554@@ -71,9 +71,9 @@
555
556 # c.InteractiveShell.logstart = True
557
558-# c.InteractiveShell.logfile = 'ipython_log.py'
559+# c.InteractiveShell.logfile = u'ipython_log.py'
560
561-# c.InteractiveShell.logappend = 'mylog.py'
562+# c.InteractiveShell.logappend = u'mylog.py'
563
564 # c.InteractiveShell.object_info_string_level = 0
565
566
567=== modified file 'IPython/config/loader.py'
568--- IPython/config/loader.py 2009-09-15 05:54:31 +0000
569+++ IPython/config/loader.py 2009-11-21 00:11:10 +0000
570@@ -244,8 +244,14 @@
571 # with the parents.
572 def load_subconfig(fname):
573 loader = PyFileConfigLoader(fname, self.path)
574- sub_config = loader.load_config()
575- self.config._merge(sub_config)
576+ try:
577+ sub_config = loader.load_config()
578+ except IOError:
579+ # Pass silently if the sub config is not there. This happens
580+ # when a user us using a profile, but not the default config.
581+ pass
582+ else:
583+ self.config._merge(sub_config)
584
585 # Again, this needs to be a closure and should be used in config
586 # files to get the config being loaded.
587@@ -271,6 +277,7 @@
588 class NoConfigDefault(object): pass
589 NoConfigDefault = NoConfigDefault()
590
591+
592 class ArgParseConfigLoader(CommandLineConfigLoader):
593
594 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
595
596=== renamed file 'IPython/config/profile/__init_.py' => 'IPython/config/profile/__init__.py'
597=== added file 'IPython/config/profile/ipython_config_cluster.py'
598--- IPython/config/profile/ipython_config_cluster.py 1970-01-01 00:00:00 +0000
599+++ IPython/config/profile/ipython_config_cluster.py 2009-11-21 00:11:10 +0000
600@@ -0,0 +1,24 @@
601+c = get_config()
602+
603+# This can be used at any point in a config file to load a sub config
604+# and merge it into the current one.
605+load_subconfig('ipython_config.py')
606+
607+lines = """
608+from IPython.kernel.client import *
609+"""
610+
611+# You have to make sure that attributes that are containers already
612+# exist before using them. Simple assigning a new list will override
613+# all previous values.
614+if hasattr(c.Global, 'exec_lines'):
615+ c.Global.exec_lines.append(lines)
616+else:
617+ c.Global.exec_lines = [lines]
618+
619+# Load the parallelmagic extension to enable %result, %px, %autopx magics.
620+if hasattr(c.Global, 'extensions'):
621+ c.Global.extensions.append('parallelmagic')
622+else:
623+ c.Global.extensions = ['parallelmagic']
624+
625
626=== modified file 'IPython/core/application.py'
627--- IPython/core/application.py 2009-09-18 03:26:43 +0000
628+++ IPython/core/application.py 2009-11-21 00:11:10 +0000
629@@ -1,7 +1,13 @@
630 #!/usr/bin/env python
631 # encoding: utf-8
632 """
633-An application for IPython
634+An application for IPython.
635+
636+All top-level applications should use the classes in this module for
637+handling configuration and creating componenets.
638+
639+The job of an :class:`Application` is to create the master configuration
640+object and then create the components, passing the config to them.
641
642 Authors:
643
644@@ -26,10 +32,9 @@
645 import logging
646 import os
647 import sys
648-import traceback
649-from copy import deepcopy
650
651-from IPython.utils.genutils import get_ipython_dir, filefind
652+from IPython.core import release
653+from IPython.utils.genutils import get_ipython_dir
654 from IPython.config.loader import (
655 PyFileConfigLoader,
656 ArgParseConfigLoader,
657@@ -42,22 +47,27 @@
658 #-----------------------------------------------------------------------------
659
660
661-class IPythonArgParseConfigLoader(ArgParseConfigLoader):
662+class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
663 """Default command line options for IPython based applications."""
664
665 def _add_other_arguments(self):
666- self.parser.add_argument('-ipythondir',dest='Global.ipythondir',type=str,
667- help='Set to override default location of Global.ipythondir.',
668+ self.parser.add_argument('--ipython-dir',
669+ dest='Global.ipython_dir',type=unicode,
670+ help='Set to override default location of Global.ipython_dir.',
671 default=NoConfigDefault,
672- metavar='Global.ipythondir')
673- self.parser.add_argument('-p','-profile',dest='Global.profile',type=str,
674+ metavar='Global.ipython_dir')
675+ self.parser.add_argument('-p', '--profile',
676+ dest='Global.profile',type=unicode,
677 help='The string name of the ipython profile to be used.',
678 default=NoConfigDefault,
679 metavar='Global.profile')
680- self.parser.add_argument('-log_level',dest="Global.log_level",type=int,
681+ self.parser.add_argument('--log-level',
682+ dest="Global.log_level",type=int,
683 help='Set the log level (0,10,20,30,40,50). Default is 30.',
684- default=NoConfigDefault)
685- self.parser.add_argument('-config_file',dest='Global.config_file',type=str,
686+ default=NoConfigDefault,
687+ metavar='Global.log_level')
688+ self.parser.add_argument('--config-file',
689+ dest='Global.config_file',type=unicode,
690 help='Set the config file name to override default.',
691 default=NoConfigDefault,
692 metavar='Global.config_file')
693@@ -68,20 +78,24 @@
694
695
696 class Application(object):
697- """Load a config, construct an app and run it.
698- """
699+ """Load a config, construct components and set them running."""
700
701- config_file_name = 'ipython_config.py'
702- name = 'ipython'
703+ name = u'ipython'
704+ description = 'IPython: an enhanced interactive Python shell.'
705+ config_file_name = u'ipython_config.py'
706+ default_log_level = logging.WARN
707
708 def __init__(self):
709+ self._exiting = False
710 self.init_logger()
711+ # Track the default and actual separately because some messages are
712+ # only printed if we aren't using the default.
713 self.default_config_file_name = self.config_file_name
714
715 def init_logger(self):
716 self.log = logging.getLogger(self.__class__.__name__)
717 # This is used as the default until the command line arguments are read.
718- self.log.setLevel(logging.WARN)
719+ self.log.setLevel(self.default_log_level)
720 self._log_handler = logging.StreamHandler()
721 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
722 self._log_handler.setFormatter(self._log_formatter)
723@@ -98,16 +112,24 @@
724 def start(self):
725 """Start the application."""
726 self.attempt(self.create_default_config)
727+ self.log_default_config()
728+ self.set_default_config_log_level()
729 self.attempt(self.pre_load_command_line_config)
730 self.attempt(self.load_command_line_config, action='abort')
731+ self.set_command_line_config_log_level()
732 self.attempt(self.post_load_command_line_config)
733- self.attempt(self.find_ipythondir)
734+ self.log_command_line_config()
735+ self.attempt(self.find_ipython_dir)
736+ self.attempt(self.find_resources)
737 self.attempt(self.find_config_file_name)
738 self.attempt(self.find_config_file_paths)
739 self.attempt(self.pre_load_file_config)
740 self.attempt(self.load_file_config)
741+ self.set_file_config_log_level()
742 self.attempt(self.post_load_file_config)
743+ self.log_file_config()
744 self.attempt(self.merge_configs)
745+ self.log_master_config()
746 self.attempt(self.pre_construct)
747 self.attempt(self.construct)
748 self.attempt(self.post_construct)
749@@ -127,65 +149,89 @@
750 don't belong to a particular component.
751 """
752 self.default_config = Config()
753- self.default_config.Global.ipythondir = get_ipython_dir()
754+ self.default_config.Global.ipython_dir = get_ipython_dir()
755+ self.default_config.Global.log_level = self.log_level
756+
757+ def log_default_config(self):
758 self.log.debug('Default config loaded:')
759 self.log.debug(repr(self.default_config))
760
761+ def set_default_config_log_level(self):
762+ try:
763+ self.log_level = self.default_config.Global.log_level
764+ except AttributeError:
765+ # Fallback to the default_log_level class attribute
766+ pass
767+
768 def create_command_line_config(self):
769 """Create and return a command line config loader."""
770- return IPythonArgParseConfigLoader(description=self.name)
771+ return BaseAppArgParseConfigLoader(
772+ description=self.description,
773+ version=release.version
774+ )
775
776 def pre_load_command_line_config(self):
777 """Do actions just before loading the command line config."""
778 pass
779
780 def load_command_line_config(self):
781- """Load the command line config.
782-
783- This method also sets ``self.debug``.
784- """
785-
786+ """Load the command line config."""
787 loader = self.create_command_line_config()
788 self.command_line_config = loader.load_config()
789 self.extra_args = loader.get_extra_args()
790
791+ def set_command_line_config_log_level(self):
792 try:
793 self.log_level = self.command_line_config.Global.log_level
794 except AttributeError:
795- pass # Use existing value which is set in Application.init_logger.
796+ pass
797+
798+ def post_load_command_line_config(self):
799+ """Do actions just after loading the command line config."""
800+ pass
801+
802+ def log_command_line_config(self):
803 self.log.debug("Command line config loaded:")
804 self.log.debug(repr(self.command_line_config))
805
806- def post_load_command_line_config(self):
807- """Do actions just after loading the command line config."""
808- pass
809-
810- def find_ipythondir(self):
811+ def find_ipython_dir(self):
812 """Set the IPython directory.
813
814- This sets ``self.ipythondir``, but the actual value that is passed
815+ This sets ``self.ipython_dir``, but the actual value that is passed
816 to the application is kept in either ``self.default_config`` or
817- ``self.command_line_config``. This also added ``self.ipythondir`` to
818+ ``self.command_line_config``. This also adds ``self.ipython_dir`` to
819 ``sys.path`` so config files there can be references by other config
820 files.
821 """
822
823 try:
824- self.ipythondir = self.command_line_config.Global.ipythondir
825+ self.ipython_dir = self.command_line_config.Global.ipython_dir
826 except AttributeError:
827- self.ipythondir = self.default_config.Global.ipythondir
828- sys.path.append(os.path.abspath(self.ipythondir))
829- if not os.path.isdir(self.ipythondir):
830- os.makedirs(self.ipythondir, mode = 0777)
831- self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
832+ self.ipython_dir = self.default_config.Global.ipython_dir
833+ sys.path.append(os.path.abspath(self.ipython_dir))
834+ if not os.path.isdir(self.ipython_dir):
835+ os.makedirs(self.ipython_dir, mode=0777)
836+ self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
837+
838+ def find_resources(self):
839+ """Find other resources that need to be in place.
840+
841+ Things like cluster directories need to be in place to find the
842+ config file. These happen right after the IPython directory has
843+ been set.
844+ """
845+ pass
846
847 def find_config_file_name(self):
848 """Find the config file name for this application.
849
850+ This must set ``self.config_file_name`` to the filename of the
851+ config file to use (just the filename). The search paths for the
852+ config file are set in :meth:`find_config_file_paths` and then passed
853+ to the config file loader where they are resolved to an absolute path.
854+
855 If a profile has been set at the command line, this will resolve
856- it. The search paths for the config file are set in
857- :meth:`find_config_file_paths` and then passed to the config file
858- loader where they are resolved to an absolute path.
859+ it.
860 """
861
862 try:
863@@ -196,14 +242,18 @@
864 try:
865 self.profile_name = self.command_line_config.Global.profile
866 name_parts = self.config_file_name.split('.')
867- name_parts.insert(1, '_' + self.profile_name + '.')
868+ name_parts.insert(1, u'_' + self.profile_name + u'.')
869 self.config_file_name = ''.join(name_parts)
870 except AttributeError:
871 pass
872
873 def find_config_file_paths(self):
874- """Set the search paths for resolving the config file."""
875- self.config_file_paths = (os.getcwd(), self.ipythondir)
876+ """Set the search paths for resolving the config file.
877+
878+ This must set ``self.config_file_paths`` to a sequence of search
879+ paths to pass to the config file loader.
880+ """
881+ self.config_file_paths = (os.getcwd(), self.ipython_dir)
882
883 def pre_load_file_config(self):
884 """Do actions before the config file is loaded."""
885@@ -216,7 +266,7 @@
886 ``CONFIG_FILE`` config variable is set to the resolved config file
887 location. If not successful, an empty config is used.
888 """
889- self.log.debug("Attempting to load config file: <%s>" % self.config_file_name)
890+ self.log.debug("Attempting to load config file: %s" % self.config_file_name)
891 loader = PyFileConfigLoader(self.config_file_name,
892 path=self.config_file_paths)
893 try:
894@@ -225,19 +275,18 @@
895 except IOError:
896 # Only warn if the default config file was NOT being used.
897 if not self.config_file_name==self.default_config_file_name:
898- self.log.warn("Config file not found, skipping: <%s>" % \
899+ self.log.warn("Config file not found, skipping: %s" % \
900 self.config_file_name, exc_info=True)
901 self.file_config = Config()
902 except:
903- self.log.warn("Error loading config file: <%s>" % \
904- self.config_file_name, exc_info=True)
905+ self.log.warn("Error loading config file: %s" % \
906+ self.config_file_name, exc_info=True)
907 self.file_config = Config()
908- else:
909- self.log.debug("Config file loaded: <%s>" % loader.full_filename)
910- self.log.debug(repr(self.file_config))
911+
912+ def set_file_config_log_level(self):
913 # We need to keeep self.log_level updated. But we only use the value
914 # of the file_config if a value was not specified at the command
915- # line.
916+ # line, because the command line overrides everything.
917 if not hasattr(self.command_line_config.Global, 'log_level'):
918 try:
919 self.log_level = self.file_config.Global.log_level
920@@ -248,6 +297,11 @@
921 """Do actions after the config file is loaded."""
922 pass
923
924+ def log_file_config(self):
925+ if hasattr(self.file_config.Global, 'config_file'):
926+ self.log.debug("Config file loaded: %s" % self.file_config.Global.config_file)
927+ self.log.debug(repr(self.file_config))
928+
929 def merge_configs(self):
930 """Merge the default, command line and file config objects."""
931 config = Config()
932@@ -255,6 +309,8 @@
933 config._merge(self.file_config)
934 config._merge(self.command_line_config)
935 self.master_config = config
936+
937+ def log_master_config(self):
938 self.log.debug("Master config created:")
939 self.log.debug(repr(self.master_config))
940
941@@ -280,21 +336,29 @@
942
943 def abort(self):
944 """Abort the starting of the application."""
945- self.log.critical("Aborting application: %s" % self.name, exc_info=True)
946- sys.exit(1)
947+ if self._exiting:
948+ pass
949+ else:
950+ self.log.critical("Aborting application: %s" % self.name, exc_info=True)
951+ self._exiting = True
952+ sys.exit(1)
953
954- def exit(self):
955- self.log.critical("Aborting application: %s" % self.name)
956- sys.exit(1)
957+ def exit(self, exit_status=0):
958+ if self._exiting:
959+ pass
960+ else:
961+ self.log.debug("Exiting application: %s" % self.name)
962+ self._exiting = True
963+ sys.exit(exit_status)
964
965 def attempt(self, func, action='abort'):
966 try:
967 func()
968 except SystemExit:
969- self.exit()
970+ raise
971 except:
972 if action == 'abort':
973 self.abort()
974 elif action == 'exit':
975- self.exit()
976-
977\ No newline at end of file
978+ self.exit(0)
979+
980
981=== modified file 'IPython/core/builtin_trap.py'
982--- IPython/core/builtin_trap.py 2009-10-09 22:54:40 +0000
983+++ IPython/core/builtin_trap.py 2009-11-21 00:11:10 +0000
984@@ -86,6 +86,7 @@
985 """Store ipython references in the __builtin__ namespace."""
986 self.add_builtin('exit', Quitter(self.shell, 'exit'))
987 self.add_builtin('quit', Quitter(self.shell, 'quit'))
988+ self.add_builtin('get_ipython', self.shell.get_ipython)
989
990 # Recursive reload function
991 try:
992
993=== modified file 'IPython/core/component.py'
994--- IPython/core/component.py 2009-09-28 05:57:44 +0000
995+++ IPython/core/component.py 2009-11-21 00:11:11 +0000
996@@ -237,14 +237,20 @@
997 self.config = config
998 # We used to deepcopy, but for now we are trying to just save
999 # by reference. This *could* have side effects as all components
1000- # will share config.
1001+ # will share config. In fact, I did find such a side effect in
1002+ # _config_changed below. If a config attribute value was a mutable type
1003+ # all instances of a component were getting the same copy, effectively
1004+ # making that a class attribute.
1005 # self.config = deepcopy(config)
1006 else:
1007 if self.parent is not None:
1008 self.config = self.parent.config
1009 # We used to deepcopy, but for now we are trying to just save
1010 # by reference. This *could* have side effects as all components
1011- # will share config.
1012+ # will share config. In fact, I did find such a side effect in
1013+ # _config_changed below. If a config attribute value was a mutable type
1014+ # all instances of a component were getting the same copy, effectively
1015+ # making that a class attribute.
1016 # self.config = deepcopy(self.parent.config)
1017
1018 self.created = datetime.datetime.now()
1019@@ -296,14 +302,29 @@
1020 if new._has_section(sname):
1021 my_config = new[sname]
1022 for k, v in traitlets.items():
1023+ # Don't allow traitlets with config=True to start with
1024+ # uppercase. Otherwise, they are confused with Config
1025+ # subsections. But, developers shouldn't have uppercase
1026+ # attributes anyways! (PEP 6)
1027+ if k[0].upper()==k[0] and not k.startswith('_'):
1028+ raise ComponentError('Component traitlets with '
1029+ 'config=True must start with a lowercase so they are '
1030+ 'not confused with Config subsections: %s.%s' % \
1031+ (self.__class__.__name__, k))
1032 try:
1033+ # Here we grab the value from the config
1034+ # If k has the naming convention of a config
1035+ # section, it will be auto created.
1036 config_value = my_config[k]
1037 except KeyError:
1038 pass
1039 else:
1040 # print "Setting %s.%s from %s.%s=%r" % \
1041 # (self.__class__.__name__,k,sname,k,config_value)
1042- setattr(self, k, config_value)
1043+ # We have to do a deepcopy here if we don't deepcopy the entire
1044+ # config object. If we don't, a mutable config_value will be
1045+ # shared by all instances, effectively making it a class attribute.
1046+ setattr(self, k, deepcopy(config_value))
1047
1048 @property
1049 def children(self):
1050
1051=== modified file 'IPython/core/crashhandler.py'
1052--- IPython/core/crashhandler.py 2009-08-26 20:54:07 +0000
1053+++ IPython/core/crashhandler.py 2009-11-21 00:11:11 +0000
1054@@ -124,7 +124,7 @@
1055 #color_scheme = 'Linux' # dbg
1056
1057 try:
1058- rptdir = self.IP.config.IPYTHONDIR
1059+ rptdir = self.IP.ipython_dir
1060 except:
1061 rptdir = os.getcwd()
1062 if not os.path.isdir(rptdir):
1063
1064=== modified file 'IPython/core/debugger.py'
1065--- IPython/core/debugger.py 2009-08-19 21:56:41 +0000
1066+++ IPython/core/debugger.py 2009-11-21 00:11:11 +0000
1067@@ -70,6 +70,7 @@
1068 def BdbQuit_IPython_excepthook(self,et,ev,tb):
1069 print 'Exiting Debugger.'
1070
1071+
1072 class Tracer(object):
1073 """Class for local debugging, similar to pdb.set_trace.
1074
1075@@ -105,12 +106,10 @@
1076 from the Python standard library for usage details.
1077 """
1078
1079- global __IPYTHON__
1080 try:
1081- __IPYTHON__
1082- except NameError:
1083+ ip = ipapi.get()
1084+ except:
1085 # Outside of ipython, we set our own exception hook manually
1086- __IPYTHON__ = ipapi.get()
1087 BdbQuit_excepthook.excepthook_ori = sys.excepthook
1088 sys.excepthook = BdbQuit_excepthook
1089 def_colors = 'NoColor'
1090@@ -122,9 +121,8 @@
1091 pass
1092 else:
1093 # In ipython, we use its custom exception handler mechanism
1094- ip = ipapi.get()
1095 def_colors = ip.colors
1096- ip.set_custom_exc((bdb.BdbQuit,),BdbQuit_IPython_excepthook)
1097+ ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
1098
1099 if colors is None:
1100 colors = def_colors
1101@@ -138,6 +136,7 @@
1102
1103 self.debugger.set_trace(sys._getframe().f_back)
1104
1105+
1106 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
1107 """Make new_fn have old_fn's doc string. This is particularly useful
1108 for the do_... commands that hook into the help system.
1109@@ -149,6 +148,7 @@
1110 wrapper.__doc__ = old_fn.__doc__ + additional_text
1111 return wrapper
1112
1113+
1114 def _file_lines(fname):
1115 """Return the contents of a named file as a list of lines.
1116
1117@@ -164,143 +164,98 @@
1118 outfile.close()
1119 return out
1120
1121+
1122 class Pdb(OldPdb):
1123 """Modified Pdb class, does not load readline."""
1124
1125- if sys.version[:3] >= '2.5' or has_pydb:
1126- def __init__(self,color_scheme='NoColor',completekey=None,
1127- stdin=None, stdout=None):
1128+ def __init__(self,color_scheme='NoColor',completekey=None,
1129+ stdin=None, stdout=None):
1130
1131- # Parent constructor:
1132- if has_pydb and completekey is None:
1133- OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
1134- else:
1135- OldPdb.__init__(self,completekey,stdin,stdout)
1136-
1137- self.prompt = prompt # The default prompt is '(Pdb)'
1138+ # Parent constructor:
1139+ if has_pydb and completekey is None:
1140+ OldPdb.__init__(self,stdin=stdin,stdout=Term.cout)
1141+ else:
1142+ OldPdb.__init__(self,completekey,stdin,stdout)
1143
1144- # IPython changes...
1145- self.is_pydb = has_pydb
1146-
1147- if self.is_pydb:
1148-
1149- # iplib.py's ipalias seems to want pdb's checkline
1150- # which located in pydb.fn
1151- import pydb.fns
1152- self.checkline = lambda filename, lineno: \
1153- pydb.fns.checkline(self, filename, lineno)
1154-
1155- self.curframe = None
1156- self.do_restart = self.new_do_restart
1157-
1158- self.old_all_completions = __IPYTHON__.Completer.all_completions
1159- __IPYTHON__.Completer.all_completions=self.all_completions
1160-
1161- self.do_list = decorate_fn_with_doc(self.list_command_pydb,
1162- OldPdb.do_list)
1163- self.do_l = self.do_list
1164- self.do_frame = decorate_fn_with_doc(self.new_do_frame,
1165- OldPdb.do_frame)
1166-
1167- self.aliases = {}
1168-
1169- # Create color table: we copy the default one from the traceback
1170- # module and add a few attributes needed for debugging
1171- self.color_scheme_table = exception_colors()
1172-
1173- # shorthands
1174- C = coloransi.TermColors
1175- cst = self.color_scheme_table
1176-
1177- cst['NoColor'].colors.breakpoint_enabled = C.NoColor
1178- cst['NoColor'].colors.breakpoint_disabled = C.NoColor
1179-
1180- cst['Linux'].colors.breakpoint_enabled = C.LightRed
1181- cst['Linux'].colors.breakpoint_disabled = C.Red
1182-
1183- cst['LightBG'].colors.breakpoint_enabled = C.LightRed
1184- cst['LightBG'].colors.breakpoint_disabled = C.Red
1185-
1186- self.set_colors(color_scheme)
1187-
1188- # Add a python parser so we can syntax highlight source while
1189- # debugging.
1190- self.parser = PyColorize.Parser()
1191-
1192-
1193- else:
1194- # Ugly hack: for Python 2.3-2.4, we can't call the parent constructor,
1195- # because it binds readline and breaks tab-completion. This means we
1196- # have to COPY the constructor here.
1197- def __init__(self,color_scheme='NoColor'):
1198- bdb.Bdb.__init__(self)
1199- cmd.Cmd.__init__(self,completekey=None) # don't load readline
1200- self.prompt = 'ipdb> ' # The default prompt is '(Pdb)'
1201- self.aliases = {}
1202-
1203- # These two lines are part of the py2.4 constructor, let's put them
1204- # unconditionally here as they won't cause any problems in 2.3.
1205- self.mainpyfile = ''
1206- self._wait_for_mainpyfile = 0
1207-
1208- # Read $HOME/.pdbrc and ./.pdbrc
1209- try:
1210- self.rcLines = _file_lines(os.path.join(os.environ['HOME'],
1211- ".pdbrc"))
1212- except KeyError:
1213- self.rcLines = []
1214- self.rcLines.extend(_file_lines(".pdbrc"))
1215-
1216- # Create color table: we copy the default one from the traceback
1217- # module and add a few attributes needed for debugging
1218- self.color_scheme_table = exception_colors()
1219-
1220- # shorthands
1221- C = coloransi.TermColors
1222- cst = self.color_scheme_table
1223-
1224- cst['NoColor'].colors.breakpoint_enabled = C.NoColor
1225- cst['NoColor'].colors.breakpoint_disabled = C.NoColor
1226-
1227- cst['Linux'].colors.breakpoint_enabled = C.LightRed
1228- cst['Linux'].colors.breakpoint_disabled = C.Red
1229-
1230- cst['LightBG'].colors.breakpoint_enabled = C.LightRed
1231- cst['LightBG'].colors.breakpoint_disabled = C.Red
1232-
1233- self.set_colors(color_scheme)
1234-
1235- # Add a python parser so we can syntax highlight source while
1236- # debugging.
1237- self.parser = PyColorize.Parser()
1238+ self.prompt = prompt # The default prompt is '(Pdb)'
1239
1240+ # IPython changes...
1241+ self.is_pydb = has_pydb
1242+
1243+ self.shell = ipapi.get()
1244+
1245+ if self.is_pydb:
1246+
1247+ # iplib.py's ipalias seems to want pdb's checkline
1248+ # which located in pydb.fn
1249+ import pydb.fns
1250+ self.checkline = lambda filename, lineno: \
1251+ pydb.fns.checkline(self, filename, lineno)
1252+
1253+ self.curframe = None
1254+ self.do_restart = self.new_do_restart
1255+
1256+ self.old_all_completions = self.shell.Completer.all_completions
1257+ self.shell.Completer.all_completions=self.all_completions
1258+
1259+ self.do_list = decorate_fn_with_doc(self.list_command_pydb,
1260+ OldPdb.do_list)
1261+ self.do_l = self.do_list
1262+ self.do_frame = decorate_fn_with_doc(self.new_do_frame,
1263+ OldPdb.do_frame)
1264+
1265+ self.aliases = {}
1266+
1267+ # Create color table: we copy the default one from the traceback
1268+ # module and add a few attributes needed for debugging
1269+ self.color_scheme_table = exception_colors()
1270+
1271+ # shorthands
1272+ C = coloransi.TermColors
1273+ cst = self.color_scheme_table
1274+
1275+ cst['NoColor'].colors.breakpoint_enabled = C.NoColor
1276+ cst['NoColor'].colors.breakpoint_disabled = C.NoColor
1277+
1278+ cst['Linux'].colors.breakpoint_enabled = C.LightRed
1279+ cst['Linux'].colors.breakpoint_disabled = C.Red
1280+
1281+ cst['LightBG'].colors.breakpoint_enabled = C.LightRed
1282+ cst['LightBG'].colors.breakpoint_disabled = C.Red
1283+
1284+ self.set_colors(color_scheme)
1285+
1286+ # Add a python parser so we can syntax highlight source while
1287+ # debugging.
1288+ self.parser = PyColorize.Parser()
1289+
1290 def set_colors(self, scheme):
1291 """Shorthand access to the color table scheme selector method."""
1292 self.color_scheme_table.set_active_scheme(scheme)
1293
1294 def interaction(self, frame, traceback):
1295- __IPYTHON__.set_completer_frame(frame)
1296+ self.shell.set_completer_frame(frame)
1297 OldPdb.interaction(self, frame, traceback)
1298
1299 def new_do_up(self, arg):
1300 OldPdb.do_up(self, arg)
1301- __IPYTHON__.set_completer_frame(self.curframe)
1302+ self.shell.set_completer_frame(self.curframe)
1303 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
1304
1305 def new_do_down(self, arg):
1306 OldPdb.do_down(self, arg)
1307- __IPYTHON__.set_completer_frame(self.curframe)
1308+ self.shell.set_completer_frame(self.curframe)
1309
1310 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
1311
1312 def new_do_frame(self, arg):
1313 OldPdb.do_frame(self, arg)
1314- __IPYTHON__.set_completer_frame(self.curframe)
1315+ self.shell.set_completer_frame(self.curframe)
1316
1317 def new_do_quit(self, arg):
1318
1319 if hasattr(self, 'old_all_completions'):
1320- __IPYTHON__.Completer.all_completions=self.old_all_completions
1321+ self.shell.Completer.all_completions=self.old_all_completions
1322
1323
1324 return OldPdb.do_quit(self, arg)
1325@@ -314,7 +269,7 @@
1326 return self.do_quit(arg)
1327
1328 def postloop(self):
1329- __IPYTHON__.set_completer_frame(None)
1330+ self.shell.set_completer_frame(None)
1331
1332 def print_stack_trace(self):
1333 try:
1334@@ -331,7 +286,7 @@
1335 # vds: >>
1336 frame, lineno = frame_lineno
1337 filename = frame.f_code.co_filename
1338- __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
1339+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
1340 # vds: <<
1341
1342 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
1343@@ -500,7 +455,7 @@
1344 # vds: >>
1345 lineno = first
1346 filename = self.curframe.f_code.co_filename
1347- __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
1348+ self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
1349 # vds: <<
1350
1351 do_l = do_list
1352@@ -509,16 +464,16 @@
1353 """The debugger interface to magic_pdef"""
1354 namespaces = [('Locals', self.curframe.f_locals),
1355 ('Globals', self.curframe.f_globals)]
1356- __IPYTHON__.magic_pdef(arg, namespaces=namespaces)
1357+ self.shell.magic_pdef(arg, namespaces=namespaces)
1358
1359 def do_pdoc(self, arg):
1360 """The debugger interface to magic_pdoc"""
1361 namespaces = [('Locals', self.curframe.f_locals),
1362 ('Globals', self.curframe.f_globals)]
1363- __IPYTHON__.magic_pdoc(arg, namespaces=namespaces)
1364+ self.shell.magic_pdoc(arg, namespaces=namespaces)
1365
1366 def do_pinfo(self, arg):
1367 """The debugger equivalant of ?obj"""
1368 namespaces = [('Locals', self.curframe.f_locals),
1369 ('Globals', self.curframe.f_globals)]
1370- __IPYTHON__.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
1371+ self.shell.magic_pinfo("pinfo %s" % arg, namespaces=namespaces)
1372
1373=== modified file 'IPython/core/display_trap.py'
1374--- IPython/core/display_trap.py 2009-10-09 22:54:40 +0000
1375+++ IPython/core/display_trap.py 2009-11-21 00:11:11 +0000
1376@@ -46,11 +46,11 @@
1377 # Only turn off the trap when the outermost call to __exit__ is made.
1378 self._nested_level = 0
1379
1380- @auto_attr
1381- def shell(self):
1382- return Component.get_instances(
1383- root=self.root,
1384- klass='IPython.core.iplib.InteractiveShell')[0]
1385+ # @auto_attr
1386+ # def shell(self):
1387+ # return Component.get_instances(
1388+ # root=self.root,
1389+ # klass='IPython.core.iplib.InteractiveShell')[0]
1390
1391 def __enter__(self):
1392 if self._nested_level == 0:
1393
1394=== modified file 'IPython/core/embed.py'
1395--- IPython/core/embed.py 2009-09-13 22:06:04 +0000
1396+++ IPython/core/embed.py 2009-11-21 00:11:11 +0000
1397@@ -68,7 +68,7 @@
1398 # is True by default.
1399 display_banner = CBool(True)
1400
1401- def __init__(self, parent=None, config=None, ipythondir=None, usage=None,
1402+ def __init__(self, parent=None, config=None, ipython_dir=None, usage=None,
1403 user_ns=None, user_global_ns=None,
1404 banner1=None, banner2=None, display_banner=None,
1405 custom_exceptions=((),None), exit_msg=''):
1406@@ -76,7 +76,7 @@
1407 self.save_sys_ipcompleter()
1408
1409 super(InteractiveShellEmbed,self).__init__(
1410- parent=parent, config=config, ipythondir=ipythondir, usage=usage,
1411+ parent=parent, config=config, ipython_dir=ipython_dir, usage=usage,
1412 user_ns=user_ns, user_global_ns=user_global_ns,
1413 banner1=banner1, banner2=banner2, display_banner=display_banner,
1414 custom_exceptions=custom_exceptions)
1415@@ -233,14 +233,6 @@
1416 for var in local_varnames:
1417 delvar(var,None)
1418
1419- def set_completer_frame(self, frame=None):
1420- if frame:
1421- self.Completer.namespace = frame.f_locals
1422- self.Completer.global_namespace = frame.f_globals
1423- else:
1424- self.Completer.namespace = self.user_ns
1425- self.Completer.global_namespace = self.user_global_ns
1426-
1427
1428 _embedded_shell = None
1429
1430
1431=== modified file 'IPython/core/ipapi.py'
1432--- IPython/core/ipapi.py 2009-09-18 02:59:36 +0000
1433+++ IPython/core/ipapi.py 2009-11-21 00:11:11 +0000
1434@@ -18,16 +18,19 @@
1435 # Imports
1436 #-----------------------------------------------------------------------------
1437
1438-from IPython.core.error import TryNext, UsageError
1439+from IPython.core.error import TryNext, UsageError, IPythonCoreError
1440
1441 #-----------------------------------------------------------------------------
1442 # Classes and functions
1443 #-----------------------------------------------------------------------------
1444
1445+
1446 def get():
1447 """Get the most recently created InteractiveShell instance."""
1448 from IPython.core.iplib import InteractiveShell
1449 insts = InteractiveShell.get_instances()
1450+ if len(insts)==0:
1451+ return None
1452 most_recent = insts[0]
1453 for inst in insts[1:]:
1454 if inst.created > most_recent.created:
1455
1456=== modified file 'IPython/core/ipapp.py'
1457--- IPython/core/ipapp.py 2009-09-18 03:26:43 +0000
1458+++ IPython/core/ipapp.py 2009-11-21 00:11:11 +0000
1459@@ -1,7 +1,8 @@
1460 #!/usr/bin/env python
1461 # encoding: utf-8
1462 """
1463-The main IPython application object
1464+The :class:`~IPython.core.application.Application` object for the command
1465+line :command:`ipython` program.
1466
1467 Authors:
1468
1469@@ -28,19 +29,17 @@
1470 import sys
1471 import warnings
1472
1473-from IPython.core.application import Application, IPythonArgParseConfigLoader
1474+from IPython.core.application import Application, BaseAppArgParseConfigLoader
1475 from IPython.core import release
1476 from IPython.core.iplib import InteractiveShell
1477 from IPython.config.loader import (
1478 NoConfigDefault,
1479 Config,
1480- ConfigError,
1481 PyFileConfigLoader
1482 )
1483
1484 from IPython.lib import inputhook
1485
1486-from IPython.utils.ipstruct import Struct
1487 from IPython.utils.genutils import filefind, get_ipython_dir
1488
1489 #-----------------------------------------------------------------------------
1490@@ -80,181 +79,181 @@
1491 #-----------------------------------------------------------------------------
1492
1493 cl_args = (
1494- (('-autocall',), dict(
1495+ (('--autocall',), dict(
1496 type=int, dest='InteractiveShell.autocall', default=NoConfigDefault,
1497 help='Set the autocall value (0,1,2).',
1498 metavar='InteractiveShell.autocall')
1499 ),
1500- (('-autoindent',), dict(
1501+ (('--autoindent',), dict(
1502 action='store_true', dest='InteractiveShell.autoindent', default=NoConfigDefault,
1503 help='Turn on autoindenting.')
1504 ),
1505- (('-noautoindent',), dict(
1506+ (('--no-autoindent',), dict(
1507 action='store_false', dest='InteractiveShell.autoindent', default=NoConfigDefault,
1508 help='Turn off autoindenting.')
1509 ),
1510- (('-automagic',), dict(
1511+ (('--automagic',), dict(
1512 action='store_true', dest='InteractiveShell.automagic', default=NoConfigDefault,
1513 help='Turn on the auto calling of magic commands.')
1514 ),
1515- (('-noautomagic',), dict(
1516+ (('--no-automagic',), dict(
1517 action='store_false', dest='InteractiveShell.automagic', default=NoConfigDefault,
1518 help='Turn off the auto calling of magic commands.')
1519 ),
1520- (('-autoedit_syntax',), dict(
1521+ (('--autoedit-syntax',), dict(
1522 action='store_true', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
1523 help='Turn on auto editing of files with syntax errors.')
1524 ),
1525- (('-noautoedit_syntax',), dict(
1526+ (('--no-autoedit-syntax',), dict(
1527 action='store_false', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
1528 help='Turn off auto editing of files with syntax errors.')
1529 ),
1530- (('-banner',), dict(
1531+ (('--banner',), dict(
1532 action='store_true', dest='Global.display_banner', default=NoConfigDefault,
1533 help='Display a banner upon starting IPython.')
1534 ),
1535- (('-nobanner',), dict(
1536+ (('--no-banner',), dict(
1537 action='store_false', dest='Global.display_banner', default=NoConfigDefault,
1538 help="Don't display a banner upon starting IPython.")
1539 ),
1540- (('-cache_size',), dict(
1541+ (('--cache-size',), dict(
1542 type=int, dest='InteractiveShell.cache_size', default=NoConfigDefault,
1543 help="Set the size of the output cache.",
1544 metavar='InteractiveShell.cache_size')
1545 ),
1546- (('-classic',), dict(
1547+ (('--classic',), dict(
1548 action='store_true', dest='Global.classic', default=NoConfigDefault,
1549 help="Gives IPython a similar feel to the classic Python prompt.")
1550 ),
1551- (('-colors',), dict(
1552+ (('--colors',), dict(
1553 type=str, dest='InteractiveShell.colors', default=NoConfigDefault,
1554 help="Set the color scheme (NoColor, Linux, and LightBG).",
1555 metavar='InteractiveShell.colors')
1556 ),
1557- (('-color_info',), dict(
1558+ (('--color-info',), dict(
1559 action='store_true', dest='InteractiveShell.color_info', default=NoConfigDefault,
1560 help="Enable using colors for info related things.")
1561 ),
1562- (('-nocolor_info',), dict(
1563+ (('--no-color-info',), dict(
1564 action='store_false', dest='InteractiveShell.color_info', default=NoConfigDefault,
1565 help="Disable using colors for info related things.")
1566 ),
1567- (('-confirm_exit',), dict(
1568+ (('--confirm-exit',), dict(
1569 action='store_true', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
1570 help="Prompt the user when existing.")
1571 ),
1572- (('-noconfirm_exit',), dict(
1573+ (('--no-confirm-exit',), dict(
1574 action='store_false', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
1575 help="Don't prompt the user when existing.")
1576 ),
1577- (('-deep_reload',), dict(
1578+ (('--deep-reload',), dict(
1579 action='store_true', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
1580 help="Enable deep (recursive) reloading by default.")
1581 ),
1582- (('-nodeep_reload',), dict(
1583+ (('--no-deep-reload',), dict(
1584 action='store_false', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
1585 help="Disable deep (recursive) reloading by default.")
1586 ),
1587- (('-editor',), dict(
1588+ (('--editor',), dict(
1589 type=str, dest='InteractiveShell.editor', default=NoConfigDefault,
1590 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
1591 metavar='InteractiveShell.editor')
1592 ),
1593- (('-log','-l'), dict(
1594+ (('--log','-l'), dict(
1595 action='store_true', dest='InteractiveShell.logstart', default=NoConfigDefault,
1596 help="Start logging to the default file (./ipython_log.py).")
1597 ),
1598- (('-logfile','-lf'), dict(
1599- type=str, dest='InteractiveShell.logfile', default=NoConfigDefault,
1600+ (('--logfile','-lf'), dict(
1601+ type=unicode, dest='InteractiveShell.logfile', default=NoConfigDefault,
1602 help="Start logging to logfile.",
1603 metavar='InteractiveShell.logfile')
1604 ),
1605- (('-logappend','-la'), dict(
1606- type=str, dest='InteractiveShell.logappend', default=NoConfigDefault,
1607- help="Start logging to logappend in append mode.",
1608+ (('--log-append','-la'), dict(
1609+ type=unicode, dest='InteractiveShell.logappend', default=NoConfigDefault,
1610+ help="Start logging to the give file in append mode.",
1611 metavar='InteractiveShell.logfile')
1612 ),
1613- (('-pdb',), dict(
1614+ (('--pdb',), dict(
1615 action='store_true', dest='InteractiveShell.pdb', default=NoConfigDefault,
1616 help="Enable auto calling the pdb debugger after every exception.")
1617 ),
1618- (('-nopdb',), dict(
1619+ (('--no-pdb',), dict(
1620 action='store_false', dest='InteractiveShell.pdb', default=NoConfigDefault,
1621 help="Disable auto calling the pdb debugger after every exception.")
1622 ),
1623- (('-pprint',), dict(
1624+ (('--pprint',), dict(
1625 action='store_true', dest='InteractiveShell.pprint', default=NoConfigDefault,
1626 help="Enable auto pretty printing of results.")
1627 ),
1628- (('-nopprint',), dict(
1629+ (('--no-pprint',), dict(
1630 action='store_false', dest='InteractiveShell.pprint', default=NoConfigDefault,
1631 help="Disable auto auto pretty printing of results.")
1632 ),
1633- (('-prompt_in1','-pi1'), dict(
1634+ (('--prompt-in1','-pi1'), dict(
1635 type=str, dest='InteractiveShell.prompt_in1', default=NoConfigDefault,
1636 help="Set the main input prompt ('In [\#]: ')",
1637 metavar='InteractiveShell.prompt_in1')
1638 ),
1639- (('-prompt_in2','-pi2'), dict(
1640+ (('--prompt-in2','-pi2'), dict(
1641 type=str, dest='InteractiveShell.prompt_in2', default=NoConfigDefault,
1642 help="Set the secondary input prompt (' .\D.: ')",
1643 metavar='InteractiveShell.prompt_in2')
1644 ),
1645- (('-prompt_out','-po'), dict(
1646+ (('--prompt-out','-po'), dict(
1647 type=str, dest='InteractiveShell.prompt_out', default=NoConfigDefault,
1648 help="Set the output prompt ('Out[\#]:')",
1649 metavar='InteractiveShell.prompt_out')
1650 ),
1651- (('-quick',), dict(
1652+ (('--quick',), dict(
1653 action='store_true', dest='Global.quick', default=NoConfigDefault,
1654 help="Enable quick startup with no config files.")
1655 ),
1656- (('-readline',), dict(
1657+ (('--readline',), dict(
1658 action='store_true', dest='InteractiveShell.readline_use', default=NoConfigDefault,
1659 help="Enable readline for command line usage.")
1660 ),
1661- (('-noreadline',), dict(
1662+ (('--no-readline',), dict(
1663 action='store_false', dest='InteractiveShell.readline_use', default=NoConfigDefault,
1664 help="Disable readline for command line usage.")
1665 ),
1666- (('-screen_length','-sl'), dict(
1667+ (('--screen-length','-sl'), dict(
1668 type=int, dest='InteractiveShell.screen_length', default=NoConfigDefault,
1669 help='Number of lines on screen, used to control printing of long strings.',
1670 metavar='InteractiveShell.screen_length')
1671 ),
1672- (('-separate_in','-si'), dict(
1673+ (('--separate-in','-si'), dict(
1674 type=str, dest='InteractiveShell.separate_in', default=NoConfigDefault,
1675 help="Separator before input prompts. Default '\n'.",
1676 metavar='InteractiveShell.separate_in')
1677 ),
1678- (('-separate_out','-so'), dict(
1679+ (('--separate-out','-so'), dict(
1680 type=str, dest='InteractiveShell.separate_out', default=NoConfigDefault,
1681 help="Separator before output prompts. Default 0 (nothing).",
1682 metavar='InteractiveShell.separate_out')
1683 ),
1684- (('-separate_out2','-so2'), dict(
1685+ (('--separate-out2','-so2'), dict(
1686 type=str, dest='InteractiveShell.separate_out2', default=NoConfigDefault,
1687 help="Separator after output prompts. Default 0 (nonight).",
1688 metavar='InteractiveShell.separate_out2')
1689 ),
1690- (('-nosep',), dict(
1691+ (('-no-sep',), dict(
1692 action='store_true', dest='Global.nosep', default=NoConfigDefault,
1693 help="Eliminate all spacing between prompts.")
1694 ),
1695- (('-term_title',), dict(
1696+ (('--term-title',), dict(
1697 action='store_true', dest='InteractiveShell.term_title', default=NoConfigDefault,
1698 help="Enable auto setting the terminal title.")
1699 ),
1700- (('-noterm_title',), dict(
1701+ (('--no-term-title',), dict(
1702 action='store_false', dest='InteractiveShell.term_title', default=NoConfigDefault,
1703 help="Disable auto setting the terminal title.")
1704 ),
1705- (('-xmode',), dict(
1706+ (('--xmode',), dict(
1707 type=str, dest='InteractiveShell.xmode', default=NoConfigDefault,
1708 help="Exception mode ('Plain','Context','Verbose')",
1709 metavar='InteractiveShell.xmode')
1710 ),
1711- (('-ext',), dict(
1712+ (('--ext',), dict(
1713 type=str, dest='Global.extra_extension', default=NoConfigDefault,
1714 help="The dotted module name of an IPython extension to load.",
1715 metavar='Global.extra_extension')
1716@@ -268,36 +267,39 @@
1717 action='store_true', dest='Global.force_interact', default=NoConfigDefault,
1718 help="If running code from the command line, become interactive afterwards.")
1719 ),
1720- (('-wthread',), dict(
1721+ (('--wthread',), dict(
1722 action='store_true', dest='Global.wthread', default=NoConfigDefault,
1723 help="Enable wxPython event loop integration.")
1724 ),
1725- (('-q4thread','-qthread'), dict(
1726+ (('--q4thread','--qthread'), dict(
1727 action='store_true', dest='Global.q4thread', default=NoConfigDefault,
1728 help="Enable Qt4 event loop integration. Qt3 is no longer supported.")
1729 ),
1730- (('-gthread',), dict(
1731+ (('--gthread',), dict(
1732 action='store_true', dest='Global.gthread', default=NoConfigDefault,
1733 help="Enable GTK event loop integration.")
1734 ),
1735 # # These are only here to get the proper deprecation warnings
1736- (('-pylab',), dict(
1737+ (('--pylab',), dict(
1738 action='store_true', dest='Global.pylab', default=NoConfigDefault,
1739- help="Disabled. Pylab has been disabled until matplotlib supports this version of IPython.")
1740+ help="Disabled. Pylab has been disabled until matplotlib "
1741+ "supports this version of IPython.")
1742 )
1743 )
1744
1745
1746-class IPythonAppCLConfigLoader(IPythonArgParseConfigLoader):
1747+class IPythonAppCLConfigLoader(BaseAppArgParseConfigLoader):
1748
1749 arguments = cl_args
1750
1751
1752-_default_config_file_name = 'ipython_config.py'
1753+default_config_file_name = u'ipython_config.py'
1754+
1755
1756 class IPythonApp(Application):
1757- name = 'ipython'
1758- config_file_name = _default_config_file_name
1759+ name = u'ipython'
1760+ description = 'IPython: an enhanced interactive Python shell.'
1761+ config_file_name = default_config_file_name
1762
1763 def create_default_config(self):
1764 super(IPythonApp, self).create_default_config()
1765@@ -313,11 +315,6 @@
1766 # By default always interact by starting the IPython mainloop.
1767 self.default_config.Global.interact = True
1768
1769- # Let the parent class set the default, but each time log_level
1770- # changes from config, we need to update self.log_level as that is
1771- # what updates the actual log level in self.log.
1772- self.default_config.Global.log_level = self.log_level
1773-
1774 # No GUI integration by default
1775 self.default_config.Global.wthread = False
1776 self.default_config.Global.q4thread = False
1777@@ -326,8 +323,9 @@
1778 def create_command_line_config(self):
1779 """Create and return a command line config loader."""
1780 return IPythonAppCLConfigLoader(
1781- description=ipython_desc,
1782- version=release.version)
1783+ description=self.description,
1784+ version=release.version
1785+ )
1786
1787 def post_load_command_line_config(self):
1788 """Do actions after loading cl config."""
1789@@ -477,9 +475,9 @@
1790 self.shell.showtraceback()
1791
1792 def _exec_file(self, fname):
1793- full_filename = filefind(fname, ['.', self.ipythondir])
1794+ full_filename = filefind(fname, [u'.', self.ipython_dir])
1795 if os.path.isfile(full_filename):
1796- if full_filename.endswith('.py'):
1797+ if full_filename.endswith(u'.py'):
1798 self.log.info("Running file in user namespace: %s" % full_filename)
1799 self.shell.safe_execfile(full_filename, self.shell.user_ns)
1800 elif full_filename.endswith('.ipy'):
1801@@ -527,20 +525,20 @@
1802 self.shell.mainloop()
1803
1804
1805-def load_default_config(ipythondir=None):
1806- """Load the default config file from the default ipythondir.
1807+def load_default_config(ipython_dir=None):
1808+ """Load the default config file from the default ipython_dir.
1809
1810 This is useful for embedded shells.
1811 """
1812- if ipythondir is None:
1813- ipythondir = get_ipython_dir()
1814- cl = PyFileConfigLoader(_default_config_file_name, ipythondir)
1815+ if ipython_dir is None:
1816+ ipython_dir = get_ipython_dir()
1817+ cl = PyFileConfigLoader(default_config_file_name, ipython_dir)
1818 config = cl.load_config()
1819 return config
1820
1821
1822 def launch_new_instance():
1823- """Create a run a full blown IPython instance"""
1824+ """Create and run a full blown IPython instance"""
1825 app = IPythonApp()
1826 app.start()
1827
1828
1829=== modified file 'IPython/core/iplib.py'
1830--- IPython/core/iplib.py 2009-10-09 22:54:40 +0000
1831+++ IPython/core/iplib.py 2009-11-21 00:11:11 +0000
1832@@ -162,6 +162,15 @@
1833 return ed
1834
1835
1836+def get_default_colors():
1837+ if sys.platform=='darwin':
1838+ return "LightBG"
1839+ elif os.name=='nt':
1840+ return 'Linux'
1841+ else:
1842+ return 'Linux'
1843+
1844+
1845 class SeparateStr(Str):
1846 """A Str subclass to validate separate_in, separate_out, etc.
1847
1848@@ -182,7 +191,7 @@
1849 class InteractiveShell(Component, Magic):
1850 """An enhanced, interactive shell for Python."""
1851
1852- autocall = Enum((0,1,2), config=True)
1853+ autocall = Enum((0,1,2), default_value=1, config=True)
1854 autoedit_syntax = CBool(False, config=True)
1855 autoindent = CBool(True, config=True)
1856 automagic = CBool(True, config=True)
1857@@ -192,7 +201,7 @@
1858 cache_size = Int(1000, config=True)
1859 color_info = CBool(True, config=True)
1860 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
1861- default_value='LightBG', config=True)
1862+ default_value=get_default_colors(), config=True)
1863 confirm_exit = CBool(True, config=True)
1864 debug = CBool(False, config=True)
1865 deep_reload = CBool(False, config=True)
1866@@ -206,7 +215,7 @@
1867 embedded_active = CBool(False)
1868 editor = Str(get_default_editor(), config=True)
1869 filename = Str("<ipython console>")
1870- ipythondir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
1871+ ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
1872 logstart = CBool(False, config=True)
1873 logfile = Str('', config=True)
1874 logappend = Str('', config=True)
1875@@ -264,7 +273,7 @@
1876 # Subclasses with thread support should override this as needed.
1877 isthreaded = False
1878
1879- def __init__(self, parent=None, config=None, ipythondir=None, usage=None,
1880+ def __init__(self, parent=None, config=None, ipython_dir=None, usage=None,
1881 user_ns=None, user_global_ns=None,
1882 banner1=None, banner2=None, display_banner=None,
1883 custom_exceptions=((),None)):
1884@@ -274,7 +283,7 @@
1885 super(InteractiveShell, self).__init__(parent, config=config)
1886
1887 # These are relatively independent and stateless
1888- self.init_ipythondir(ipythondir)
1889+ self.init_ipython_dir(ipython_dir)
1890 self.init_instance_attrs()
1891 self.init_term_title()
1892 self.init_usage(usage)
1893@@ -332,7 +341,7 @@
1894 def _banner2_changed(self):
1895 self.compute_banner()
1896
1897- def _ipythondir_changed(self, name, new):
1898+ def _ipython_dir_changed(self, name, new):
1899 if not os.path.isdir(new):
1900 os.makedirs(new, mode = 0777)
1901 if not os.path.isdir(self.ipython_extension_dir):
1902@@ -340,7 +349,7 @@
1903
1904 @property
1905 def ipython_extension_dir(self):
1906- return os.path.join(self.ipythondir, 'extensions')
1907+ return os.path.join(self.ipython_dir, 'extensions')
1908
1909 @property
1910 def usable_screen_length(self):
1911@@ -372,19 +381,19 @@
1912 # init_* methods called by __init__
1913 #-------------------------------------------------------------------------
1914
1915- def init_ipythondir(self, ipythondir):
1916- if ipythondir is not None:
1917- self.ipythondir = ipythondir
1918- self.config.Global.ipythondir = self.ipythondir
1919+ def init_ipython_dir(self, ipython_dir):
1920+ if ipython_dir is not None:
1921+ self.ipython_dir = ipython_dir
1922+ self.config.Global.ipython_dir = self.ipython_dir
1923 return
1924
1925- if hasattr(self.config.Global, 'ipythondir'):
1926- self.ipythondir = self.config.Global.ipythondir
1927+ if hasattr(self.config.Global, 'ipython_dir'):
1928+ self.ipython_dir = self.config.Global.ipython_dir
1929 else:
1930- self.ipythondir = get_ipython_dir()
1931+ self.ipython_dir = get_ipython_dir()
1932
1933 # All children can just read this
1934- self.config.Global.ipythondir = self.ipythondir
1935+ self.config.Global.ipython_dir = self.ipython_dir
1936
1937 def init_instance_attrs(self):
1938 self.jobs = BackgroundJobManager()
1939@@ -1070,7 +1079,7 @@
1940 histfname = 'history-%s' % self.profile
1941 else:
1942 histfname = 'history'
1943- self.histfile = os.path.join(self.ipythondir, histfname)
1944+ self.histfile = os.path.join(self.ipython_dir, histfname)
1945
1946 # Fill the history zero entry, user counter starts at 1
1947 self.input_hist.append('\n')
1948@@ -1078,12 +1087,12 @@
1949
1950 def init_shadow_hist(self):
1951 try:
1952- self.db = pickleshare.PickleShareDB(self.ipythondir + "/db")
1953+ self.db = pickleshare.PickleShareDB(self.ipython_dir + "/db")
1954 except exceptions.UnicodeDecodeError:
1955- print "Your ipythondir can't be decoded to unicode!"
1956+ print "Your ipython_dir can't be decoded to unicode!"
1957 print "Please set HOME environment variable to something that"
1958 print r"only has ASCII characters, e.g. c:\home"
1959- print "Now it is", self.ipythondir
1960+ print "Now it is", self.ipython_dir
1961 sys.exit()
1962 self.shadowhist = ipcorehist.ShadowHist(self.db)
1963
1964@@ -1426,9 +1435,7 @@
1965 return outcomps
1966
1967 def set_custom_completer(self,completer,pos=0):
1968- """set_custom_completer(completer,pos=0)
1969-
1970- Adds a new custom completer function.
1971+ """Adds a new custom completer function.
1972
1973 The position argument (defaults to 0) is the index in the completers
1974 list where you want the completer to be inserted."""
1975@@ -1438,9 +1445,18 @@
1976 self.Completer.matchers.insert(pos,newcomp)
1977
1978 def set_completer(self):
1979- """reset readline's completer to be our own."""
1980+ """Reset readline's completer to be our own."""
1981 self.readline.set_completer(self.Completer.complete)
1982
1983+ def set_completer_frame(self, frame=None):
1984+ """Set the frame of the completer."""
1985+ if frame:
1986+ self.Completer.namespace = frame.f_locals
1987+ self.Completer.global_namespace = frame.f_globals
1988+ else:
1989+ self.Completer.namespace = self.user_ns
1990+ self.Completer.global_namespace = self.user_global_ns
1991+
1992 #-------------------------------------------------------------------------
1993 # Things related to readline
1994 #-------------------------------------------------------------------------
1995@@ -1913,7 +1929,7 @@
1996 # SystemExit exception changed between Python 2.4 and 2.5, so
1997 # the checks must be done in a version-dependent way.
1998 show = False
1999- if status.message!=0 and not kw['exit_ignore']:
2000+ if status.args[0]==0 and not kw['exit_ignore']:
2001 show = True
2002 if show:
2003 self.showtraceback()
2004@@ -2278,6 +2294,8 @@
2005 def get_component(self, name=None, klass=None):
2006 """Fetch a component by name and klass in my tree."""
2007 c = Component.get_instances(root=self, name=name, klass=klass)
2008+ if len(c) == 0:
2009+ return None
2010 if len(c) == 1:
2011 return c[0]
2012 else:
2013@@ -2309,7 +2327,7 @@
2014 You can put your extension modules anywhere you want, as long as
2015 they can be imported by Python's standard import mechanism. However,
2016 to make it easy to write extensions, you can also put your extensions
2017- in ``os.path.join(self.ipythondir, 'extensions')``. This directory
2018+ in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
2019 is added to ``sys.path`` automatically.
2020 """
2021 from IPython.utils.syspathcontext import prepended_to_syspath
2022
2023=== modified file 'IPython/core/magic.py'
2024--- IPython/core/magic.py 2009-10-09 22:54:40 +0000
2025+++ IPython/core/magic.py 2009-11-21 00:11:11 +0000
2026@@ -21,6 +21,7 @@
2027 import pdb
2028 import pydoc
2029 import sys
2030+import shutil
2031 import re
2032 import tempfile
2033 import time
2034@@ -1268,7 +1269,6 @@
2035 If you want IPython to automatically do this on every exception, see
2036 the %pdb magic for more details.
2037 """
2038-
2039 self.shell.debugger(force=True)
2040
2041 @testdec.skip_doctest
2042@@ -3378,34 +3378,6 @@
2043 qr = IPython.core.usage.quick_reference + self.magic_magic('-brief')
2044
2045 page(qr)
2046-
2047- def magic_upgrade(self,arg):
2048- """ Upgrade your IPython installation
2049-
2050- This will copy the config files that don't yet exist in your
2051- ipython dir from the system config dir. Use this after upgrading
2052- IPython if you don't wish to delete your .ipython dir.
2053-
2054- Call with -nolegacy to get rid of ipythonrc* files (recommended for
2055- new users)
2056-
2057- """
2058- ip = self.getapi()
2059- ipinstallation = path(IPython.__file__).dirname()
2060- upgrade_script = '%s "%s"' % (sys.executable,ipinstallation / 'utils' / 'upgradedir.py')
2061- src_config = ipinstallation / 'config' / 'userconfig'
2062- userdir = path(ip.config.IPYTHONDIR)
2063- cmd = '%s "%s" "%s"' % (upgrade_script, src_config, userdir)
2064- print ">",cmd
2065- shell(cmd)
2066- if arg == '-nolegacy':
2067- legacy = userdir.files('ipythonrc*')
2068- print "Nuking legacy files:",legacy
2069-
2070- [p.remove() for p in legacy]
2071- suffix = (sys.platform == 'win32' and '.ini' or '')
2072- (userdir / ('ipythonrc' + suffix)).write_text('# Empty, see ipy_user_conf.py\n')
2073-
2074
2075 def magic_doctest_mode(self,parameter_s=''):
2076 """Toggle doctest mode on and off.
2077@@ -3550,4 +3522,59 @@
2078 """Reload an IPython extension by its module name."""
2079 self.reload_extension(module_str)
2080
2081+ def magic_install_profiles(self, s):
2082+ """Install the default IPython profiles into the .ipython dir.
2083+
2084+ If the default profiles have already been installed, they will not
2085+ be overwritten. You can force overwriting them by using the ``-o``
2086+ option::
2087+
2088+ In [1]: %install_profiles -o
2089+ """
2090+ if '-o' in s:
2091+ overwrite = True
2092+ else:
2093+ overwrite = False
2094+ from IPython.config import profile
2095+ profile_dir = os.path.split(profile.__file__)[0]
2096+ ipython_dir = self.ipython_dir
2097+ files = os.listdir(profile_dir)
2098+
2099+ to_install = []
2100+ for f in files:
2101+ if f.startswith('ipython_config'):
2102+ src = os.path.join(profile_dir, f)
2103+ dst = os.path.join(ipython_dir, f)
2104+ if (not os.path.isfile(dst)) or overwrite:
2105+ to_install.append((f, src, dst))
2106+ if len(to_install)>0:
2107+ print "Installing profiles to: ", ipython_dir
2108+ for (f, src, dst) in to_install:
2109+ shutil.copy(src, dst)
2110+ print " %s" % f
2111+
2112+ def magic_install_default_config(self, s):
2113+ """Install IPython's default config file into the .ipython dir.
2114+
2115+ If the default config file (:file:`ipython_config.py`) is already
2116+ installed, it will not be overwritten. You can force overwriting
2117+ by using the ``-o`` option::
2118+
2119+ In [1]: %install_default_config
2120+ """
2121+ if '-o' in s:
2122+ overwrite = True
2123+ else:
2124+ overwrite = False
2125+ from IPython.config import default
2126+ config_dir = os.path.split(default.__file__)[0]
2127+ ipython_dir = self.ipython_dir
2128+ default_config_file_name = 'ipython_config.py'
2129+ src = os.path.join(config_dir, default_config_file_name)
2130+ dst = os.path.join(ipython_dir, default_config_file_name)
2131+ if (not os.path.isfile(dst)) or overwrite:
2132+ shutil.copy(src, dst)
2133+ print "Installing default config file: %s" % dst
2134+
2135+
2136 # end Magic
2137
2138=== removed file 'IPython/core/oldusersetup.py'
2139--- IPython/core/oldusersetup.py 2009-08-18 06:55:22 +0000
2140+++ IPython/core/oldusersetup.py 1970-01-01 00:00:00 +0000
2141@@ -1,219 +0,0 @@
2142-# -*- coding: utf-8 -*-
2143-"""
2144-Main IPython Component
2145-"""
2146-
2147-#-----------------------------------------------------------------------------
2148-# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de>
2149-# Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
2150-# Copyright (C) 2008-2009 The IPython Development Team
2151-#
2152-# Distributed under the terms of the BSD License. The full license is in
2153-# the file COPYING, distributed as part of this software.
2154-#-----------------------------------------------------------------------------
2155-
2156-#-----------------------------------------------------------------------------
2157-# Imports
2158-#-----------------------------------------------------------------------------
2159-
2160-import glob
2161-import os
2162-import shutil
2163-import sys
2164-
2165-from IPython.utils.genutils import *
2166-
2167-def user_setup(ipythondir,rc_suffix,mode='install',interactive=True):
2168- """Install or upgrade the user configuration directory.
2169-
2170- Can be called when running for the first time or to upgrade the user's
2171- .ipython/ directory.
2172-
2173- Parameters
2174- ----------
2175- ipythondir : path
2176- The directory to be used for installation/upgrade. In 'install' mode,
2177- if this path already exists, the function exits immediately.
2178-
2179- rc_suffix : str
2180- Extension for the config files. On *nix platforms it is typically the
2181- empty string, while Windows normally uses '.ini'.
2182-
2183- mode : str, optional
2184- Valid modes are 'install' and 'upgrade'.
2185-
2186- interactive : bool, optional
2187- If False, do not wait for user input on any errors. Normally after
2188- printing its status information, this function waits for the user to
2189- hit Return before proceeding. This is because the default use case is
2190- when first installing the IPython configuration, so we want the user to
2191- acknowledge the initial message, which contains some useful
2192- information.
2193- """
2194-
2195- # For automatic use, deactivate all i/o
2196- if interactive:
2197- def wait():
2198- try:
2199- raw_input("Please press <RETURN> to start IPython.")
2200- except EOFError:
2201- print >> Term.cout
2202- print '*'*70
2203-
2204- def printf(s):
2205- print s
2206- else:
2207- wait = lambda : None
2208- printf = lambda s : None
2209-
2210- # Install mode should be re-entrant: if the install dir already exists,
2211- # bail out cleanly.
2212- # XXX. This is too hasty to return. We need to check to make sure that
2213- # all the expected config files and directories are actually there. We
2214- # currently have a failure mode if someone deletes a needed config file
2215- # but still has the ipythondir.
2216- if mode == 'install' and os.path.isdir(ipythondir):
2217- return
2218-
2219- cwd = os.getcwd() # remember where we started
2220- glb = glob.glob
2221-
2222- printf('*'*70)
2223- if mode == 'install':
2224- printf(
2225-"""Welcome to IPython. I will try to create a personal configuration directory
2226-where you can customize many aspects of IPython's functionality in:\n""")
2227- else:
2228- printf('I am going to upgrade your configuration in:')
2229-
2230- printf(ipythondir)
2231-
2232- rcdirend = os.path.join('IPython','config','userconfig')
2233- cfg = lambda d: os.path.join(d,rcdirend)
2234- try:
2235- rcdir = filter(os.path.isdir,map(cfg,sys.path))[0]
2236- printf("Initializing from configuration: %s" % rcdir)
2237- except IndexError:
2238- warning = """
2239-Installation error. IPython's directory was not found.
2240-
2241-Check the following:
2242-
2243-The ipython/IPython directory should be in a directory belonging to your
2244-PYTHONPATH environment variable (that is, it should be in a directory
2245-belonging to sys.path). You can copy it explicitly there or just link to it.
2246-
2247-IPython will create a minimal default configuration for you.
2248-
2249-"""
2250- warn(warning)
2251- wait()
2252-
2253- if sys.platform =='win32':
2254- inif = 'ipythonrc.ini'
2255- else:
2256- inif = 'ipythonrc'
2257- minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults',
2258- inif : '# intentionally left blank' }
2259- os.makedirs(ipythondir, mode = 0777)
2260- for f, cont in minimal_setup.items():
2261- # In 2.5, this can be more cleanly done using 'with'
2262- fobj = file(ipythondir + '/' + f,'w')
2263- fobj.write(cont)
2264- fobj.close()
2265-
2266- return
2267-
2268- if mode == 'install':
2269- try:
2270- shutil.copytree(rcdir,ipythondir)
2271- os.chdir(ipythondir)
2272- rc_files = glb("ipythonrc*")
2273- for rc_file in rc_files:
2274- os.rename(rc_file,rc_file+rc_suffix)
2275- except:
2276- warning = """
2277-
2278-There was a problem with the installation:
2279-%s
2280-Try to correct it or contact the developers if you think it's a bug.
2281-IPython will proceed with builtin defaults.""" % sys.exc_info()[1]
2282- warn(warning)
2283- wait()
2284- return
2285-
2286- elif mode == 'upgrade':
2287- try:
2288- os.chdir(ipythondir)
2289- except:
2290- printf("""
2291-Can not upgrade: changing to directory %s failed. Details:
2292-%s
2293-""" % (ipythondir,sys.exc_info()[1]) )
2294- wait()
2295- return
2296- else:
2297- sources = glb(os.path.join(rcdir,'[A-Za-z]*'))
2298- for new_full_path in sources:
2299- new_filename = os.path.basename(new_full_path)
2300- if new_filename.startswith('ipythonrc'):
2301- new_filename = new_filename + rc_suffix
2302- # The config directory should only contain files, skip any
2303- # directories which may be there (like CVS)
2304- if os.path.isdir(new_full_path):
2305- continue
2306- if os.path.exists(new_filename):
2307- old_file = new_filename+'.old'
2308- if os.path.exists(old_file):
2309- os.remove(old_file)
2310- os.rename(new_filename,old_file)
2311- shutil.copy(new_full_path,new_filename)
2312- else:
2313- raise ValueError('unrecognized mode for install: %r' % mode)
2314-
2315- # Fix line-endings to those native to each platform in the config
2316- # directory.
2317- try:
2318- os.chdir(ipythondir)
2319- except:
2320- printf("""
2321-Problem: changing to directory %s failed.
2322-Details:
2323-%s
2324-
2325-Some configuration files may have incorrect line endings. This should not
2326-cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) )
2327- wait()
2328- else:
2329- for fname in glb('ipythonrc*'):
2330- try:
2331- native_line_ends(fname,backup=0)
2332- except IOError:
2333- pass
2334-
2335- if mode == 'install':
2336- printf("""
2337-Successful installation!
2338-
2339-Please read the sections 'Initial Configuration' and 'Quick Tips' in the
2340-IPython manual (there are both HTML and PDF versions supplied with the
2341-distribution) to make sure that your system environment is properly configured
2342-to take advantage of IPython's features.
2343-
2344-Important note: the configuration system has changed! The old system is
2345-still in place, but its setting may be partly overridden by the settings in
2346-"~/.ipython/ipy_user_conf.py" config file. Please take a look at the file
2347-if some of the new settings bother you.
2348-
2349-""")
2350- else:
2351- printf("""
2352-Successful upgrade!
2353-
2354-All files in your directory:
2355-%(ipythondir)s
2356-which would have been overwritten by the upgrade were backed up with a .old
2357-extension. If you had made particular customizations in those files you may
2358-want to merge them back into the new files.""" % locals() )
2359- wait()
2360- os.chdir(cwd)
2361\ No newline at end of file
2362
2363=== modified file 'IPython/core/prefilter.py' (properties changed: -x to +x)
2364--- IPython/core/prefilter.py 2009-09-28 05:57:44 +0000
2365+++ IPython/core/prefilter.py 2009-11-21 00:11:11 +0000
2366@@ -39,7 +39,7 @@
2367 from IPython.core.page import page
2368
2369 from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool
2370-from IPython.utils.genutils import make_quoted_expr
2371+from IPython.utils.genutils import make_quoted_expr, Term
2372 from IPython.utils.autoattr import auto_attr
2373
2374 #-----------------------------------------------------------------------------
2375
2376=== modified file 'IPython/core/tests/test_iplib.py'
2377--- IPython/core/tests/test_iplib.py 2009-08-19 21:56:41 +0000
2378+++ IPython/core/tests/test_iplib.py 2009-11-21 00:11:11 +0000
2379@@ -15,7 +15,7 @@
2380 # our own packages
2381 from IPython.core import iplib
2382 from IPython.core import ipapi
2383-from IPython.core.oldusersetup import user_setup
2384+
2385
2386 #-----------------------------------------------------------------------------
2387 # Globals
2388@@ -54,27 +54,4 @@
2389 continue
2390 nt.assert_equals(len(ns),0)
2391
2392-
2393-# make sure that user_setup can be run re-entrantly in 'install' mode.
2394-def test_user_setup():
2395- # use a lambda to pass kwargs to the generator
2396- user_setup = lambda a,k: user_setup(*a,**k)
2397- kw = dict(mode='install', interactive=False)
2398-
2399- # Call the user setup and verify that the directory exists
2400- yield user_setup, (ip.config.IPYTHONDIR,''), kw
2401- yield os.path.isdir, ip.config.IPYTHONDIR
2402-
2403- # Now repeat the operation with a non-existent directory. Check both that
2404- # the call succeeds and that the directory is created.
2405- tmpdir = tempfile.mktemp(prefix='ipython-test-')
2406- # Use a try with an empty except because try/finally doesn't work with a
2407- # yield in Python 2.4.
2408- try:
2409- yield user_setup, (tmpdir,''), kw
2410- yield os.path.isdir, tmpdir
2411- except:
2412- pass
2413- # Clean up the temp dir once done
2414- shutil.rmtree(tmpdir)
2415
2416\ No newline at end of file
2417
2418=== modified file 'IPython/core/usage.py'
2419--- IPython/core/usage.py 2009-08-18 23:09:56 +0000
2420+++ IPython/core/usage.py 2009-11-21 00:11:11 +0000
2421@@ -40,7 +40,7 @@
2422 in directories.
2423
2424 In the rest of this text, we will refer to this directory as
2425- IPYTHONDIR.
2426+ IPYTHON_DIR.
2427
2428 REGULAR OPTIONS
2429 After the above threading options have been given, regular options can
2430@@ -150,9 +150,9 @@
2431 here (in case your default EDITOR is something like Emacs).
2432
2433 -ipythondir <name>
2434- The name of your IPython configuration directory IPYTHONDIR.
2435+ The name of your IPython configuration directory IPYTHON_DIR.
2436 This can also be specified through the environment variable
2437- IPYTHONDIR.
2438+ IPYTHON_DIR.
2439
2440 -log|l Generate a log file of all input. The file is named
2441 ipython_log.py in your current directory (which prevents logs
2442@@ -201,10 +201,10 @@
2443
2444 -profile|p <name>
2445 Assume that your config file is ipythonrc-<name> (looks in cur-
2446- rent dir first, then in IPYTHONDIR). This is a quick way to keep
2447+ rent dir first, then in IPYTHON_DIR). This is a quick way to keep
2448 and load multiple config files for different tasks, especially
2449 if you use the include option of config files. You can keep a
2450- basic IPYTHONDIR/ipythonrc file and then have other 'profiles'
2451+ basic IPYTHON_DIR/ipythonrc file and then have other 'profiles'
2452 which include this one and load extra things for particular
2453 tasks. For example:
2454
2455@@ -245,7 +245,7 @@
2456 -rcfile <name>
2457 Name of your IPython resource configuration file. normally
2458 IPython loads ipythonrc (from current directory) or
2459- IPYTHONDIR/ipythonrc. If the loading of your config file fails,
2460+ IPYTHON_DIR/ipythonrc. If the loading of your config file fails,
2461 IPython starts with a bare bones configuration (no modules
2462 loaded at all).
2463
2464@@ -284,7 +284,7 @@
2465 Simply removes all input/output separators.
2466
2467 -upgrade
2468- Allows you to upgrade your IPYTHONDIR configuration when you
2469+ Allows you to upgrade your IPYTHON_DIR configuration when you
2470 install a new version of IPython. Since new versions may
2471 include new command lines options or example files, this copies
2472 updated ipythonrc-type files. However, it backs up (with a .old
2473
2474=== renamed file 'IPython/kernel/magic.py' => 'IPython/extensions/parallelmagic.py'
2475--- IPython/kernel/magic.py 2009-09-12 15:58:55 +0000
2476+++ IPython/extensions/parallelmagic.py 2009-11-21 00:11:11 +0000
2477@@ -1,171 +1,205 @@
2478+#!/usr/bin/env python
2479 # encoding: utf-8
2480
2481 """Magic command interface for interactive parallel work."""
2482
2483-__docformat__ = "restructuredtext en"
2484-
2485-#-------------------------------------------------------------------------------
2486-# Copyright (C) 2008 The IPython Development Team
2487+#-----------------------------------------------------------------------------
2488+# Copyright (C) 2008-2009 The IPython Development Team
2489 #
2490 # Distributed under the terms of the BSD License. The full license is in
2491 # the file COPYING, distributed as part of this software.
2492-#-------------------------------------------------------------------------------
2493+#-----------------------------------------------------------------------------
2494
2495-#-------------------------------------------------------------------------------
2496+#-----------------------------------------------------------------------------
2497 # Imports
2498-#-------------------------------------------------------------------------------
2499+#-----------------------------------------------------------------------------
2500
2501 import new
2502
2503-from IPython.core.iplib import InteractiveShell
2504-from IPython.core.shell import MTInteractiveShell
2505-
2506-from twisted.internet.defer import Deferred
2507-
2508-
2509-#-------------------------------------------------------------------------------
2510+from IPython.core.component import Component
2511+from IPython.utils.traitlets import Bool, Any
2512+from IPython.utils.autoattr import auto_attr
2513+
2514+#-----------------------------------------------------------------------------
2515 # Definitions of magic functions for use with IPython
2516-#-------------------------------------------------------------------------------
2517-
2518-NO_ACTIVE_CONTROLLER = """
2519-Error: No Controller is activated
2520-Use activate() on a RemoteController object to activate it for magics.
2521+#-----------------------------------------------------------------------------
2522+
2523+
2524+NO_ACTIVE_MULTIENGINE_CLIENT = """
2525+Use activate() on a MultiEngineClient object to activate it for magics.
2526 """
2527
2528-def magic_result(self,parameter_s=''):
2529- """Print the result of command i on all engines of the active controller.
2530-
2531- To activate a controller in IPython, first create it and then call
2532- the activate() method.
2533-
2534- Then you can do the following:
2535-
2536- >>> result # Print the latest result
2537- Printing result...
2538- [127.0.0.1:0] In [1]: b = 10
2539- [127.0.0.1:1] In [1]: b = 10
2540-
2541- >>> result 0 # Print result 0
2542- In [14]: result 0
2543- Printing result...
2544- [127.0.0.1:0] In [0]: a = 5
2545- [127.0.0.1:1] In [0]: a = 5
2546- """
2547- try:
2548- activeController = __IPYTHON__.activeController
2549- except AttributeError:
2550- print NO_ACTIVE_CONTROLLER
2551- else:
2552+
2553+class ParalleMagicComponent(Component):
2554+ """A component to manage the %result, %px and %autopx magics."""
2555+
2556+ active_multiengine_client = Any()
2557+ verbose = Bool(False, config=True)
2558+
2559+ def __init__(self, parent, name=None, config=None):
2560+ super(ParalleMagicComponent, self).__init__(parent, name=name, config=config)
2561+ self._define_magics()
2562+ # A flag showing if autopx is activated or not
2563+ self.autopx = False
2564+
2565+ # Access other components like this rather than by a regular attribute.
2566+ # This won't lookup the InteractiveShell object until it is used and
2567+ # then it is cached. This is both efficient and couples this class
2568+ # more loosely to InteractiveShell.
2569+ @auto_attr
2570+ def shell(self):
2571+ return Component.get_instances(
2572+ root=self.root,
2573+ klass='IPython.core.iplib.InteractiveShell')[0]
2574+
2575+ def _define_magics(self):
2576+ """Define the magic functions."""
2577+ self.shell.define_magic('result', self.magic_result)
2578+ self.shell.define_magic('px', self.magic_px)
2579+ self.shell.define_magic('autopx', self.magic_autopx)
2580+
2581+ def magic_result(self, ipself, parameter_s=''):
2582+ """Print the result of command i on all engines..
2583+
2584+ To use this a :class:`MultiEngineClient` instance must be created
2585+ and then activated by calling its :meth:`activate` method.
2586+
2587+ Then you can do the following::
2588+
2589+ In [23]: %result
2590+ Out[23]:
2591+ <Results List>
2592+ [0] In [6]: a = 10
2593+ [1] In [6]: a = 10
2594+
2595+ In [22]: %result 6
2596+ Out[22]:
2597+ <Results List>
2598+ [0] In [6]: a = 10
2599+ [1] In [6]: a = 10
2600+ """
2601+ if self.active_multiengine_client is None:
2602+ print NO_ACTIVE_MULTIENGINE_CLIENT
2603+ return
2604+
2605 try:
2606 index = int(parameter_s)
2607 except:
2608 index = None
2609- result = activeController.get_result(index)
2610- return result
2611-
2612-def magic_px(self,parameter_s=''):
2613- """Executes the given python command on the active IPython Controller.
2614-
2615- To activate a Controller in IPython, first create it and then call
2616- the activate() method.
2617-
2618- Then you can do the following:
2619-
2620- >>> %px a = 5 # Runs a = 5 on all nodes
2621- """
2622-
2623- try:
2624- activeController = __IPYTHON__.activeController
2625- except AttributeError:
2626- print NO_ACTIVE_CONTROLLER
2627- else:
2628- print "Parallel execution on engines: %s" % activeController.targets
2629- result = activeController.execute(parameter_s)
2630- return result
2631-
2632-def pxrunsource(self, source, filename="<input>", symbol="single"):
2633-
2634- try:
2635- code = self.compile(source, filename, symbol)
2636- except (OverflowError, SyntaxError, ValueError):
2637- # Case 1
2638- self.showsyntaxerror(filename)
2639- return None
2640-
2641- if code is None:
2642- # Case 2
2643- return True
2644-
2645- # Case 3
2646- # Because autopx is enabled, we now call executeAll or disable autopx if
2647- # %autopx or autopx has been called
2648- if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source:
2649- _disable_autopx(self)
2650- return False
2651- else:
2652- try:
2653- result = self.activeController.execute(source)
2654- except:
2655- self.showtraceback()
2656- else:
2657- print result.__repr__()
2658- return False
2659+ result = self.active_multiengine_client.get_result(index)
2660+ return result
2661+
2662+ def magic_px(self, ipself, parameter_s=''):
2663+ """Executes the given python command in parallel.
2664+
2665+ To use this a :class:`MultiEngineClient` instance must be created
2666+ and then activated by calling its :meth:`activate` method.
2667
2668-def magic_autopx(self, parameter_s=''):
2669- """Toggles auto parallel mode for the active IPython Controller.
2670-
2671- To activate a Controller in IPython, first create it and then call
2672- the activate() method.
2673-
2674- Then you can do the following:
2675-
2676- >>> %autopx # Now all commands are executed in parallel
2677- Auto Parallel Enabled
2678- Type %autopx to disable
2679- ...
2680- >>> %autopx # Now all commands are locally executed
2681- Auto Parallel Disabled
2682- """
2683-
2684- if hasattr(self, 'autopx'):
2685- if self.autopx == True:
2686- _disable_autopx(self)
2687+ Then you can do the following::
2688+
2689+ In [24]: %px a = 5
2690+ Parallel execution on engines: all
2691+ Out[24]:
2692+ <Results List>
2693+ [0] In [7]: a = 5
2694+ [1] In [7]: a = 5
2695+ """
2696+
2697+ if self.active_multiengine_client is None:
2698+ print NO_ACTIVE_MULTIENGINE_CLIENT
2699+ return
2700+ print "Parallel execution on engines: %s" % self.active_multiengine_client.targets
2701+ result = self.active_multiengine_client.execute(parameter_s)
2702+ return result
2703+
2704+ def magic_autopx(self, ipself, parameter_s=''):
2705+ """Toggles auto parallel mode.
2706+
2707+ To use this a :class:`MultiEngineClient` instance must be created
2708+ and then activated by calling its :meth:`activate` method. Once this
2709+ is called, all commands typed at the command line are send to
2710+ the engines to be executed in parallel. To control which engine
2711+ are used, set the ``targets`` attributed of the multiengine client
2712+ before entering ``%autopx`` mode.
2713+
2714+ Then you can do the following::
2715+
2716+ In [25]: %autopx
2717+ %autopx to enabled
2718+
2719+ In [26]: a = 10
2720+ <Results List>
2721+ [0] In [8]: a = 10
2722+ [1] In [8]: a = 10
2723+
2724+
2725+ In [27]: %autopx
2726+ %autopx disabled
2727+ """
2728+ if self.autopx:
2729+ self._disable_autopx()
2730 else:
2731- _enable_autopx(self)
2732- else:
2733- _enable_autopx(self)
2734-
2735-def _enable_autopx(self):
2736- """Enable %autopx mode by saving the original runsource and installing
2737- pxrunsource.
2738- """
2739- try:
2740- activeController = __IPYTHON__.activeController
2741- except AttributeError:
2742- print "No active RemoteController found, use RemoteController.activate()."
2743- else:
2744- self._original_runsource = self.runsource
2745- self.runsource = new.instancemethod(pxrunsource, self, self.__class__)
2746+ self._enable_autopx()
2747+
2748+ def _enable_autopx(self):
2749+ """Enable %autopx mode by saving the original runsource and installing
2750+ pxrunsource.
2751+ """
2752+ if self.active_multiengine_client is None:
2753+ print NO_ACTIVE_MULTIENGINE_CLIENT
2754+ return
2755+
2756+ self._original_runsource = self.shell.runsource
2757+ self.shell.runsource = new.instancemethod(
2758+ self.pxrunsource, self.shell, self.shell.__class__
2759+ )
2760 self.autopx = True
2761- print "Auto Parallel Enabled\nType %autopx to disable"
2762-
2763-def _disable_autopx(self):
2764- """Disable %autopx by restoring the original runsource."""
2765- if hasattr(self, 'autopx'):
2766- if self.autopx == True:
2767- self.runsource = self._original_runsource
2768+ print "%autopx enabled"
2769+
2770+ def _disable_autopx(self):
2771+ """Disable %autopx by restoring the original InteractiveShell.runsource."""
2772+ if self.autopx:
2773+ self.shell.runsource = self._original_runsource
2774 self.autopx = False
2775- print "Auto Parallel Disabled"
2776-
2777-# Add the new magic function to the class dict:
2778-
2779-InteractiveShell.magic_result = magic_result
2780-InteractiveShell.magic_px = magic_px
2781-InteractiveShell.magic_autopx = magic_autopx
2782-
2783-# And remove the global name to keep global namespace clean. Don't worry, the
2784-# copy bound to IPython stays, we're just removing the global name.
2785-del magic_result
2786-del magic_px
2787-del magic_autopx
2788+ print "%autopx disabled"
2789+
2790+ def pxrunsource(self, ipself, source, filename="<input>", symbol="single"):
2791+ """A parallel replacement for InteractiveShell.runsource."""
2792+
2793+ try:
2794+ code = ipself.compile(source, filename, symbol)
2795+ except (OverflowError, SyntaxError, ValueError):
2796+ # Case 1
2797+ ipself.showsyntaxerror(filename)
2798+ return None
2799+
2800+ if code is None:
2801+ # Case 2
2802+ return True
2803+
2804+ # Case 3
2805+ # Because autopx is enabled, we now call executeAll or disable autopx if
2806+ # %autopx or autopx has been called
2807+ if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source:
2808+ self._disable_autopx()
2809+ return False
2810+ else:
2811+ try:
2812+ result = self.active_multiengine_client.execute(source)
2813+ except:
2814+ ipself.showtraceback()
2815+ else:
2816+ print result.__repr__()
2817+ return False
2818+
2819+
2820+_loaded = False
2821+
2822+
2823+def load_ipython_extension(ip):
2824+ """Load the extension in IPython."""
2825+ global _loaded
2826+ if not _loaded:
2827+ prd = ParalleMagicComponent(ip, name='parallel_magic')
2828+ _loaded = True
2829
2830
2831=== modified file 'IPython/frontend/wx/ipythonx.py'
2832--- IPython/frontend/wx/ipythonx.py 2009-03-29 01:05:30 +0000
2833+++ IPython/frontend/wx/ipythonx.py 2009-11-21 00:11:11 +0000
2834@@ -6,11 +6,10 @@
2835 try:
2836 import wx
2837 except ImportError, e:
2838- e.message = """%s
2839+ e.args[0] = """%s
2840 ________________________________________________________________________________
2841 You need wxPython to run this application.
2842-""" % e.message
2843- e.args = (e.message, ) + e.args[1:]
2844+""" % e.args[0]
2845 raise e
2846
2847 from wx_frontend import WxController
2848
2849=== modified file 'IPython/gui/wx/wxIPython.py'
2850--- IPython/gui/wx/wxIPython.py 2009-08-19 21:56:41 +0000
2851+++ IPython/gui/wx/wxIPython.py 2009-11-21 00:11:11 +0000
2852@@ -109,7 +109,7 @@
2853
2854 def optionSave(self, name, value):
2855 ip = get()
2856- path = ip.config.IPYTHONDIR
2857+ path = ip.ipython_dir
2858 opt = open(path + '/options.conf','w')
2859
2860 try:
2861@@ -126,7 +126,7 @@
2862 def optionLoad(self):
2863 try:
2864 ip = get()
2865- path = ip.config.IPYTHONDIR
2866+ path = ip.ipython_dir
2867 opt = open(path + '/options.conf','r')
2868 lines = opt.readlines()
2869 opt.close()
2870
2871=== modified file 'IPython/kernel/asyncclient.py'
2872--- IPython/kernel/asyncclient.py 2008-07-22 02:39:52 +0000
2873+++ IPython/kernel/asyncclient.py 2009-11-21 00:11:11 +0000
2874@@ -1,3 +1,4 @@
2875+#!/usr/bin/env python
2876 # encoding: utf-8
2877
2878 """Asynchronous clients for the IPython controller.
2879@@ -9,32 +10,32 @@
2880
2881 The main methods are are `get_*_client` and `get_client`.
2882 """
2883-
2884-__docformat__ = "restructuredtext en"
2885-
2886-#-------------------------------------------------------------------------------
2887-# Copyright (C) 2008 The IPython Development Team
2888+#-----------------------------------------------------------------------------
2889+# Copyright (C) 2008-2009 The IPython Development Team
2890 #
2891 # Distributed under the terms of the BSD License. The full license is in
2892 # the file COPYING, distributed as part of this software.
2893-#-------------------------------------------------------------------------------
2894+#-----------------------------------------------------------------------------
2895
2896-#-------------------------------------------------------------------------------
2897+#-----------------------------------------------------------------------------
2898 # Imports
2899-#-------------------------------------------------------------------------------
2900+#-----------------------------------------------------------------------------
2901
2902 from IPython.kernel import codeutil
2903-from IPython.kernel.clientconnector import ClientConnector
2904+from IPython.kernel.clientconnector import (
2905+ AsyncClientConnector,
2906+ AsyncCluster
2907+)
2908
2909 # Other things that the user will need
2910 from IPython.kernel.task import MapTask, StringTask
2911 from IPython.kernel.error import CompositeError
2912
2913-#-------------------------------------------------------------------------------
2914+#-----------------------------------------------------------------------------
2915 # Code
2916-#-------------------------------------------------------------------------------
2917+#-----------------------------------------------------------------------------
2918
2919-_client_tub = ClientConnector()
2920+_client_tub = AsyncClientConnector()
2921 get_multiengine_client = _client_tub.get_multiengine_client
2922 get_task_client = _client_tub.get_task_client
2923 get_client = _client_tub.get_client
2924
2925=== modified file 'IPython/kernel/client.py'
2926--- IPython/kernel/client.py 2009-07-02 21:35:36 +0000
2927+++ IPython/kernel/client.py 2009-11-21 00:11:11 +0000
2928@@ -1,3 +1,4 @@
2929+#!/usr/bin/env python
2930 # encoding: utf-8
2931
2932 """This module contains blocking clients for the controller interfaces.
2933@@ -15,33 +16,36 @@
2934 * CompositeError
2935 """
2936
2937-__docformat__ = "restructuredtext en"
2938-
2939-#-------------------------------------------------------------------------------
2940-# Copyright (C) 2008 The IPython Development Team
2941+#-----------------------------------------------------------------------------
2942+# Copyright (C) 2008-2009 The IPython Development Team
2943 #
2944 # Distributed under the terms of the BSD License. The full license is in
2945 # the file COPYING, distributed as part of this software.
2946-#-------------------------------------------------------------------------------
2947+#-----------------------------------------------------------------------------
2948
2949-#-------------------------------------------------------------------------------
2950+#-----------------------------------------------------------------------------
2951 # Imports
2952-#-------------------------------------------------------------------------------
2953+#-----------------------------------------------------------------------------
2954
2955+from cStringIO import StringIO
2956 import sys
2957+import warnings
2958
2959 # from IPython.utils import growl
2960 # growl.start("IPython1 Client")
2961
2962
2963 from twisted.internet import reactor
2964-from IPython.kernel.clientconnector import ClientConnector
2965+from twisted.internet.error import PotentialZombieWarning
2966+from twisted.python import log
2967+
2968+from IPython.kernel.clientconnector import ClientConnector, Cluster
2969 from IPython.kernel.twistedutil import ReactorInThread
2970 from IPython.kernel.twistedutil import blockingCallFromThread
2971
2972 # These enable various things
2973 from IPython.kernel import codeutil
2974-import IPython.kernel.magic
2975+# import IPython.kernel.magic
2976
2977 # Other things that the user will need
2978 from IPython.kernel.task import MapTask, StringTask
2979@@ -51,46 +55,34 @@
2980 # Code
2981 #-------------------------------------------------------------------------------
2982
2983+warnings.simplefilter('ignore', PotentialZombieWarning)
2984+
2985 _client_tub = ClientConnector()
2986
2987-
2988-def get_multiengine_client(furl_or_file=''):
2989- """Get the blocking MultiEngine client.
2990-
2991- :Parameters:
2992- furl_or_file : str
2993- A furl or a filename containing a furl. If empty, the
2994- default furl_file will be used
2995-
2996- :Returns:
2997- The connected MultiEngineClient instance
2998- """
2999- client = blockingCallFromThread(_client_tub.get_multiengine_client,
3000- furl_or_file)
3001- return client.adapt_to_blocking_client()
3002-
3003-def get_task_client(furl_or_file=''):
3004- """Get the blocking Task client.
3005-
3006- :Parameters:
3007- furl_or_file : str
3008- A furl or a filename containing a furl. If empty, the
3009- default furl_file will be used
3010-
3011- :Returns:
3012- The connected TaskClient instance
3013- """
3014- client = blockingCallFromThread(_client_tub.get_task_client,
3015- furl_or_file)
3016- return client.adapt_to_blocking_client()
3017-
3018-
3019+get_multiengine_client = _client_tub.get_multiengine_client
3020+get_task_client = _client_tub.get_task_client
3021 MultiEngineClient = get_multiengine_client
3022 TaskClient = get_task_client
3023
3024-
3025+# This isn't great. I should probably set this up in the ReactorInThread
3026+# class below. But, it does work for now.
3027+log.startLogging(sys.stdout, setStdout=0)
3028
3029 # Now we start the reactor in a thread
3030 rit = ReactorInThread()
3031 rit.setDaemon(True)
3032-rit.start()
3033\ No newline at end of file
3034+rit.start()
3035+
3036+
3037+
3038+
3039+__all__ = [
3040+ 'MapTask',
3041+ 'StringTask',
3042+ 'MultiEngineClient',
3043+ 'TaskClient',
3044+ 'CompositeError',
3045+ 'get_task_client',
3046+ 'get_multiengine_client',
3047+ 'Cluster'
3048+]
3049
3050=== modified file 'IPython/kernel/clientconnector.py'
3051--- IPython/kernel/clientconnector.py 2009-08-27 21:34:41 +0000
3052+++ IPython/kernel/clientconnector.py 2009-11-21 00:11:11 +0000
3053@@ -1,142 +1,268 @@
3054+#!/usr/bin/env python
3055 # encoding: utf-8
3056
3057-"""A class for handling client connections to the controller."""
3058-
3059-__docformat__ = "restructuredtext en"
3060-
3061-#-------------------------------------------------------------------------------
3062-# Copyright (C) 2008 The IPython Development Team
3063+"""Facilities for handling client connections to the controller."""
3064+
3065+#-----------------------------------------------------------------------------
3066+# Copyright (C) 2008-2009 The IPython Development Team
3067 #
3068 # Distributed under the terms of the BSD License. The full license is in
3069 # the file COPYING, distributed as part of this software.
3070-#-------------------------------------------------------------------------------
3071+#-----------------------------------------------------------------------------
3072
3073-#-------------------------------------------------------------------------------
3074+#-----------------------------------------------------------------------------
3075 # Imports
3076-#-------------------------------------------------------------------------------
3077+#-----------------------------------------------------------------------------
3078+
3079+from __future__ import with_statement
3080+import os
3081+
3082+from IPython.kernel.fcutil import (
3083+ Tub,
3084+ find_furl,
3085+ is_valid_furl_or_file,
3086+ validate_furl_or_file,
3087+ FURLError
3088+)
3089+from IPython.kernel.clusterdir import ClusterDir, ClusterDirError
3090+from IPython.kernel.launcher import IPClusterLauncher
3091+from IPython.kernel.twistedutil import (
3092+ gatherBoth,
3093+ make_deferred,
3094+ blockingCallFromThread,
3095+ sleep_deferred
3096+)
3097+from IPython.utils.importstring import import_item
3098+from IPython.utils.genutils import get_ipython_dir
3099
3100 from twisted.internet import defer
3101-
3102-from IPython.kernel.fcutil import Tub, UnauthenticatedTub
3103-
3104-from IPython.kernel.config import config_manager as kernel_config_manager
3105-from IPython.utils.importstring import import_item
3106-from IPython.kernel.fcutil import find_furl
3107-
3108-co = kernel_config_manager.get_config_obj()
3109-client_co = co['client']
3110-
3111-#-------------------------------------------------------------------------------
3112+from twisted.internet.defer import inlineCallbacks, returnValue
3113+from twisted.python import failure, log
3114+
3115+#-----------------------------------------------------------------------------
3116 # The ClientConnector class
3117-#-------------------------------------------------------------------------------
3118-
3119-class ClientConnector(object):
3120- """
3121- This class gets remote references from furls and returns the wrapped clients.
3122-
3123- This class is also used in `client.py` and `asyncclient.py` to create
3124- a single per client-process Tub.
3125- """
3126-
3127+#-----------------------------------------------------------------------------
3128+
3129+DELAY = 0.2
3130+MAX_TRIES = 9
3131+
3132+
3133+class ClientConnectorError(Exception):
3134+ pass
3135+
3136+
3137+class AsyncClientConnector(object):
3138+ """A class for getting remote references and clients from furls.
3139+
3140+ This start a single :class:`Tub` for all remote reference and caches
3141+ references.
3142+ """
3143+
3144 def __init__(self):
3145 self._remote_refs = {}
3146 self.tub = Tub()
3147 self.tub.startService()
3148-
3149+
3150+ def _find_furl(self, profile='default', cluster_dir=None,
3151+ furl_or_file=None, furl_file_name=None,
3152+ ipython_dir=None):
3153+ """Find a FURL file by profile+ipython_dir or cluster dir.
3154+
3155+ This raises an :exc:`~IPython.kernel.fcutil.FURLError` exception
3156+ if a FURL file can't be found.
3157+ """
3158+ # Try by furl_or_file
3159+ if furl_or_file is not None:
3160+ validate_furl_or_file(furl_or_file)
3161+ return furl_or_file
3162+
3163+ if furl_file_name is None:
3164+ raise FURLError('A furl_file_name must be provided')
3165+
3166+ # Try by cluster_dir
3167+ if cluster_dir is not None:
3168+ cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
3169+ sdir = cluster_dir_obj.security_dir
3170+ furl_file = os.path.join(sdir, furl_file_name)
3171+ validate_furl_or_file(furl_file)
3172+ return furl_file
3173+
3174+ # Try by profile
3175+ if ipython_dir is None:
3176+ ipython_dir = get_ipython_dir()
3177+ if profile is not None:
3178+ cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
3179+ ipython_dir, profile)
3180+ sdir = cluster_dir_obj.security_dir
3181+ furl_file = os.path.join(sdir, furl_file_name)
3182+ validate_furl_or_file(furl_file)
3183+ return furl_file
3184+
3185+ raise FURLError('Could not find a valid FURL file.')
3186+
3187 def get_reference(self, furl_or_file):
3188- """
3189- Get a remote reference using a furl or a file containing a furl.
3190-
3191+ """Get a remote reference using a furl or a file containing a furl.
3192+
3193 Remote references are cached locally so once a remote reference
3194 has been retrieved for a given furl, the cached version is
3195 returned.
3196-
3197- :Parameters:
3198- furl_or_file : str
3199- A furl or a filename containing a furl
3200-
3201- :Returns:
3202- A deferred to a remote reference
3203+
3204+ Parameters
3205+ ----------
3206+ furl_or_file : str
3207+ A furl or a filename containing a furl. This should already be
3208+ validated, but might not yet exist.
3209+
3210+ Returns
3211+ -------
3212+ A deferred to a remote reference
3213 """
3214- furl = find_furl(furl_or_file)
3215+ furl = furl_or_file
3216 if furl in self._remote_refs:
3217 d = defer.succeed(self._remote_refs[furl])
3218 else:
3219 d = self.tub.getReference(furl)
3220- d.addCallback(self.save_ref, furl)
3221+ d.addCallback(self._save_ref, furl)
3222 return d
3223
3224- def save_ref(self, ref, furl):
3225- """
3226- Cache a remote reference by its furl.
3227- """
3228+ def _save_ref(self, ref, furl):
3229+ """Cache a remote reference by its furl."""
3230 self._remote_refs[furl] = ref
3231 return ref
3232
3233- def get_task_client(self, furl_or_file=''):
3234- """
3235- Get the task controller client.
3236-
3237- This method is a simple wrapper around `get_client` that allow
3238- `furl_or_file` to be empty, in which case, the furls is taken
3239- from the default furl file given in the configuration.
3240-
3241- :Parameters:
3242- furl_or_file : str
3243- A furl or a filename containing a furl. If empty, the
3244- default furl_file will be used
3245-
3246- :Returns:
3247- A deferred to the actual client class
3248- """
3249- task_co = client_co['client_interfaces']['task']
3250- if furl_or_file:
3251- ff = furl_or_file
3252- else:
3253- ff = task_co['furl_file']
3254- return self.get_client(ff)
3255-
3256- def get_multiengine_client(self, furl_or_file=''):
3257- """
3258- Get the multiengine controller client.
3259-
3260- This method is a simple wrapper around `get_client` that allow
3261- `furl_or_file` to be empty, in which case, the furls is taken
3262- from the default furl file given in the configuration.
3263-
3264- :Parameters:
3265- furl_or_file : str
3266- A furl or a filename containing a furl. If empty, the
3267- default furl_file will be used
3268-
3269- :Returns:
3270- A deferred to the actual client class
3271- """
3272- task_co = client_co['client_interfaces']['multiengine']
3273- if furl_or_file:
3274- ff = furl_or_file
3275- else:
3276- ff = task_co['furl_file']
3277- return self.get_client(ff)
3278+ def get_task_client(self, profile='default', cluster_dir=None,
3279+ furl_or_file=None, ipython_dir=None,
3280+ delay=DELAY, max_tries=MAX_TRIES):
3281+ """Get the task controller client.
3282+
3283+ This method is a simple wrapper around `get_client` that passes in
3284+ the default name of the task client FURL file. Usually only
3285+ the ``profile`` option will be needed. If a FURL file can't be
3286+ found by its profile, use ``cluster_dir`` or ``furl_or_file``.
3287+
3288+ Parameters
3289+ ----------
3290+ profile : str
3291+ The name of a cluster directory profile (default="default"). The
3292+ cluster directory "cluster_<profile>" will be searched for
3293+ in ``os.getcwd()``, the ipython_dir and then in the directories
3294+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3295+ cluster_dir : str
3296+ The full path to a cluster directory. This is useful if profiles
3297+ are not being used.
3298+ furl_or_file : str
3299+ A furl or a filename containing a FURLK. This is useful if you
3300+ simply know the location of the FURL file.
3301+ ipython_dir : str
3302+ The location of the ipython_dir if different from the default.
3303+ This is used if the cluster directory is being found by profile.
3304+ delay : float
3305+ The initial delay between re-connection attempts. Susequent delays
3306+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3307+ max_tries : int
3308+ The max number of re-connection attempts.
3309+
3310+ Returns
3311+ -------
3312+ A deferred to the actual client class.
3313+ """
3314+ return self.get_client(
3315+ profile, cluster_dir, furl_or_file,
3316+ 'ipcontroller-tc.furl', ipython_dir,
3317+ delay, max_tries
3318+ )
3319+
3320+ def get_multiengine_client(self, profile='default', cluster_dir=None,
3321+ furl_or_file=None, ipython_dir=None,
3322+ delay=DELAY, max_tries=MAX_TRIES):
3323+ """Get the multiengine controller client.
3324+
3325+ This method is a simple wrapper around `get_client` that passes in
3326+ the default name of the task client FURL file. Usually only
3327+ the ``profile`` option will be needed. If a FURL file can't be
3328+ found by its profile, use ``cluster_dir`` or ``furl_or_file``.
3329+
3330+ Parameters
3331+ ----------
3332+ profile : str
3333+ The name of a cluster directory profile (default="default"). The
3334+ cluster directory "cluster_<profile>" will be searched for
3335+ in ``os.getcwd()``, the ipython_dir and then in the directories
3336+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3337+ cluster_dir : str
3338+ The full path to a cluster directory. This is useful if profiles
3339+ are not being used.
3340+ furl_or_file : str
3341+ A furl or a filename containing a FURLK. This is useful if you
3342+ simply know the location of the FURL file.
3343+ ipython_dir : str
3344+ The location of the ipython_dir if different from the default.
3345+ This is used if the cluster directory is being found by profile.
3346+ delay : float
3347+ The initial delay between re-connection attempts. Susequent delays
3348+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3349+ max_tries : int
3350+ The max number of re-connection attempts.
3351+
3352+ Returns
3353+ -------
3354+ A deferred to the actual client class.
3355+ """
3356+ return self.get_client(
3357+ profile, cluster_dir, furl_or_file,
3358+ 'ipcontroller-mec.furl', ipython_dir,
3359+ delay, max_tries
3360+ )
3361
3362- def get_client(self, furl_or_file):
3363- """
3364- Get a remote reference and wrap it in a client by furl.
3365-
3366- This method first gets a remote reference and then calls its
3367- `get_client_name` method to find the apprpriate client class
3368- that should be used to wrap the remote reference.
3369-
3370- :Parameters:
3371- furl_or_file : str
3372- A furl or a filename containing a furl
3373-
3374- :Returns:
3375- A deferred to the actual client class
3376- """
3377- furl = find_furl(furl_or_file)
3378- d = self.get_reference(furl)
3379- def wrap_remote_reference(rr):
3380+ def get_client(self, profile='default', cluster_dir=None,
3381+ furl_or_file=None, furl_file_name=None, ipython_dir=None,
3382+ delay=DELAY, max_tries=MAX_TRIES):
3383+ """Get a remote reference and wrap it in a client by furl.
3384+
3385+ This method is a simple wrapper around `get_client` that passes in
3386+ the default name of the task client FURL file. Usually only
3387+ the ``profile`` option will be needed. If a FURL file can't be
3388+ found by its profile, use ``cluster_dir`` or ``furl_or_file``.
3389+
3390+ Parameters
3391+ ----------
3392+ profile : str
3393+ The name of a cluster directory profile (default="default"). The
3394+ cluster directory "cluster_<profile>" will be searched for
3395+ in ``os.getcwd()``, the ipython_dir and then in the directories
3396+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3397+ cluster_dir : str
3398+ The full path to a cluster directory. This is useful if profiles
3399+ are not being used.
3400+ furl_or_file : str
3401+ A furl or a filename containing a FURL. This is useful if you
3402+ simply know the location of the FURL file.
3403+ furl_file_name : str
3404+ The filename (not the full path) of the FURL. This must be
3405+ provided if ``furl_or_file`` is not.
3406+ ipython_dir : str
3407+ The location of the ipython_dir if different from the default.
3408+ This is used if the cluster directory is being found by profile.
3409+ delay : float
3410+ The initial delay between re-connection attempts. Susequent delays
3411+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3412+ max_tries : int
3413+ The max number of re-connection attempts.
3414+
3415+ Returns
3416+ -------
3417+ A deferred to the actual client class. Or a failure to a
3418+ :exc:`FURLError`.
3419+ """
3420+ try:
3421+ furl_file = self._find_furl(
3422+ profile, cluster_dir, furl_or_file,
3423+ furl_file_name, ipython_dir
3424+ )
3425+ except FURLError:
3426+ return defer.fail(failure.Failure())
3427+
3428+ def _wrap_remote_reference(rr):
3429 d = rr.callRemote('get_client_name')
3430 d.addCallback(lambda name: import_item(name))
3431 def adapt(client_interface):
3432@@ -146,5 +272,502 @@
3433 d.addCallback(adapt)
3434
3435 return d
3436- d.addCallback(wrap_remote_reference)
3437- return d
3438+
3439+ d = self._try_to_connect(furl_file, delay, max_tries, attempt=0)
3440+ d.addCallback(_wrap_remote_reference)
3441+ d.addErrback(self._handle_error, furl_file)
3442+ return d
3443+
3444+ def _handle_error(self, f, furl_file):
3445+ raise ClientConnectorError('Could not connect to the controller '
3446+ 'using the FURL file. This usually means that i) the controller '
3447+ 'was not started or ii) a firewall was blocking the client from '
3448+ 'connecting to the controller: %s' % furl_file)
3449+
3450+ @inlineCallbacks
3451+ def _try_to_connect(self, furl_or_file, delay, max_tries, attempt):
3452+ """Try to connect to the controller with retry logic."""
3453+ if attempt < max_tries:
3454+ log.msg("Connecting [%r]" % attempt)
3455+ try:
3456+ self.furl = find_furl(furl_or_file)
3457+ # Uncomment this to see the FURL being tried.
3458+ # log.msg("FURL: %s" % self.furl)
3459+ rr = yield self.get_reference(self.furl)
3460+ log.msg("Connected: %s" % furl_or_file)
3461+ except:
3462+ if attempt==max_tries-1:
3463+ # This will propagate the exception all the way to the top
3464+ # where it can be handled.
3465+ raise
3466+ else:
3467+ yield sleep_deferred(delay)
3468+ rr = yield self._try_to_connect(
3469+ furl_or_file, 1.5*delay, max_tries, attempt+1
3470+ )
3471+ returnValue(rr)
3472+ else:
3473+ returnValue(rr)
3474+ else:
3475+ raise ClientConnectorError(
3476+ 'Could not connect to controller, max_tries (%r) exceeded. '
3477+ 'This usually means that i) the controller was not started, '
3478+ 'or ii) a firewall was blocking the client from connecting '
3479+ 'to the controller.' % max_tries
3480+ )
3481+
3482+
3483+class ClientConnector(object):
3484+ """A blocking version of a client connector.
3485+
3486+ This class creates a single :class:`Tub` instance and allows remote
3487+ references and client to be retrieved by their FURLs. Remote references
3488+ are cached locally and FURL files can be found using profiles and cluster
3489+ directories.
3490+ """
3491+
3492+ def __init__(self):
3493+ self.async_cc = AsyncClientConnector()
3494+
3495+ def get_task_client(self, profile='default', cluster_dir=None,
3496+ furl_or_file=None, ipython_dir=None,
3497+ delay=DELAY, max_tries=MAX_TRIES):
3498+ """Get the task client.
3499+
3500+ Usually only the ``profile`` option will be needed. If a FURL file
3501+ can't be found by its profile, use ``cluster_dir`` or
3502+ ``furl_or_file``.
3503+
3504+ Parameters
3505+ ----------
3506+ profile : str
3507+ The name of a cluster directory profile (default="default"). The
3508+ cluster directory "cluster_<profile>" will be searched for
3509+ in ``os.getcwd()``, the ipython_dir and then in the directories
3510+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3511+ cluster_dir : str
3512+ The full path to a cluster directory. This is useful if profiles
3513+ are not being used.
3514+ furl_or_file : str
3515+ A furl or a filename containing a FURLK. This is useful if you
3516+ simply know the location of the FURL file.
3517+ ipython_dir : str
3518+ The location of the ipython_dir if different from the default.
3519+ This is used if the cluster directory is being found by profile.
3520+ delay : float
3521+ The initial delay between re-connection attempts. Susequent delays
3522+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3523+ max_tries : int
3524+ The max number of re-connection attempts.
3525+
3526+ Returns
3527+ -------
3528+ The task client instance.
3529+ """
3530+ client = blockingCallFromThread(
3531+ self.async_cc.get_task_client, profile, cluster_dir,
3532+ furl_or_file, ipython_dir, delay, max_tries
3533+ )
3534+ return client.adapt_to_blocking_client()
3535+
3536+ def get_multiengine_client(self, profile='default', cluster_dir=None,
3537+ furl_or_file=None, ipython_dir=None,
3538+ delay=DELAY, max_tries=MAX_TRIES):
3539+ """Get the multiengine client.
3540+
3541+ Usually only the ``profile`` option will be needed. If a FURL file
3542+ can't be found by its profile, use ``cluster_dir`` or
3543+ ``furl_or_file``.
3544+
3545+ Parameters
3546+ ----------
3547+ profile : str
3548+ The name of a cluster directory profile (default="default"). The
3549+ cluster directory "cluster_<profile>" will be searched for
3550+ in ``os.getcwd()``, the ipython_dir and then in the directories
3551+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3552+ cluster_dir : str
3553+ The full path to a cluster directory. This is useful if profiles
3554+ are not being used.
3555+ furl_or_file : str
3556+ A furl or a filename containing a FURLK. This is useful if you
3557+ simply know the location of the FURL file.
3558+ ipython_dir : str
3559+ The location of the ipython_dir if different from the default.
3560+ This is used if the cluster directory is being found by profile.
3561+ delay : float
3562+ The initial delay between re-connection attempts. Susequent delays
3563+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3564+ max_tries : int
3565+ The max number of re-connection attempts.
3566+
3567+ Returns
3568+ -------
3569+ The multiengine client instance.
3570+ """
3571+ client = blockingCallFromThread(
3572+ self.async_cc.get_multiengine_client, profile, cluster_dir,
3573+ furl_or_file, ipython_dir, delay, max_tries
3574+ )
3575+ return client.adapt_to_blocking_client()
3576+
3577+ def get_client(self, profile='default', cluster_dir=None,
3578+ furl_or_file=None, ipython_dir=None,
3579+ delay=DELAY, max_tries=MAX_TRIES):
3580+ client = blockingCallFromThread(
3581+ self.async_cc.get_client, profile, cluster_dir,
3582+ furl_or_file, ipython_dir,
3583+ delay, max_tries
3584+ )
3585+ return client.adapt_to_blocking_client()
3586+
3587+
3588+class ClusterStateError(Exception):
3589+ pass
3590+
3591+
3592+class AsyncCluster(object):
3593+ """An class that wraps the :command:`ipcluster` script."""
3594+
3595+ def __init__(self, profile='default', cluster_dir=None, ipython_dir=None,
3596+ auto_create=False, auto_stop=True):
3597+ """Create a class to manage an IPython cluster.
3598+
3599+ This class calls the :command:`ipcluster` command with the right
3600+ options to start an IPython cluster. Typically a cluster directory
3601+ must be created (:command:`ipcluster create`) and configured before
3602+ using this class. Configuration is done by editing the
3603+ configuration files in the top level of the cluster directory.
3604+
3605+ Parameters
3606+ ----------
3607+ profile : str
3608+ The name of a cluster directory profile (default="default"). The
3609+ cluster directory "cluster_<profile>" will be searched for
3610+ in ``os.getcwd()``, the ipython_dir and then in the directories
3611+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3612+ cluster_dir : str
3613+ The full path to a cluster directory. This is useful if profiles
3614+ are not being used.
3615+ ipython_dir : str
3616+ The location of the ipython_dir if different from the default.
3617+ This is used if the cluster directory is being found by profile.
3618+ auto_create : bool
3619+ Automatically create the cluster directory it is dones't exist.
3620+ This will usually only make sense if using a local cluster
3621+ (default=False).
3622+ auto_stop : bool
3623+ Automatically stop the cluster when this instance is garbage
3624+ collected (default=True). This is useful if you want the cluster
3625+ to live beyond your current process. There is also an instance
3626+ attribute ``auto_stop`` to change this behavior.
3627+ """
3628+ self._setup_cluster_dir(profile, cluster_dir, ipython_dir, auto_create)
3629+ self.state = 'before'
3630+ self.launcher = None
3631+ self.client_connector = None
3632+ self.auto_stop = auto_stop
3633+
3634+ def __del__(self):
3635+ if self.auto_stop and self.state=='running':
3636+ print "Auto stopping the cluster..."
3637+ self.stop()
3638+
3639+ @property
3640+ def location(self):
3641+ if hasattr(self, 'cluster_dir_obj'):
3642+ return self.cluster_dir_obj.location
3643+ else:
3644+ return ''
3645+
3646+ @property
3647+ def running(self):
3648+ if self.state=='running':
3649+ return True
3650+ else:
3651+ return False
3652+
3653+ def _setup_cluster_dir(self, profile, cluster_dir, ipython_dir, auto_create):
3654+ if ipython_dir is None:
3655+ ipython_dir = get_ipython_dir()
3656+ if cluster_dir is not None:
3657+ try:
3658+ self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
3659+ except ClusterDirError:
3660+ pass
3661+ if profile is not None:
3662+ try:
3663+ self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
3664+ ipython_dir, profile)
3665+ except ClusterDirError:
3666+ pass
3667+ if auto_create or profile=='default':
3668+ # This should call 'ipcluster create --profile default
3669+ self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
3670+ ipython_dir, profile)
3671+ else:
3672+ raise ClusterDirError('Cluster dir not found.')
3673+
3674+ @make_deferred
3675+ def start(self, n=2):
3676+ """Start the IPython cluster with n engines.
3677+
3678+ Parameters
3679+ ----------
3680+ n : int
3681+ The number of engine to start.
3682+ """
3683+ # We might want to add logic to test if the cluster has started
3684+ # by another process....
3685+ if not self.state=='running':
3686+ self.launcher = IPClusterLauncher(os.getcwd())
3687+ self.launcher.ipcluster_n = n
3688+ self.launcher.ipcluster_subcommand = 'start'
3689+ d = self.launcher.start()
3690+ d.addCallback(self._handle_start)
3691+ return d
3692+ else:
3693+ raise ClusterStateError('Cluster is already running')
3694+
3695+ @make_deferred
3696+ def stop(self):
3697+ """Stop the IPython cluster if it is running."""
3698+ if self.state=='running':
3699+ d1 = self.launcher.observe_stop()
3700+ d1.addCallback(self._handle_stop)
3701+ d2 = self.launcher.stop()
3702+ return gatherBoth([d1, d2], consumeErrors=True)
3703+ else:
3704+ raise ClusterStateError("Cluster not running")
3705+
3706+ def get_multiengine_client(self, delay=DELAY, max_tries=MAX_TRIES):
3707+ """Get the multiengine client for the running cluster.
3708+
3709+ If this fails, it means that the cluster has not finished starting.
3710+ Usually waiting a few seconds are re-trying will solve this.
3711+ """
3712+ if self.client_connector is None:
3713+ self.client_connector = AsyncClientConnector()
3714+ return self.client_connector.get_multiengine_client(
3715+ cluster_dir=self.cluster_dir_obj.location,
3716+ delay=delay, max_tries=max_tries
3717+ )
3718+
3719+ def get_task_client(self, delay=DELAY, max_tries=MAX_TRIES):
3720+ """Get the task client for the running cluster.
3721+
3722+ If this fails, it means that the cluster has not finished starting.
3723+ Usually waiting a few seconds are re-trying will solve this.
3724+ """
3725+ if self.client_connector is None:
3726+ self.client_connector = AsyncClientConnector()
3727+ return self.client_connector.get_task_client(
3728+ cluster_dir=self.cluster_dir_obj.location,
3729+ delay=delay, max_tries=max_tries
3730+ )
3731+
3732+ def get_ipengine_logs(self):
3733+ return self.get_logs_by_name('ipengine')
3734+
3735+ def get_ipcontroller_logs(self):
3736+ return self.get_logs_by_name('ipcontroller')
3737+
3738+ def get_ipcluster_logs(self):
3739+ return self.get_logs_by_name('ipcluster')
3740+
3741+ def get_logs_by_name(self, name='ipcluster'):
3742+ log_dir = self.cluster_dir_obj.log_dir
3743+ logs = {}
3744+ for log in os.listdir(log_dir):
3745+ if log.startswith(name + '-') and log.endswith('.log'):
3746+ with open(os.path.join(log_dir, log), 'r') as f:
3747+ logs[log] = f.read()
3748+ return logs
3749+
3750+ def get_logs(self):
3751+ d = self.get_ipcluster_logs()
3752+ d.update(self.get_ipengine_logs())
3753+ d.update(self.get_ipcontroller_logs())
3754+ return d
3755+
3756+ def _handle_start(self, r):
3757+ self.state = 'running'
3758+
3759+ def _handle_stop(self, r):
3760+ self.state = 'after'
3761+
3762+
3763+class Cluster(object):
3764+
3765+
3766+ def __init__(self, profile='default', cluster_dir=None, ipython_dir=None,
3767+ auto_create=False, auto_stop=True):
3768+ """Create a class to manage an IPython cluster.
3769+
3770+ This class calls the :command:`ipcluster` command with the right
3771+ options to start an IPython cluster. Typically a cluster directory
3772+ must be created (:command:`ipcluster create`) and configured before
3773+ using this class. Configuration is done by editing the
3774+ configuration files in the top level of the cluster directory.
3775+
3776+ Parameters
3777+ ----------
3778+ profile : str
3779+ The name of a cluster directory profile (default="default"). The
3780+ cluster directory "cluster_<profile>" will be searched for
3781+ in ``os.getcwd()``, the ipython_dir and then in the directories
3782+ listed in the :env:`IPCLUSTER_DIR_PATH` environment variable.
3783+ cluster_dir : str
3784+ The full path to a cluster directory. This is useful if profiles
3785+ are not being used.
3786+ ipython_dir : str
3787+ The location of the ipython_dir if different from the default.
3788+ This is used if the cluster directory is being found by profile.
3789+ auto_create : bool
3790+ Automatically create the cluster directory it is dones't exist.
3791+ This will usually only make sense if using a local cluster
3792+ (default=False).
3793+ auto_stop : bool
3794+ Automatically stop the cluster when this instance is garbage
3795+ collected (default=True). This is useful if you want the cluster
3796+ to live beyond your current process. There is also an instance
3797+ attribute ``auto_stop`` to change this behavior.
3798+ """
3799+ self.async_cluster = AsyncCluster(
3800+ profile, cluster_dir, ipython_dir, auto_create, auto_stop
3801+ )
3802+ self.cluster_dir_obj = self.async_cluster.cluster_dir_obj
3803+ self.client_connector = None
3804+
3805+ def _set_auto_stop(self, value):
3806+ self.async_cluster.auto_stop = value
3807+
3808+ def _get_auto_stop(self):
3809+ return self.async_cluster.auto_stop
3810+
3811+ auto_stop = property(_get_auto_stop, _set_auto_stop)
3812+
3813+ @property
3814+ def location(self):
3815+ return self.async_cluster.location
3816+
3817+ @property
3818+ def running(self):
3819+ return self.async_cluster.running
3820+
3821+ def start(self, n=2):
3822+ """Start the IPython cluster with n engines.
3823+
3824+ Parameters
3825+ ----------
3826+ n : int
3827+ The number of engine to start.
3828+ """
3829+ return blockingCallFromThread(self.async_cluster.start, n)
3830+
3831+ def stop(self):
3832+ """Stop the IPython cluster if it is running."""
3833+ return blockingCallFromThread(self.async_cluster.stop)
3834+
3835+ def get_multiengine_client(self, delay=DELAY, max_tries=MAX_TRIES):
3836+ """Get the multiengine client for the running cluster.
3837+
3838+ This will try to attempt to the controller multiple times. If this
3839+ fails altogether, try looking at the following:
3840+ * Make sure the controller is starting properly by looking at its
3841+ log files.
3842+ * Make sure the controller is writing its FURL file in the location
3843+ expected by the client.
3844+ * Make sure a firewall on the controller's host is not blocking the
3845+ client from connecting.
3846+
3847+ Parameters
3848+ ----------
3849+ delay : float
3850+ The initial delay between re-connection attempts. Susequent delays
3851+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3852+ max_tries : int
3853+ The max number of re-connection attempts.
3854+ """
3855+ if self.client_connector is None:
3856+ self.client_connector = ClientConnector()
3857+ return self.client_connector.get_multiengine_client(
3858+ cluster_dir=self.cluster_dir_obj.location,
3859+ delay=delay, max_tries=max_tries
3860+ )
3861+
3862+ def get_task_client(self, delay=DELAY, max_tries=MAX_TRIES):
3863+ """Get the task client for the running cluster.
3864+
3865+ This will try to attempt to the controller multiple times. If this
3866+ fails altogether, try looking at the following:
3867+ * Make sure the controller is starting properly by looking at its
3868+ log files.
3869+ * Make sure the controller is writing its FURL file in the location
3870+ expected by the client.
3871+ * Make sure a firewall on the controller's host is not blocking the
3872+ client from connecting.
3873+
3874+ Parameters
3875+ ----------
3876+ delay : float
3877+ The initial delay between re-connection attempts. Susequent delays
3878+ get longer according to ``delay[i] = 1.5*delay[i-1]``.
3879+ max_tries : int
3880+ The max number of re-connection attempts.
3881+ """
3882+ if self.client_connector is None:
3883+ self.client_connector = ClientConnector()
3884+ return self.client_connector.get_task_client(
3885+ cluster_dir=self.cluster_dir_obj.location,
3886+ delay=delay, max_tries=max_tries
3887+ )
3888+
3889+ def __repr__(self):
3890+ s = "<Cluster(running=%r, location=%s)" % (self.running, self.location)
3891+ return s
3892+
3893+ def get_logs_by_name(self, name='ipcluter'):
3894+ """Get a dict of logs by process name (ipcluster, ipengine, etc.)"""
3895+ return self.async_cluster.get_logs_by_name(name)
3896+
3897+ def get_ipengine_logs(self):
3898+ """Get a dict of logs for all engines in this cluster."""
3899+ return self.async_cluster.get_ipengine_logs()
3900+
3901+ def get_ipcontroller_logs(self):
3902+ """Get a dict of logs for the controller in this cluster."""
3903+ return self.async_cluster.get_ipcontroller_logs()
3904+
3905+ def get_ipcluster_logs(self):
3906+ """Get a dict of the ipcluster logs for this cluster."""
3907+ return self.async_cluster.get_ipcluster_logs()
3908+
3909+ def get_logs(self):
3910+ """Get a dict of all logs for this cluster."""
3911+ return self.async_cluster.get_logs()
3912+
3913+ def _print_logs(self, logs):
3914+ for k, v in logs.iteritems():
3915+ print "==================================="
3916+ print "Logfile: %s" % k
3917+ print "==================================="
3918+ print v
3919+ print
3920+
3921+ def print_ipengine_logs(self):
3922+ """Print the ipengine logs for this cluster to stdout."""
3923+ self._print_logs(self.get_ipengine_logs())
3924+
3925+ def print_ipcontroller_logs(self):
3926+ """Print the ipcontroller logs for this cluster to stdout."""
3927+ self._print_logs(self.get_ipcontroller_logs())
3928+
3929+ def print_ipcluster_logs(self):
3930+ """Print the ipcluster logs for this cluster to stdout."""
3931+ self._print_logs(self.get_ipcluster_logs())
3932+
3933+ def print_logs(self):
3934+ """Print all the logs for this cluster to stdout."""
3935+ self._print_logs(self.get_logs())
3936+
3937
3938=== added file 'IPython/kernel/clusterdir.py'
3939--- IPython/kernel/clusterdir.py 1970-01-01 00:00:00 +0000
3940+++ IPython/kernel/clusterdir.py 2009-11-21 00:11:11 +0000
3941@@ -0,0 +1,475 @@
3942+#!/usr/bin/env python
3943+# encoding: utf-8
3944+"""
3945+The IPython cluster directory
3946+"""
3947+
3948+#-----------------------------------------------------------------------------
3949+# Copyright (C) 2008-2009 The IPython Development Team
3950+#
3951+# Distributed under the terms of the BSD License. The full license is in
3952+# the file COPYING, distributed as part of this software.
3953+#-----------------------------------------------------------------------------
3954+
3955+#-----------------------------------------------------------------------------
3956+# Imports
3957+#-----------------------------------------------------------------------------
3958+
3959+from __future__ import with_statement
3960+
3961+import os
3962+import shutil
3963+import sys
3964+
3965+from twisted.python import log
3966+
3967+from IPython.core import release
3968+from IPython.config.loader import PyFileConfigLoader
3969+from IPython.core.application import Application
3970+from IPython.core.component import Component
3971+from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
3972+from IPython.utils.traitlets import Unicode, Bool
3973+from IPython.utils import genutils
3974+
3975+#-----------------------------------------------------------------------------
3976+# Imports
3977+#-----------------------------------------------------------------------------
3978+
3979+
3980+class ClusterDirError(Exception):
3981+ pass
3982+
3983+
3984+class PIDFileError(Exception):
3985+ pass
3986+
3987+
3988+class ClusterDir(Component):
3989+ """An object to manage the cluster directory and its resources.
3990+
3991+ The cluster directory is used by :command:`ipcontroller`,
3992+ :command:`ipcontroller` and :command:`ipcontroller` to manage the
3993+ configuration, logging and security of these applications.
3994+
3995+ This object knows how to find, create and manage these directories. This
3996+ should be used by any code that want's to handle cluster directories.
3997+ """
3998+
3999+ security_dir_name = Unicode('security')
4000+ log_dir_name = Unicode('log')
4001+ pid_dir_name = Unicode('pid')
4002+ security_dir = Unicode(u'')
4003+ log_dir = Unicode(u'')
4004+ pid_dir = Unicode(u'')
4005+ location = Unicode(u'')
4006+
4007+ def __init__(self, location):
4008+ super(ClusterDir, self).__init__(None)
4009+ self.location = location
4010+
4011+ def _location_changed(self, name, old, new):
4012+ if not os.path.isdir(new):
4013+ os.makedirs(new)
4014+ self.security_dir = os.path.join(new, self.security_dir_name)
4015+ self.log_dir = os.path.join(new, self.log_dir_name)
4016+ self.pid_dir = os.path.join(new, self.pid_dir_name)
4017+ self.check_dirs()
4018+
4019+ def _log_dir_changed(self, name, old, new):
4020+ self.check_log_dir()
4021+
4022+ def check_log_dir(self):
4023+ if not os.path.isdir(self.log_dir):
4024+ os.mkdir(self.log_dir)
4025+
4026+ def _security_dir_changed(self, name, old, new):
4027+ self.check_security_dir()
4028+
4029+ def check_security_dir(self):
4030+ if not os.path.isdir(self.security_dir):
4031+ os.mkdir(self.security_dir, 0700)
4032+ os.chmod(self.security_dir, 0700)
4033+
4034+ def _pid_dir_changed(self, name, old, new):
4035+ self.check_pid_dir()
4036+
4037+ def check_pid_dir(self):
4038+ if not os.path.isdir(self.pid_dir):
4039+ os.mkdir(self.pid_dir, 0700)
4040+ os.chmod(self.pid_dir, 0700)
4041+
4042+ def check_dirs(self):
4043+ self.check_security_dir()
4044+ self.check_log_dir()
4045+ self.check_pid_dir()
4046+
4047+ def load_config_file(self, filename):
4048+ """Load a config file from the top level of the cluster dir.
4049+
4050+ Parameters
4051+ ----------
4052+ filename : unicode or str
4053+ The filename only of the config file that must be located in
4054+ the top-level of the cluster directory.
4055+ """
4056+ loader = PyFileConfigLoader(filename, self.location)
4057+ return loader.load_config()
4058+
4059+ def copy_config_file(self, config_file, path=None, overwrite=False):
4060+ """Copy a default config file into the active cluster directory.
4061+
4062+ Default configuration files are kept in :mod:`IPython.config.default`.
4063+ This function moves these from that location to the working cluster
4064+ directory.
4065+ """
4066+ if path is None:
4067+ import IPython.config.default
4068+ path = IPython.config.default.__file__.split(os.path.sep)[:-1]
4069+ path = os.path.sep.join(path)
4070+ src = os.path.join(path, config_file)
4071+ dst = os.path.join(self.location, config_file)
4072+ if not os.path.isfile(dst) or overwrite:
4073+ shutil.copy(src, dst)
4074+
4075+ def copy_all_config_files(self, path=None, overwrite=False):
4076+ """Copy all config files into the active cluster directory."""
4077+ for f in [u'ipcontroller_config.py', u'ipengine_config.py',
4078+ u'ipcluster_config.py']:
4079+ self.copy_config_file(f, path=path, overwrite=overwrite)
4080+
4081+ @classmethod
4082+ def create_cluster_dir(csl, cluster_dir):
4083+ """Create a new cluster directory given a full path.
4084+
4085+ Parameters
4086+ ----------
4087+ cluster_dir : str
4088+ The full path to the cluster directory. If it does exist, it will
4089+ be used. If not, it will be created.
4090+ """
4091+ return ClusterDir(cluster_dir)
4092+
4093+ @classmethod
4094+ def create_cluster_dir_by_profile(cls, path, profile=u'default'):
4095+ """Create a cluster dir by profile name and path.
4096+
4097+ Parameters
4098+ ----------
4099+ path : str
4100+ The path (directory) to put the cluster directory in.
4101+ profile : str
4102+ The name of the profile. The name of the cluster directory will
4103+ be "cluster_<profile>".
4104+ """
4105+ if not os.path.isdir(path):
4106+ raise ClusterDirError('Directory not found: %s' % path)
4107+ cluster_dir = os.path.join(path, u'cluster_' + profile)
4108+ return ClusterDir(cluster_dir)
4109+
4110+ @classmethod
4111+ def find_cluster_dir_by_profile(cls, ipython_dir, profile=u'default'):
4112+ """Find an existing cluster dir by profile name, return its ClusterDir.
4113+
4114+ This searches through a sequence of paths for a cluster dir. If it
4115+ is not found, a :class:`ClusterDirError` exception will be raised.
4116+
4117+ The search path algorithm is:
4118+ 1. ``os.getcwd()``
4119+ 2. ``ipython_dir``
4120+ 3. The directories found in the ":" separated
4121+ :env:`IPCLUSTER_DIR_PATH` environment variable.
4122+
4123+ Parameters
4124+ ----------
4125+ ipython_dir : unicode or str
4126+ The IPython directory to use.
4127+ profile : unicode or str
4128+ The name of the profile. The name of the cluster directory
4129+ will be "cluster_<profile>".
4130+ """
4131+ dirname = u'cluster_' + profile
4132+ cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
4133+ if cluster_dir_paths:
4134+ cluster_dir_paths = cluster_dir_paths.split(':')
4135+ else:
4136+ cluster_dir_paths = []
4137+ paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
4138+ for p in paths:
4139+ cluster_dir = os.path.join(p, dirname)
4140+ if os.path.isdir(cluster_dir):
4141+ return ClusterDir(cluster_dir)
4142+ else:
4143+ raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
4144+
4145+ @classmethod
4146+ def find_cluster_dir(cls, cluster_dir):
4147+ """Find/create a cluster dir and return its ClusterDir.
4148+
4149+ This will create the cluster directory if it doesn't exist.
4150+
4151+ Parameters
4152+ ----------
4153+ cluster_dir : unicode or str
4154+ The path of the cluster directory. This is expanded using
4155+ :func:`IPython.utils.genutils.expand_path`.
4156+ """
4157+ cluster_dir = genutils.expand_path(cluster_dir)
4158+ if not os.path.isdir(cluster_dir):
4159+ raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
4160+ return ClusterDir(cluster_dir)
4161+
4162+
4163+class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
4164+ """Default command line options for IPython cluster applications."""
4165+
4166+ def _add_other_arguments(self):
4167+ self.parser.add_argument('--ipython-dir',
4168+ dest='Global.ipython_dir',type=unicode,
4169+ help='Set to override default location of Global.ipython_dir.',
4170+ default=NoConfigDefault,
4171+ metavar='Global.ipython_dir'
4172+ )
4173+ self.parser.add_argument('-p', '--profile',
4174+ dest='Global.profile',type=unicode,
4175+ help='The string name of the profile to be used. This determines '
4176+ 'the name of the cluster dir as: cluster_<profile>. The default profile '
4177+ 'is named "default". The cluster directory is resolve this way '
4178+ 'if the --cluster-dir option is not used.',
4179+ default=NoConfigDefault,
4180+ metavar='Global.profile'
4181+ )
4182+ self.parser.add_argument('--log-level',
4183+ dest="Global.log_level",type=int,
4184+ help='Set the log level (0,10,20,30,40,50). Default is 30.',
4185+ default=NoConfigDefault,
4186+ metavar="Global.log_level"
4187+ )
4188+ self.parser.add_argument('--cluster-dir',
4189+ dest='Global.cluster_dir',type=unicode,
4190+ help='Set the cluster dir. This overrides the logic used by the '
4191+ '--profile option.',
4192+ default=NoConfigDefault,
4193+ metavar='Global.cluster_dir'
4194+ ),
4195+ self.parser.add_argument('--work-dir',
4196+ dest='Global.work_dir',type=unicode,
4197+ help='Set the working dir for the process.',
4198+ default=NoConfigDefault,
4199+ metavar='Global.work_dir'
4200+ )
4201+ self.parser.add_argument('--clean-logs',
4202+ dest='Global.clean_logs', action='store_true',
4203+ help='Delete old log flies before starting.',
4204+ default=NoConfigDefault
4205+ )
4206+ self.parser.add_argument('--no-clean-logs',
4207+ dest='Global.clean_logs', action='store_false',
4208+ help="Don't Delete old log flies before starting.",
4209+ default=NoConfigDefault
4210+ )
4211+
4212+class ApplicationWithClusterDir(Application):
4213+ """An application that puts everything into a cluster directory.
4214+
4215+ Instead of looking for things in the ipython_dir, this type of application
4216+ will use its own private directory called the "cluster directory"
4217+ for things like config files, log files, etc.
4218+
4219+ The cluster directory is resolved as follows:
4220+
4221+ * If the ``--cluster-dir`` option is given, it is used.
4222+ * If ``--cluster-dir`` is not given, the application directory is
4223+ resolve using the profile name as ``cluster_<profile>``. The search
4224+ path for this directory is then i) cwd if it is found there
4225+ and ii) in ipython_dir otherwise.
4226+
4227+ The config file for the application is to be put in the cluster
4228+ dir and named the value of the ``config_file_name`` class attribute.
4229+ """
4230+
4231+ auto_create_cluster_dir = True
4232+
4233+ def create_default_config(self):
4234+ super(ApplicationWithClusterDir, self).create_default_config()
4235+ self.default_config.Global.profile = u'default'
4236+ self.default_config.Global.cluster_dir = u''
4237+ self.default_config.Global.work_dir = os.getcwd()
4238+ self.default_config.Global.log_to_file = False
4239+ self.default_config.Global.clean_logs = False
4240+
4241+ def create_command_line_config(self):
4242+ """Create and return a command line config loader."""
4243+ return AppWithClusterDirArgParseConfigLoader(
4244+ description=self.description,
4245+ version=release.version
4246+ )
4247+
4248+ def find_resources(self):
4249+ """This resolves the cluster directory.
4250+
4251+ This tries to find the cluster directory and if successful, it will
4252+ have done:
4253+ * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
4254+ the application.
4255+ * Sets ``self.cluster_dir`` attribute of the application and config
4256+ objects.
4257+
4258+ The algorithm used for this is as follows:
4259+ 1. Try ``Global.cluster_dir``.
4260+ 2. Try using ``Global.profile``.
4261+ 3. If both of these fail and ``self.auto_create_cluster_dir`` is
4262+ ``True``, then create the new cluster dir in the IPython directory.
4263+ 4. If all fails, then raise :class:`ClusterDirError`.
4264+ """
4265+
4266+ try:
4267+ cluster_dir = self.command_line_config.Global.cluster_dir
4268+ except AttributeError:
4269+ cluster_dir = self.default_config.Global.cluster_dir
4270+ cluster_dir = genutils.expand_path(cluster_dir)
4271+ try:
4272+ self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
4273+ except ClusterDirError:
4274+ pass
4275+ else:
4276+ self.log.info('Using existing cluster dir: %s' % \
4277+ self.cluster_dir_obj.location
4278+ )
4279+ self.finish_cluster_dir()
4280+ return
4281+
4282+ try:
4283+ self.profile = self.command_line_config.Global.profile
4284+ except AttributeError:
4285+ self.profile = self.default_config.Global.profile
4286+ try:
4287+ self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
4288+ self.ipython_dir, self.profile)
4289+ except ClusterDirError:
4290+ pass
4291+ else:
4292+ self.log.info('Using existing cluster dir: %s' % \
4293+ self.cluster_dir_obj.location
4294+ )
4295+ self.finish_cluster_dir()
4296+ return
4297+
4298+ if self.auto_create_cluster_dir:
4299+ self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
4300+ self.ipython_dir, self.profile
4301+ )
4302+ self.log.info('Creating new cluster dir: %s' % \
4303+ self.cluster_dir_obj.location
4304+ )
4305+ self.finish_cluster_dir()
4306+ else:
4307+ raise ClusterDirError('Could not find a valid cluster directory.')
4308+
4309+ def finish_cluster_dir(self):
4310+ # Set the cluster directory
4311+ self.cluster_dir = self.cluster_dir_obj.location
4312+
4313+ # These have to be set because they could be different from the one
4314+ # that we just computed. Because command line has the highest
4315+ # priority, this will always end up in the master_config.
4316+ self.default_config.Global.cluster_dir = self.cluster_dir
4317+ self.command_line_config.Global.cluster_dir = self.cluster_dir
4318+
4319+ # Set the search path to the cluster directory
4320+ self.config_file_paths = (self.cluster_dir,)
4321+
4322+ def find_config_file_name(self):
4323+ """Find the config file name for this application."""
4324+ # For this type of Application it should be set as a class attribute.
4325+ if not hasattr(self, 'config_file_name'):
4326+ self.log.critical("No config filename found")
4327+
4328+ def find_config_file_paths(self):
4329+ # Set the search path to the cluster directory
4330+ self.config_file_paths = (self.cluster_dir,)
4331+
4332+ def pre_construct(self):
4333+ # The log and security dirs were set earlier, but here we put them
4334+ # into the config and log them.
4335+ config = self.master_config
4336+ sdir = self.cluster_dir_obj.security_dir
4337+ self.security_dir = config.Global.security_dir = sdir
4338+ ldir = self.cluster_dir_obj.log_dir
4339+ self.log_dir = config.Global.log_dir = ldir
4340+ pdir = self.cluster_dir_obj.pid_dir
4341+ self.pid_dir = config.Global.pid_dir = pdir
4342+ self.log.info("Cluster directory set to: %s" % self.cluster_dir)
4343+ config.Global.work_dir = unicode(genutils.expand_path(config.Global.work_dir))
4344+ # Change to the working directory. We do this just before construct
4345+ # is called so all the components there have the right working dir.
4346+ self.to_work_dir()
4347+
4348+ def to_work_dir(self):
4349+ wd = self.master_config.Global.work_dir
4350+ if unicode(wd) != unicode(os.getcwd()):
4351+ os.chdir(wd)
4352+ self.log.info("Changing to working dir: %s" % wd)
4353+
4354+ def start_logging(self):
4355+ # Remove old log files
4356+ if self.master_config.Global.clean_logs:
4357+ log_dir = self.master_config.Global.log_dir
4358+ for f in os.listdir(log_dir):
4359+ if f.startswith(self.name + u'-') and f.endswith('.log'):
4360+ os.remove(os.path.join(log_dir, f))
4361+ # Start logging to the new log file
4362+ if self.master_config.Global.log_to_file:
4363+ log_filename = self.name + u'-' + str(os.getpid()) + u'.log'
4364+ logfile = os.path.join(self.log_dir, log_filename)
4365+ open_log_file = open(logfile, 'w')
4366+ else:
4367+ open_log_file = sys.stdout
4368+ log.startLogging(open_log_file)
4369+
4370+ def write_pid_file(self, overwrite=False):
4371+ """Create a .pid file in the pid_dir with my pid.
4372+
4373+ This must be called after pre_construct, which sets `self.pid_dir`.
4374+ This raises :exc:`PIDFileError` if the pid file exists already.
4375+ """
4376+ pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
4377+ if os.path.isfile(pid_file):
4378+ pid = self.get_pid_from_file()
4379+ if not overwrite:
4380+ raise PIDFileError(
4381+ 'The pid file [%s] already exists. \nThis could mean that this '
4382+ 'server is already running with [pid=%s].' % (pid_file, pid)
4383+ )
4384+ with open(pid_file, 'w') as f:
4385+ self.log.info("Creating pid file: %s" % pid_file)
4386+ f.write(repr(os.getpid())+'\n')
4387+
4388+ def remove_pid_file(self):
4389+ """Remove the pid file.
4390+
4391+ This should be called at shutdown by registering a callback with
4392+ :func:`reactor.addSystemEventTrigger`. This needs to return
4393+ ``None``.
4394+ """
4395+ pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
4396+ if os.path.isfile(pid_file):
4397+ try:
4398+ self.log.info("Removing pid file: %s" % pid_file)
4399+ os.remove(pid_file)
4400+ except:
4401+ self.log.warn("Error removing the pid file: %s" % pid_file)
4402+
4403+ def get_pid_from_file(self):
4404+ """Get the pid from the pid file.
4405+
4406+ If the pid file doesn't exist a :exc:`PIDFileError` is raised.
4407+ """
4408+ pid_file = os.path.join(self.pid_dir, self.name + u'.pid')
4409+ if os.path.isfile(pid_file):
4410+ with open(pid_file, 'r') as f:
4411+ pid = int(f.read().strip())
4412+ return pid
4413+ else:
4414+ raise PIDFileError('pid file not found: %s' % pid_file)
4415+
4416+
4417
4418=== removed directory 'IPython/kernel/config'
4419=== removed file 'IPython/kernel/config/__init__.py'
4420--- IPython/kernel/config/__init__.py 2009-07-02 02:49:32 +0000
4421+++ IPython/kernel/config/__init__.py 1970-01-01 00:00:00 +0000
4422@@ -1,126 +0,0 @@
4423-# encoding: utf-8
4424-
4425-"""Default kernel configuration."""
4426-
4427-__docformat__ = "restructuredtext en"
4428-
4429-#-------------------------------------------------------------------------------
4430-# Copyright (C) 2008 The IPython Development Team
4431-#
4432-# Distributed under the terms of the BSD License. The full license is in
4433-# the file COPYING, distributed as part of this software.
4434-#-------------------------------------------------------------------------------
4435-
4436-#-------------------------------------------------------------------------------
4437-# Imports
4438-#-------------------------------------------------------------------------------
4439-
4440-import os, sys
4441-from os.path import join as pjoin
4442-
4443-from IPython.external.configobj import ConfigObj
4444-from IPython.config.api import ConfigObjManager
4445-from IPython.utils.genutils import get_ipython_dir, get_security_dir
4446-
4447-default_kernel_config = ConfigObj()
4448-
4449-# This will raise OSError if ipythondir doesn't exist.
4450-security_dir = get_security_dir()
4451-
4452-#-------------------------------------------------------------------------------
4453-# Engine Configuration
4454-#-------------------------------------------------------------------------------
4455-
4456-engine_config = dict(
4457- logfile = '', # Empty means log to stdout
4458- furl_file = pjoin(security_dir, 'ipcontroller-engine.furl')
4459-)
4460-
4461-#-------------------------------------------------------------------------------
4462-# MPI Configuration
4463-#-------------------------------------------------------------------------------
4464-
4465-mpi_config = dict(
4466- mpi4py = """from mpi4py import MPI as mpi
4467-mpi.size = mpi.COMM_WORLD.Get_size()
4468-mpi.rank = mpi.COMM_WORLD.Get_rank()
4469-""",
4470- pytrilinos = """from PyTrilinos import Epetra
4471-class SimpleStruct:
4472- pass
4473-mpi = SimpleStruct()
4474-mpi.rank = 0
4475-mpi.size = 0
4476-""",
4477- default = ''
4478-)
4479-
4480-#-------------------------------------------------------------------------------
4481-# Controller Configuration
4482-#-------------------------------------------------------------------------------
4483-
4484-controller_config = dict(
4485-
4486- logfile = '', # Empty means log to stdout
4487- import_statement = '',
4488- reuse_furls = False, # If False, old furl files are deleted
4489-
4490- engine_tub = dict(
4491- ip = '', # Empty string means all interfaces
4492- port = 0, # 0 means pick a port for me
4493- location = '', # Empty string means try to set automatically
4494- secure = True,
4495- cert_file = pjoin(security_dir, 'ipcontroller-engine.pem'),
4496- ),
4497- engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase',
4498- engine_furl_file = pjoin(security_dir, 'ipcontroller-engine.furl'),
4499-
4500- controller_interfaces = dict(
4501- # multiengine = dict(
4502- # controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
4503- # fc_interface = 'IPython.kernel.multienginefc.IFCMultiEngine',
4504- # furl_file = 'ipcontroller-mec.furl'
4505- # ),
4506- task = dict(
4507- controller_interface = 'IPython.kernel.task.ITaskController',
4508- fc_interface = 'IPython.kernel.taskfc.IFCTaskController',
4509- furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
4510- ),
4511- multiengine = dict(
4512- controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
4513- fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
4514- furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
4515- )
4516- ),
4517-
4518- client_tub = dict(
4519- ip = '', # Empty string means all interfaces
4520- port = 0, # 0 means pick a port for me
4521- location = '', # Empty string means try to set automatically
4522- secure = True,
4523- cert_file = pjoin(security_dir, 'ipcontroller-client.pem')
4524- )
4525-)
4526-
4527-#-------------------------------------------------------------------------------
4528-# Client Configuration
4529-#-------------------------------------------------------------------------------
4530-
4531-client_config = dict(
4532- client_interfaces = dict(
4533- task = dict(
4534- furl_file = pjoin(security_dir, 'ipcontroller-tc.furl')
4535- ),
4536- multiengine = dict(
4537- furl_file = pjoin(security_dir, 'ipcontroller-mec.furl')
4538- )
4539- )
4540-)
4541-
4542-default_kernel_config['engine'] = engine_config
4543-default_kernel_config['mpi'] = mpi_config
4544-default_kernel_config['controller'] = controller_config
4545-default_kernel_config['client'] = client_config
4546-
4547-
4548-config_manager = ConfigObjManager(default_kernel_config, 'IPython.kernel.ini')
4549\ No newline at end of file
4550
4551=== added file 'IPython/kernel/configobjfactory.py'
4552--- IPython/kernel/configobjfactory.py 1970-01-01 00:00:00 +0000
4553+++ IPython/kernel/configobjfactory.py 2009-11-21 00:11:11 +0000
4554@@ -0,0 +1,79 @@
4555+#!/usr/bin/env python
4556+# encoding: utf-8
4557+"""
4558+A class for creating a Twisted service that is configured using IPython's
4559+configuration system.
4560+"""
4561+
4562+#-----------------------------------------------------------------------------
4563+# Copyright (C) 2008-2009 The IPython Development Team
4564+#
4565+# Distributed under the terms of the BSD License. The full license is in
4566+# the file COPYING, distributed as part of this software.
4567+#-----------------------------------------------------------------------------
4568+
4569+#-----------------------------------------------------------------------------
4570+# Imports
4571+#-----------------------------------------------------------------------------
4572+
4573+import zope.interface as zi
4574+
4575+from IPython.core.component import Component
4576+
4577+#-----------------------------------------------------------------------------
4578+# Code
4579+#-----------------------------------------------------------------------------
4580+
4581+
4582+class IConfiguredObjectFactory(zi.Interface):
4583+ """I am a component that creates a configured object.
4584+
4585+ This class is useful if you want to configure a class that is not a
4586+ subclass of :class:`IPython.core.component.Component`.
4587+ """
4588+
4589+ def __init__(config):
4590+ """Get ready to configure the object using config."""
4591+
4592+ def create():
4593+ """Return an instance of the configured object."""
4594+
4595+
4596+class ConfiguredObjectFactory(Component):
4597+
4598+ zi.implements(IConfiguredObjectFactory)
4599+
4600+ def __init__(self, config):
4601+ super(ConfiguredObjectFactory, self).__init__(None, config=config)
4602+
4603+ def create(self):
4604+ raise NotImplementedError('create must be implemented in a subclass')
4605+
4606+
4607+class IAdaptedConfiguredObjectFactory(zi.Interface):
4608+ """I am a component that adapts and configures an object.
4609+
4610+ This class is useful if you have the adapt an instance and configure it.
4611+ """
4612+
4613+ def __init__(config, adaptee=None):
4614+ """Get ready to adapt adaptee and then configure it using config."""
4615+
4616+ def create():
4617+ """Return an instance of the adapted and configured object."""
4618+
4619+
4620+class AdaptedConfiguredObjectFactory(Component):
4621+
4622+ # zi.implements(IAdaptedConfiguredObjectFactory)
4623+
4624+ def __init__(self, config, adaptee):
4625+ # print
4626+ # print "config pre:", config
4627+ super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config)
4628+ # print
4629+ # print "config post:", config
4630+ self.adaptee = adaptee
4631+
4632+ def create(self):
4633+ raise NotImplementedError('create must be implemented in a subclass')
4634\ No newline at end of file
4635
4636=== removed directory 'IPython/kernel/core/config'
4637=== removed file 'IPython/kernel/core/config/__init__.py'
4638--- IPython/kernel/core/config/__init__.py 2008-06-06 19:41:55 +0000
4639+++ IPython/kernel/core/config/__init__.py 1970-01-01 00:00:00 +0000
4640@@ -1,25 +0,0 @@
4641-# encoding: utf-8
4642-
4643-__docformat__ = "restructuredtext en"
4644-
4645-#-------------------------------------------------------------------------------
4646-# Copyright (C) 2008 The IPython Development Team
4647-#
4648-# Distributed under the terms of the BSD License. The full license is in
4649-# the file COPYING, distributed as part of this software.
4650-#-------------------------------------------------------------------------------
4651-
4652-#-------------------------------------------------------------------------------
4653-# Imports
4654-#-------------------------------------------------------------------------------
4655-
4656-from IPython.external.configobj import ConfigObj
4657-from IPython.config.api import ConfigObjManager
4658-
4659-default_core_config = ConfigObj()
4660-default_core_config['shell'] = dict(
4661- shell_class = 'IPython.kernel.core.interpreter.Interpreter',
4662- import_statement = ''
4663-)
4664-
4665-config_manager = ConfigObjManager(default_core_config, 'IPython.kernel.core.ini')
4666\ No newline at end of file
4667
4668=== modified file 'IPython/kernel/engineconnector.py'
4669--- IPython/kernel/engineconnector.py 2009-04-18 21:53:37 +0000
4670+++ IPython/kernel/engineconnector.py 2009-11-21 00:11:11 +0000
4671@@ -1,32 +1,38 @@
4672+#!/usr/bin/env python
4673 # encoding: utf-8
4674
4675 """A class that manages the engines connection to the controller."""
4676
4677-__docformat__ = "restructuredtext en"
4678-
4679-#-------------------------------------------------------------------------------
4680-# Copyright (C) 2008 The IPython Development Team
4681+#-----------------------------------------------------------------------------
4682+# Copyright (C) 2008-2009 The IPython Development Team
4683 #
4684 # Distributed under the terms of the BSD License. The full license is in
4685 # the file COPYING, distributed as part of this software.
4686-#-------------------------------------------------------------------------------
4687+#-----------------------------------------------------------------------------
4688
4689-#-------------------------------------------------------------------------------
4690+#-----------------------------------------------------------------------------
4691 # Imports
4692-#-------------------------------------------------------------------------------
4693+#-----------------------------------------------------------------------------
4694
4695 import os
4696 import cPickle as pickle
4697
4698 from twisted.python import log, failure
4699 from twisted.internet import defer
4700+from twisted.internet.defer import inlineCallbacks, returnValue
4701
4702-from IPython.kernel.fcutil import find_furl
4703+from IPython.kernel.fcutil import find_furl, validate_furl_or_file
4704 from IPython.kernel.enginefc import IFCEngine
4705+from IPython.kernel.twistedutil import sleep_deferred, make_deferred
4706
4707-#-------------------------------------------------------------------------------
4708+#-----------------------------------------------------------------------------
4709 # The ClientConnector class
4710-#-------------------------------------------------------------------------------
4711+#-----------------------------------------------------------------------------
4712+
4713+
4714+class EngineConnectorError(Exception):
4715+ pass
4716+
4717
4718 class EngineConnector(object):
4719 """Manage an engines connection to a controller.
4720@@ -38,8 +44,10 @@
4721
4722 def __init__(self, tub):
4723 self.tub = tub
4724-
4725- def connect_to_controller(self, engine_service, furl_or_file):
4726+
4727+ @make_deferred
4728+ def connect_to_controller(self, engine_service, furl_or_file,
4729+ delay=0.1, max_tries=10):
4730 """
4731 Make a connection to a controller specified by a furl.
4732
4733@@ -48,34 +56,73 @@
4734 foolscap URL contains all the information needed to connect to the
4735 controller, including the ip and port as well as any encryption and
4736 authentication information needed for the connection.
4737-
4738+
4739 After getting a reference to the controller, this method calls the
4740 `register_engine` method of the controller to actually register the
4741 engine.
4742-
4743- :Parameters:
4744- engine_service : IEngineBase
4745- An instance of an `IEngineBase` implementer
4746- furl_or_file : str
4747- A furl or a filename containing a furl
4748+
4749+ This method will try to connect to the controller multiple times with
4750+ a delay in between. Each time the FURL file is read anew.
4751+
4752+ Parameters
4753+ __________
4754+ engine_service : IEngineBase
4755+ An instance of an `IEngineBase` implementer
4756+ furl_or_file : str
4757+ A furl or a filename containing a furl
4758+ delay : float
4759+ The intial time to wait between connection attempts. Subsequent
4760+ attempts have increasing delays.
4761+ max_tries : int
4762+ The maximum number of connection attempts.
4763+
4764+ Returns
4765+ -------
4766+ A deferred to the registered client or a failure to an error
4767+ like :exc:`FURLError`.
4768 """
4769 if not self.tub.running:
4770 self.tub.startService()
4771 self.engine_service = engine_service
4772 self.engine_reference = IFCEngine(self.engine_service)
4773- try:
4774- self.furl = find_furl(furl_or_file)
4775- except ValueError:
4776- return defer.fail(failure.Failure())
4777+
4778+ validate_furl_or_file(furl_or_file)
4779+ d = self._try_to_connect(furl_or_file, delay, max_tries, attempt=0)
4780+ d.addCallback(self._register)
4781+ return d
4782+
4783+ @inlineCallbacks
4784+ def _try_to_connect(self, furl_or_file, delay, max_tries, attempt):
4785+ """Try to connect to the controller with retry logic."""
4786+ if attempt < max_tries:
4787+ log.msg("Attempting to connect to controller [%r]: %s" % \
4788+ (attempt, furl_or_file))
4789+ try:
4790+ self.furl = find_furl(furl_or_file)
4791+ # Uncomment this to see the FURL being tried.
4792+ # log.msg("FURL: %s" % self.furl)
4793+ rr = yield self.tub.getReference(self.furl)
4794+ except:
4795+ if attempt==max_tries-1:
4796+ # This will propagate the exception all the way to the top
4797+ # where it can be handled.
4798+ raise
4799+ else:
4800+ yield sleep_deferred(delay)
4801+ rr = yield self._try_to_connect(
4802+ furl_or_file, 1.5*delay, max_tries, attempt+1
4803+ )
4804+ # rr becomes an int when there is a connection!!!
4805+ returnValue(rr)
4806+ else:
4807+ returnValue(rr)
4808 else:
4809- d = self.tub.getReference(self.furl)
4810- d.addCallbacks(self._register, self._log_failure)
4811- return d
4812-
4813- def _log_failure(self, reason):
4814- log.err('EngineConnector: engine registration failed:')
4815- log.err(reason)
4816- return reason
4817+ raise EngineConnectorError(
4818+ 'Could not connect to controller, max_tries (%r) exceeded. '
4819+ 'This usually means that i) the controller was not started, '
4820+ 'or ii) a firewall was blocking the engine from connecting '
4821+ 'to the controller.' % max_tries
4822+ )
4823
4824 def _register(self, rr):
4825 self.remote_ref = rr
4826@@ -83,7 +130,7 @@
4827 desired_id = self.engine_service.id
4828 d = self.remote_ref.callRemote('register_engine', self.engine_reference,
4829 desired_id, os.getpid(), pickle.dumps(self.engine_service.properties,2))
4830- return d.addCallbacks(self._reference_sent, self._log_failure)
4831+ return d.addCallback(self._reference_sent)
4832
4833 def _reference_sent(self, registration_dict):
4834 self.engine_service.id = registration_dict['id']
4835
4836=== modified file 'IPython/kernel/error.py'
4837--- IPython/kernel/error.py 2009-08-04 01:18:28 +0000
4838+++ IPython/kernel/error.py 2009-11-21 00:11:11 +0000
4839@@ -127,9 +127,11 @@
4840 class CompositeError(KernelError):
4841 def __init__(self, message, elist):
4842 Exception.__init__(self, *(message, elist))
4843- self.message = message
4844+ # Don't use pack_exception because it will conflict with the .message
4845+ # attribute that is being deprecated in 2.6 and beyond.
4846+ self.msg = message
4847 self.elist = elist
4848-
4849+
4850 def _get_engine_str(self, ev):
4851 try:
4852 ei = ev._ipython_engine_info
4853@@ -137,7 +139,7 @@
4854 return '[Engine Exception]'
4855 else:
4856 return '[%i:%s]: ' % (ei['engineid'], ei['method'])
4857-
4858+
4859 def _get_traceback(self, ev):
4860 try:
4861 tb = ev._ipython_traceback_text
4862@@ -145,14 +147,14 @@
4863 return 'No traceback available'
4864 else:
4865 return tb
4866-
4867+
4868 def __str__(self):
4869- s = str(self.message)
4870+ s = str(self.msg)
4871 for et, ev, etb in self.elist:
4872 engine_str = self._get_engine_str(ev)
4873 s = s + '\n' + engine_str + str(et.__name__) + ': ' + str(ev)
4874 return s
4875-
4876+
4877 def print_tracebacks(self, excid=None):
4878 if excid is None:
4879 for (et,ev,etb) in self.elist:
4880
4881=== modified file 'IPython/kernel/fcutil.py'
4882--- IPython/kernel/fcutil.py 2008-06-06 19:41:55 +0000
4883+++ IPython/kernel/fcutil.py 2009-11-21 00:11:11 +0000
4884@@ -1,27 +1,62 @@
4885+#!/usr/bin/env python
4886 # encoding: utf-8
4887-
4888-"""Foolscap related utilities."""
4889-
4890-__docformat__ = "restructuredtext en"
4891-
4892-#-------------------------------------------------------------------------------
4893-# Copyright (C) 2008 The IPython Development Team
4894+"""
4895+Foolscap related utilities.
4896+"""
4897+
4898+#-----------------------------------------------------------------------------
4899+# Copyright (C) 2008-2009 The IPython Development Team
4900 #
4901 # Distributed under the terms of the BSD License. The full license is in
4902 # the file COPYING, distributed as part of this software.
4903-#-------------------------------------------------------------------------------
4904+#-----------------------------------------------------------------------------
4905
4906-#-------------------------------------------------------------------------------
4907+#-----------------------------------------------------------------------------
4908 # Imports
4909-#-------------------------------------------------------------------------------
4910+#-----------------------------------------------------------------------------
4911+
4912+from __future__ import with_statement
4913
4914 import os
4915+import tempfile
4916+
4917+from twisted.internet import reactor, defer
4918+from twisted.python import log
4919
4920 from foolscap import Tub, UnauthenticatedTub
4921
4922+from IPython.config.loader import Config
4923+
4924+from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
4925+
4926+from IPython.kernel.error import SecurityError
4927+
4928+from IPython.utils.traitlets import Int, Str, Bool, Instance
4929+from IPython.utils.importstring import import_item
4930+
4931+#-----------------------------------------------------------------------------
4932+# Code
4933+#-----------------------------------------------------------------------------
4934+
4935+
4936+# We do this so if a user doesn't have OpenSSL installed, it will try to use
4937+# an UnauthenticatedTub. But, they will still run into problems if they
4938+# try to use encrypted furls.
4939+try:
4940+ import OpenSSL
4941+except:
4942+ Tub = UnauthenticatedTub
4943+ have_crypto = False
4944+else:
4945+ have_crypto = True
4946+
4947+
4948+class FURLError(Exception):
4949+ pass
4950+
4951+
4952 def check_furl_file_security(furl_file, secure):
4953 """Remove the old furl_file if changing security modes."""
4954-
4955 if os.path.isfile(furl_file):
4956 f = open(furl_file, 'r')
4957 oldfurl = f.read().strip()
4958@@ -29,41 +64,210 @@
4959 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
4960 os.remove(furl_file)
4961
4962+
4963 def is_secure(furl):
4964+ """Is the given FURL secure or not."""
4965 if is_valid(furl):
4966 if furl.startswith("pb://"):
4967 return True
4968 elif furl.startswith("pbu://"):
4969 return False
4970 else:
4971- raise ValueError("invalid furl: %s" % furl)
4972+ raise FURLError("invalid FURL: %s" % furl)
4973+
4974
4975 def is_valid(furl):
4976+ """Is the str a valid FURL or not."""
4977 if isinstance(furl, str):
4978 if furl.startswith("pb://") or furl.startswith("pbu://"):
4979 return True
4980 else:
4981 return False
4982
4983+
4984 def find_furl(furl_or_file):
4985+ """Find, validate and return a FURL in a string or file."""
4986 if isinstance(furl_or_file, str):
4987 if is_valid(furl_or_file):
4988 return furl_or_file
4989 if os.path.isfile(furl_or_file):
4990- furl = open(furl_or_file, 'r').read().strip()
4991+ with open(furl_or_file, 'r') as f:
4992+ furl = f.read().strip()
4993 if is_valid(furl):
4994 return furl
4995- raise ValueError("not a furl or a file containing a furl: %s" % furl_or_file)
4996-
4997-# We do this so if a user doesn't have OpenSSL installed, it will try to use
4998-# an UnauthenticatedTub. But, they will still run into problems if they
4999-# try to use encrypted furls.
5000-try:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches