##// END OF EJS Templates
Created genutils.get_ipython_dir to encapsulate the logic...
Brian Granger -
Show More
@@ -1,104 +1,104 b''
1 1 # encoding: utf-8
2 2
3 3 """This is the official entry point to IPython's configuration system. """
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 import os
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
19 from IPython.genutils import get_home_dir, get_ipython_dir
20 20 from IPython.external.configobj import ConfigObj
21 21
22 22 # Traitlets config imports
23 23 from IPython.config import traitlets
24 24 from IPython.config.config import *
25 25 from traitlets import *
26 26
27 27 class ConfigObjManager(object):
28 28
29 29 def __init__(self, configObj, filename):
30 30 self.current = configObj
31 31 self.current.indent_type = ' '
32 32 self.filename = filename
33 33 # self.write_default_config_file()
34 34
35 35 def get_config_obj(self):
36 36 return self.current
37 37
38 38 def update_config_obj(self, newConfig):
39 39 self.current.merge(newConfig)
40 40
41 41 def update_config_obj_from_file(self, filename):
42 42 newConfig = ConfigObj(filename, file_error=False)
43 43 self.current.merge(newConfig)
44 44
45 45 def update_config_obj_from_default_file(self, ipythondir=None):
46 46 fname = self.resolve_file_path(self.filename, ipythondir)
47 47 self.update_config_obj_from_file(fname)
48 48
49 49 def write_config_obj_to_file(self, filename):
50 50 f = open(filename, 'w')
51 51 self.current.write(f)
52 52 f.close()
53 53
54 54 def write_default_config_file(self):
55 55 ipdir = get_ipython_dir()
56 56 fname = ipdir + '/' + self.filename
57 57 if not os.path.isfile(fname):
58 58 print "Writing the configuration file to: " + fname
59 59 self.write_config_obj_to_file(fname)
60 60
61 61 def _import(self, key):
62 62 package = '.'.join(key.split('.')[0:-1])
63 63 obj = key.split('.')[-1]
64 64 execString = 'from %s import %s' % (package, obj)
65 65 exec execString
66 66 exec 'temp = %s' % obj
67 67 return temp
68 68
69 69 def resolve_file_path(self, filename, ipythondir = None):
70 70 """Resolve filenames into absolute paths.
71 71
72 72 This function looks in the following directories in order:
73 73
74 74 1. In the current working directory or by absolute path with ~ expanded
75 75 2. In ipythondir if that is set
76 76 3. In the IPYTHONDIR environment variable if it exists
77 77 4. In the ~/.ipython directory
78 78
79 79 Note: The IPYTHONDIR is also used by the trunk version of IPython so
80 80 changing it will also affect it was well.
81 81 """
82 82
83 83 # In cwd or by absolute path with ~ expanded
84 84 trythis = os.path.expanduser(filename)
85 85 if os.path.isfile(trythis):
86 86 return trythis
87 87
88 88 # In ipythondir if it is set
89 89 if ipythondir is not None:
90 90 trythis = ipythondir + '/' + filename
91 91 if os.path.isfile(trythis):
92 92 return trythis
93 93
94 94 trythis = get_ipython_dir() + '/' + filename
95 95 if os.path.isfile(trythis):
96 96 return trythis
97 97
98 98 return None
99 99
100 100
101 101
102 102
103 103
104 104
@@ -1,99 +1,34 b''
1 1 # encoding: utf-8
2 2
3 3 """Configuration-related utilities for all IPython."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 import os
19 19 import sys
20 20
21 21 #---------------------------------------------------------------------------
22 22 # Normal code begins
23 23 #---------------------------------------------------------------------------
24 24
25 class HomeDirError(Exception):
26 pass
27
28 def get_home_dir():
29 """Return the closest possible equivalent to a 'home' directory.
30
31 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
32
33 Currently only Posix and NT are implemented, a HomeDirError exception is
34 raised for all other OSes. """
35
36 isdir = os.path.isdir
37 env = os.environ
38 try:
39 homedir = env['HOME']
40 if not isdir(homedir):
41 # in case a user stuck some string which does NOT resolve to a
42 # valid path, it's as good as if we hadn't foud it
43 raise KeyError
44 return homedir
45 except KeyError:
46 if os.name == 'posix':
47 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
48 elif os.name == 'nt':
49 # For some strange reason, win9x returns 'nt' for os.name.
50 try:
51 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
52 if not isdir(homedir):
53 homedir = os.path.join(env['USERPROFILE'])
54 if not isdir(homedir):
55 raise HomeDirError
56 return homedir
57 except:
58 try:
59 # Use the registry to get the 'My Documents' folder.
60 import _winreg as wreg
61 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
62 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
63 homedir = wreg.QueryValueEx(key,'Personal')[0]
64 key.Close()
65 if not isdir(homedir):
66 e = ('Invalid "Personal" folder registry key '
67 'typically "My Documents".\n'
68 'Value: %s\n'
69 'This is not a valid directory on your system.' %
70 homedir)
71 raise HomeDirError(e)
72 return homedir
73 except HomeDirError:
74 raise
75 except:
76 return 'C:\\'
77 elif os.name == 'dos':
78 # Desperate, may do absurd things in classic MacOS. May work under DOS.
79 return 'C:\\'
80 else:
81 raise HomeDirError,'support for your operating system not implemented.'
82
83 def get_ipython_dir():
84 ipdir_def = '.ipython'
85 home_dir = get_home_dir()
86 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
87 os.path.join(home_dir,ipdir_def)))
88 return ipdir
89
90 25 def import_item(key):
91 26 """
92 27 Import and return bar given the string foo.bar.
93 28 """
94 29 package = '.'.join(key.split('.')[0:-1])
95 30 obj = key.split('.')[-1]
96 31 execString = 'from %s import %s' % (package, obj)
97 32 exec execString
98 33 exec 'temp = %s' % obj
99 34 return temp
@@ -1,2132 +1,2148 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 General purpose utilities.
4 4
5 5 This is a grab-bag of stuff I find useful in most programs I write. Some of
6 6 these things are also convenient when working at the command line.
7 7
8 8 $Id: genutils.py 2998 2008-01-31 10:06:04Z vivainio $"""
9 9
10 10 #*****************************************************************************
11 11 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #*****************************************************************************
16 16
17 17 from IPython import Release
18 18 __author__ = '%s <%s>' % Release.authors['Fernando']
19 19 __license__ = Release.license
20 20
21 21 #****************************************************************************
22 22 # required modules from the Python standard library
23 23 import __main__
24 24 import commands
25 25 try:
26 26 import doctest
27 27 except ImportError:
28 28 pass
29 29 import os
30 30 import platform
31 31 import re
32 32 import shlex
33 33 import shutil
34 34 import subprocess
35 35 import sys
36 36 import tempfile
37 37 import time
38 38 import types
39 39 import warnings
40 40
41 41 # Curses and termios are Unix-only modules
42 42 try:
43 43 import curses
44 44 # We need termios as well, so if its import happens to raise, we bail on
45 45 # using curses altogether.
46 46 import termios
47 47 except ImportError:
48 48 USE_CURSES = False
49 49 else:
50 50 # Curses on Solaris may not be complete, so we can't use it there
51 51 USE_CURSES = hasattr(curses,'initscr')
52 52
53 53 # Other IPython utilities
54 54 import IPython
55 55 from IPython.Itpl import Itpl,itpl,printpl
56 56 from IPython import DPyGetOpt, platutils
57 57 from IPython.generics import result_display
58 58 import IPython.ipapi
59 59 from IPython.external.path import path
60 60 if os.name == "nt":
61 61 from IPython.winconsole import get_console_size
62 62
63 63 try:
64 64 set
65 65 except:
66 66 from sets import Set as set
67 67
68 68
69 69 #****************************************************************************
70 70 # Exceptions
71 71 class Error(Exception):
72 72 """Base class for exceptions in this module."""
73 73 pass
74 74
75 75 #----------------------------------------------------------------------------
76 76 class IOStream:
77 77 def __init__(self,stream,fallback):
78 78 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
79 79 stream = fallback
80 80 self.stream = stream
81 81 self._swrite = stream.write
82 82 self.flush = stream.flush
83 83
84 84 def write(self,data):
85 85 try:
86 86 self._swrite(data)
87 87 except:
88 88 try:
89 89 # print handles some unicode issues which may trip a plain
90 90 # write() call. Attempt to emulate write() by using a
91 91 # trailing comma
92 92 print >> self.stream, data,
93 93 except:
94 94 # if we get here, something is seriously broken.
95 95 print >> sys.stderr, \
96 96 'ERROR - failed to write data to stream:', self.stream
97 97
98 98 def close(self):
99 99 pass
100 100
101 101
102 102 class IOTerm:
103 103 """ Term holds the file or file-like objects for handling I/O operations.
104 104
105 105 These are normally just sys.stdin, sys.stdout and sys.stderr but for
106 106 Windows they can can replaced to allow editing the strings before they are
107 107 displayed."""
108 108
109 109 # In the future, having IPython channel all its I/O operations through
110 110 # this class will make it easier to embed it into other environments which
111 111 # are not a normal terminal (such as a GUI-based shell)
112 112 def __init__(self,cin=None,cout=None,cerr=None):
113 113 self.cin = IOStream(cin,sys.stdin)
114 114 self.cout = IOStream(cout,sys.stdout)
115 115 self.cerr = IOStream(cerr,sys.stderr)
116 116
117 117 # Global variable to be used for all I/O
118 118 Term = IOTerm()
119 119
120 120 import IPython.rlineimpl as readline
121 121 # Remake Term to use the readline i/o facilities
122 122 if sys.platform == 'win32' and readline.have_readline:
123 123
124 124 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
125 125
126 126
127 127 #****************************************************************************
128 128 # Generic warning/error printer, used by everything else
129 129 def warn(msg,level=2,exit_val=1):
130 130 """Standard warning printer. Gives formatting consistency.
131 131
132 132 Output is sent to Term.cerr (sys.stderr by default).
133 133
134 134 Options:
135 135
136 136 -level(2): allows finer control:
137 137 0 -> Do nothing, dummy function.
138 138 1 -> Print message.
139 139 2 -> Print 'WARNING:' + message. (Default level).
140 140 3 -> Print 'ERROR:' + message.
141 141 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
142 142
143 143 -exit_val (1): exit value returned by sys.exit() for a level 4
144 144 warning. Ignored for all other levels."""
145 145
146 146 if level>0:
147 147 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
148 148 print >> Term.cerr, '%s%s' % (header[level],msg)
149 149 if level == 4:
150 150 print >> Term.cerr,'Exiting.\n'
151 151 sys.exit(exit_val)
152 152
153 153 def info(msg):
154 154 """Equivalent to warn(msg,level=1)."""
155 155
156 156 warn(msg,level=1)
157 157
158 158 def error(msg):
159 159 """Equivalent to warn(msg,level=3)."""
160 160
161 161 warn(msg,level=3)
162 162
163 163 def fatal(msg,exit_val=1):
164 164 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
165 165
166 166 warn(msg,exit_val=exit_val,level=4)
167 167
168 168 #---------------------------------------------------------------------------
169 169 # Debugging routines
170 170 #
171 171 def debugx(expr,pre_msg=''):
172 172 """Print the value of an expression from the caller's frame.
173 173
174 174 Takes an expression, evaluates it in the caller's frame and prints both
175 175 the given expression and the resulting value (as well as a debug mark
176 176 indicating the name of the calling function. The input must be of a form
177 177 suitable for eval().
178 178
179 179 An optional message can be passed, which will be prepended to the printed
180 180 expr->value pair."""
181 181
182 182 cf = sys._getframe(1)
183 183 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
184 184 eval(expr,cf.f_globals,cf.f_locals))
185 185
186 186 # deactivate it by uncommenting the following line, which makes it a no-op
187 187 #def debugx(expr,pre_msg=''): pass
188 188
189 189 #----------------------------------------------------------------------------
190 190 StringTypes = types.StringTypes
191 191
192 192 # Basic timing functionality
193 193
194 194 # If possible (Unix), use the resource module instead of time.clock()
195 195 try:
196 196 import resource
197 197 def clocku():
198 198 """clocku() -> floating point number
199 199
200 200 Return the *USER* CPU time in seconds since the start of the process.
201 201 This is done via a call to resource.getrusage, so it avoids the
202 202 wraparound problems in time.clock()."""
203 203
204 204 return resource.getrusage(resource.RUSAGE_SELF)[0]
205 205
206 206 def clocks():
207 207 """clocks() -> floating point number
208 208
209 209 Return the *SYSTEM* CPU time in seconds since the start of the process.
210 210 This is done via a call to resource.getrusage, so it avoids the
211 211 wraparound problems in time.clock()."""
212 212
213 213 return resource.getrusage(resource.RUSAGE_SELF)[1]
214 214
215 215 def clock():
216 216 """clock() -> floating point number
217 217
218 218 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
219 219 the process. This is done via a call to resource.getrusage, so it
220 220 avoids the wraparound problems in time.clock()."""
221 221
222 222 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
223 223 return u+s
224 224
225 225 def clock2():
226 226 """clock2() -> (t_user,t_system)
227 227
228 228 Similar to clock(), but return a tuple of user/system times."""
229 229 return resource.getrusage(resource.RUSAGE_SELF)[:2]
230 230
231 231 except ImportError:
232 232 # There is no distinction of user/system time under windows, so we just use
233 233 # time.clock() for everything...
234 234 clocku = clocks = clock = time.clock
235 235 def clock2():
236 236 """Under windows, system CPU time can't be measured.
237 237
238 238 This just returns clock() and zero."""
239 239 return time.clock(),0.0
240 240
241 241 def timings_out(reps,func,*args,**kw):
242 242 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
243 243
244 244 Execute a function reps times, return a tuple with the elapsed total
245 245 CPU time in seconds, the time per call and the function's output.
246 246
247 247 Under Unix, the return value is the sum of user+system time consumed by
248 248 the process, computed via the resource module. This prevents problems
249 249 related to the wraparound effect which the time.clock() function has.
250 250
251 251 Under Windows the return value is in wall clock seconds. See the
252 252 documentation for the time module for more details."""
253 253
254 254 reps = int(reps)
255 255 assert reps >=1, 'reps must be >= 1'
256 256 if reps==1:
257 257 start = clock()
258 258 out = func(*args,**kw)
259 259 tot_time = clock()-start
260 260 else:
261 261 rng = xrange(reps-1) # the last time is executed separately to store output
262 262 start = clock()
263 263 for dummy in rng: func(*args,**kw)
264 264 out = func(*args,**kw) # one last time
265 265 tot_time = clock()-start
266 266 av_time = tot_time / reps
267 267 return tot_time,av_time,out
268 268
269 269 def timings(reps,func,*args,**kw):
270 270 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
271 271
272 272 Execute a function reps times, return a tuple with the elapsed total CPU
273 273 time in seconds and the time per call. These are just the first two values
274 274 in timings_out()."""
275 275
276 276 return timings_out(reps,func,*args,**kw)[0:2]
277 277
278 278 def timing(func,*args,**kw):
279 279 """timing(func,*args,**kw) -> t_total
280 280
281 281 Execute a function once, return the elapsed total CPU time in
282 282 seconds. This is just the first value in timings_out()."""
283 283
284 284 return timings_out(1,func,*args,**kw)[0]
285 285
286 286 #****************************************************************************
287 287 # file and system
288 288
289 289 def arg_split(s,posix=False):
290 290 """Split a command line's arguments in a shell-like manner.
291 291
292 292 This is a modified version of the standard library's shlex.split()
293 293 function, but with a default of posix=False for splitting, so that quotes
294 294 in inputs are respected."""
295 295
296 296 # XXX - there may be unicode-related problems here!!! I'm not sure that
297 297 # shlex is truly unicode-safe, so it might be necessary to do
298 298 #
299 299 # s = s.encode(sys.stdin.encoding)
300 300 #
301 301 # first, to ensure that shlex gets a normal string. Input from anyone who
302 302 # knows more about unicode and shlex than I would be good to have here...
303 303 lex = shlex.shlex(s, posix=posix)
304 304 lex.whitespace_split = True
305 305 return list(lex)
306 306
307 307 def system(cmd,verbose=0,debug=0,header=''):
308 308 """Execute a system command, return its exit status.
309 309
310 310 Options:
311 311
312 312 - verbose (0): print the command to be executed.
313 313
314 314 - debug (0): only print, do not actually execute.
315 315
316 316 - header (''): Header to print on screen prior to the executed command (it
317 317 is only prepended to the command, no newlines are added).
318 318
319 319 Note: a stateful version of this function is available through the
320 320 SystemExec class."""
321 321
322 322 stat = 0
323 323 if verbose or debug: print header+cmd
324 324 sys.stdout.flush()
325 325 if not debug: stat = os.system(cmd)
326 326 return stat
327 327
328 328 def abbrev_cwd():
329 329 """ Return abbreviated version of cwd, e.g. d:mydir """
330 330 cwd = os.getcwd().replace('\\','/')
331 331 drivepart = ''
332 332 tail = cwd
333 333 if sys.platform == 'win32':
334 334 if len(cwd) < 4:
335 335 return cwd
336 336 drivepart,tail = os.path.splitdrive(cwd)
337 337
338 338
339 339 parts = tail.split('/')
340 340 if len(parts) > 2:
341 341 tail = '/'.join(parts[-2:])
342 342
343 343 return (drivepart + (
344 344 cwd == '/' and '/' or tail))
345 345
346 346
347 347 # This function is used by ipython in a lot of places to make system calls.
348 348 # We need it to be slightly different under win32, due to the vagaries of
349 349 # 'network shares'. A win32 override is below.
350 350
351 351 def shell(cmd,verbose=0,debug=0,header=''):
352 352 """Execute a command in the system shell, always return None.
353 353
354 354 Options:
355 355
356 356 - verbose (0): print the command to be executed.
357 357
358 358 - debug (0): only print, do not actually execute.
359 359
360 360 - header (''): Header to print on screen prior to the executed command (it
361 361 is only prepended to the command, no newlines are added).
362 362
363 363 Note: this is similar to genutils.system(), but it returns None so it can
364 364 be conveniently used in interactive loops without getting the return value
365 365 (typically 0) printed many times."""
366 366
367 367 stat = 0
368 368 if verbose or debug: print header+cmd
369 369 # flush stdout so we don't mangle python's buffering
370 370 sys.stdout.flush()
371 371
372 372 if not debug:
373 373 platutils.set_term_title("IPy " + cmd)
374 374 os.system(cmd)
375 375 platutils.set_term_title("IPy " + abbrev_cwd())
376 376
377 377 # override shell() for win32 to deal with network shares
378 378 if os.name in ('nt','dos'):
379 379
380 380 shell_ori = shell
381 381
382 382 def shell(cmd,verbose=0,debug=0,header=''):
383 383 if os.getcwd().startswith(r"\\"):
384 384 path = os.getcwd()
385 385 # change to c drive (cannot be on UNC-share when issuing os.system,
386 386 # as cmd.exe cannot handle UNC addresses)
387 387 os.chdir("c:")
388 388 # issue pushd to the UNC-share and then run the command
389 389 try:
390 390 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
391 391 finally:
392 392 os.chdir(path)
393 393 else:
394 394 shell_ori(cmd,verbose,debug,header)
395 395
396 396 shell.__doc__ = shell_ori.__doc__
397 397
398 398 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
399 399 """Dummy substitute for perl's backquotes.
400 400
401 401 Executes a command and returns the output.
402 402
403 403 Accepts the same arguments as system(), plus:
404 404
405 405 - split(0): if true, the output is returned as a list split on newlines.
406 406
407 407 Note: a stateful version of this function is available through the
408 408 SystemExec class.
409 409
410 410 This is pretty much deprecated and rarely used,
411 411 genutils.getoutputerror may be what you need.
412 412
413 413 """
414 414
415 415 if verbose or debug: print header+cmd
416 416 if not debug:
417 417 output = os.popen(cmd).read()
418 418 # stipping last \n is here for backwards compat.
419 419 if output.endswith('\n'):
420 420 output = output[:-1]
421 421 if split:
422 422 return output.split('\n')
423 423 else:
424 424 return output
425 425
426 426 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
427 427 """Return (standard output,standard error) of executing cmd in a shell.
428 428
429 429 Accepts the same arguments as system(), plus:
430 430
431 431 - split(0): if true, each of stdout/err is returned as a list split on
432 432 newlines.
433 433
434 434 Note: a stateful version of this function is available through the
435 435 SystemExec class."""
436 436
437 437 if verbose or debug: print header+cmd
438 438 if not cmd:
439 439 if split:
440 440 return [],[]
441 441 else:
442 442 return '',''
443 443 if not debug:
444 444 pin,pout,perr = os.popen3(cmd)
445 445 tout = pout.read().rstrip()
446 446 terr = perr.read().rstrip()
447 447 pin.close()
448 448 pout.close()
449 449 perr.close()
450 450 if split:
451 451 return tout.split('\n'),terr.split('\n')
452 452 else:
453 453 return tout,terr
454 454
455 455 # for compatibility with older naming conventions
456 456 xsys = system
457 457 bq = getoutput
458 458
459 459 class SystemExec:
460 460 """Access the system and getoutput functions through a stateful interface.
461 461
462 462 Note: here we refer to the system and getoutput functions from this
463 463 library, not the ones from the standard python library.
464 464
465 465 This class offers the system and getoutput functions as methods, but the
466 466 verbose, debug and header parameters can be set for the instance (at
467 467 creation time or later) so that they don't need to be specified on each
468 468 call.
469 469
470 470 For efficiency reasons, there's no way to override the parameters on a
471 471 per-call basis other than by setting instance attributes. If you need
472 472 local overrides, it's best to directly call system() or getoutput().
473 473
474 474 The following names are provided as alternate options:
475 475 - xsys: alias to system
476 476 - bq: alias to getoutput
477 477
478 478 An instance can then be created as:
479 479 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
480 480 """
481 481
482 482 def __init__(self,verbose=0,debug=0,header='',split=0):
483 483 """Specify the instance's values for verbose, debug and header."""
484 484 setattr_list(self,'verbose debug header split')
485 485
486 486 def system(self,cmd):
487 487 """Stateful interface to system(), with the same keyword parameters."""
488 488
489 489 system(cmd,self.verbose,self.debug,self.header)
490 490
491 491 def shell(self,cmd):
492 492 """Stateful interface to shell(), with the same keyword parameters."""
493 493
494 494 shell(cmd,self.verbose,self.debug,self.header)
495 495
496 496 xsys = system # alias
497 497
498 498 def getoutput(self,cmd):
499 499 """Stateful interface to getoutput()."""
500 500
501 501 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
502 502
503 503 def getoutputerror(self,cmd):
504 504 """Stateful interface to getoutputerror()."""
505 505
506 506 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
507 507
508 508 bq = getoutput # alias
509 509
510 510 #-----------------------------------------------------------------------------
511 511 def mutex_opts(dict,ex_op):
512 512 """Check for presence of mutually exclusive keys in a dict.
513 513
514 514 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
515 515 for op1,op2 in ex_op:
516 516 if op1 in dict and op2 in dict:
517 517 raise ValueError,'\n*** ERROR in Arguments *** '\
518 518 'Options '+op1+' and '+op2+' are mutually exclusive.'
519 519
520 520 #-----------------------------------------------------------------------------
521 521 def get_py_filename(name):
522 522 """Return a valid python filename in the current directory.
523 523
524 524 If the given name is not a file, it adds '.py' and searches again.
525 525 Raises IOError with an informative message if the file isn't found."""
526 526
527 527 name = os.path.expanduser(name)
528 528 if not os.path.isfile(name) and not name.endswith('.py'):
529 529 name += '.py'
530 530 if os.path.isfile(name):
531 531 return name
532 532 else:
533 533 raise IOError,'File `%s` not found.' % name
534 534
535 535 #-----------------------------------------------------------------------------
536 536 def filefind(fname,alt_dirs = None):
537 537 """Return the given filename either in the current directory, if it
538 538 exists, or in a specified list of directories.
539 539
540 540 ~ expansion is done on all file and directory names.
541 541
542 542 Upon an unsuccessful search, raise an IOError exception."""
543 543
544 544 if alt_dirs is None:
545 545 try:
546 546 alt_dirs = get_home_dir()
547 547 except HomeDirError:
548 548 alt_dirs = os.getcwd()
549 549 search = [fname] + list_strings(alt_dirs)
550 550 search = map(os.path.expanduser,search)
551 551 #print 'search list for',fname,'list:',search # dbg
552 552 fname = search[0]
553 553 if os.path.isfile(fname):
554 554 return fname
555 555 for direc in search[1:]:
556 556 testname = os.path.join(direc,fname)
557 557 #print 'testname',testname # dbg
558 558 if os.path.isfile(testname):
559 559 return testname
560 560 raise IOError,'File' + `fname` + \
561 561 ' not found in current or supplied directories:' + `alt_dirs`
562 562
563 563 #----------------------------------------------------------------------------
564 564 def file_read(filename):
565 565 """Read a file and close it. Returns the file source."""
566 566 fobj = open(filename,'r');
567 567 source = fobj.read();
568 568 fobj.close()
569 569 return source
570 570
571 571 def file_readlines(filename):
572 572 """Read a file and close it. Returns the file source using readlines()."""
573 573 fobj = open(filename,'r');
574 574 lines = fobj.readlines();
575 575 fobj.close()
576 576 return lines
577 577
578 578 #----------------------------------------------------------------------------
579 579 def target_outdated(target,deps):
580 580 """Determine whether a target is out of date.
581 581
582 582 target_outdated(target,deps) -> 1/0
583 583
584 584 deps: list of filenames which MUST exist.
585 585 target: single filename which may or may not exist.
586 586
587 587 If target doesn't exist or is older than any file listed in deps, return
588 588 true, otherwise return false.
589 589 """
590 590 try:
591 591 target_time = os.path.getmtime(target)
592 592 except os.error:
593 593 return 1
594 594 for dep in deps:
595 595 dep_time = os.path.getmtime(dep)
596 596 if dep_time > target_time:
597 597 #print "For target",target,"Dep failed:",dep # dbg
598 598 #print "times (dep,tar):",dep_time,target_time # dbg
599 599 return 1
600 600 return 0
601 601
602 602 #-----------------------------------------------------------------------------
603 603 def target_update(target,deps,cmd):
604 604 """Update a target with a given command given a list of dependencies.
605 605
606 606 target_update(target,deps,cmd) -> runs cmd if target is outdated.
607 607
608 608 This is just a wrapper around target_outdated() which calls the given
609 609 command if target is outdated."""
610 610
611 611 if target_outdated(target,deps):
612 612 xsys(cmd)
613 613
614 614 #----------------------------------------------------------------------------
615 615 def unquote_ends(istr):
616 616 """Remove a single pair of quotes from the endpoints of a string."""
617 617
618 618 if not istr:
619 619 return istr
620 620 if (istr[0]=="'" and istr[-1]=="'") or \
621 621 (istr[0]=='"' and istr[-1]=='"'):
622 622 return istr[1:-1]
623 623 else:
624 624 return istr
625 625
626 626 #----------------------------------------------------------------------------
627 627 def process_cmdline(argv,names=[],defaults={},usage=''):
628 628 """ Process command-line options and arguments.
629 629
630 630 Arguments:
631 631
632 632 - argv: list of arguments, typically sys.argv.
633 633
634 634 - names: list of option names. See DPyGetOpt docs for details on options
635 635 syntax.
636 636
637 637 - defaults: dict of default values.
638 638
639 639 - usage: optional usage notice to print if a wrong argument is passed.
640 640
641 641 Return a dict of options and a list of free arguments."""
642 642
643 643 getopt = DPyGetOpt.DPyGetOpt()
644 644 getopt.setIgnoreCase(0)
645 645 getopt.parseConfiguration(names)
646 646
647 647 try:
648 648 getopt.processArguments(argv)
649 649 except DPyGetOpt.ArgumentError, exc:
650 650 print usage
651 651 warn('"%s"' % exc,level=4)
652 652
653 653 defaults.update(getopt.optionValues)
654 654 args = getopt.freeValues
655 655
656 656 return defaults,args
657 657
658 658 #----------------------------------------------------------------------------
659 659 def optstr2types(ostr):
660 660 """Convert a string of option names to a dict of type mappings.
661 661
662 662 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
663 663
664 664 This is used to get the types of all the options in a string formatted
665 665 with the conventions of DPyGetOpt. The 'type' None is used for options
666 666 which are strings (they need no further conversion). This function's main
667 667 use is to get a typemap for use with read_dict().
668 668 """
669 669
670 670 typeconv = {None:'',int:'',float:''}
671 671 typemap = {'s':None,'i':int,'f':float}
672 672 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
673 673
674 674 for w in ostr.split():
675 675 oname,alias,otype = opt_re.match(w).groups()
676 676 if otype == '' or alias == '!': # simple switches are integers too
677 677 otype = 'i'
678 678 typeconv[typemap[otype]] += oname + ' '
679 679 return typeconv
680 680
681 681 #----------------------------------------------------------------------------
682 682 def read_dict(filename,type_conv=None,**opt):
683 683 r"""Read a dictionary of key=value pairs from an input file, optionally
684 684 performing conversions on the resulting values.
685 685
686 686 read_dict(filename,type_conv,**opt) -> dict
687 687
688 688 Only one value per line is accepted, the format should be
689 689 # optional comments are ignored
690 690 key value\n
691 691
692 692 Args:
693 693
694 694 - type_conv: A dictionary specifying which keys need to be converted to
695 695 which types. By default all keys are read as strings. This dictionary
696 696 should have as its keys valid conversion functions for strings
697 697 (int,long,float,complex, or your own). The value for each key
698 698 (converter) should be a whitespace separated string containing the names
699 699 of all the entries in the file to be converted using that function. For
700 700 keys to be left alone, use None as the conversion function (only needed
701 701 with purge=1, see below).
702 702
703 703 - opt: dictionary with extra options as below (default in parens)
704 704
705 705 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
706 706 of the dictionary to be returned. If purge is going to be used, the
707 707 set of keys to be left as strings also has to be explicitly specified
708 708 using the (non-existent) conversion function None.
709 709
710 710 fs(None): field separator. This is the key/value separator to be used
711 711 when parsing the file. The None default means any whitespace [behavior
712 712 of string.split()].
713 713
714 714 strip(0): if 1, strip string values of leading/trailinig whitespace.
715 715
716 716 warn(1): warning level if requested keys are not found in file.
717 717 - 0: silently ignore.
718 718 - 1: inform but proceed.
719 719 - 2: raise KeyError exception.
720 720
721 721 no_empty(0): if 1, remove keys with whitespace strings as a value.
722 722
723 723 unique([]): list of keys (or space separated string) which can't be
724 724 repeated. If one such key is found in the file, each new instance
725 725 overwrites the previous one. For keys not listed here, the behavior is
726 726 to make a list of all appearances.
727 727
728 728 Example:
729 729
730 730 If the input file test.ini contains (we put it in a string to keep the test
731 731 self-contained):
732 732
733 733 >>> test_ini = '''\
734 734 ... i 3
735 735 ... x 4.5
736 736 ... y 5.5
737 737 ... s hi ho'''
738 738
739 739 Then we can use it as follows:
740 740 >>> type_conv={int:'i',float:'x',None:'s'}
741 741
742 742 >>> d = read_dict(test_ini)
743 743
744 744 >>> sorted(d.items())
745 745 [('i', '3'), ('s', 'hi ho'), ('x', '4.5'), ('y', '5.5')]
746 746
747 747 >>> d = read_dict(test_ini,type_conv)
748 748
749 749 >>> sorted(d.items())
750 750 [('i', 3), ('s', 'hi ho'), ('x', 4.5), ('y', '5.5')]
751 751
752 752 >>> d = read_dict(test_ini,type_conv,purge=True)
753 753
754 754 >>> sorted(d.items())
755 755 [('i', 3), ('s', 'hi ho'), ('x', 4.5)]
756 756 """
757 757
758 758 # starting config
759 759 opt.setdefault('purge',0)
760 760 opt.setdefault('fs',None) # field sep defaults to any whitespace
761 761 opt.setdefault('strip',0)
762 762 opt.setdefault('warn',1)
763 763 opt.setdefault('no_empty',0)
764 764 opt.setdefault('unique','')
765 765 if type(opt['unique']) in StringTypes:
766 766 unique_keys = qw(opt['unique'])
767 767 elif type(opt['unique']) in (types.TupleType,types.ListType):
768 768 unique_keys = opt['unique']
769 769 else:
770 770 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
771 771
772 772 dict = {}
773 773
774 774 # first read in table of values as strings
775 775 if '\n' in filename:
776 776 lines = filename.splitlines()
777 777 file = None
778 778 else:
779 779 file = open(filename,'r')
780 780 lines = file.readlines()
781 781 for line in lines:
782 782 line = line.strip()
783 783 if len(line) and line[0]=='#': continue
784 784 if len(line)>0:
785 785 lsplit = line.split(opt['fs'],1)
786 786 try:
787 787 key,val = lsplit
788 788 except ValueError:
789 789 key,val = lsplit[0],''
790 790 key = key.strip()
791 791 if opt['strip']: val = val.strip()
792 792 if val == "''" or val == '""': val = ''
793 793 if opt['no_empty'] and (val=='' or val.isspace()):
794 794 continue
795 795 # if a key is found more than once in the file, build a list
796 796 # unless it's in the 'unique' list. In that case, last found in file
797 797 # takes precedence. User beware.
798 798 try:
799 799 if dict[key] and key in unique_keys:
800 800 dict[key] = val
801 801 elif type(dict[key]) is types.ListType:
802 802 dict[key].append(val)
803 803 else:
804 804 dict[key] = [dict[key],val]
805 805 except KeyError:
806 806 dict[key] = val
807 807 # purge if requested
808 808 if opt['purge']:
809 809 accepted_keys = qwflat(type_conv.values())
810 810 for key in dict.keys():
811 811 if key in accepted_keys: continue
812 812 del(dict[key])
813 813 # now convert if requested
814 814 if type_conv==None: return dict
815 815 conversions = type_conv.keys()
816 816 try: conversions.remove(None)
817 817 except: pass
818 818 for convert in conversions:
819 819 for val in qw(type_conv[convert]):
820 820 try:
821 821 dict[val] = convert(dict[val])
822 822 except KeyError,e:
823 823 if opt['warn'] == 0:
824 824 pass
825 825 elif opt['warn'] == 1:
826 826 print >>sys.stderr, 'Warning: key',val,\
827 827 'not found in file',filename
828 828 elif opt['warn'] == 2:
829 829 raise KeyError,e
830 830 else:
831 831 raise ValueError,'Warning level must be 0,1 or 2'
832 832
833 833 return dict
834 834
835 835 #----------------------------------------------------------------------------
836 836 def flag_calls(func):
837 837 """Wrap a function to detect and flag when it gets called.
838 838
839 839 This is a decorator which takes a function and wraps it in a function with
840 840 a 'called' attribute. wrapper.called is initialized to False.
841 841
842 842 The wrapper.called attribute is set to False right before each call to the
843 843 wrapped function, so if the call fails it remains False. After the call
844 844 completes, wrapper.called is set to True and the output is returned.
845 845
846 846 Testing for truth in wrapper.called allows you to determine if a call to
847 847 func() was attempted and succeeded."""
848 848
849 849 def wrapper(*args,**kw):
850 850 wrapper.called = False
851 851 out = func(*args,**kw)
852 852 wrapper.called = True
853 853 return out
854 854
855 855 wrapper.called = False
856 856 wrapper.__doc__ = func.__doc__
857 857 return wrapper
858 858
859 859 #----------------------------------------------------------------------------
860 860 def dhook_wrap(func,*a,**k):
861 861 """Wrap a function call in a sys.displayhook controller.
862 862
863 863 Returns a wrapper around func which calls func, with all its arguments and
864 864 keywords unmodified, using the default sys.displayhook. Since IPython
865 865 modifies sys.displayhook, it breaks the behavior of certain systems that
866 866 rely on the default behavior, notably doctest.
867 867 """
868 868
869 869 def f(*a,**k):
870 870
871 871 dhook_s = sys.displayhook
872 872 sys.displayhook = sys.__displayhook__
873 873 try:
874 874 out = func(*a,**k)
875 875 finally:
876 876 sys.displayhook = dhook_s
877 877
878 878 return out
879 879
880 880 f.__doc__ = func.__doc__
881 881 return f
882 882
883 883 #----------------------------------------------------------------------------
884 884 def doctest_reload():
885 885 """Properly reload doctest to reuse it interactively.
886 886
887 887 This routine:
888 888
889 889 - reloads doctest
890 890
891 891 - resets its global 'master' attribute to None, so that multiple uses of
892 892 the module interactively don't produce cumulative reports.
893 893
894 894 - Monkeypatches its core test runner method to protect it from IPython's
895 895 modified displayhook. Doctest expects the default displayhook behavior
896 896 deep down, so our modification breaks it completely. For this reason, a
897 897 hard monkeypatch seems like a reasonable solution rather than asking
898 898 users to manually use a different doctest runner when under IPython."""
899 899
900 900 import doctest
901 901 reload(doctest)
902 902 doctest.master=None
903 903
904 904 try:
905 905 doctest.DocTestRunner
906 906 except AttributeError:
907 907 # This is only for python 2.3 compatibility, remove once we move to
908 908 # 2.4 only.
909 909 pass
910 910 else:
911 911 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
912 912
913 913 #----------------------------------------------------------------------------
914 914 class HomeDirError(Error):
915 915 pass
916 916
917 917 def get_home_dir():
918 918 """Return the closest possible equivalent to a 'home' directory.
919 919
920 920 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
921 921
922 922 Currently only Posix and NT are implemented, a HomeDirError exception is
923 923 raised for all other OSes. """
924 924
925 925 isdir = os.path.isdir
926 926 env = os.environ
927 927
928 928 # first, check py2exe distribution root directory for _ipython.
929 929 # This overrides all. Normally does not exist.
930 930
931 931 if '\\library.zip\\' in IPython.__file__.lower():
932 932 root, rest = IPython.__file__.lower().split('library.zip')
933 933 if isdir(root + '_ipython'):
934 934 os.environ["IPYKITROOT"] = root.rstrip('\\')
935 935 return root
936 936
937 937 try:
938 938 homedir = env['HOME']
939 939 if not isdir(homedir):
940 940 # in case a user stuck some string which does NOT resolve to a
941 941 # valid path, it's as good as if we hadn't foud it
942 942 raise KeyError
943 943 return homedir
944 944 except KeyError:
945 945 if os.name == 'posix':
946 946 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
947 947 elif os.name == 'nt':
948 948 # For some strange reason, win9x returns 'nt' for os.name.
949 949 try:
950 950 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
951 951 if not isdir(homedir):
952 952 homedir = os.path.join(env['USERPROFILE'])
953 953 if not isdir(homedir):
954 954 raise HomeDirError
955 955 return homedir
956 956 except:
957 957 try:
958 958 # Use the registry to get the 'My Documents' folder.
959 959 import _winreg as wreg
960 960 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
961 961 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
962 962 homedir = wreg.QueryValueEx(key,'Personal')[0]
963 963 key.Close()
964 964 if not isdir(homedir):
965 965 e = ('Invalid "Personal" folder registry key '
966 966 'typically "My Documents".\n'
967 967 'Value: %s\n'
968 968 'This is not a valid directory on your system.' %
969 969 homedir)
970 970 raise HomeDirError(e)
971 971 return homedir
972 972 except HomeDirError:
973 973 raise
974 974 except:
975 975 return 'C:\\'
976 976 elif os.name == 'dos':
977 977 # Desperate, may do absurd things in classic MacOS. May work under DOS.
978 978 return 'C:\\'
979 979 else:
980 980 raise HomeDirError,'support for your operating system not implemented.'
981 981
982
983 def get_ipython_dir():
984 """Get the IPython directory for this platform and user.
985
986 This uses the logic in `get_home_dir` to find the home directory
987 and the adds either .ipython or _ipython to the end of the path.
988 """
989 if os.name == 'posix':
990 ipdir_def = '.ipython'
991 else:
992 ipdir_def = '_ipython'
993 home_dir = get_home_dir()
994 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
995 os.path.join(home_dir,ipdir_def)))
996 return ipdir
997
982 998 #****************************************************************************
983 999 # strings and text
984 1000
985 1001 class LSString(str):
986 1002 """String derivative with a special access attributes.
987 1003
988 1004 These are normal strings, but with the special attributes:
989 1005
990 1006 .l (or .list) : value as list (split on newlines).
991 1007 .n (or .nlstr): original value (the string itself).
992 1008 .s (or .spstr): value as whitespace-separated string.
993 1009 .p (or .paths): list of path objects
994 1010
995 1011 Any values which require transformations are computed only once and
996 1012 cached.
997 1013
998 1014 Such strings are very useful to efficiently interact with the shell, which
999 1015 typically only understands whitespace-separated options for commands."""
1000 1016
1001 1017 def get_list(self):
1002 1018 try:
1003 1019 return self.__list
1004 1020 except AttributeError:
1005 1021 self.__list = self.split('\n')
1006 1022 return self.__list
1007 1023
1008 1024 l = list = property(get_list)
1009 1025
1010 1026 def get_spstr(self):
1011 1027 try:
1012 1028 return self.__spstr
1013 1029 except AttributeError:
1014 1030 self.__spstr = self.replace('\n',' ')
1015 1031 return self.__spstr
1016 1032
1017 1033 s = spstr = property(get_spstr)
1018 1034
1019 1035 def get_nlstr(self):
1020 1036 return self
1021 1037
1022 1038 n = nlstr = property(get_nlstr)
1023 1039
1024 1040 def get_paths(self):
1025 1041 try:
1026 1042 return self.__paths
1027 1043 except AttributeError:
1028 1044 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
1029 1045 return self.__paths
1030 1046
1031 1047 p = paths = property(get_paths)
1032 1048
1033 1049 def print_lsstring(arg):
1034 1050 """ Prettier (non-repr-like) and more informative printer for LSString """
1035 1051 print "LSString (.p, .n, .l, .s available). Value:"
1036 1052 print arg
1037 1053
1038 1054 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1039 1055
1040 1056 #----------------------------------------------------------------------------
1041 1057 class SList(list):
1042 1058 """List derivative with a special access attributes.
1043 1059
1044 1060 These are normal lists, but with the special attributes:
1045 1061
1046 1062 .l (or .list) : value as list (the list itself).
1047 1063 .n (or .nlstr): value as a string, joined on newlines.
1048 1064 .s (or .spstr): value as a string, joined on spaces.
1049 1065 .p (or .paths): list of path objects
1050 1066
1051 1067 Any values which require transformations are computed only once and
1052 1068 cached."""
1053 1069
1054 1070 def get_list(self):
1055 1071 return self
1056 1072
1057 1073 l = list = property(get_list)
1058 1074
1059 1075 def get_spstr(self):
1060 1076 try:
1061 1077 return self.__spstr
1062 1078 except AttributeError:
1063 1079 self.__spstr = ' '.join(self)
1064 1080 return self.__spstr
1065 1081
1066 1082 s = spstr = property(get_spstr)
1067 1083
1068 1084 def get_nlstr(self):
1069 1085 try:
1070 1086 return self.__nlstr
1071 1087 except AttributeError:
1072 1088 self.__nlstr = '\n'.join(self)
1073 1089 return self.__nlstr
1074 1090
1075 1091 n = nlstr = property(get_nlstr)
1076 1092
1077 1093 def get_paths(self):
1078 1094 try:
1079 1095 return self.__paths
1080 1096 except AttributeError:
1081 1097 self.__paths = [path(p) for p in self if os.path.exists(p)]
1082 1098 return self.__paths
1083 1099
1084 1100 p = paths = property(get_paths)
1085 1101
1086 1102 def grep(self, pattern, prune = False, field = None):
1087 1103 """ Return all strings matching 'pattern' (a regex or callable)
1088 1104
1089 1105 This is case-insensitive. If prune is true, return all items
1090 1106 NOT matching the pattern.
1091 1107
1092 1108 If field is specified, the match must occur in the specified
1093 1109 whitespace-separated field.
1094 1110
1095 1111 Examples::
1096 1112
1097 1113 a.grep( lambda x: x.startswith('C') )
1098 1114 a.grep('Cha.*log', prune=1)
1099 1115 a.grep('chm', field=-1)
1100 1116 """
1101 1117
1102 1118 def match_target(s):
1103 1119 if field is None:
1104 1120 return s
1105 1121 parts = s.split()
1106 1122 try:
1107 1123 tgt = parts[field]
1108 1124 return tgt
1109 1125 except IndexError:
1110 1126 return ""
1111 1127
1112 1128 if isinstance(pattern, basestring):
1113 1129 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1114 1130 else:
1115 1131 pred = pattern
1116 1132 if not prune:
1117 1133 return SList([el for el in self if pred(match_target(el))])
1118 1134 else:
1119 1135 return SList([el for el in self if not pred(match_target(el))])
1120 1136 def fields(self, *fields):
1121 1137 """ Collect whitespace-separated fields from string list
1122 1138
1123 1139 Allows quick awk-like usage of string lists.
1124 1140
1125 1141 Example data (in var a, created by 'a = !ls -l')::
1126 1142 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1127 1143 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1128 1144
1129 1145 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1130 1146 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1131 1147 (note the joining by space).
1132 1148 a.fields(-1) is ['ChangeLog', 'IPython']
1133 1149
1134 1150 IndexErrors are ignored.
1135 1151
1136 1152 Without args, fields() just split()'s the strings.
1137 1153 """
1138 1154 if len(fields) == 0:
1139 1155 return [el.split() for el in self]
1140 1156
1141 1157 res = SList()
1142 1158 for el in [f.split() for f in self]:
1143 1159 lineparts = []
1144 1160
1145 1161 for fd in fields:
1146 1162 try:
1147 1163 lineparts.append(el[fd])
1148 1164 except IndexError:
1149 1165 pass
1150 1166 if lineparts:
1151 1167 res.append(" ".join(lineparts))
1152 1168
1153 1169 return res
1154 1170 def sort(self,field= None, nums = False):
1155 1171 """ sort by specified fields (see fields())
1156 1172
1157 1173 Example::
1158 1174 a.sort(1, nums = True)
1159 1175
1160 1176 Sorts a by second field, in numerical order (so that 21 > 3)
1161 1177
1162 1178 """
1163 1179
1164 1180 #decorate, sort, undecorate
1165 1181 if field is not None:
1166 1182 dsu = [[SList([line]).fields(field), line] for line in self]
1167 1183 else:
1168 1184 dsu = [[line, line] for line in self]
1169 1185 if nums:
1170 1186 for i in range(len(dsu)):
1171 1187 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1172 1188 try:
1173 1189 n = int(numstr)
1174 1190 except ValueError:
1175 1191 n = 0;
1176 1192 dsu[i][0] = n
1177 1193
1178 1194
1179 1195 dsu.sort()
1180 1196 return SList([t[1] for t in dsu])
1181 1197
1182 1198 def print_slist(arg):
1183 1199 """ Prettier (non-repr-like) and more informative printer for SList """
1184 1200 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1185 1201 if hasattr(arg, 'hideonce') and arg.hideonce:
1186 1202 arg.hideonce = False
1187 1203 return
1188 1204
1189 1205 nlprint(arg)
1190 1206
1191 1207 print_slist = result_display.when_type(SList)(print_slist)
1192 1208
1193 1209
1194 1210
1195 1211 #----------------------------------------------------------------------------
1196 1212 def esc_quotes(strng):
1197 1213 """Return the input string with single and double quotes escaped out"""
1198 1214
1199 1215 return strng.replace('"','\\"').replace("'","\\'")
1200 1216
1201 1217 #----------------------------------------------------------------------------
1202 1218 def make_quoted_expr(s):
1203 1219 """Return string s in appropriate quotes, using raw string if possible.
1204 1220
1205 1221 Effectively this turns string: cd \ao\ao\
1206 1222 to: r"cd \ao\ao\_"[:-1]
1207 1223
1208 1224 Note the use of raw string and padding at the end to allow trailing backslash.
1209 1225
1210 1226 """
1211 1227
1212 1228 tail = ''
1213 1229 tailpadding = ''
1214 1230 raw = ''
1215 1231 if "\\" in s:
1216 1232 raw = 'r'
1217 1233 if s.endswith('\\'):
1218 1234 tail = '[:-1]'
1219 1235 tailpadding = '_'
1220 1236 if '"' not in s:
1221 1237 quote = '"'
1222 1238 elif "'" not in s:
1223 1239 quote = "'"
1224 1240 elif '"""' not in s and not s.endswith('"'):
1225 1241 quote = '"""'
1226 1242 elif "'''" not in s and not s.endswith("'"):
1227 1243 quote = "'''"
1228 1244 else:
1229 1245 # give up, backslash-escaped string will do
1230 1246 return '"%s"' % esc_quotes(s)
1231 1247 res = raw + quote + s + tailpadding + quote + tail
1232 1248 return res
1233 1249
1234 1250
1235 1251 #----------------------------------------------------------------------------
1236 1252 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1237 1253 """Take multiple lines of input.
1238 1254
1239 1255 A list with each line of input as a separate element is returned when a
1240 1256 termination string is entered (defaults to a single '.'). Input can also
1241 1257 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1242 1258
1243 1259 Lines of input which end in \\ are joined into single entries (and a
1244 1260 secondary continuation prompt is issued as long as the user terminates
1245 1261 lines with \\). This allows entering very long strings which are still
1246 1262 meant to be treated as single entities.
1247 1263 """
1248 1264
1249 1265 try:
1250 1266 if header:
1251 1267 header += '\n'
1252 1268 lines = [raw_input(header + ps1)]
1253 1269 except EOFError:
1254 1270 return []
1255 1271 terminate = [terminate_str]
1256 1272 try:
1257 1273 while lines[-1:] != terminate:
1258 1274 new_line = raw_input(ps1)
1259 1275 while new_line.endswith('\\'):
1260 1276 new_line = new_line[:-1] + raw_input(ps2)
1261 1277 lines.append(new_line)
1262 1278
1263 1279 return lines[:-1] # don't return the termination command
1264 1280 except EOFError:
1265 1281 print
1266 1282 return lines
1267 1283
1268 1284 #----------------------------------------------------------------------------
1269 1285 def raw_input_ext(prompt='', ps2='... '):
1270 1286 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1271 1287
1272 1288 line = raw_input(prompt)
1273 1289 while line.endswith('\\'):
1274 1290 line = line[:-1] + raw_input(ps2)
1275 1291 return line
1276 1292
1277 1293 #----------------------------------------------------------------------------
1278 1294 def ask_yes_no(prompt,default=None):
1279 1295 """Asks a question and returns a boolean (y/n) answer.
1280 1296
1281 1297 If default is given (one of 'y','n'), it is used if the user input is
1282 1298 empty. Otherwise the question is repeated until an answer is given.
1283 1299
1284 1300 An EOF is treated as the default answer. If there is no default, an
1285 1301 exception is raised to prevent infinite loops.
1286 1302
1287 1303 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1288 1304
1289 1305 answers = {'y':True,'n':False,'yes':True,'no':False}
1290 1306 ans = None
1291 1307 while ans not in answers.keys():
1292 1308 try:
1293 1309 ans = raw_input(prompt+' ').lower()
1294 1310 if not ans: # response was an empty string
1295 1311 ans = default
1296 1312 except KeyboardInterrupt:
1297 1313 pass
1298 1314 except EOFError:
1299 1315 if default in answers.keys():
1300 1316 ans = default
1301 1317 print
1302 1318 else:
1303 1319 raise
1304 1320
1305 1321 return answers[ans]
1306 1322
1307 1323 #----------------------------------------------------------------------------
1308 1324 def marquee(txt='',width=78,mark='*'):
1309 1325 """Return the input string centered in a 'marquee'."""
1310 1326 if not txt:
1311 1327 return (mark*width)[:width]
1312 1328 nmark = (width-len(txt)-2)/len(mark)/2
1313 1329 if nmark < 0: nmark =0
1314 1330 marks = mark*nmark
1315 1331 return '%s %s %s' % (marks,txt,marks)
1316 1332
1317 1333 #----------------------------------------------------------------------------
1318 1334 class EvalDict:
1319 1335 """
1320 1336 Emulate a dict which evaluates its contents in the caller's frame.
1321 1337
1322 1338 Usage:
1323 1339 >>> number = 19
1324 1340
1325 1341 >>> text = "python"
1326 1342
1327 1343 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1328 1344 Python 2.1 rules!
1329 1345 """
1330 1346
1331 1347 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1332 1348 # modified (shorter) version of:
1333 1349 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1334 1350 # Skip Montanaro (skip@pobox.com).
1335 1351
1336 1352 def __getitem__(self, name):
1337 1353 frame = sys._getframe(1)
1338 1354 return eval(name, frame.f_globals, frame.f_locals)
1339 1355
1340 1356 EvalString = EvalDict # for backwards compatibility
1341 1357 #----------------------------------------------------------------------------
1342 1358 def qw(words,flat=0,sep=None,maxsplit=-1):
1343 1359 """Similar to Perl's qw() operator, but with some more options.
1344 1360
1345 1361 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1346 1362
1347 1363 words can also be a list itself, and with flat=1, the output will be
1348 1364 recursively flattened.
1349 1365
1350 1366 Examples:
1351 1367
1352 1368 >>> qw('1 2')
1353 1369 ['1', '2']
1354 1370
1355 1371 >>> qw(['a b','1 2',['m n','p q']])
1356 1372 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1357 1373
1358 1374 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1359 1375 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1360 1376 """
1361 1377
1362 1378 if type(words) in StringTypes:
1363 1379 return [word.strip() for word in words.split(sep,maxsplit)
1364 1380 if word and not word.isspace() ]
1365 1381 if flat:
1366 1382 return flatten(map(qw,words,[1]*len(words)))
1367 1383 return map(qw,words)
1368 1384
1369 1385 #----------------------------------------------------------------------------
1370 1386 def qwflat(words,sep=None,maxsplit=-1):
1371 1387 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1372 1388 return qw(words,1,sep,maxsplit)
1373 1389
1374 1390 #----------------------------------------------------------------------------
1375 1391 def qw_lol(indata):
1376 1392 """qw_lol('a b') -> [['a','b']],
1377 1393 otherwise it's just a call to qw().
1378 1394
1379 1395 We need this to make sure the modules_some keys *always* end up as a
1380 1396 list of lists."""
1381 1397
1382 1398 if type(indata) in StringTypes:
1383 1399 return [qw(indata)]
1384 1400 else:
1385 1401 return qw(indata)
1386 1402
1387 1403 #-----------------------------------------------------------------------------
1388 1404 def list_strings(arg):
1389 1405 """Always return a list of strings, given a string or list of strings
1390 1406 as input."""
1391 1407
1392 1408 if type(arg) in StringTypes: return [arg]
1393 1409 else: return arg
1394 1410
1395 1411 #----------------------------------------------------------------------------
1396 1412 def grep(pat,list,case=1):
1397 1413 """Simple minded grep-like function.
1398 1414 grep(pat,list) returns occurrences of pat in list, None on failure.
1399 1415
1400 1416 It only does simple string matching, with no support for regexps. Use the
1401 1417 option case=0 for case-insensitive matching."""
1402 1418
1403 1419 # This is pretty crude. At least it should implement copying only references
1404 1420 # to the original data in case it's big. Now it copies the data for output.
1405 1421 out=[]
1406 1422 if case:
1407 1423 for term in list:
1408 1424 if term.find(pat)>-1: out.append(term)
1409 1425 else:
1410 1426 lpat=pat.lower()
1411 1427 for term in list:
1412 1428 if term.lower().find(lpat)>-1: out.append(term)
1413 1429
1414 1430 if len(out): return out
1415 1431 else: return None
1416 1432
1417 1433 #----------------------------------------------------------------------------
1418 1434 def dgrep(pat,*opts):
1419 1435 """Return grep() on dir()+dir(__builtins__).
1420 1436
1421 1437 A very common use of grep() when working interactively."""
1422 1438
1423 1439 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1424 1440
1425 1441 #----------------------------------------------------------------------------
1426 1442 def idgrep(pat):
1427 1443 """Case-insensitive dgrep()"""
1428 1444
1429 1445 return dgrep(pat,0)
1430 1446
1431 1447 #----------------------------------------------------------------------------
1432 1448 def igrep(pat,list):
1433 1449 """Synonym for case-insensitive grep."""
1434 1450
1435 1451 return grep(pat,list,case=0)
1436 1452
1437 1453 #----------------------------------------------------------------------------
1438 1454 def indent(str,nspaces=4,ntabs=0):
1439 1455 """Indent a string a given number of spaces or tabstops.
1440 1456
1441 1457 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1442 1458 """
1443 1459 if str is None:
1444 1460 return
1445 1461 ind = '\t'*ntabs+' '*nspaces
1446 1462 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1447 1463 if outstr.endswith(os.linesep+ind):
1448 1464 return outstr[:-len(ind)]
1449 1465 else:
1450 1466 return outstr
1451 1467
1452 1468 #-----------------------------------------------------------------------------
1453 1469 def native_line_ends(filename,backup=1):
1454 1470 """Convert (in-place) a file to line-ends native to the current OS.
1455 1471
1456 1472 If the optional backup argument is given as false, no backup of the
1457 1473 original file is left. """
1458 1474
1459 1475 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1460 1476
1461 1477 bak_filename = filename + backup_suffixes[os.name]
1462 1478
1463 1479 original = open(filename).read()
1464 1480 shutil.copy2(filename,bak_filename)
1465 1481 try:
1466 1482 new = open(filename,'wb')
1467 1483 new.write(os.linesep.join(original.splitlines()))
1468 1484 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1469 1485 new.close()
1470 1486 except:
1471 1487 os.rename(bak_filename,filename)
1472 1488 if not backup:
1473 1489 try:
1474 1490 os.remove(bak_filename)
1475 1491 except:
1476 1492 pass
1477 1493
1478 1494 #----------------------------------------------------------------------------
1479 1495 def get_pager_cmd(pager_cmd = None):
1480 1496 """Return a pager command.
1481 1497
1482 1498 Makes some attempts at finding an OS-correct one."""
1483 1499
1484 1500 if os.name == 'posix':
1485 1501 default_pager_cmd = 'less -r' # -r for color control sequences
1486 1502 elif os.name in ['nt','dos']:
1487 1503 default_pager_cmd = 'type'
1488 1504
1489 1505 if pager_cmd is None:
1490 1506 try:
1491 1507 pager_cmd = os.environ['PAGER']
1492 1508 except:
1493 1509 pager_cmd = default_pager_cmd
1494 1510 return pager_cmd
1495 1511
1496 1512 #-----------------------------------------------------------------------------
1497 1513 def get_pager_start(pager,start):
1498 1514 """Return the string for paging files with an offset.
1499 1515
1500 1516 This is the '+N' argument which less and more (under Unix) accept.
1501 1517 """
1502 1518
1503 1519 if pager in ['less','more']:
1504 1520 if start:
1505 1521 start_string = '+' + str(start)
1506 1522 else:
1507 1523 start_string = ''
1508 1524 else:
1509 1525 start_string = ''
1510 1526 return start_string
1511 1527
1512 1528 #----------------------------------------------------------------------------
1513 1529 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1514 1530 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1515 1531 import msvcrt
1516 1532 def page_more():
1517 1533 """ Smart pausing between pages
1518 1534
1519 1535 @return: True if need print more lines, False if quit
1520 1536 """
1521 1537 Term.cout.write('---Return to continue, q to quit--- ')
1522 1538 ans = msvcrt.getch()
1523 1539 if ans in ("q", "Q"):
1524 1540 result = False
1525 1541 else:
1526 1542 result = True
1527 1543 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1528 1544 return result
1529 1545 else:
1530 1546 def page_more():
1531 1547 ans = raw_input('---Return to continue, q to quit--- ')
1532 1548 if ans.lower().startswith('q'):
1533 1549 return False
1534 1550 else:
1535 1551 return True
1536 1552
1537 1553 esc_re = re.compile(r"(\x1b[^m]+m)")
1538 1554
1539 1555 def page_dumb(strng,start=0,screen_lines=25):
1540 1556 """Very dumb 'pager' in Python, for when nothing else works.
1541 1557
1542 1558 Only moves forward, same interface as page(), except for pager_cmd and
1543 1559 mode."""
1544 1560
1545 1561 out_ln = strng.splitlines()[start:]
1546 1562 screens = chop(out_ln,screen_lines-1)
1547 1563 if len(screens) == 1:
1548 1564 print >>Term.cout, os.linesep.join(screens[0])
1549 1565 else:
1550 1566 last_escape = ""
1551 1567 for scr in screens[0:-1]:
1552 1568 hunk = os.linesep.join(scr)
1553 1569 print >>Term.cout, last_escape + hunk
1554 1570 if not page_more():
1555 1571 return
1556 1572 esc_list = esc_re.findall(hunk)
1557 1573 if len(esc_list) > 0:
1558 1574 last_escape = esc_list[-1]
1559 1575 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1560 1576
1561 1577 #----------------------------------------------------------------------------
1562 1578 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1563 1579 """Print a string, piping through a pager after a certain length.
1564 1580
1565 1581 The screen_lines parameter specifies the number of *usable* lines of your
1566 1582 terminal screen (total lines minus lines you need to reserve to show other
1567 1583 information).
1568 1584
1569 1585 If you set screen_lines to a number <=0, page() will try to auto-determine
1570 1586 your screen size and will only use up to (screen_size+screen_lines) for
1571 1587 printing, paging after that. That is, if you want auto-detection but need
1572 1588 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1573 1589 auto-detection without any lines reserved simply use screen_lines = 0.
1574 1590
1575 1591 If a string won't fit in the allowed lines, it is sent through the
1576 1592 specified pager command. If none given, look for PAGER in the environment,
1577 1593 and ultimately default to less.
1578 1594
1579 1595 If no system pager works, the string is sent through a 'dumb pager'
1580 1596 written in python, very simplistic.
1581 1597 """
1582 1598
1583 1599 # Some routines may auto-compute start offsets incorrectly and pass a
1584 1600 # negative value. Offset to 0 for robustness.
1585 1601 start = max(0,start)
1586 1602
1587 1603 # first, try the hook
1588 1604 ip = IPython.ipapi.get()
1589 1605 if ip:
1590 1606 try:
1591 1607 ip.IP.hooks.show_in_pager(strng)
1592 1608 return
1593 1609 except IPython.ipapi.TryNext:
1594 1610 pass
1595 1611
1596 1612 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1597 1613 TERM = os.environ.get('TERM','dumb')
1598 1614 if TERM in ['dumb','emacs'] and os.name != 'nt':
1599 1615 print strng
1600 1616 return
1601 1617 # chop off the topmost part of the string we don't want to see
1602 1618 str_lines = strng.split(os.linesep)[start:]
1603 1619 str_toprint = os.linesep.join(str_lines)
1604 1620 num_newlines = len(str_lines)
1605 1621 len_str = len(str_toprint)
1606 1622
1607 1623 # Dumb heuristics to guesstimate number of on-screen lines the string
1608 1624 # takes. Very basic, but good enough for docstrings in reasonable
1609 1625 # terminals. If someone later feels like refining it, it's not hard.
1610 1626 numlines = max(num_newlines,int(len_str/80)+1)
1611 1627
1612 1628 if os.name == "nt":
1613 1629 screen_lines_def = get_console_size(defaulty=25)[1]
1614 1630 else:
1615 1631 screen_lines_def = 25 # default value if we can't auto-determine
1616 1632
1617 1633 # auto-determine screen size
1618 1634 if screen_lines <= 0:
1619 1635 if TERM=='xterm':
1620 1636 use_curses = USE_CURSES
1621 1637 else:
1622 1638 # curses causes problems on many terminals other than xterm.
1623 1639 use_curses = False
1624 1640 if use_curses:
1625 1641 # There is a bug in curses, where *sometimes* it fails to properly
1626 1642 # initialize, and then after the endwin() call is made, the
1627 1643 # terminal is left in an unusable state. Rather than trying to
1628 1644 # check everytime for this (by requesting and comparing termios
1629 1645 # flags each time), we just save the initial terminal state and
1630 1646 # unconditionally reset it every time. It's cheaper than making
1631 1647 # the checks.
1632 1648 term_flags = termios.tcgetattr(sys.stdout)
1633 1649 scr = curses.initscr()
1634 1650 screen_lines_real,screen_cols = scr.getmaxyx()
1635 1651 curses.endwin()
1636 1652 # Restore terminal state in case endwin() didn't.
1637 1653 termios.tcsetattr(sys.stdout,termios.TCSANOW,term_flags)
1638 1654 # Now we have what we needed: the screen size in rows/columns
1639 1655 screen_lines += screen_lines_real
1640 1656 #print '***Screen size:',screen_lines_real,'lines x',\
1641 1657 #screen_cols,'columns.' # dbg
1642 1658 else:
1643 1659 screen_lines += screen_lines_def
1644 1660
1645 1661 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1646 1662 if numlines <= screen_lines :
1647 1663 #print '*** normal print' # dbg
1648 1664 print >>Term.cout, str_toprint
1649 1665 else:
1650 1666 # Try to open pager and default to internal one if that fails.
1651 1667 # All failure modes are tagged as 'retval=1', to match the return
1652 1668 # value of a failed system command. If any intermediate attempt
1653 1669 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1654 1670 pager_cmd = get_pager_cmd(pager_cmd)
1655 1671 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1656 1672 if os.name == 'nt':
1657 1673 if pager_cmd.startswith('type'):
1658 1674 # The default WinXP 'type' command is failing on complex strings.
1659 1675 retval = 1
1660 1676 else:
1661 1677 tmpname = tempfile.mktemp('.txt')
1662 1678 tmpfile = file(tmpname,'wt')
1663 1679 tmpfile.write(strng)
1664 1680 tmpfile.close()
1665 1681 cmd = "%s < %s" % (pager_cmd,tmpname)
1666 1682 if os.system(cmd):
1667 1683 retval = 1
1668 1684 else:
1669 1685 retval = None
1670 1686 os.remove(tmpname)
1671 1687 else:
1672 1688 try:
1673 1689 retval = None
1674 1690 # if I use popen4, things hang. No idea why.
1675 1691 #pager,shell_out = os.popen4(pager_cmd)
1676 1692 pager = os.popen(pager_cmd,'w')
1677 1693 pager.write(strng)
1678 1694 pager.close()
1679 1695 retval = pager.close() # success returns None
1680 1696 except IOError,msg: # broken pipe when user quits
1681 1697 if msg.args == (32,'Broken pipe'):
1682 1698 retval = None
1683 1699 else:
1684 1700 retval = 1
1685 1701 except OSError:
1686 1702 # Other strange problems, sometimes seen in Win2k/cygwin
1687 1703 retval = 1
1688 1704 if retval is not None:
1689 1705 page_dumb(strng,screen_lines=screen_lines)
1690 1706
1691 1707 #----------------------------------------------------------------------------
1692 1708 def page_file(fname,start = 0, pager_cmd = None):
1693 1709 """Page a file, using an optional pager command and starting line.
1694 1710 """
1695 1711
1696 1712 pager_cmd = get_pager_cmd(pager_cmd)
1697 1713 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1698 1714
1699 1715 try:
1700 1716 if os.environ['TERM'] in ['emacs','dumb']:
1701 1717 raise EnvironmentError
1702 1718 xsys(pager_cmd + ' ' + fname)
1703 1719 except:
1704 1720 try:
1705 1721 if start > 0:
1706 1722 start -= 1
1707 1723 page(open(fname).read(),start)
1708 1724 except:
1709 1725 print 'Unable to show file',`fname`
1710 1726
1711 1727
1712 1728 #----------------------------------------------------------------------------
1713 1729 def snip_print(str,width = 75,print_full = 0,header = ''):
1714 1730 """Print a string snipping the midsection to fit in width.
1715 1731
1716 1732 print_full: mode control:
1717 1733 - 0: only snip long strings
1718 1734 - 1: send to page() directly.
1719 1735 - 2: snip long strings and ask for full length viewing with page()
1720 1736 Return 1 if snipping was necessary, 0 otherwise."""
1721 1737
1722 1738 if print_full == 1:
1723 1739 page(header+str)
1724 1740 return 0
1725 1741
1726 1742 print header,
1727 1743 if len(str) < width:
1728 1744 print str
1729 1745 snip = 0
1730 1746 else:
1731 1747 whalf = int((width -5)/2)
1732 1748 print str[:whalf] + ' <...> ' + str[-whalf:]
1733 1749 snip = 1
1734 1750 if snip and print_full == 2:
1735 1751 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1736 1752 page(str)
1737 1753 return snip
1738 1754
1739 1755 #****************************************************************************
1740 1756 # lists, dicts and structures
1741 1757
1742 1758 def belong(candidates,checklist):
1743 1759 """Check whether a list of items appear in a given list of options.
1744 1760
1745 1761 Returns a list of 1 and 0, one for each candidate given."""
1746 1762
1747 1763 return [x in checklist for x in candidates]
1748 1764
1749 1765 #----------------------------------------------------------------------------
1750 1766 def uniq_stable(elems):
1751 1767 """uniq_stable(elems) -> list
1752 1768
1753 1769 Return from an iterable, a list of all the unique elements in the input,
1754 1770 but maintaining the order in which they first appear.
1755 1771
1756 1772 A naive solution to this problem which just makes a dictionary with the
1757 1773 elements as keys fails to respect the stability condition, since
1758 1774 dictionaries are unsorted by nature.
1759 1775
1760 1776 Note: All elements in the input must be valid dictionary keys for this
1761 1777 routine to work, as it internally uses a dictionary for efficiency
1762 1778 reasons."""
1763 1779
1764 1780 unique = []
1765 1781 unique_dict = {}
1766 1782 for nn in elems:
1767 1783 if nn not in unique_dict:
1768 1784 unique.append(nn)
1769 1785 unique_dict[nn] = None
1770 1786 return unique
1771 1787
1772 1788 #----------------------------------------------------------------------------
1773 1789 class NLprinter:
1774 1790 """Print an arbitrarily nested list, indicating index numbers.
1775 1791
1776 1792 An instance of this class called nlprint is available and callable as a
1777 1793 function.
1778 1794
1779 1795 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1780 1796 and using 'sep' to separate the index from the value. """
1781 1797
1782 1798 def __init__(self):
1783 1799 self.depth = 0
1784 1800
1785 1801 def __call__(self,lst,pos='',**kw):
1786 1802 """Prints the nested list numbering levels."""
1787 1803 kw.setdefault('indent',' ')
1788 1804 kw.setdefault('sep',': ')
1789 1805 kw.setdefault('start',0)
1790 1806 kw.setdefault('stop',len(lst))
1791 1807 # we need to remove start and stop from kw so they don't propagate
1792 1808 # into a recursive call for a nested list.
1793 1809 start = kw['start']; del kw['start']
1794 1810 stop = kw['stop']; del kw['stop']
1795 1811 if self.depth == 0 and 'header' in kw.keys():
1796 1812 print kw['header']
1797 1813
1798 1814 for idx in range(start,stop):
1799 1815 elem = lst[idx]
1800 1816 if type(elem)==type([]):
1801 1817 self.depth += 1
1802 1818 self.__call__(elem,itpl('$pos$idx,'),**kw)
1803 1819 self.depth -= 1
1804 1820 else:
1805 1821 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1806 1822
1807 1823 nlprint = NLprinter()
1808 1824 #----------------------------------------------------------------------------
1809 1825 def all_belong(candidates,checklist):
1810 1826 """Check whether a list of items ALL appear in a given list of options.
1811 1827
1812 1828 Returns a single 1 or 0 value."""
1813 1829
1814 1830 return 1-(0 in [x in checklist for x in candidates])
1815 1831
1816 1832 #----------------------------------------------------------------------------
1817 1833 def sort_compare(lst1,lst2,inplace = 1):
1818 1834 """Sort and compare two lists.
1819 1835
1820 1836 By default it does it in place, thus modifying the lists. Use inplace = 0
1821 1837 to avoid that (at the cost of temporary copy creation)."""
1822 1838 if not inplace:
1823 1839 lst1 = lst1[:]
1824 1840 lst2 = lst2[:]
1825 1841 lst1.sort(); lst2.sort()
1826 1842 return lst1 == lst2
1827 1843
1828 1844 #----------------------------------------------------------------------------
1829 1845 def list2dict(lst):
1830 1846 """Takes a list of (key,value) pairs and turns it into a dict."""
1831 1847
1832 1848 dic = {}
1833 1849 for k,v in lst: dic[k] = v
1834 1850 return dic
1835 1851
1836 1852 #----------------------------------------------------------------------------
1837 1853 def list2dict2(lst,default=''):
1838 1854 """Takes a list and turns it into a dict.
1839 1855 Much slower than list2dict, but more versatile. This version can take
1840 1856 lists with sublists of arbitrary length (including sclars)."""
1841 1857
1842 1858 dic = {}
1843 1859 for elem in lst:
1844 1860 if type(elem) in (types.ListType,types.TupleType):
1845 1861 size = len(elem)
1846 1862 if size == 0:
1847 1863 pass
1848 1864 elif size == 1:
1849 1865 dic[elem] = default
1850 1866 else:
1851 1867 k,v = elem[0], elem[1:]
1852 1868 if len(v) == 1: v = v[0]
1853 1869 dic[k] = v
1854 1870 else:
1855 1871 dic[elem] = default
1856 1872 return dic
1857 1873
1858 1874 #----------------------------------------------------------------------------
1859 1875 def flatten(seq):
1860 1876 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1861 1877
1862 1878 return [x for subseq in seq for x in subseq]
1863 1879
1864 1880 #----------------------------------------------------------------------------
1865 1881 def get_slice(seq,start=0,stop=None,step=1):
1866 1882 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1867 1883 if stop == None:
1868 1884 stop = len(seq)
1869 1885 item = lambda i: seq[i]
1870 1886 return map(item,xrange(start,stop,step))
1871 1887
1872 1888 #----------------------------------------------------------------------------
1873 1889 def chop(seq,size):
1874 1890 """Chop a sequence into chunks of the given size."""
1875 1891 chunk = lambda i: seq[i:i+size]
1876 1892 return map(chunk,xrange(0,len(seq),size))
1877 1893
1878 1894 #----------------------------------------------------------------------------
1879 1895 # with is a keyword as of python 2.5, so this function is renamed to withobj
1880 1896 # from its old 'with' name.
1881 1897 def with_obj(object, **args):
1882 1898 """Set multiple attributes for an object, similar to Pascal's with.
1883 1899
1884 1900 Example:
1885 1901 with_obj(jim,
1886 1902 born = 1960,
1887 1903 haircolour = 'Brown',
1888 1904 eyecolour = 'Green')
1889 1905
1890 1906 Credit: Greg Ewing, in
1891 1907 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1892 1908
1893 1909 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1894 1910 has become a keyword for Python 2.5, so we had to rename it."""
1895 1911
1896 1912 object.__dict__.update(args)
1897 1913
1898 1914 #----------------------------------------------------------------------------
1899 1915 def setattr_list(obj,alist,nspace = None):
1900 1916 """Set a list of attributes for an object taken from a namespace.
1901 1917
1902 1918 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1903 1919 alist with their values taken from nspace, which must be a dict (something
1904 1920 like locals() will often do) If nspace isn't given, locals() of the
1905 1921 *caller* is used, so in most cases you can omit it.
1906 1922
1907 1923 Note that alist can be given as a string, which will be automatically
1908 1924 split into a list on whitespace. If given as a list, it must be a list of
1909 1925 *strings* (the variable names themselves), not of variables."""
1910 1926
1911 1927 # this grabs the local variables from the *previous* call frame -- that is
1912 1928 # the locals from the function that called setattr_list().
1913 1929 # - snipped from weave.inline()
1914 1930 if nspace is None:
1915 1931 call_frame = sys._getframe().f_back
1916 1932 nspace = call_frame.f_locals
1917 1933
1918 1934 if type(alist) in StringTypes:
1919 1935 alist = alist.split()
1920 1936 for attr in alist:
1921 1937 val = eval(attr,nspace)
1922 1938 setattr(obj,attr,val)
1923 1939
1924 1940 #----------------------------------------------------------------------------
1925 1941 def getattr_list(obj,alist,*args):
1926 1942 """getattr_list(obj,alist[, default]) -> attribute list.
1927 1943
1928 1944 Get a list of named attributes for an object. When a default argument is
1929 1945 given, it is returned when the attribute doesn't exist; without it, an
1930 1946 exception is raised in that case.
1931 1947
1932 1948 Note that alist can be given as a string, which will be automatically
1933 1949 split into a list on whitespace. If given as a list, it must be a list of
1934 1950 *strings* (the variable names themselves), not of variables."""
1935 1951
1936 1952 if type(alist) in StringTypes:
1937 1953 alist = alist.split()
1938 1954 if args:
1939 1955 if len(args)==1:
1940 1956 default = args[0]
1941 1957 return map(lambda attr: getattr(obj,attr,default),alist)
1942 1958 else:
1943 1959 raise ValueError,'getattr_list() takes only one optional argument'
1944 1960 else:
1945 1961 return map(lambda attr: getattr(obj,attr),alist)
1946 1962
1947 1963 #----------------------------------------------------------------------------
1948 1964 def map_method(method,object_list,*argseq,**kw):
1949 1965 """map_method(method,object_list,*args,**kw) -> list
1950 1966
1951 1967 Return a list of the results of applying the methods to the items of the
1952 1968 argument sequence(s). If more than one sequence is given, the method is
1953 1969 called with an argument list consisting of the corresponding item of each
1954 1970 sequence. All sequences must be of the same length.
1955 1971
1956 1972 Keyword arguments are passed verbatim to all objects called.
1957 1973
1958 1974 This is Python code, so it's not nearly as fast as the builtin map()."""
1959 1975
1960 1976 out_list = []
1961 1977 idx = 0
1962 1978 for object in object_list:
1963 1979 try:
1964 1980 handler = getattr(object, method)
1965 1981 except AttributeError:
1966 1982 out_list.append(None)
1967 1983 else:
1968 1984 if argseq:
1969 1985 args = map(lambda lst:lst[idx],argseq)
1970 1986 #print 'ob',object,'hand',handler,'ar',args # dbg
1971 1987 out_list.append(handler(args,**kw))
1972 1988 else:
1973 1989 out_list.append(handler(**kw))
1974 1990 idx += 1
1975 1991 return out_list
1976 1992
1977 1993 #----------------------------------------------------------------------------
1978 1994 def get_class_members(cls):
1979 1995 ret = dir(cls)
1980 1996 if hasattr(cls,'__bases__'):
1981 1997 for base in cls.__bases__:
1982 1998 ret.extend(get_class_members(base))
1983 1999 return ret
1984 2000
1985 2001 #----------------------------------------------------------------------------
1986 2002 def dir2(obj):
1987 2003 """dir2(obj) -> list of strings
1988 2004
1989 2005 Extended version of the Python builtin dir(), which does a few extra
1990 2006 checks, and supports common objects with unusual internals that confuse
1991 2007 dir(), such as Traits and PyCrust.
1992 2008
1993 2009 This version is guaranteed to return only a list of true strings, whereas
1994 2010 dir() returns anything that objects inject into themselves, even if they
1995 2011 are later not really valid for attribute access (many extension libraries
1996 2012 have such bugs).
1997 2013 """
1998 2014
1999 2015 # Start building the attribute list via dir(), and then complete it
2000 2016 # with a few extra special-purpose calls.
2001 2017 words = dir(obj)
2002 2018
2003 2019 if hasattr(obj,'__class__'):
2004 2020 words.append('__class__')
2005 2021 words.extend(get_class_members(obj.__class__))
2006 2022 #if '__base__' in words: 1/0
2007 2023
2008 2024 # Some libraries (such as traits) may introduce duplicates, we want to
2009 2025 # track and clean this up if it happens
2010 2026 may_have_dupes = False
2011 2027
2012 2028 # this is the 'dir' function for objects with Enthought's traits
2013 2029 if hasattr(obj, 'trait_names'):
2014 2030 try:
2015 2031 words.extend(obj.trait_names())
2016 2032 may_have_dupes = True
2017 2033 except TypeError:
2018 2034 # This will happen if `obj` is a class and not an instance.
2019 2035 pass
2020 2036
2021 2037 # Support for PyCrust-style _getAttributeNames magic method.
2022 2038 if hasattr(obj, '_getAttributeNames'):
2023 2039 try:
2024 2040 words.extend(obj._getAttributeNames())
2025 2041 may_have_dupes = True
2026 2042 except TypeError:
2027 2043 # `obj` is a class and not an instance. Ignore
2028 2044 # this error.
2029 2045 pass
2030 2046
2031 2047 if may_have_dupes:
2032 2048 # eliminate possible duplicates, as some traits may also
2033 2049 # appear as normal attributes in the dir() call.
2034 2050 words = list(set(words))
2035 2051 words.sort()
2036 2052
2037 2053 # filter out non-string attributes which may be stuffed by dir() calls
2038 2054 # and poor coding in third-party modules
2039 2055 return [w for w in words if isinstance(w, basestring)]
2040 2056
2041 2057 #----------------------------------------------------------------------------
2042 2058 def import_fail_info(mod_name,fns=None):
2043 2059 """Inform load failure for a module."""
2044 2060
2045 2061 if fns == None:
2046 2062 warn("Loading of %s failed.\n" % (mod_name,))
2047 2063 else:
2048 2064 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
2049 2065
2050 2066 #----------------------------------------------------------------------------
2051 2067 # Proposed popitem() extension, written as a method
2052 2068
2053 2069
2054 2070 class NotGiven: pass
2055 2071
2056 2072 def popkey(dct,key,default=NotGiven):
2057 2073 """Return dct[key] and delete dct[key].
2058 2074
2059 2075 If default is given, return it if dct[key] doesn't exist, otherwise raise
2060 2076 KeyError. """
2061 2077
2062 2078 try:
2063 2079 val = dct[key]
2064 2080 except KeyError:
2065 2081 if default is NotGiven:
2066 2082 raise
2067 2083 else:
2068 2084 return default
2069 2085 else:
2070 2086 del dct[key]
2071 2087 return val
2072 2088
2073 2089 def wrap_deprecated(func, suggest = '<nothing>'):
2074 2090 def newFunc(*args, **kwargs):
2075 2091 warnings.warn("Call to deprecated function %s, use %s instead" %
2076 2092 ( func.__name__, suggest),
2077 2093 category=DeprecationWarning,
2078 2094 stacklevel = 2)
2079 2095 return func(*args, **kwargs)
2080 2096 return newFunc
2081 2097
2082 2098
2083 2099 def _num_cpus_unix():
2084 2100 """Return the number of active CPUs on a Unix system."""
2085 2101 return os.sysconf("SC_NPROCESSORS_ONLN")
2086 2102
2087 2103
2088 2104 def _num_cpus_darwin():
2089 2105 """Return the number of active CPUs on a Darwin system."""
2090 2106 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
2091 2107 return p.stdout.read()
2092 2108
2093 2109
2094 2110 def _num_cpus_windows():
2095 2111 """Return the number of active CPUs on a Windows system."""
2096 2112 return os.environ.get("NUMBER_OF_PROCESSORS")
2097 2113
2098 2114
2099 2115 def num_cpus():
2100 2116 """Return the effective number of CPUs in the system as an integer.
2101 2117
2102 2118 This cross-platform function makes an attempt at finding the total number of
2103 2119 available CPUs in the system, as returned by various underlying system and
2104 2120 python calls.
2105 2121
2106 2122 If it can't find a sensible answer, it returns 1 (though an error *may* make
2107 2123 it return a large positive number that's actually incorrect).
2108 2124 """
2109 2125
2110 2126 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
2111 2127 # for the names of the keys we needed to look up for this function. This
2112 2128 # code was inspired by their equivalent function.
2113 2129
2114 2130 ncpufuncs = {'Linux':_num_cpus_unix,
2115 2131 'Darwin':_num_cpus_darwin,
2116 2132 'Windows':_num_cpus_windows,
2117 2133 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
2118 2134 # See http://bugs.python.org/issue1082 for details.
2119 2135 'Microsoft':_num_cpus_windows,
2120 2136 }
2121 2137
2122 2138 ncpufunc = ncpufuncs.get(platform.system(),
2123 2139 # default to unix version (Solaris, AIX, etc)
2124 2140 _num_cpus_unix)
2125 2141
2126 2142 try:
2127 2143 ncpus = max(1,int(ncpufunc()))
2128 2144 except:
2129 2145 ncpus = 1
2130 2146 return ncpus
2131 2147
2132 2148 #*************************** end of file <genutils.py> **********************
@@ -1,125 +1,125 b''
1 1 # encoding: utf-8
2 2
3 3 """Default kernel configuration."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 from IPython.external.configobj import ConfigObj
19 19 from IPython.config.api import ConfigObjManager
20 from IPython.config.cutils import get_ipython_dir
20 from IPython.genutils import get_ipython_dir
21 21
22 22 default_kernel_config = ConfigObj()
23 23
24 24 try:
25 25 ipython_dir = get_ipython_dir() + '/'
26 26 except:
27 27 # This will defaults to the cwd
28 28 ipython_dir = ''
29 29
30 30 #-------------------------------------------------------------------------------
31 31 # Engine Configuration
32 32 #-------------------------------------------------------------------------------
33 33
34 34 engine_config = dict(
35 35 logfile = '', # Empty means log to stdout
36 36 furl_file = ipython_dir + 'ipcontroller-engine.furl'
37 37 )
38 38
39 39 #-------------------------------------------------------------------------------
40 40 # MPI Configuration
41 41 #-------------------------------------------------------------------------------
42 42
43 43 mpi_config = dict(
44 44 mpi4py = """from mpi4py import MPI as mpi
45 45 mpi.size = mpi.COMM_WORLD.Get_size()
46 46 mpi.rank = mpi.COMM_WORLD.Get_rank()
47 47 """,
48 48 pytrilinos = """from PyTrilinos import Epetra
49 49 class SimpleStruct:
50 50 pass
51 51 mpi = SimpleStruct()
52 52 mpi.rank = 0
53 53 mpi.size = 0
54 54 """,
55 55 default = ''
56 56 )
57 57
58 58 #-------------------------------------------------------------------------------
59 59 # Controller Configuration
60 60 #-------------------------------------------------------------------------------
61 61
62 62 controller_config = dict(
63 63
64 64 logfile = '', # Empty means log to stdout
65 65 import_statement = '',
66 66
67 67 engine_tub = dict(
68 68 ip = '', # Empty string means all interfaces
69 69 port = 0, # 0 means pick a port for me
70 70 location = '', # Empty string means try to set automatically
71 71 secure = True,
72 72 cert_file = ipython_dir + 'ipcontroller-engine.pem',
73 73 ),
74 74 engine_fc_interface = 'IPython.kernel.enginefc.IFCControllerBase',
75 75 engine_furl_file = ipython_dir + 'ipcontroller-engine.furl',
76 76
77 77 controller_interfaces = dict(
78 78 # multiengine = dict(
79 79 # controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
80 80 # fc_interface = 'IPython.kernel.multienginefc.IFCMultiEngine',
81 81 # furl_file = 'ipcontroller-mec.furl'
82 82 # ),
83 83 task = dict(
84 84 controller_interface = 'IPython.kernel.task.ITaskController',
85 85 fc_interface = 'IPython.kernel.taskfc.IFCTaskController',
86 86 furl_file = ipython_dir + 'ipcontroller-tc.furl'
87 87 ),
88 88 multiengine = dict(
89 89 controller_interface = 'IPython.kernel.multiengine.IMultiEngine',
90 90 fc_interface = 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine',
91 91 furl_file = ipython_dir + 'ipcontroller-mec.furl'
92 92 )
93 93 ),
94 94
95 95 client_tub = dict(
96 96 ip = '', # Empty string means all interfaces
97 97 port = 0, # 0 means pick a port for me
98 98 location = '', # Empty string means try to set automatically
99 99 secure = True,
100 100 cert_file = ipython_dir + 'ipcontroller-client.pem'
101 101 )
102 102 )
103 103
104 104 #-------------------------------------------------------------------------------
105 105 # Client Configuration
106 106 #-------------------------------------------------------------------------------
107 107
108 108 client_config = dict(
109 109 client_interfaces = dict(
110 110 task = dict(
111 111 furl_file = ipython_dir + 'ipcontroller-tc.furl'
112 112 ),
113 113 multiengine = dict(
114 114 furl_file = ipython_dir + 'ipcontroller-mec.furl'
115 115 )
116 116 )
117 117 )
118 118
119 119 default_kernel_config['engine'] = engine_config
120 120 default_kernel_config['mpi'] = mpi_config
121 121 default_kernel_config['controller'] = controller_config
122 122 default_kernel_config['client'] = client_config
123 123
124 124
125 125 config_manager = ConfigObjManager(default_kernel_config, 'IPython.kernel.ini') No newline at end of file
@@ -1,376 +1,376 b''
1 1 # encoding: utf-8
2 2 # -*- test-case-name: IPython.kernel.test.test_controllerservice -*-
3 3
4 4 """A Twisted Service for the IPython Controller.
5 5
6 6 The IPython Controller:
7 7
8 8 * Listens for Engines to connect and then manages access to those engines.
9 9 * Listens for clients and passes commands from client to the Engines.
10 10 * Exposes an asynchronous interfaces to the Engines which themselves can block.
11 11 * Acts as a gateway to the Engines.
12 12
13 13 The design of the controller is somewhat abstract to allow flexibility in how
14 14 the controller is presented to clients. This idea is that there is a basic
15 15 ControllerService class that allows engines to connect to it. But, this
16 16 basic class has no client interfaces. To expose client interfaces developers
17 17 provide an adapter that makes the ControllerService look like something. For
18 18 example, one client interface might support task farming and another might
19 19 support interactive usage. The important thing is that by using interfaces
20 20 and adapters, a single controller can be accessed from multiple interfaces.
21 21 Furthermore, by adapting various client interfaces to various network
22 22 protocols, each client interface can be exposed to multiple network protocols.
23 23 See multiengine.py for an example of how to adapt the ControllerService
24 24 to a client interface.
25 25 """
26 26
27 27 __docformat__ = "restructuredtext en"
28 28
29 29 #-------------------------------------------------------------------------------
30 30 # Copyright (C) 2008 The IPython Development Team
31 31 #
32 32 # Distributed under the terms of the BSD License. The full license is in
33 33 # the file COPYING, distributed as part of this software.
34 34 #-------------------------------------------------------------------------------
35 35
36 36 #-------------------------------------------------------------------------------
37 37 # Imports
38 38 #-------------------------------------------------------------------------------
39 39
40 40 import os, sys
41 41
42 42 from twisted.application import service
43 43 from twisted.internet import defer, reactor
44 44 from twisted.python import log, components
45 45 from zope.interface import Interface, implements, Attribute
46 46 import zope.interface as zi
47 47
48 48 from IPython.kernel.engineservice import \
49 49 IEngineCore, \
50 50 IEngineSerialized, \
51 51 IEngineQueued
52 52
53 from IPython.config import cutils
53 from IPython.genutils import get_ipython_dir
54 54 from IPython.kernel import codeutil
55 55
56 56 #-------------------------------------------------------------------------------
57 57 # Interfaces for the Controller
58 58 #-------------------------------------------------------------------------------
59 59
60 60 class IControllerCore(Interface):
61 61 """Basic methods any controller must have.
62 62
63 63 This is basically the aspect of the controller relevant to the
64 64 engines and does not assume anything about how the engines will
65 65 be presented to a client.
66 66 """
67 67
68 68 engines = Attribute("A dict of engine ids and engine instances.")
69 69
70 70 def register_engine(remoteEngine, id=None, ip=None, port=None,
71 71 pid=None):
72 72 """Register new remote engine.
73 73
74 74 The controller can use the ip, port, pid of the engine to do useful things
75 75 like kill the engines.
76 76
77 77 :Parameters:
78 78 remoteEngine
79 79 An implementer of IEngineCore, IEngineSerialized and IEngineQueued.
80 80 id : int
81 81 Requested id.
82 82 ip : str
83 83 IP address the engine is running on.
84 84 port : int
85 85 Port the engine is on.
86 86 pid : int
87 87 pid of the running engine.
88 88
89 89 :Returns: A dict of {'id':id} and possibly other key, value pairs.
90 90 """
91 91
92 92 def unregister_engine(id):
93 93 """Handle a disconnecting engine.
94 94
95 95 :Parameters:
96 96 id
97 97 The integer engine id of the engine to unregister.
98 98 """
99 99
100 100 def on_register_engine_do(f, includeID, *args, **kwargs):
101 101 """Call ``f(*args, **kwargs)`` when an engine is registered.
102 102
103 103 :Parameters:
104 104 includeID : int
105 105 If True the first argument to f will be the id of the engine.
106 106 """
107 107
108 108 def on_unregister_engine_do(f, includeID, *args, **kwargs):
109 109 """Call ``f(*args, **kwargs)`` when an engine is unregistered.
110 110
111 111 :Parameters:
112 112 includeID : int
113 113 If True the first argument to f will be the id of the engine.
114 114 """
115 115
116 116 def on_register_engine_do_not(f):
117 117 """Stop calling f on engine registration"""
118 118
119 119 def on_unregister_engine_do_not(f):
120 120 """Stop calling f on engine unregistration"""
121 121
122 122 def on_n_engines_registered_do(n, f, *arg, **kwargs):
123 123 """Call f(*args, **kwargs) the first time the nth engine registers."""
124 124
125 125 class IControllerBase(IControllerCore):
126 126 """The basic controller interface."""
127 127 pass
128 128
129 129
130 130 #-------------------------------------------------------------------------------
131 131 # Implementation of the ControllerService
132 132 #-------------------------------------------------------------------------------
133 133
134 134 class ControllerService(object, service.Service):
135 135 """A basic Controller represented as a Twisted Service.
136 136
137 137 This class doesn't implement any client notification mechanism. That
138 138 is up to adapted subclasses.
139 139 """
140 140
141 141 # I also pick up the IService interface by inheritance from service.Service
142 142 implements(IControllerBase)
143 143 name = 'ControllerService'
144 144
145 145 def __init__(self, maxEngines=511, saveIDs=False):
146 146 self.saveIDs = saveIDs
147 147 self.engines = {}
148 148 self.availableIDs = range(maxEngines,-1,-1) # [511,...,0]
149 149 self._onRegister = []
150 150 self._onUnregister = []
151 151 self._onNRegistered = []
152 152
153 153 #---------------------------------------------------------------------------
154 154 # Methods used to save the engine info to a log file
155 155 #---------------------------------------------------------------------------
156 156
157 157 def _buildEngineInfoString(self, id, ip, port, pid):
158 158 if id is None:
159 159 id = -99
160 160 if ip is None:
161 161 ip = "-99"
162 162 if port is None:
163 163 port = -99
164 164 if pid is None:
165 165 pid = -99
166 166 return "Engine Info: %d %s %d %d" % (id, ip , port, pid)
167 167
168 168 def _logEngineInfo(self, id, ip, port, pid):
169 169 log.msg(self._buildEngineInfoString(id,ip,port,pid))
170 170
171 171 def _getEngineInfoLogFile(self):
172 172 # Store all logs inside the ipython directory
173 ipdir = cutils.get_ipython_dir()
173 ipdir = get_ipython_dir()
174 174 pjoin = os.path.join
175 175 logdir_base = pjoin(ipdir,'log')
176 176 if not os.path.isdir(logdir_base):
177 177 os.makedirs(logdir_base)
178 178 logfile = os.path.join(logdir_base,'ipcontroller-%s-engine-info.log' % os.getpid())
179 179 return logfile
180 180
181 181 def _logEngineInfoToFile(self, id, ip, port, pid):
182 182 """Log info about an engine to a log file.
183 183
184 184 When an engine registers with a ControllerService, the ControllerService
185 185 saves information about the engine to a log file. That information
186 186 can be useful for various purposes, such as killing hung engines, etc.
187 187
188 188 This method takes the assigned id, ip/port and pid of the engine
189 189 and saves it to a file of the form:
190 190
191 191 ~/.ipython/log/ipcontroller-###-engine-info.log
192 192
193 193 where ### is the pid of the controller.
194 194
195 195 Each line of this file has the form:
196 196
197 197 Engine Info: ip ip port pid
198 198
199 199 If any of the entries are not known, they are replaced by -99.
200 200 """
201 201
202 202 fname = self._getEngineInfoLogFile()
203 203 f = open(fname, 'a')
204 204 s = self._buildEngineInfoString(id,ip,port,pid)
205 205 f.write(s + '\n')
206 206 f.close()
207 207
208 208 #---------------------------------------------------------------------------
209 209 # IControllerCore methods
210 210 #---------------------------------------------------------------------------
211 211
212 212 def register_engine(self, remoteEngine, id=None,
213 213 ip=None, port=None, pid=None):
214 214 """Register new engine connection"""
215 215
216 216 # What happens if these assertions fail?
217 217 assert IEngineCore.providedBy(remoteEngine), \
218 218 "engine passed to register_engine doesn't provide IEngineCore"
219 219 assert IEngineSerialized.providedBy(remoteEngine), \
220 220 "engine passed to register_engine doesn't provide IEngineSerialized"
221 221 assert IEngineQueued.providedBy(remoteEngine), \
222 222 "engine passed to register_engine doesn't provide IEngineQueued"
223 223 assert isinstance(id, int) or id is None, \
224 224 "id to register_engine must be an integer or None"
225 225 assert isinstance(ip, str) or ip is None, \
226 226 "ip to register_engine must be a string or None"
227 227 assert isinstance(port, int) or port is None, \
228 228 "port to register_engine must be an integer or None"
229 229 assert isinstance(pid, int) or pid is None, \
230 230 "pid to register_engine must be an integer or None"
231 231
232 232 desiredID = id
233 233 if desiredID in self.engines.keys():
234 234 desiredID = None
235 235
236 236 if desiredID in self.availableIDs:
237 237 getID = desiredID
238 238 self.availableIDs.remove(desiredID)
239 239 else:
240 240 getID = self.availableIDs.pop()
241 241 remoteEngine.id = getID
242 242 remoteEngine.service = self
243 243 self.engines[getID] = remoteEngine
244 244
245 245 # Log the Engine Information for monitoring purposes
246 246 self._logEngineInfoToFile(getID, ip, port, pid)
247 247
248 248 msg = "registered engine with id: %i" %getID
249 249 log.msg(msg)
250 250
251 251 for i in range(len(self._onRegister)):
252 252 (f,args,kwargs,ifid) = self._onRegister[i]
253 253 try:
254 254 if ifid:
255 255 f(getID, *args, **kwargs)
256 256 else:
257 257 f(*args, **kwargs)
258 258 except:
259 259 self._onRegister.pop(i)
260 260
261 261 # Call functions when the nth engine is registered and them remove them
262 262 for i, (n, f, args, kwargs) in enumerate(self._onNRegistered):
263 263 if len(self.engines.keys()) == n:
264 264 try:
265 265 try:
266 266 f(*args, **kwargs)
267 267 except:
268 268 log.msg("Function %r failed when the %ith engine registered" % (f, n))
269 269 finally:
270 270 self._onNRegistered.pop(i)
271 271
272 272 return {'id':getID}
273 273
274 274 def unregister_engine(self, id):
275 275 """Unregister engine by id."""
276 276
277 277 assert isinstance(id, int) or id is None, \
278 278 "id to unregister_engine must be an integer or None"
279 279
280 280 msg = "unregistered engine with id: %i" %id
281 281 log.msg(msg)
282 282 try:
283 283 del self.engines[id]
284 284 except KeyError:
285 285 log.msg("engine with id %i was not registered" % id)
286 286 else:
287 287 if not self.saveIDs:
288 288 self.availableIDs.append(id)
289 289 # Sort to assign lower ids first
290 290 self.availableIDs.sort(reverse=True)
291 291 else:
292 292 log.msg("preserving id %i" %id)
293 293
294 294 for i in range(len(self._onUnregister)):
295 295 (f,args,kwargs,ifid) = self._onUnregister[i]
296 296 try:
297 297 if ifid:
298 298 f(id, *args, **kwargs)
299 299 else:
300 300 f(*args, **kwargs)
301 301 except:
302 302 self._onUnregister.pop(i)
303 303
304 304 def on_register_engine_do(self, f, includeID, *args, **kwargs):
305 305 assert callable(f), "f must be callable"
306 306 self._onRegister.append((f,args,kwargs,includeID))
307 307
308 308 def on_unregister_engine_do(self, f, includeID, *args, **kwargs):
309 309 assert callable(f), "f must be callable"
310 310 self._onUnregister.append((f,args,kwargs,includeID))
311 311
312 312 def on_register_engine_do_not(self, f):
313 313 for i in range(len(self._onRegister)):
314 314 g = self._onRegister[i][0]
315 315 if f == g:
316 316 self._onRegister.pop(i)
317 317 return
318 318
319 319 def on_unregister_engine_do_not(self, f):
320 320 for i in range(len(self._onUnregister)):
321 321 g = self._onUnregister[i][0]
322 322 if f == g:
323 323 self._onUnregister.pop(i)
324 324 return
325 325
326 326 def on_n_engines_registered_do(self, n, f, *args, **kwargs):
327 327 if len(self.engines.keys()) >= n:
328 328 f(*args, **kwargs)
329 329 else:
330 330 self._onNRegistered.append((n,f,args,kwargs))
331 331
332 332
333 333 #-------------------------------------------------------------------------------
334 334 # Base class for adapting controller to different client APIs
335 335 #-------------------------------------------------------------------------------
336 336
337 337 class ControllerAdapterBase(object):
338 338 """All Controller adapters should inherit from this class.
339 339
340 340 This class provides a wrapped version of the IControllerBase interface that
341 341 can be used to easily create new custom controllers. Subclasses of this
342 342 will provide a full implementation of IControllerBase.
343 343
344 344 This class doesn't implement any client notification mechanism. That
345 345 is up to subclasses.
346 346 """
347 347
348 348 implements(IControllerBase)
349 349
350 350 def __init__(self, controller):
351 351 self.controller = controller
352 352 # Needed for IControllerCore
353 353 self.engines = self.controller.engines
354 354
355 355 def register_engine(self, remoteEngine, id=None,
356 356 ip=None, port=None, pid=None):
357 357 return self.controller.register_engine(remoteEngine,
358 358 id, ip, port, pid)
359 359
360 360 def unregister_engine(self, id):
361 361 return self.controller.unregister_engine(id)
362 362
363 363 def on_register_engine_do(self, f, includeID, *args, **kwargs):
364 364 return self.controller.on_register_engine_do(f, includeID, *args, **kwargs)
365 365
366 366 def on_unregister_engine_do(self, f, includeID, *args, **kwargs):
367 367 return self.controller.on_unregister_engine_do(f, includeID, *args, **kwargs)
368 368
369 369 def on_register_engine_do_not(self, f):
370 370 return self.controller.on_register_engine_do_not(f)
371 371
372 372 def on_unregister_engine_do_not(self, f):
373 373 return self.controller.on_unregister_engine_do_not(f)
374 374
375 375 def on_n_engines_registered_do(self, n, f, *args, **kwargs):
376 376 return self.controller.on_n_engines_registered_do(n, f, *args, **kwargs)
@@ -1,324 +1,324 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3
4 4 """Start an IPython cluster conveniently, either locally or remotely.
5 5
6 6 Basic usage
7 7 -----------
8 8
9 9 For local operation, the simplest mode of usage is:
10 10
11 11 %prog -n N
12 12
13 13 where N is the number of engines you want started.
14 14
15 15 For remote operation, you must call it with a cluster description file:
16 16
17 17 %prog -f clusterfile.py
18 18
19 19 The cluster file is a normal Python script which gets run via execfile(). You
20 20 can have arbitrary logic in it, but all that matters is that at the end of the
21 21 execution, it declares the variables 'controller', 'engines', and optionally
22 22 'sshx'. See the accompanying examples for details on what these variables must
23 23 contain.
24 24
25 25
26 26 Notes
27 27 -----
28 28
29 29 WARNING: this code is still UNFINISHED and EXPERIMENTAL! It is incomplete,
30 30 some listed options are not really implemented, and all of its interfaces are
31 31 subject to change.
32 32
33 33 When operating over SSH for a remote cluster, this program relies on the
34 34 existence of a particular script called 'sshx'. This script must live in the
35 35 target systems where you'll be running your controller and engines, and is
36 36 needed to configure your PATH and PYTHONPATH variables for further execution of
37 37 python code at the other end of an SSH connection. The script can be as simple
38 38 as:
39 39
40 40 #!/bin/sh
41 41 . $HOME/.bashrc
42 42 "$@"
43 43
44 44 which is the default one provided by IPython. You can modify this or provide
45 45 your own. Since it's quite likely that for different clusters you may need
46 46 this script to configure things differently or that it may live in different
47 47 locations, its full path can be set in the same file where you define the
48 48 cluster setup. IPython's order of evaluation for this variable is the
49 49 following:
50 50
51 51 a) Internal default: 'sshx'. This only works if it is in the default system
52 52 path which SSH sets up in non-interactive mode.
53 53
54 54 b) Environment variable: if $IPYTHON_SSHX is defined, this overrides the
55 55 internal default.
56 56
57 57 c) Variable 'sshx' in the cluster configuration file: finally, this will
58 58 override the previous two values.
59 59
60 60 This code is Unix-only, with precious little hope of any of this ever working
61 61 under Windows, since we need SSH from the ground up, we background processes,
62 62 etc. Ports of this functionality to Windows are welcome.
63 63
64 64
65 65 Call summary
66 66 ------------
67 67
68 68 %prog [options]
69 69 """
70 70
71 71 __docformat__ = "restructuredtext en"
72 72
73 73 #-------------------------------------------------------------------------------
74 74 # Copyright (C) 2008 The IPython Development Team
75 75 #
76 76 # Distributed under the terms of the BSD License. The full license is in
77 77 # the file COPYING, distributed as part of this software.
78 78 #-------------------------------------------------------------------------------
79 79
80 80 #-------------------------------------------------------------------------------
81 81 # Stdlib imports
82 82 #-------------------------------------------------------------------------------
83 83
84 84 import os
85 85 import signal
86 86 import sys
87 87 import time
88 88
89 89 from optparse import OptionParser
90 90 from subprocess import Popen,call
91 91
92 92 #---------------------------------------------------------------------------
93 93 # IPython imports
94 94 #---------------------------------------------------------------------------
95 95 from IPython.tools import utils
96 from IPython.config import cutils
96 from IPython.genutils import get_ipython_dir
97 97
98 98 #---------------------------------------------------------------------------
99 99 # Normal code begins
100 100 #---------------------------------------------------------------------------
101 101
102 102 def parse_args():
103 103 """Parse command line and return opts,args."""
104 104
105 105 parser = OptionParser(usage=__doc__)
106 106 newopt = parser.add_option # shorthand
107 107
108 108 newopt("--controller-port", type="int", dest="controllerport",
109 109 help="the TCP port the controller is listening on")
110 110
111 111 newopt("--controller-ip", type="string", dest="controllerip",
112 112 help="the TCP ip address of the controller")
113 113
114 114 newopt("-n", "--num", type="int", dest="n",default=2,
115 115 help="the number of engines to start")
116 116
117 117 newopt("--engine-port", type="int", dest="engineport",
118 118 help="the TCP port the controller will listen on for engine "
119 119 "connections")
120 120
121 121 newopt("--engine-ip", type="string", dest="engineip",
122 122 help="the TCP ip address the controller will listen on "
123 123 "for engine connections")
124 124
125 125 newopt("--mpi", type="string", dest="mpi",
126 126 help="use mpi with package: for instance --mpi=mpi4py")
127 127
128 128 newopt("-l", "--logfile", type="string", dest="logfile",
129 129 help="log file name")
130 130
131 131 newopt('-f','--cluster-file',dest='clusterfile',
132 132 help='file describing a remote cluster')
133 133
134 134 return parser.parse_args()
135 135
136 136 def numAlive(controller,engines):
137 137 """Return the number of processes still alive."""
138 138 retcodes = [controller.poll()] + \
139 139 [e.poll() for e in engines]
140 140 return retcodes.count(None)
141 141
142 142 stop = lambda pid: os.kill(pid,signal.SIGINT)
143 143 kill = lambda pid: os.kill(pid,signal.SIGTERM)
144 144
145 145 def cleanup(clean,controller,engines):
146 146 """Stop the controller and engines with the given cleanup method."""
147 147
148 148 for e in engines:
149 149 if e.poll() is None:
150 150 print 'Stopping engine, pid',e.pid
151 151 clean(e.pid)
152 152 if controller.poll() is None:
153 153 print 'Stopping controller, pid',controller.pid
154 154 clean(controller.pid)
155 155
156 156
157 157 def ensureDir(path):
158 158 """Ensure a directory exists or raise an exception."""
159 159 if not os.path.isdir(path):
160 160 os.makedirs(path)
161 161
162 162
163 163 def startMsg(control_host,control_port=10105):
164 164 """Print a startup message"""
165 165 print
166 166 print 'Your cluster is up and running.'
167 167 print
168 168 print 'For interactive use, you can make a MultiEngineClient with:'
169 169 print
170 170 print 'from IPython.kernel import client'
171 171 print "mec = client.MultiEngineClient()"
172 172 print
173 173 print 'You can then cleanly stop the cluster from IPython using:'
174 174 print
175 175 print 'mec.kill(controller=True)'
176 176 print
177 177
178 178
179 179 def clusterLocal(opt,arg):
180 180 """Start a cluster on the local machine."""
181 181
182 182 # Store all logs inside the ipython directory
183 ipdir = cutils.get_ipython_dir()
183 ipdir = get_ipython_dir()
184 184 pjoin = os.path.join
185 185
186 186 logfile = opt.logfile
187 187 if logfile is None:
188 188 logdir_base = pjoin(ipdir,'log')
189 189 ensureDir(logdir_base)
190 190 logfile = pjoin(logdir_base,'ipcluster-')
191 191
192 192 print 'Starting controller:',
193 193 controller = Popen(['ipcontroller','--logfile',logfile,'-x','-y'])
194 194 print 'Controller PID:',controller.pid
195 195
196 196 print 'Starting engines: ',
197 197 time.sleep(5)
198 198
199 199 englogfile = '%s%s-' % (logfile,controller.pid)
200 200 mpi = opt.mpi
201 201 if mpi: # start with mpi - killing the engines with sigterm will not work if you do this
202 202 engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi',
203 203 mpi, '--logfile',englogfile])]
204 204 # engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', mpi])]
205 205 else: # do what we would normally do
206 206 engines = [ Popen(['ipengine','--logfile',englogfile])
207 207 for i in range(opt.n) ]
208 208 eids = [e.pid for e in engines]
209 209 print 'Engines PIDs: ',eids
210 210 print 'Log files: %s*' % englogfile
211 211
212 212 proc_ids = eids + [controller.pid]
213 213 procs = engines + [controller]
214 214
215 215 grpid = os.getpgrp()
216 216 try:
217 217 startMsg('127.0.0.1')
218 218 print 'You can also hit Ctrl-C to stop it, or use from the cmd line:'
219 219 print
220 220 print 'kill -INT',grpid
221 221 print
222 222 try:
223 223 while True:
224 224 time.sleep(5)
225 225 except:
226 226 pass
227 227 finally:
228 228 print 'Stopping cluster. Cleaning up...'
229 229 cleanup(stop,controller,engines)
230 230 for i in range(4):
231 231 time.sleep(i+2)
232 232 nZombies = numAlive(controller,engines)
233 233 if nZombies== 0:
234 234 print 'OK: All processes cleaned up.'
235 235 break
236 236 print 'Trying again, %d processes did not stop...' % nZombies
237 237 cleanup(kill,controller,engines)
238 238 if numAlive(controller,engines) == 0:
239 239 print 'OK: All processes cleaned up.'
240 240 break
241 241 else:
242 242 print '*'*75
243 243 print 'ERROR: could not kill some processes, try to do it',
244 244 print 'manually.'
245 245 zombies = []
246 246 if controller.returncode is None:
247 247 print 'Controller is alive: pid =',controller.pid
248 248 zombies.append(controller.pid)
249 249 liveEngines = [ e for e in engines if e.returncode is None ]
250 250 for e in liveEngines:
251 251 print 'Engine is alive: pid =',e.pid
252 252 zombies.append(e.pid)
253 253 print
254 254 print 'Zombie summary:',' '.join(map(str,zombies))
255 255
256 256 def clusterRemote(opt,arg):
257 257 """Start a remote cluster over SSH"""
258 258
259 259 # Load the remote cluster configuration
260 260 clConfig = {}
261 261 execfile(opt.clusterfile,clConfig)
262 262 contConfig = clConfig['controller']
263 263 engConfig = clConfig['engines']
264 264 # Determine where to find sshx:
265 265 sshx = clConfig.get('sshx',os.environ.get('IPYTHON_SSHX','sshx'))
266 266
267 267 # Store all logs inside the ipython directory
268 ipdir = cutils.get_ipython_dir()
268 ipdir = get_ipython_dir()
269 269 pjoin = os.path.join
270 270
271 271 logfile = opt.logfile
272 272 if logfile is None:
273 273 logdir_base = pjoin(ipdir,'log')
274 274 ensureDir(logdir_base)
275 275 logfile = pjoin(logdir_base,'ipcluster')
276 276
277 277 # Append this script's PID to the logfile name always
278 278 logfile = '%s-%s' % (logfile,os.getpid())
279 279
280 280 print 'Starting controller:'
281 281 # Controller data:
282 282 xsys = os.system
283 283
284 284 contHost = contConfig['host']
285 285 contLog = '%s-con-%s-' % (logfile,contHost)
286 286 cmd = "ssh %s '%s' 'ipcontroller --logfile %s' &" % \
287 287 (contHost,sshx,contLog)
288 288 #print 'cmd:<%s>' % cmd # dbg
289 289 xsys(cmd)
290 290 time.sleep(2)
291 291
292 292 print 'Starting engines: '
293 293 for engineHost,engineData in engConfig.iteritems():
294 294 if isinstance(engineData,int):
295 295 numEngines = engineData
296 296 else:
297 297 raise NotImplementedError('port configuration not finished for engines')
298 298
299 299 print 'Sarting %d engines on %s' % (numEngines,engineHost)
300 300 engLog = '%s-eng-%s-' % (logfile,engineHost)
301 301 for i in range(numEngines):
302 302 cmd = "ssh %s '%s' 'ipengine --controller-ip %s --logfile %s' &" % \
303 303 (engineHost,sshx,contHost,engLog)
304 304 #print 'cmd:<%s>' % cmd # dbg
305 305 xsys(cmd)
306 306 # Wait after each host a little bit
307 307 time.sleep(1)
308 308
309 309 startMsg(contConfig['host'])
310 310
311 311 def main():
312 312 """Main driver for the two big options: local or remote cluster."""
313 313
314 314 opt,arg = parse_args()
315 315
316 316 clusterfile = opt.clusterfile
317 317 if clusterfile:
318 318 clusterRemote(opt,arg)
319 319 else:
320 320 clusterLocal(opt,arg)
321 321
322 322
323 323 if __name__=='__main__':
324 324 main()
General Comments 0
You need to be logged in to leave comments. Login now