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