Merge lp:~ipython-dev/ipython/kernel-config into lp:ipython/0.11
- kernel-config
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Fernando Perez | Approve | ||
Vishal Vatsa | Pending | ||
Review via email: mp+14900@code.launchpad.net |
Commit message
Description of the change
Brian Granger (ellisonbg) wrote : | # |
- 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.
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/
-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.
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/
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:/
> 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>
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
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 | |
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 | |
4626 | + # print "config pre:", config |
4627 | + super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config) |
4628 | |
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: |
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. %unload_ ext/%reload_ ext/%install_ profiles/ %install_ default_ config.
* %debug is fixed.
* New magics %load_ext/
Enjoy!