|
@@
-1,1761
+1,1761
b''
|
|
1
|
1
|
# -*- coding: utf-8 -*-
|
|
2
|
2
|
"""
|
|
3
|
3
|
General purpose utilities.
|
|
4
|
4
|
|
|
5
|
5
|
This is a grab-bag of stuff I find useful in most programs I write. Some of
|
|
6
|
6
|
these things are also convenient when working at the command line.
|
|
7
|
7
|
|
|
8
|
|
$Id: genutils.py 1100 2006-01-29 21:27:16Z vivainio $"""
|
|
|
8
|
$Id: genutils.py 1110 2006-01-30 20:43:30Z vivainio $"""
|
|
9
|
9
|
|
|
10
|
10
|
#*****************************************************************************
|
|
11
|
11
|
# Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
|
|
12
|
12
|
#
|
|
13
|
13
|
# Distributed under the terms of the BSD License. The full license is in
|
|
14
|
14
|
# the file COPYING, distributed as part of this software.
|
|
15
|
15
|
#*****************************************************************************
|
|
16
|
16
|
|
|
17
|
17
|
from __future__ import generators # 2.2 compatibility
|
|
18
|
18
|
|
|
19
|
19
|
from IPython import Release
|
|
20
|
20
|
__author__ = '%s <%s>' % Release.authors['Fernando']
|
|
21
|
21
|
__license__ = Release.license
|
|
22
|
22
|
|
|
23
|
23
|
#****************************************************************************
|
|
24
|
24
|
# required modules from the Python standard library
|
|
25
|
25
|
import __main__
|
|
26
|
26
|
import commands
|
|
27
|
27
|
import os
|
|
28
|
28
|
import re
|
|
29
|
29
|
import shlex
|
|
30
|
30
|
import shutil
|
|
31
|
31
|
import sys
|
|
32
|
32
|
import tempfile
|
|
33
|
33
|
import time
|
|
34
|
34
|
import types
|
|
35
|
35
|
|
|
36
|
36
|
# Other IPython utilities
|
|
37
|
37
|
from IPython.Itpl import Itpl,itpl,printpl
|
|
38
|
38
|
from IPython import DPyGetOpt
|
|
39
|
|
from IPython.path import path
|
|
|
39
|
from path import path
|
|
40
|
40
|
if os.name == "nt":
|
|
41
|
41
|
from IPython.winconsole import get_console_size
|
|
42
|
42
|
|
|
43
|
43
|
# Build objects which appeared in Python 2.3 for 2.2, to make ipython
|
|
44
|
44
|
# 2.2-friendly
|
|
45
|
45
|
try:
|
|
46
|
46
|
basestring
|
|
47
|
47
|
except NameError:
|
|
48
|
48
|
import types
|
|
49
|
49
|
basestring = (types.StringType, types.UnicodeType)
|
|
50
|
50
|
True = 1==1
|
|
51
|
51
|
False = 1==0
|
|
52
|
52
|
|
|
53
|
53
|
def enumerate(obj):
|
|
54
|
54
|
i = -1
|
|
55
|
55
|
for item in obj:
|
|
56
|
56
|
i += 1
|
|
57
|
57
|
yield i, item
|
|
58
|
58
|
|
|
59
|
59
|
# add these to the builtin namespace, so that all modules find them
|
|
60
|
60
|
import __builtin__
|
|
61
|
61
|
__builtin__.basestring = basestring
|
|
62
|
62
|
__builtin__.True = True
|
|
63
|
63
|
__builtin__.False = False
|
|
64
|
64
|
__builtin__.enumerate = enumerate
|
|
65
|
65
|
|
|
66
|
66
|
# Try to use shlex.split for converting an input string into a sys.argv-type
|
|
67
|
67
|
# list. This appeared in Python 2.3, so here's a quick backport for 2.2.
|
|
68
|
68
|
try:
|
|
69
|
69
|
shlex_split = shlex.split
|
|
70
|
70
|
except AttributeError:
|
|
71
|
71
|
_quotesre = re.compile(r'[\'"](.*)[\'"]')
|
|
72
|
72
|
_wordchars = ('abcdfeghijklmnopqrstuvwxyz'
|
|
73
|
73
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.~*?'
|
|
74
|
74
|
'ßà áâãäåæçèéêëìÃîïðñòóôõöøùúûüýþÿ'
|
|
75
|
75
|
'À�ÂÃÄÅÆÇÈÉÊËÌ�Î��ÑÒÓÔÕÖØÙÚÛÜ�Þ%s'
|
|
76
|
76
|
% os.sep)
|
|
77
|
77
|
|
|
78
|
78
|
def shlex_split(s):
|
|
79
|
79
|
"""Simplified backport to Python 2.2 of shlex.split().
|
|
80
|
80
|
|
|
81
|
81
|
This is a quick and dirty hack, since the shlex module under 2.2 lacks
|
|
82
|
82
|
several of the features needed to really match the functionality of
|
|
83
|
83
|
shlex.split() in 2.3."""
|
|
84
|
84
|
|
|
85
|
85
|
lex = shlex.shlex(StringIO(s))
|
|
86
|
86
|
# Try to get options, extensions and path separators as characters
|
|
87
|
87
|
lex.wordchars = _wordchars
|
|
88
|
88
|
lex.commenters = ''
|
|
89
|
89
|
# Make a list out of the lexer by hand, since in 2.2 it's not an
|
|
90
|
90
|
# iterator.
|
|
91
|
91
|
lout = []
|
|
92
|
92
|
while 1:
|
|
93
|
93
|
token = lex.get_token()
|
|
94
|
94
|
if token == '':
|
|
95
|
95
|
break
|
|
96
|
96
|
# Try to handle quoted tokens correctly
|
|
97
|
97
|
quotes = _quotesre.match(token)
|
|
98
|
98
|
if quotes:
|
|
99
|
99
|
token = quotes.group(1)
|
|
100
|
100
|
lout.append(token)
|
|
101
|
101
|
return lout
|
|
102
|
102
|
|
|
103
|
103
|
#****************************************************************************
|
|
104
|
104
|
# Exceptions
|
|
105
|
105
|
class Error(Exception):
|
|
106
|
106
|
"""Base class for exceptions in this module."""
|
|
107
|
107
|
pass
|
|
108
|
108
|
|
|
109
|
109
|
#----------------------------------------------------------------------------
|
|
110
|
110
|
class IOStream:
|
|
111
|
111
|
def __init__(self,stream,fallback):
|
|
112
|
112
|
if not hasattr(stream,'write') or not hasattr(stream,'flush'):
|
|
113
|
113
|
stream = fallback
|
|
114
|
114
|
self.stream = stream
|
|
115
|
115
|
self._swrite = stream.write
|
|
116
|
116
|
self.flush = stream.flush
|
|
117
|
117
|
|
|
118
|
118
|
def write(self,data):
|
|
119
|
119
|
try:
|
|
120
|
120
|
self._swrite(data)
|
|
121
|
121
|
except:
|
|
122
|
122
|
try:
|
|
123
|
123
|
# print handles some unicode issues which may trip a plain
|
|
124
|
124
|
# write() call. Attempt to emulate write() by using a
|
|
125
|
125
|
# trailing comma
|
|
126
|
126
|
print >> self.stream, data,
|
|
127
|
127
|
except:
|
|
128
|
128
|
# if we get here, something is seriously broken.
|
|
129
|
129
|
print >> sys.stderr, \
|
|
130
|
130
|
'ERROR - failed to write data to stream:', stream
|
|
131
|
131
|
|
|
132
|
132
|
class IOTerm:
|
|
133
|
133
|
""" Term holds the file or file-like objects for handling I/O operations.
|
|
134
|
134
|
|
|
135
|
135
|
These are normally just sys.stdin, sys.stdout and sys.stderr but for
|
|
136
|
136
|
Windows they can can replaced to allow editing the strings before they are
|
|
137
|
137
|
displayed."""
|
|
138
|
138
|
|
|
139
|
139
|
# In the future, having IPython channel all its I/O operations through
|
|
140
|
140
|
# this class will make it easier to embed it into other environments which
|
|
141
|
141
|
# are not a normal terminal (such as a GUI-based shell)
|
|
142
|
142
|
def __init__(self,cin=None,cout=None,cerr=None):
|
|
143
|
143
|
self.cin = IOStream(cin,sys.stdin)
|
|
144
|
144
|
self.cout = IOStream(cout,sys.stdout)
|
|
145
|
145
|
self.cerr = IOStream(cerr,sys.stderr)
|
|
146
|
146
|
|
|
147
|
147
|
# Global variable to be used for all I/O
|
|
148
|
148
|
Term = IOTerm()
|
|
149
|
149
|
|
|
150
|
150
|
import IPython.rlineimpl as readline
|
|
151
|
151
|
# Remake Term to use the readline i/o facilities
|
|
152
|
152
|
if sys.platform == 'win32' and readline.have_readline:
|
|
153
|
153
|
|
|
154
|
154
|
Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
|
|
155
|
155
|
|
|
156
|
156
|
|
|
157
|
157
|
#****************************************************************************
|
|
158
|
158
|
# Generic warning/error printer, used by everything else
|
|
159
|
159
|
def warn(msg,level=2,exit_val=1):
|
|
160
|
160
|
"""Standard warning printer. Gives formatting consistency.
|
|
161
|
161
|
|
|
162
|
162
|
Output is sent to Term.cerr (sys.stderr by default).
|
|
163
|
163
|
|
|
164
|
164
|
Options:
|
|
165
|
165
|
|
|
166
|
166
|
-level(2): allows finer control:
|
|
167
|
167
|
0 -> Do nothing, dummy function.
|
|
168
|
168
|
1 -> Print message.
|
|
169
|
169
|
2 -> Print 'WARNING:' + message. (Default level).
|
|
170
|
170
|
3 -> Print 'ERROR:' + message.
|
|
171
|
171
|
4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
|
|
172
|
172
|
|
|
173
|
173
|
-exit_val (1): exit value returned by sys.exit() for a level 4
|
|
174
|
174
|
warning. Ignored for all other levels."""
|
|
175
|
175
|
|
|
176
|
176
|
if level>0:
|
|
177
|
177
|
header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
|
|
178
|
178
|
print >> Term.cerr, '%s%s' % (header[level],msg)
|
|
179
|
179
|
if level == 4:
|
|
180
|
180
|
print >> Term.cerr,'Exiting.\n'
|
|
181
|
181
|
sys.exit(exit_val)
|
|
182
|
182
|
|
|
183
|
183
|
def info(msg):
|
|
184
|
184
|
"""Equivalent to warn(msg,level=1)."""
|
|
185
|
185
|
|
|
186
|
186
|
warn(msg,level=1)
|
|
187
|
187
|
|
|
188
|
188
|
def error(msg):
|
|
189
|
189
|
"""Equivalent to warn(msg,level=3)."""
|
|
190
|
190
|
|
|
191
|
191
|
warn(msg,level=3)
|
|
192
|
192
|
|
|
193
|
193
|
def fatal(msg,exit_val=1):
|
|
194
|
194
|
"""Equivalent to warn(msg,exit_val=exit_val,level=4)."""
|
|
195
|
195
|
|
|
196
|
196
|
warn(msg,exit_val=exit_val,level=4)
|
|
197
|
197
|
|
|
198
|
198
|
#---------------------------------------------------------------------------
|
|
199
|
199
|
# Debugging routines
|
|
200
|
200
|
#
|
|
201
|
201
|
def debugx(expr,pre_msg=''):
|
|
202
|
202
|
"""Print the value of an expression from the caller's frame.
|
|
203
|
203
|
|
|
204
|
204
|
Takes an expression, evaluates it in the caller's frame and prints both
|
|
205
|
205
|
the given expression and the resulting value (as well as a debug mark
|
|
206
|
206
|
indicating the name of the calling function. The input must be of a form
|
|
207
|
207
|
suitable for eval().
|
|
208
|
208
|
|
|
209
|
209
|
An optional message can be passed, which will be prepended to the printed
|
|
210
|
210
|
expr->value pair."""
|
|
211
|
211
|
|
|
212
|
212
|
cf = sys._getframe(1)
|
|
213
|
213
|
print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
|
|
214
|
214
|
eval(expr,cf.f_globals,cf.f_locals))
|
|
215
|
215
|
|
|
216
|
216
|
# deactivate it by uncommenting the following line, which makes it a no-op
|
|
217
|
217
|
#def debugx(expr,pre_msg=''): pass
|
|
218
|
218
|
|
|
219
|
219
|
#----------------------------------------------------------------------------
|
|
220
|
220
|
StringTypes = types.StringTypes
|
|
221
|
221
|
|
|
222
|
222
|
# Basic timing functionality
|
|
223
|
223
|
|
|
224
|
224
|
# If possible (Unix), use the resource module instead of time.clock()
|
|
225
|
225
|
try:
|
|
226
|
226
|
import resource
|
|
227
|
227
|
def clock():
|
|
228
|
228
|
"""clock() -> floating point number
|
|
229
|
229
|
|
|
230
|
230
|
Return the CPU time in seconds (user time only, system time is
|
|
231
|
231
|
ignored) since the start of the process. This is done via a call to
|
|
232
|
232
|
resource.getrusage, so it avoids the wraparound problems in
|
|
233
|
233
|
time.clock()."""
|
|
234
|
234
|
|
|
235
|
235
|
return resource.getrusage(resource.RUSAGE_SELF)[0]
|
|
236
|
236
|
|
|
237
|
237
|
def clock2():
|
|
238
|
238
|
"""clock2() -> (t_user,t_system)
|
|
239
|
239
|
|
|
240
|
240
|
Similar to clock(), but return a tuple of user/system times."""
|
|
241
|
241
|
return resource.getrusage(resource.RUSAGE_SELF)[:2]
|
|
242
|
242
|
|
|
243
|
243
|
except ImportError:
|
|
244
|
244
|
clock = time.clock
|
|
245
|
245
|
def clock2():
|
|
246
|
246
|
"""Under windows, system CPU time can't be measured.
|
|
247
|
247
|
|
|
248
|
248
|
This just returns clock() and zero."""
|
|
249
|
249
|
return time.clock(),0.0
|
|
250
|
250
|
|
|
251
|
251
|
def timings_out(reps,func,*args,**kw):
|
|
252
|
252
|
"""timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
|
|
253
|
253
|
|
|
254
|
254
|
Execute a function reps times, return a tuple with the elapsed total
|
|
255
|
255
|
CPU time in seconds, the time per call and the function's output.
|
|
256
|
256
|
|
|
257
|
257
|
Under Unix, the return value is the sum of user+system time consumed by
|
|
258
|
258
|
the process, computed via the resource module. This prevents problems
|
|
259
|
259
|
related to the wraparound effect which the time.clock() function has.
|
|
260
|
260
|
|
|
261
|
261
|
Under Windows the return value is in wall clock seconds. See the
|
|
262
|
262
|
documentation for the time module for more details."""
|
|
263
|
263
|
|
|
264
|
264
|
reps = int(reps)
|
|
265
|
265
|
assert reps >=1, 'reps must be >= 1'
|
|
266
|
266
|
if reps==1:
|
|
267
|
267
|
start = clock()
|
|
268
|
268
|
out = func(*args,**kw)
|
|
269
|
269
|
tot_time = clock()-start
|
|
270
|
270
|
else:
|
|
271
|
271
|
rng = xrange(reps-1) # the last time is executed separately to store output
|
|
272
|
272
|
start = clock()
|
|
273
|
273
|
for dummy in rng: func(*args,**kw)
|
|
274
|
274
|
out = func(*args,**kw) # one last time
|
|
275
|
275
|
tot_time = clock()-start
|
|
276
|
276
|
av_time = tot_time / reps
|
|
277
|
277
|
return tot_time,av_time,out
|
|
278
|
278
|
|
|
279
|
279
|
def timings(reps,func,*args,**kw):
|
|
280
|
280
|
"""timings(reps,func,*args,**kw) -> (t_total,t_per_call)
|
|
281
|
281
|
|
|
282
|
282
|
Execute a function reps times, return a tuple with the elapsed total CPU
|
|
283
|
283
|
time in seconds and the time per call. These are just the first two values
|
|
284
|
284
|
in timings_out()."""
|
|
285
|
285
|
|
|
286
|
286
|
return timings_out(reps,func,*args,**kw)[0:2]
|
|
287
|
287
|
|
|
288
|
288
|
def timing(func,*args,**kw):
|
|
289
|
289
|
"""timing(func,*args,**kw) -> t_total
|
|
290
|
290
|
|
|
291
|
291
|
Execute a function once, return the elapsed total CPU time in
|
|
292
|
292
|
seconds. This is just the first value in timings_out()."""
|
|
293
|
293
|
|
|
294
|
294
|
return timings_out(1,func,*args,**kw)[0]
|
|
295
|
295
|
|
|
296
|
296
|
#****************************************************************************
|
|
297
|
297
|
# file and system
|
|
298
|
298
|
|
|
299
|
299
|
def system(cmd,verbose=0,debug=0,header=''):
|
|
300
|
300
|
"""Execute a system command, return its exit status.
|
|
301
|
301
|
|
|
302
|
302
|
Options:
|
|
303
|
303
|
|
|
304
|
304
|
- verbose (0): print the command to be executed.
|
|
305
|
305
|
|
|
306
|
306
|
- debug (0): only print, do not actually execute.
|
|
307
|
307
|
|
|
308
|
308
|
- header (''): Header to print on screen prior to the executed command (it
|
|
309
|
309
|
is only prepended to the command, no newlines are added).
|
|
310
|
310
|
|
|
311
|
311
|
Note: a stateful version of this function is available through the
|
|
312
|
312
|
SystemExec class."""
|
|
313
|
313
|
|
|
314
|
314
|
stat = 0
|
|
315
|
315
|
if verbose or debug: print header+cmd
|
|
316
|
316
|
sys.stdout.flush()
|
|
317
|
317
|
if not debug: stat = os.system(cmd)
|
|
318
|
318
|
return stat
|
|
319
|
319
|
|
|
320
|
320
|
# This function is used by ipython in a lot of places to make system calls.
|
|
321
|
321
|
# We need it to be slightly different under win32, due to the vagaries of
|
|
322
|
322
|
# 'network shares'. A win32 override is below.
|
|
323
|
323
|
|
|
324
|
324
|
def shell(cmd,verbose=0,debug=0,header=''):
|
|
325
|
325
|
"""Execute a command in the system shell, always return None.
|
|
326
|
326
|
|
|
327
|
327
|
Options:
|
|
328
|
328
|
|
|
329
|
329
|
- verbose (0): print the command to be executed.
|
|
330
|
330
|
|
|
331
|
331
|
- debug (0): only print, do not actually execute.
|
|
332
|
332
|
|
|
333
|
333
|
- header (''): Header to print on screen prior to the executed command (it
|
|
334
|
334
|
is only prepended to the command, no newlines are added).
|
|
335
|
335
|
|
|
336
|
336
|
Note: this is similar to genutils.system(), but it returns None so it can
|
|
337
|
337
|
be conveniently used in interactive loops without getting the return value
|
|
338
|
338
|
(typically 0) printed many times."""
|
|
339
|
339
|
|
|
340
|
340
|
stat = 0
|
|
341
|
341
|
if verbose or debug: print header+cmd
|
|
342
|
342
|
# flush stdout so we don't mangle python's buffering
|
|
343
|
343
|
sys.stdout.flush()
|
|
344
|
344
|
if not debug:
|
|
345
|
345
|
os.system(cmd)
|
|
346
|
346
|
|
|
347
|
347
|
# override shell() for win32 to deal with network shares
|
|
348
|
348
|
if os.name in ('nt','dos'):
|
|
349
|
349
|
|
|
350
|
350
|
shell_ori = shell
|
|
351
|
351
|
|
|
352
|
352
|
def shell(cmd,verbose=0,debug=0,header=''):
|
|
353
|
353
|
if os.getcwd().startswith(r"\\"):
|
|
354
|
354
|
path = os.getcwd()
|
|
355
|
355
|
# change to c drive (cannot be on UNC-share when issuing os.system,
|
|
356
|
356
|
# as cmd.exe cannot handle UNC addresses)
|
|
357
|
357
|
os.chdir("c:")
|
|
358
|
358
|
# issue pushd to the UNC-share and then run the command
|
|
359
|
359
|
try:
|
|
360
|
360
|
shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
|
|
361
|
361
|
finally:
|
|
362
|
362
|
os.chdir(path)
|
|
363
|
363
|
else:
|
|
364
|
364
|
shell_ori(cmd,verbose,debug,header)
|
|
365
|
365
|
|
|
366
|
366
|
shell.__doc__ = shell_ori.__doc__
|
|
367
|
367
|
|
|
368
|
368
|
def getoutput(cmd,verbose=0,debug=0,header='',split=0):
|
|
369
|
369
|
"""Dummy substitute for perl's backquotes.
|
|
370
|
370
|
|
|
371
|
371
|
Executes a command and returns the output.
|
|
372
|
372
|
|
|
373
|
373
|
Accepts the same arguments as system(), plus:
|
|
374
|
374
|
|
|
375
|
375
|
- split(0): if true, the output is returned as a list split on newlines.
|
|
376
|
376
|
|
|
377
|
377
|
Note: a stateful version of this function is available through the
|
|
378
|
378
|
SystemExec class."""
|
|
379
|
379
|
|
|
380
|
380
|
if verbose or debug: print header+cmd
|
|
381
|
381
|
if not debug:
|
|
382
|
382
|
output = commands.getoutput(cmd)
|
|
383
|
383
|
if split:
|
|
384
|
384
|
return output.split('\n')
|
|
385
|
385
|
else:
|
|
386
|
386
|
return output
|
|
387
|
387
|
|
|
388
|
388
|
def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
|
|
389
|
389
|
"""Return (standard output,standard error) of executing cmd in a shell.
|
|
390
|
390
|
|
|
391
|
391
|
Accepts the same arguments as system(), plus:
|
|
392
|
392
|
|
|
393
|
393
|
- split(0): if true, each of stdout/err is returned as a list split on
|
|
394
|
394
|
newlines.
|
|
395
|
395
|
|
|
396
|
396
|
Note: a stateful version of this function is available through the
|
|
397
|
397
|
SystemExec class."""
|
|
398
|
398
|
|
|
399
|
399
|
if verbose or debug: print header+cmd
|
|
400
|
400
|
if not cmd:
|
|
401
|
401
|
if split:
|
|
402
|
402
|
return [],[]
|
|
403
|
403
|
else:
|
|
404
|
404
|
return '',''
|
|
405
|
405
|
if not debug:
|
|
406
|
406
|
pin,pout,perr = os.popen3(cmd)
|
|
407
|
407
|
tout = pout.read().rstrip()
|
|
408
|
408
|
terr = perr.read().rstrip()
|
|
409
|
409
|
pin.close()
|
|
410
|
410
|
pout.close()
|
|
411
|
411
|
perr.close()
|
|
412
|
412
|
if split:
|
|
413
|
413
|
return tout.split('\n'),terr.split('\n')
|
|
414
|
414
|
else:
|
|
415
|
415
|
return tout,terr
|
|
416
|
416
|
|
|
417
|
417
|
# for compatibility with older naming conventions
|
|
418
|
418
|
xsys = system
|
|
419
|
419
|
bq = getoutput
|
|
420
|
420
|
|
|
421
|
421
|
class SystemExec:
|
|
422
|
422
|
"""Access the system and getoutput functions through a stateful interface.
|
|
423
|
423
|
|
|
424
|
424
|
Note: here we refer to the system and getoutput functions from this
|
|
425
|
425
|
library, not the ones from the standard python library.
|
|
426
|
426
|
|
|
427
|
427
|
This class offers the system and getoutput functions as methods, but the
|
|
428
|
428
|
verbose, debug and header parameters can be set for the instance (at
|
|
429
|
429
|
creation time or later) so that they don't need to be specified on each
|
|
430
|
430
|
call.
|
|
431
|
431
|
|
|
432
|
432
|
For efficiency reasons, there's no way to override the parameters on a
|
|
433
|
433
|
per-call basis other than by setting instance attributes. If you need
|
|
434
|
434
|
local overrides, it's best to directly call system() or getoutput().
|
|
435
|
435
|
|
|
436
|
436
|
The following names are provided as alternate options:
|
|
437
|
437
|
- xsys: alias to system
|
|
438
|
438
|
- bq: alias to getoutput
|
|
439
|
439
|
|
|
440
|
440
|
An instance can then be created as:
|
|
441
|
441
|
>>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
|
|
442
|
442
|
|
|
443
|
443
|
And used as:
|
|
444
|
444
|
>>> sysexec.xsys('pwd')
|
|
445
|
445
|
>>> dirlist = sysexec.bq('ls -l')
|
|
446
|
446
|
"""
|
|
447
|
447
|
|
|
448
|
448
|
def __init__(self,verbose=0,debug=0,header='',split=0):
|
|
449
|
449
|
"""Specify the instance's values for verbose, debug and header."""
|
|
450
|
450
|
setattr_list(self,'verbose debug header split')
|
|
451
|
451
|
|
|
452
|
452
|
def system(self,cmd):
|
|
453
|
453
|
"""Stateful interface to system(), with the same keyword parameters."""
|
|
454
|
454
|
|
|
455
|
455
|
system(cmd,self.verbose,self.debug,self.header)
|
|
456
|
456
|
|
|
457
|
457
|
def shell(self,cmd):
|
|
458
|
458
|
"""Stateful interface to shell(), with the same keyword parameters."""
|
|
459
|
459
|
|
|
460
|
460
|
shell(cmd,self.verbose,self.debug,self.header)
|
|
461
|
461
|
|
|
462
|
462
|
xsys = system # alias
|
|
463
|
463
|
|
|
464
|
464
|
def getoutput(self,cmd):
|
|
465
|
465
|
"""Stateful interface to getoutput()."""
|
|
466
|
466
|
|
|
467
|
467
|
return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
|
|
468
|
468
|
|
|
469
|
469
|
def getoutputerror(self,cmd):
|
|
470
|
470
|
"""Stateful interface to getoutputerror()."""
|
|
471
|
471
|
|
|
472
|
472
|
return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
|
|
473
|
473
|
|
|
474
|
474
|
bq = getoutput # alias
|
|
475
|
475
|
|
|
476
|
476
|
#-----------------------------------------------------------------------------
|
|
477
|
477
|
def mutex_opts(dict,ex_op):
|
|
478
|
478
|
"""Check for presence of mutually exclusive keys in a dict.
|
|
479
|
479
|
|
|
480
|
480
|
Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
|
|
481
|
481
|
for op1,op2 in ex_op:
|
|
482
|
482
|
if op1 in dict and op2 in dict:
|
|
483
|
483
|
raise ValueError,'\n*** ERROR in Arguments *** '\
|
|
484
|
484
|
'Options '+op1+' and '+op2+' are mutually exclusive.'
|
|
485
|
485
|
|
|
486
|
486
|
#-----------------------------------------------------------------------------
|
|
487
|
487
|
def get_py_filename(name):
|
|
488
|
488
|
"""Return a valid python filename in the current directory.
|
|
489
|
489
|
|
|
490
|
490
|
If the given name is not a file, it adds '.py' and searches again.
|
|
491
|
491
|
Raises IOError with an informative message if the file isn't found."""
|
|
492
|
492
|
|
|
493
|
493
|
name = os.path.expanduser(name)
|
|
494
|
494
|
if not os.path.isfile(name) and not name.endswith('.py'):
|
|
495
|
495
|
name += '.py'
|
|
496
|
496
|
if os.path.isfile(name):
|
|
497
|
497
|
return name
|
|
498
|
498
|
else:
|
|
499
|
499
|
raise IOError,'File `%s` not found.' % name
|
|
500
|
500
|
|
|
501
|
501
|
#-----------------------------------------------------------------------------
|
|
502
|
502
|
def filefind(fname,alt_dirs = None):
|
|
503
|
503
|
"""Return the given filename either in the current directory, if it
|
|
504
|
504
|
exists, or in a specified list of directories.
|
|
505
|
505
|
|
|
506
|
506
|
~ expansion is done on all file and directory names.
|
|
507
|
507
|
|
|
508
|
508
|
Upon an unsuccessful search, raise an IOError exception."""
|
|
509
|
509
|
|
|
510
|
510
|
if alt_dirs is None:
|
|
511
|
511
|
try:
|
|
512
|
512
|
alt_dirs = get_home_dir()
|
|
513
|
513
|
except HomeDirError:
|
|
514
|
514
|
alt_dirs = os.getcwd()
|
|
515
|
515
|
search = [fname] + list_strings(alt_dirs)
|
|
516
|
516
|
search = map(os.path.expanduser,search)
|
|
517
|
517
|
#print 'search list for',fname,'list:',search # dbg
|
|
518
|
518
|
fname = search[0]
|
|
519
|
519
|
if os.path.isfile(fname):
|
|
520
|
520
|
return fname
|
|
521
|
521
|
for direc in search[1:]:
|
|
522
|
522
|
testname = os.path.join(direc,fname)
|
|
523
|
523
|
#print 'testname',testname # dbg
|
|
524
|
524
|
if os.path.isfile(testname):
|
|
525
|
525
|
return testname
|
|
526
|
526
|
raise IOError,'File' + `fname` + \
|
|
527
|
527
|
' not found in current or supplied directories:' + `alt_dirs`
|
|
528
|
528
|
|
|
529
|
529
|
#----------------------------------------------------------------------------
|
|
530
|
530
|
def file_read(filename):
|
|
531
|
531
|
"""Read a file and close it. Returns the file source."""
|
|
532
|
532
|
fobj=open(filename,'r');
|
|
533
|
533
|
source = fobj.read();
|
|
534
|
534
|
fobj.close()
|
|
535
|
535
|
return source
|
|
536
|
536
|
|
|
537
|
537
|
#----------------------------------------------------------------------------
|
|
538
|
538
|
def target_outdated(target,deps):
|
|
539
|
539
|
"""Determine whether a target is out of date.
|
|
540
|
540
|
|
|
541
|
541
|
target_outdated(target,deps) -> 1/0
|
|
542
|
542
|
|
|
543
|
543
|
deps: list of filenames which MUST exist.
|
|
544
|
544
|
target: single filename which may or may not exist.
|
|
545
|
545
|
|
|
546
|
546
|
If target doesn't exist or is older than any file listed in deps, return
|
|
547
|
547
|
true, otherwise return false.
|
|
548
|
548
|
"""
|
|
549
|
549
|
try:
|
|
550
|
550
|
target_time = os.path.getmtime(target)
|
|
551
|
551
|
except os.error:
|
|
552
|
552
|
return 1
|
|
553
|
553
|
for dep in deps:
|
|
554
|
554
|
dep_time = os.path.getmtime(dep)
|
|
555
|
555
|
if dep_time > target_time:
|
|
556
|
556
|
#print "For target",target,"Dep failed:",dep # dbg
|
|
557
|
557
|
#print "times (dep,tar):",dep_time,target_time # dbg
|
|
558
|
558
|
return 1
|
|
559
|
559
|
return 0
|
|
560
|
560
|
|
|
561
|
561
|
#-----------------------------------------------------------------------------
|
|
562
|
562
|
def target_update(target,deps,cmd):
|
|
563
|
563
|
"""Update a target with a given command given a list of dependencies.
|
|
564
|
564
|
|
|
565
|
565
|
target_update(target,deps,cmd) -> runs cmd if target is outdated.
|
|
566
|
566
|
|
|
567
|
567
|
This is just a wrapper around target_outdated() which calls the given
|
|
568
|
568
|
command if target is outdated."""
|
|
569
|
569
|
|
|
570
|
570
|
if target_outdated(target,deps):
|
|
571
|
571
|
xsys(cmd)
|
|
572
|
572
|
|
|
573
|
573
|
#----------------------------------------------------------------------------
|
|
574
|
574
|
def unquote_ends(istr):
|
|
575
|
575
|
"""Remove a single pair of quotes from the endpoints of a string."""
|
|
576
|
576
|
|
|
577
|
577
|
if not istr:
|
|
578
|
578
|
return istr
|
|
579
|
579
|
if (istr[0]=="'" and istr[-1]=="'") or \
|
|
580
|
580
|
(istr[0]=='"' and istr[-1]=='"'):
|
|
581
|
581
|
return istr[1:-1]
|
|
582
|
582
|
else:
|
|
583
|
583
|
return istr
|
|
584
|
584
|
|
|
585
|
585
|
#----------------------------------------------------------------------------
|
|
586
|
586
|
def process_cmdline(argv,names=[],defaults={},usage=''):
|
|
587
|
587
|
""" Process command-line options and arguments.
|
|
588
|
588
|
|
|
589
|
589
|
Arguments:
|
|
590
|
590
|
|
|
591
|
591
|
- argv: list of arguments, typically sys.argv.
|
|
592
|
592
|
|
|
593
|
593
|
- names: list of option names. See DPyGetOpt docs for details on options
|
|
594
|
594
|
syntax.
|
|
595
|
595
|
|
|
596
|
596
|
- defaults: dict of default values.
|
|
597
|
597
|
|
|
598
|
598
|
- usage: optional usage notice to print if a wrong argument is passed.
|
|
599
|
599
|
|
|
600
|
600
|
Return a dict of options and a list of free arguments."""
|
|
601
|
601
|
|
|
602
|
602
|
getopt = DPyGetOpt.DPyGetOpt()
|
|
603
|
603
|
getopt.setIgnoreCase(0)
|
|
604
|
604
|
getopt.parseConfiguration(names)
|
|
605
|
605
|
|
|
606
|
606
|
try:
|
|
607
|
607
|
getopt.processArguments(argv)
|
|
608
|
608
|
except:
|
|
609
|
609
|
print usage
|
|
610
|
610
|
warn(`sys.exc_value`,level=4)
|
|
611
|
611
|
|
|
612
|
612
|
defaults.update(getopt.optionValues)
|
|
613
|
613
|
args = getopt.freeValues
|
|
614
|
614
|
|
|
615
|
615
|
return defaults,args
|
|
616
|
616
|
|
|
617
|
617
|
#----------------------------------------------------------------------------
|
|
618
|
618
|
def optstr2types(ostr):
|
|
619
|
619
|
"""Convert a string of option names to a dict of type mappings.
|
|
620
|
620
|
|
|
621
|
621
|
optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
|
|
622
|
622
|
|
|
623
|
623
|
This is used to get the types of all the options in a string formatted
|
|
624
|
624
|
with the conventions of DPyGetOpt. The 'type' None is used for options
|
|
625
|
625
|
which are strings (they need no further conversion). This function's main
|
|
626
|
626
|
use is to get a typemap for use with read_dict().
|
|
627
|
627
|
"""
|
|
628
|
628
|
|
|
629
|
629
|
typeconv = {None:'',int:'',float:''}
|
|
630
|
630
|
typemap = {'s':None,'i':int,'f':float}
|
|
631
|
631
|
opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
|
|
632
|
632
|
|
|
633
|
633
|
for w in ostr.split():
|
|
634
|
634
|
oname,alias,otype = opt_re.match(w).groups()
|
|
635
|
635
|
if otype == '' or alias == '!': # simple switches are integers too
|
|
636
|
636
|
otype = 'i'
|
|
637
|
637
|
typeconv[typemap[otype]] += oname + ' '
|
|
638
|
638
|
return typeconv
|
|
639
|
639
|
|
|
640
|
640
|
#----------------------------------------------------------------------------
|
|
641
|
641
|
def read_dict(filename,type_conv=None,**opt):
|
|
642
|
642
|
|
|
643
|
643
|
"""Read a dictionary of key=value pairs from an input file, optionally
|
|
644
|
644
|
performing conversions on the resulting values.
|
|
645
|
645
|
|
|
646
|
646
|
read_dict(filename,type_conv,**opt) -> dict
|
|
647
|
647
|
|
|
648
|
648
|
Only one value per line is accepted, the format should be
|
|
649
|
649
|
# optional comments are ignored
|
|
650
|
650
|
key value\n
|
|
651
|
651
|
|
|
652
|
652
|
Args:
|
|
653
|
653
|
|
|
654
|
654
|
- type_conv: A dictionary specifying which keys need to be converted to
|
|
655
|
655
|
which types. By default all keys are read as strings. This dictionary
|
|
656
|
656
|
should have as its keys valid conversion functions for strings
|
|
657
|
657
|
(int,long,float,complex, or your own). The value for each key
|
|
658
|
658
|
(converter) should be a whitespace separated string containing the names
|
|
659
|
659
|
of all the entries in the file to be converted using that function. For
|
|
660
|
660
|
keys to be left alone, use None as the conversion function (only needed
|
|
661
|
661
|
with purge=1, see below).
|
|
662
|
662
|
|
|
663
|
663
|
- opt: dictionary with extra options as below (default in parens)
|
|
664
|
664
|
|
|
665
|
665
|
purge(0): if set to 1, all keys *not* listed in type_conv are purged out
|
|
666
|
666
|
of the dictionary to be returned. If purge is going to be used, the
|
|
667
|
667
|
set of keys to be left as strings also has to be explicitly specified
|
|
668
|
668
|
using the (non-existent) conversion function None.
|
|
669
|
669
|
|
|
670
|
670
|
fs(None): field separator. This is the key/value separator to be used
|
|
671
|
671
|
when parsing the file. The None default means any whitespace [behavior
|
|
672
|
672
|
of string.split()].
|
|
673
|
673
|
|
|
674
|
674
|
strip(0): if 1, strip string values of leading/trailinig whitespace.
|
|
675
|
675
|
|
|
676
|
676
|
warn(1): warning level if requested keys are not found in file.
|
|
677
|
677
|
- 0: silently ignore.
|
|
678
|
678
|
- 1: inform but proceed.
|
|
679
|
679
|
- 2: raise KeyError exception.
|
|
680
|
680
|
|
|
681
|
681
|
no_empty(0): if 1, remove keys with whitespace strings as a value.
|
|
682
|
682
|
|
|
683
|
683
|
unique([]): list of keys (or space separated string) which can't be
|
|
684
|
684
|
repeated. If one such key is found in the file, each new instance
|
|
685
|
685
|
overwrites the previous one. For keys not listed here, the behavior is
|
|
686
|
686
|
to make a list of all appearances.
|
|
687
|
687
|
|
|
688
|
688
|
Example:
|
|
689
|
689
|
If the input file test.ini has:
|
|
690
|
690
|
i 3
|
|
691
|
691
|
x 4.5
|
|
692
|
692
|
y 5.5
|
|
693
|
693
|
s hi ho
|
|
694
|
694
|
Then:
|
|
695
|
695
|
|
|
696
|
696
|
>>> type_conv={int:'i',float:'x',None:'s'}
|
|
697
|
697
|
>>> read_dict('test.ini')
|
|
698
|
698
|
{'i': '3', 's': 'hi ho', 'x': '4.5', 'y': '5.5'}
|
|
699
|
699
|
>>> read_dict('test.ini',type_conv)
|
|
700
|
700
|
{'i': 3, 's': 'hi ho', 'x': 4.5, 'y': '5.5'}
|
|
701
|
701
|
>>> read_dict('test.ini',type_conv,purge=1)
|
|
702
|
702
|
{'i': 3, 's': 'hi ho', 'x': 4.5}
|
|
703
|
703
|
"""
|
|
704
|
704
|
|
|
705
|
705
|
# starting config
|
|
706
|
706
|
opt.setdefault('purge',0)
|
|
707
|
707
|
opt.setdefault('fs',None) # field sep defaults to any whitespace
|
|
708
|
708
|
opt.setdefault('strip',0)
|
|
709
|
709
|
opt.setdefault('warn',1)
|
|
710
|
710
|
opt.setdefault('no_empty',0)
|
|
711
|
711
|
opt.setdefault('unique','')
|
|
712
|
712
|
if type(opt['unique']) in StringTypes:
|
|
713
|
713
|
unique_keys = qw(opt['unique'])
|
|
714
|
714
|
elif type(opt['unique']) in (types.TupleType,types.ListType):
|
|
715
|
715
|
unique_keys = opt['unique']
|
|
716
|
716
|
else:
|
|
717
|
717
|
raise ValueError, 'Unique keys must be given as a string, List or Tuple'
|
|
718
|
718
|
|
|
719
|
719
|
dict = {}
|
|
720
|
720
|
# first read in table of values as strings
|
|
721
|
721
|
file = open(filename,'r')
|
|
722
|
722
|
for line in file.readlines():
|
|
723
|
723
|
line = line.strip()
|
|
724
|
724
|
if len(line) and line[0]=='#': continue
|
|
725
|
725
|
if len(line)>0:
|
|
726
|
726
|
lsplit = line.split(opt['fs'],1)
|
|
727
|
727
|
try:
|
|
728
|
728
|
key,val = lsplit
|
|
729
|
729
|
except ValueError:
|
|
730
|
730
|
key,val = lsplit[0],''
|
|
731
|
731
|
key = key.strip()
|
|
732
|
732
|
if opt['strip']: val = val.strip()
|
|
733
|
733
|
if val == "''" or val == '""': val = ''
|
|
734
|
734
|
if opt['no_empty'] and (val=='' or val.isspace()):
|
|
735
|
735
|
continue
|
|
736
|
736
|
# if a key is found more than once in the file, build a list
|
|
737
|
737
|
# unless it's in the 'unique' list. In that case, last found in file
|
|
738
|
738
|
# takes precedence. User beware.
|
|
739
|
739
|
try:
|
|
740
|
740
|
if dict[key] and key in unique_keys:
|
|
741
|
741
|
dict[key] = val
|
|
742
|
742
|
elif type(dict[key]) is types.ListType:
|
|
743
|
743
|
dict[key].append(val)
|
|
744
|
744
|
else:
|
|
745
|
745
|
dict[key] = [dict[key],val]
|
|
746
|
746
|
except KeyError:
|
|
747
|
747
|
dict[key] = val
|
|
748
|
748
|
# purge if requested
|
|
749
|
749
|
if opt['purge']:
|
|
750
|
750
|
accepted_keys = qwflat(type_conv.values())
|
|
751
|
751
|
for key in dict.keys():
|
|
752
|
752
|
if key in accepted_keys: continue
|
|
753
|
753
|
del(dict[key])
|
|
754
|
754
|
# now convert if requested
|
|
755
|
755
|
if type_conv==None: return dict
|
|
756
|
756
|
conversions = type_conv.keys()
|
|
757
|
757
|
try: conversions.remove(None)
|
|
758
|
758
|
except: pass
|
|
759
|
759
|
for convert in conversions:
|
|
760
|
760
|
for val in qw(type_conv[convert]):
|
|
761
|
761
|
try:
|
|
762
|
762
|
dict[val] = convert(dict[val])
|
|
763
|
763
|
except KeyError,e:
|
|
764
|
764
|
if opt['warn'] == 0:
|
|
765
|
765
|
pass
|
|
766
|
766
|
elif opt['warn'] == 1:
|
|
767
|
767
|
print >>sys.stderr, 'Warning: key',val,\
|
|
768
|
768
|
'not found in file',filename
|
|
769
|
769
|
elif opt['warn'] == 2:
|
|
770
|
770
|
raise KeyError,e
|
|
771
|
771
|
else:
|
|
772
|
772
|
raise ValueError,'Warning level must be 0,1 or 2'
|
|
773
|
773
|
|
|
774
|
774
|
return dict
|
|
775
|
775
|
|
|
776
|
776
|
#----------------------------------------------------------------------------
|
|
777
|
777
|
def flag_calls(func):
|
|
778
|
778
|
"""Wrap a function to detect and flag when it gets called.
|
|
779
|
779
|
|
|
780
|
780
|
This is a decorator which takes a function and wraps it in a function with
|
|
781
|
781
|
a 'called' attribute. wrapper.called is initialized to False.
|
|
782
|
782
|
|
|
783
|
783
|
The wrapper.called attribute is set to False right before each call to the
|
|
784
|
784
|
wrapped function, so if the call fails it remains False. After the call
|
|
785
|
785
|
completes, wrapper.called is set to True and the output is returned.
|
|
786
|
786
|
|
|
787
|
787
|
Testing for truth in wrapper.called allows you to determine if a call to
|
|
788
|
788
|
func() was attempted and succeeded."""
|
|
789
|
789
|
|
|
790
|
790
|
def wrapper(*args,**kw):
|
|
791
|
791
|
wrapper.called = False
|
|
792
|
792
|
out = func(*args,**kw)
|
|
793
|
793
|
wrapper.called = True
|
|
794
|
794
|
return out
|
|
795
|
795
|
|
|
796
|
796
|
wrapper.called = False
|
|
797
|
797
|
wrapper.__doc__ = func.__doc__
|
|
798
|
798
|
return wrapper
|
|
799
|
799
|
|
|
800
|
800
|
#----------------------------------------------------------------------------
|
|
801
|
801
|
class HomeDirError(Error):
|
|
802
|
802
|
pass
|
|
803
|
803
|
|
|
804
|
804
|
def get_home_dir():
|
|
805
|
805
|
"""Return the closest possible equivalent to a 'home' directory.
|
|
806
|
806
|
|
|
807
|
807
|
We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
|
|
808
|
808
|
|
|
809
|
809
|
Currently only Posix and NT are implemented, a HomeDirError exception is
|
|
810
|
810
|
raised for all other OSes. """
|
|
811
|
811
|
|
|
812
|
812
|
isdir = os.path.isdir
|
|
813
|
813
|
env = os.environ
|
|
814
|
814
|
try:
|
|
815
|
815
|
homedir = env['HOME']
|
|
816
|
816
|
if not isdir(homedir):
|
|
817
|
817
|
# in case a user stuck some string which does NOT resolve to a
|
|
818
|
818
|
# valid path, it's as good as if we hadn't foud it
|
|
819
|
819
|
raise KeyError
|
|
820
|
820
|
return homedir
|
|
821
|
821
|
except KeyError:
|
|
822
|
822
|
if os.name == 'posix':
|
|
823
|
823
|
raise HomeDirError,'undefined $HOME, IPython can not proceed.'
|
|
824
|
824
|
elif os.name == 'nt':
|
|
825
|
825
|
# For some strange reason, win9x returns 'nt' for os.name.
|
|
826
|
826
|
try:
|
|
827
|
827
|
homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
|
|
828
|
828
|
if not isdir(homedir):
|
|
829
|
829
|
homedir = os.path.join(env['USERPROFILE'])
|
|
830
|
830
|
if not isdir(homedir):
|
|
831
|
831
|
raise HomeDirError
|
|
832
|
832
|
return homedir
|
|
833
|
833
|
except:
|
|
834
|
834
|
try:
|
|
835
|
835
|
# Use the registry to get the 'My Documents' folder.
|
|
836
|
836
|
import _winreg as wreg
|
|
837
|
837
|
key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
|
|
838
|
838
|
"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
|
|
839
|
839
|
homedir = wreg.QueryValueEx(key,'Personal')[0]
|
|
840
|
840
|
key.Close()
|
|
841
|
841
|
if not isdir(homedir):
|
|
842
|
842
|
e = ('Invalid "Personal" folder registry key '
|
|
843
|
843
|
'typically "My Documents".\n'
|
|
844
|
844
|
'Value: %s\n'
|
|
845
|
845
|
'This is not a valid directory on your system.' %
|
|
846
|
846
|
homedir)
|
|
847
|
847
|
raise HomeDirError(e)
|
|
848
|
848
|
return homedir
|
|
849
|
849
|
except HomeDirError:
|
|
850
|
850
|
raise
|
|
851
|
851
|
except:
|
|
852
|
852
|
return 'C:\\'
|
|
853
|
853
|
elif os.name == 'dos':
|
|
854
|
854
|
# Desperate, may do absurd things in classic MacOS. May work under DOS.
|
|
855
|
855
|
return 'C:\\'
|
|
856
|
856
|
else:
|
|
857
|
857
|
raise HomeDirError,'support for your operating system not implemented.'
|
|
858
|
858
|
|
|
859
|
859
|
#****************************************************************************
|
|
860
|
860
|
# strings and text
|
|
861
|
861
|
|
|
862
|
862
|
class LSString(str):
|
|
863
|
863
|
"""String derivative with a special access attributes.
|
|
864
|
864
|
|
|
865
|
865
|
These are normal strings, but with the special attributes:
|
|
866
|
866
|
|
|
867
|
867
|
.l (or .list) : value as list (split on newlines).
|
|
868
|
868
|
.n (or .nlstr): original value (the string itself).
|
|
869
|
869
|
.s (or .spstr): value as whitespace-separated string.
|
|
870
|
870
|
|
|
871
|
871
|
Any values which require transformations are computed only once and
|
|
872
|
872
|
cached.
|
|
873
|
873
|
|
|
874
|
874
|
Such strings are very useful to efficiently interact with the shell, which
|
|
875
|
875
|
typically only understands whitespace-separated options for commands."""
|
|
876
|
876
|
|
|
877
|
877
|
def get_list(self):
|
|
878
|
878
|
try:
|
|
879
|
879
|
return self.__list
|
|
880
|
880
|
except AttributeError:
|
|
881
|
881
|
self.__list = self.split('\n')
|
|
882
|
882
|
return self.__list
|
|
883
|
883
|
|
|
884
|
884
|
l = list = property(get_list)
|
|
885
|
885
|
|
|
886
|
886
|
def get_spstr(self):
|
|
887
|
887
|
try:
|
|
888
|
888
|
return self.__spstr
|
|
889
|
889
|
except AttributeError:
|
|
890
|
890
|
self.__spstr = self.replace('\n',' ')
|
|
891
|
891
|
return self.__spstr
|
|
892
|
892
|
|
|
893
|
893
|
s = spstr = property(get_spstr)
|
|
894
|
894
|
|
|
895
|
895
|
def get_nlstr(self):
|
|
896
|
896
|
return self
|
|
897
|
897
|
|
|
898
|
898
|
n = nlstr = property(get_nlstr)
|
|
899
|
899
|
|
|
900
|
900
|
def get_paths(self):
|
|
901
|
901
|
try:
|
|
902
|
902
|
return self.__paths
|
|
903
|
903
|
except AttributeError:
|
|
904
|
904
|
self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
|
|
905
|
905
|
return self.__paths
|
|
906
|
906
|
|
|
907
|
907
|
p = paths = property(get_paths)
|
|
908
|
908
|
|
|
909
|
909
|
|
|
910
|
910
|
#----------------------------------------------------------------------------
|
|
911
|
911
|
class SList(list):
|
|
912
|
912
|
"""List derivative with a special access attributes.
|
|
913
|
913
|
|
|
914
|
914
|
These are normal lists, but with the special attributes:
|
|
915
|
915
|
|
|
916
|
916
|
.l (or .list) : value as list (the list itself).
|
|
917
|
917
|
.n (or .nlstr): value as a string, joined on newlines.
|
|
918
|
918
|
.s (or .spstr): value as a string, joined on spaces.
|
|
919
|
919
|
|
|
920
|
920
|
Any values which require transformations are computed only once and
|
|
921
|
921
|
cached."""
|
|
922
|
922
|
|
|
923
|
923
|
def get_list(self):
|
|
924
|
924
|
return self
|
|
925
|
925
|
|
|
926
|
926
|
l = list = property(get_list)
|
|
927
|
927
|
|
|
928
|
928
|
def get_spstr(self):
|
|
929
|
929
|
try:
|
|
930
|
930
|
return self.__spstr
|
|
931
|
931
|
except AttributeError:
|
|
932
|
932
|
self.__spstr = ' '.join(self)
|
|
933
|
933
|
return self.__spstr
|
|
934
|
934
|
|
|
935
|
935
|
s = spstr = property(get_spstr)
|
|
936
|
936
|
|
|
937
|
937
|
def get_nlstr(self):
|
|
938
|
938
|
try:
|
|
939
|
939
|
return self.__nlstr
|
|
940
|
940
|
except AttributeError:
|
|
941
|
941
|
self.__nlstr = '\n'.join(self)
|
|
942
|
942
|
return self.__nlstr
|
|
943
|
943
|
|
|
944
|
944
|
n = nlstr = property(get_nlstr)
|
|
945
|
945
|
|
|
946
|
946
|
def get_paths(self):
|
|
947
|
947
|
try:
|
|
948
|
948
|
return self.__paths
|
|
949
|
949
|
except AttributeError:
|
|
950
|
950
|
self.__paths = [path(p) for p in self if os.path.exists(p)]
|
|
951
|
951
|
return self.__paths
|
|
952
|
952
|
|
|
953
|
953
|
p = paths = property(get_paths)
|
|
954
|
954
|
|
|
955
|
955
|
#----------------------------------------------------------------------------
|
|
956
|
956
|
def esc_quotes(strng):
|
|
957
|
957
|
"""Return the input string with single and double quotes escaped out"""
|
|
958
|
958
|
|
|
959
|
959
|
return strng.replace('"','\\"').replace("'","\\'")
|
|
960
|
960
|
|
|
961
|
961
|
#----------------------------------------------------------------------------
|
|
962
|
962
|
def make_quoted_expr(s):
|
|
963
|
963
|
"""Return string s in appropriate quotes, using raw string if possible.
|
|
964
|
964
|
|
|
965
|
965
|
Effectively this turns string: cd \ao\ao\
|
|
966
|
966
|
to: r"cd \ao\ao\_"[:-1]
|
|
967
|
967
|
|
|
968
|
968
|
Note the use of raw string and padding at the end to allow trailing backslash.
|
|
969
|
969
|
|
|
970
|
970
|
"""
|
|
971
|
971
|
|
|
972
|
972
|
tail = ''
|
|
973
|
973
|
tailpadding = ''
|
|
974
|
974
|
raw = ''
|
|
975
|
975
|
if "\\" in s:
|
|
976
|
976
|
raw = 'r'
|
|
977
|
977
|
if s.endswith('\\'):
|
|
978
|
978
|
tail = '[:-1]'
|
|
979
|
979
|
tailpadding = '_'
|
|
980
|
980
|
if '"' not in s:
|
|
981
|
981
|
quote = '"'
|
|
982
|
982
|
elif "'" not in s:
|
|
983
|
983
|
quote = "'"
|
|
984
|
984
|
elif '"""' not in s and not s.endswith('"'):
|
|
985
|
985
|
quote = '"""'
|
|
986
|
986
|
elif "'''" not in s and not s.endswith("'"):
|
|
987
|
987
|
quote = "'''"
|
|
988
|
988
|
else:
|
|
989
|
989
|
# give up, backslash-escaped string will do
|
|
990
|
990
|
return '"%s"' % esc_quotes(s)
|
|
991
|
991
|
res = itpl("$raw$quote$s$tailpadding$quote$tail")
|
|
992
|
992
|
return res
|
|
993
|
993
|
|
|
994
|
994
|
|
|
995
|
995
|
#----------------------------------------------------------------------------
|
|
996
|
996
|
def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
|
|
997
|
997
|
"""Take multiple lines of input.
|
|
998
|
998
|
|
|
999
|
999
|
A list with each line of input as a separate element is returned when a
|
|
1000
|
1000
|
termination string is entered (defaults to a single '.'). Input can also
|
|
1001
|
1001
|
terminate via EOF (^D in Unix, ^Z-RET in Windows).
|
|
1002
|
1002
|
|
|
1003
|
1003
|
Lines of input which end in \\ are joined into single entries (and a
|
|
1004
|
1004
|
secondary continuation prompt is issued as long as the user terminates
|
|
1005
|
1005
|
lines with \\). This allows entering very long strings which are still
|
|
1006
|
1006
|
meant to be treated as single entities.
|
|
1007
|
1007
|
"""
|
|
1008
|
1008
|
|
|
1009
|
1009
|
try:
|
|
1010
|
1010
|
if header:
|
|
1011
|
1011
|
header += '\n'
|
|
1012
|
1012
|
lines = [raw_input(header + ps1)]
|
|
1013
|
1013
|
except EOFError:
|
|
1014
|
1014
|
return []
|
|
1015
|
1015
|
terminate = [terminate_str]
|
|
1016
|
1016
|
try:
|
|
1017
|
1017
|
while lines[-1:] != terminate:
|
|
1018
|
1018
|
new_line = raw_input(ps1)
|
|
1019
|
1019
|
while new_line.endswith('\\'):
|
|
1020
|
1020
|
new_line = new_line[:-1] + raw_input(ps2)
|
|
1021
|
1021
|
lines.append(new_line)
|
|
1022
|
1022
|
|
|
1023
|
1023
|
return lines[:-1] # don't return the termination command
|
|
1024
|
1024
|
except EOFError:
|
|
1025
|
1025
|
print
|
|
1026
|
1026
|
return lines
|
|
1027
|
1027
|
|
|
1028
|
1028
|
#----------------------------------------------------------------------------
|
|
1029
|
1029
|
def raw_input_ext(prompt='', ps2='... '):
|
|
1030
|
1030
|
"""Similar to raw_input(), but accepts extended lines if input ends with \\."""
|
|
1031
|
1031
|
|
|
1032
|
1032
|
line = raw_input(prompt)
|
|
1033
|
1033
|
while line.endswith('\\'):
|
|
1034
|
1034
|
line = line[:-1] + raw_input(ps2)
|
|
1035
|
1035
|
return line
|
|
1036
|
1036
|
|
|
1037
|
1037
|
#----------------------------------------------------------------------------
|
|
1038
|
1038
|
def ask_yes_no(prompt,default=None):
|
|
1039
|
1039
|
"""Asks a question and returns an integer 1/0 (y/n) answer.
|
|
1040
|
1040
|
|
|
1041
|
1041
|
If default is given (one of 'y','n'), it is used if the user input is
|
|
1042
|
1042
|
empty. Otherwise the question is repeated until an answer is given.
|
|
1043
|
1043
|
If EOF occurs 20 times consecutively, the default answer is assumed,
|
|
1044
|
1044
|
or if there is no default, an exception is raised to prevent infinite
|
|
1045
|
1045
|
loops.
|
|
1046
|
1046
|
|
|
1047
|
1047
|
Valid answers are: y/yes/n/no (match is not case sensitive)."""
|
|
1048
|
1048
|
|
|
1049
|
1049
|
answers = {'y':True,'n':False,'yes':True,'no':False}
|
|
1050
|
1050
|
ans = None
|
|
1051
|
1051
|
eofs, max_eofs = 0, 20
|
|
1052
|
1052
|
while ans not in answers.keys():
|
|
1053
|
1053
|
try:
|
|
1054
|
1054
|
ans = raw_input(prompt+' ').lower()
|
|
1055
|
1055
|
if not ans: # response was an empty string
|
|
1056
|
1056
|
ans = default
|
|
1057
|
1057
|
eofs = 0
|
|
1058
|
1058
|
except (EOFError,KeyboardInterrupt):
|
|
1059
|
1059
|
eofs = eofs + 1
|
|
1060
|
1060
|
if eofs >= max_eofs:
|
|
1061
|
1061
|
if default in answers.keys():
|
|
1062
|
1062
|
ans = default
|
|
1063
|
1063
|
else:
|
|
1064
|
1064
|
raise
|
|
1065
|
1065
|
|
|
1066
|
1066
|
return answers[ans]
|
|
1067
|
1067
|
|
|
1068
|
1068
|
#----------------------------------------------------------------------------
|
|
1069
|
1069
|
def marquee(txt='',width=78,mark='*'):
|
|
1070
|
1070
|
"""Return the input string centered in a 'marquee'."""
|
|
1071
|
1071
|
if not txt:
|
|
1072
|
1072
|
return (mark*width)[:width]
|
|
1073
|
1073
|
nmark = (width-len(txt)-2)/len(mark)/2
|
|
1074
|
1074
|
if nmark < 0: nmark =0
|
|
1075
|
1075
|
marks = mark*nmark
|
|
1076
|
1076
|
return '%s %s %s' % (marks,txt,marks)
|
|
1077
|
1077
|
|
|
1078
|
1078
|
#----------------------------------------------------------------------------
|
|
1079
|
1079
|
class EvalDict:
|
|
1080
|
1080
|
"""
|
|
1081
|
1081
|
Emulate a dict which evaluates its contents in the caller's frame.
|
|
1082
|
1082
|
|
|
1083
|
1083
|
Usage:
|
|
1084
|
1084
|
>>>number = 19
|
|
1085
|
1085
|
>>>text = "python"
|
|
1086
|
1086
|
>>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
|
|
1087
|
1087
|
"""
|
|
1088
|
1088
|
|
|
1089
|
1089
|
# This version is due to sismex01@hebmex.com on c.l.py, and is basically a
|
|
1090
|
1090
|
# modified (shorter) version of:
|
|
1091
|
1091
|
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
|
|
1092
|
1092
|
# Skip Montanaro (skip@pobox.com).
|
|
1093
|
1093
|
|
|
1094
|
1094
|
def __getitem__(self, name):
|
|
1095
|
1095
|
frame = sys._getframe(1)
|
|
1096
|
1096
|
return eval(name, frame.f_globals, frame.f_locals)
|
|
1097
|
1097
|
|
|
1098
|
1098
|
EvalString = EvalDict # for backwards compatibility
|
|
1099
|
1099
|
#----------------------------------------------------------------------------
|
|
1100
|
1100
|
def qw(words,flat=0,sep=None,maxsplit=-1):
|
|
1101
|
1101
|
"""Similar to Perl's qw() operator, but with some more options.
|
|
1102
|
1102
|
|
|
1103
|
1103
|
qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
|
|
1104
|
1104
|
|
|
1105
|
1105
|
words can also be a list itself, and with flat=1, the output will be
|
|
1106
|
1106
|
recursively flattened. Examples:
|
|
1107
|
1107
|
|
|
1108
|
1108
|
>>> qw('1 2')
|
|
1109
|
1109
|
['1', '2']
|
|
1110
|
1110
|
>>> qw(['a b','1 2',['m n','p q']])
|
|
1111
|
1111
|
[['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
|
|
1112
|
1112
|
>>> qw(['a b','1 2',['m n','p q']],flat=1)
|
|
1113
|
1113
|
['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] """
|
|
1114
|
1114
|
|
|
1115
|
1115
|
if type(words) in StringTypes:
|
|
1116
|
1116
|
return [word.strip() for word in words.split(sep,maxsplit)
|
|
1117
|
1117
|
if word and not word.isspace() ]
|
|
1118
|
1118
|
if flat:
|
|
1119
|
1119
|
return flatten(map(qw,words,[1]*len(words)))
|
|
1120
|
1120
|
return map(qw,words)
|
|
1121
|
1121
|
|
|
1122
|
1122
|
#----------------------------------------------------------------------------
|
|
1123
|
1123
|
def qwflat(words,sep=None,maxsplit=-1):
|
|
1124
|
1124
|
"""Calls qw(words) in flat mode. It's just a convenient shorthand."""
|
|
1125
|
1125
|
return qw(words,1,sep,maxsplit)
|
|
1126
|
1126
|
|
|
1127
|
1127
|
#----------------------------------------------------------------------------
|
|
1128
|
1128
|
def qw_lol(indata):
|
|
1129
|
1129
|
"""qw_lol('a b') -> [['a','b']],
|
|
1130
|
1130
|
otherwise it's just a call to qw().
|
|
1131
|
1131
|
|
|
1132
|
1132
|
We need this to make sure the modules_some keys *always* end up as a
|
|
1133
|
1133
|
list of lists."""
|
|
1134
|
1134
|
|
|
1135
|
1135
|
if type(indata) in StringTypes:
|
|
1136
|
1136
|
return [qw(indata)]
|
|
1137
|
1137
|
else:
|
|
1138
|
1138
|
return qw(indata)
|
|
1139
|
1139
|
|
|
1140
|
1140
|
#-----------------------------------------------------------------------------
|
|
1141
|
1141
|
def list_strings(arg):
|
|
1142
|
1142
|
"""Always return a list of strings, given a string or list of strings
|
|
1143
|
1143
|
as input."""
|
|
1144
|
1144
|
|
|
1145
|
1145
|
if type(arg) in StringTypes: return [arg]
|
|
1146
|
1146
|
else: return arg
|
|
1147
|
1147
|
|
|
1148
|
1148
|
#----------------------------------------------------------------------------
|
|
1149
|
1149
|
def grep(pat,list,case=1):
|
|
1150
|
1150
|
"""Simple minded grep-like function.
|
|
1151
|
1151
|
grep(pat,list) returns occurrences of pat in list, None on failure.
|
|
1152
|
1152
|
|
|
1153
|
1153
|
It only does simple string matching, with no support for regexps. Use the
|
|
1154
|
1154
|
option case=0 for case-insensitive matching."""
|
|
1155
|
1155
|
|
|
1156
|
1156
|
# This is pretty crude. At least it should implement copying only references
|
|
1157
|
1157
|
# to the original data in case it's big. Now it copies the data for output.
|
|
1158
|
1158
|
out=[]
|
|
1159
|
1159
|
if case:
|
|
1160
|
1160
|
for term in list:
|
|
1161
|
1161
|
if term.find(pat)>-1: out.append(term)
|
|
1162
|
1162
|
else:
|
|
1163
|
1163
|
lpat=pat.lower()
|
|
1164
|
1164
|
for term in list:
|
|
1165
|
1165
|
if term.lower().find(lpat)>-1: out.append(term)
|
|
1166
|
1166
|
|
|
1167
|
1167
|
if len(out): return out
|
|
1168
|
1168
|
else: return None
|
|
1169
|
1169
|
|
|
1170
|
1170
|
#----------------------------------------------------------------------------
|
|
1171
|
1171
|
def dgrep(pat,*opts):
|
|
1172
|
1172
|
"""Return grep() on dir()+dir(__builtins__).
|
|
1173
|
1173
|
|
|
1174
|
1174
|
A very common use of grep() when working interactively."""
|
|
1175
|
1175
|
|
|
1176
|
1176
|
return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
|
|
1177
|
1177
|
|
|
1178
|
1178
|
#----------------------------------------------------------------------------
|
|
1179
|
1179
|
def idgrep(pat):
|
|
1180
|
1180
|
"""Case-insensitive dgrep()"""
|
|
1181
|
1181
|
|
|
1182
|
1182
|
return dgrep(pat,0)
|
|
1183
|
1183
|
|
|
1184
|
1184
|
#----------------------------------------------------------------------------
|
|
1185
|
1185
|
def igrep(pat,list):
|
|
1186
|
1186
|
"""Synonym for case-insensitive grep."""
|
|
1187
|
1187
|
|
|
1188
|
1188
|
return grep(pat,list,case=0)
|
|
1189
|
1189
|
|
|
1190
|
1190
|
#----------------------------------------------------------------------------
|
|
1191
|
1191
|
def indent(str,nspaces=4,ntabs=0):
|
|
1192
|
1192
|
"""Indent a string a given number of spaces or tabstops.
|
|
1193
|
1193
|
|
|
1194
|
1194
|
indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
|
|
1195
|
1195
|
"""
|
|
1196
|
1196
|
if str is None:
|
|
1197
|
1197
|
return
|
|
1198
|
1198
|
ind = '\t'*ntabs+' '*nspaces
|
|
1199
|
1199
|
outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
|
|
1200
|
1200
|
if outstr.endswith(os.linesep+ind):
|
|
1201
|
1201
|
return outstr[:-len(ind)]
|
|
1202
|
1202
|
else:
|
|
1203
|
1203
|
return outstr
|
|
1204
|
1204
|
|
|
1205
|
1205
|
#-----------------------------------------------------------------------------
|
|
1206
|
1206
|
def native_line_ends(filename,backup=1):
|
|
1207
|
1207
|
"""Convert (in-place) a file to line-ends native to the current OS.
|
|
1208
|
1208
|
|
|
1209
|
1209
|
If the optional backup argument is given as false, no backup of the
|
|
1210
|
1210
|
original file is left. """
|
|
1211
|
1211
|
|
|
1212
|
1212
|
backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
|
|
1213
|
1213
|
|
|
1214
|
1214
|
bak_filename = filename + backup_suffixes[os.name]
|
|
1215
|
1215
|
|
|
1216
|
1216
|
original = open(filename).read()
|
|
1217
|
1217
|
shutil.copy2(filename,bak_filename)
|
|
1218
|
1218
|
try:
|
|
1219
|
1219
|
new = open(filename,'wb')
|
|
1220
|
1220
|
new.write(os.linesep.join(original.splitlines()))
|
|
1221
|
1221
|
new.write(os.linesep) # ALWAYS put an eol at the end of the file
|
|
1222
|
1222
|
new.close()
|
|
1223
|
1223
|
except:
|
|
1224
|
1224
|
os.rename(bak_filename,filename)
|
|
1225
|
1225
|
if not backup:
|
|
1226
|
1226
|
try:
|
|
1227
|
1227
|
os.remove(bak_filename)
|
|
1228
|
1228
|
except:
|
|
1229
|
1229
|
pass
|
|
1230
|
1230
|
|
|
1231
|
1231
|
#----------------------------------------------------------------------------
|
|
1232
|
1232
|
def get_pager_cmd(pager_cmd = None):
|
|
1233
|
1233
|
"""Return a pager command.
|
|
1234
|
1234
|
|
|
1235
|
1235
|
Makes some attempts at finding an OS-correct one."""
|
|
1236
|
1236
|
|
|
1237
|
1237
|
if os.name == 'posix':
|
|
1238
|
1238
|
default_pager_cmd = 'less -r' # -r for color control sequences
|
|
1239
|
1239
|
elif os.name in ['nt','dos']:
|
|
1240
|
1240
|
default_pager_cmd = 'type'
|
|
1241
|
1241
|
|
|
1242
|
1242
|
if pager_cmd is None:
|
|
1243
|
1243
|
try:
|
|
1244
|
1244
|
pager_cmd = os.environ['PAGER']
|
|
1245
|
1245
|
except:
|
|
1246
|
1246
|
pager_cmd = default_pager_cmd
|
|
1247
|
1247
|
return pager_cmd
|
|
1248
|
1248
|
|
|
1249
|
1249
|
#-----------------------------------------------------------------------------
|
|
1250
|
1250
|
def get_pager_start(pager,start):
|
|
1251
|
1251
|
"""Return the string for paging files with an offset.
|
|
1252
|
1252
|
|
|
1253
|
1253
|
This is the '+N' argument which less and more (under Unix) accept.
|
|
1254
|
1254
|
"""
|
|
1255
|
1255
|
|
|
1256
|
1256
|
if pager in ['less','more']:
|
|
1257
|
1257
|
if start:
|
|
1258
|
1258
|
start_string = '+' + str(start)
|
|
1259
|
1259
|
else:
|
|
1260
|
1260
|
start_string = ''
|
|
1261
|
1261
|
else:
|
|
1262
|
1262
|
start_string = ''
|
|
1263
|
1263
|
return start_string
|
|
1264
|
1264
|
|
|
1265
|
1265
|
#----------------------------------------------------------------------------
|
|
1266
|
1266
|
if os.name == "nt":
|
|
1267
|
1267
|
import msvcrt
|
|
1268
|
1268
|
def page_more():
|
|
1269
|
1269
|
""" Smart pausing between pages
|
|
1270
|
1270
|
|
|
1271
|
1271
|
@return: True if need print more lines, False if quit
|
|
1272
|
1272
|
"""
|
|
1273
|
1273
|
Term.cout.write('---Return to continue, q to quit--- ')
|
|
1274
|
1274
|
ans = msvcrt.getch()
|
|
1275
|
1275
|
if ans in ("q", "Q"):
|
|
1276
|
1276
|
result = False
|
|
1277
|
1277
|
else:
|
|
1278
|
1278
|
result = True
|
|
1279
|
1279
|
Term.cout.write("\b"*37 + " "*37 + "\b"*37)
|
|
1280
|
1280
|
return result
|
|
1281
|
1281
|
else:
|
|
1282
|
1282
|
def page_more():
|
|
1283
|
1283
|
ans = raw_input('---Return to continue, q to quit--- ')
|
|
1284
|
1284
|
if ans.lower().startswith('q'):
|
|
1285
|
1285
|
return False
|
|
1286
|
1286
|
else:
|
|
1287
|
1287
|
return True
|
|
1288
|
1288
|
|
|
1289
|
1289
|
esc_re = re.compile(r"(\x1b[^m]+m)")
|
|
1290
|
1290
|
|
|
1291
|
1291
|
def page_dumb(strng,start=0,screen_lines=25):
|
|
1292
|
1292
|
"""Very dumb 'pager' in Python, for when nothing else works.
|
|
1293
|
1293
|
|
|
1294
|
1294
|
Only moves forward, same interface as page(), except for pager_cmd and
|
|
1295
|
1295
|
mode."""
|
|
1296
|
1296
|
|
|
1297
|
1297
|
out_ln = strng.splitlines()[start:]
|
|
1298
|
1298
|
screens = chop(out_ln,screen_lines-1)
|
|
1299
|
1299
|
if len(screens) == 1:
|
|
1300
|
1300
|
print >>Term.cout, os.linesep.join(screens[0])
|
|
1301
|
1301
|
else:
|
|
1302
|
1302
|
last_escape = ""
|
|
1303
|
1303
|
for scr in screens[0:-1]:
|
|
1304
|
1304
|
hunk = os.linesep.join(scr)
|
|
1305
|
1305
|
print >>Term.cout, last_escape + hunk
|
|
1306
|
1306
|
if not page_more():
|
|
1307
|
1307
|
return
|
|
1308
|
1308
|
esc_list = esc_re.findall(hunk)
|
|
1309
|
1309
|
if len(esc_list) > 0:
|
|
1310
|
1310
|
last_escape = esc_list[-1]
|
|
1311
|
1311
|
print >>Term.cout, last_escape + os.linesep.join(screens[-1])
|
|
1312
|
1312
|
|
|
1313
|
1313
|
#----------------------------------------------------------------------------
|
|
1314
|
1314
|
def page(strng,start=0,screen_lines=0,pager_cmd = None):
|
|
1315
|
1315
|
"""Print a string, piping through a pager after a certain length.
|
|
1316
|
1316
|
|
|
1317
|
1317
|
The screen_lines parameter specifies the number of *usable* lines of your
|
|
1318
|
1318
|
terminal screen (total lines minus lines you need to reserve to show other
|
|
1319
|
1319
|
information).
|
|
1320
|
1320
|
|
|
1321
|
1321
|
If you set screen_lines to a number <=0, page() will try to auto-determine
|
|
1322
|
1322
|
your screen size and will only use up to (screen_size+screen_lines) for
|
|
1323
|
1323
|
printing, paging after that. That is, if you want auto-detection but need
|
|
1324
|
1324
|
to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
|
|
1325
|
1325
|
auto-detection without any lines reserved simply use screen_lines = 0.
|
|
1326
|
1326
|
|
|
1327
|
1327
|
If a string won't fit in the allowed lines, it is sent through the
|
|
1328
|
1328
|
specified pager command. If none given, look for PAGER in the environment,
|
|
1329
|
1329
|
and ultimately default to less.
|
|
1330
|
1330
|
|
|
1331
|
1331
|
If no system pager works, the string is sent through a 'dumb pager'
|
|
1332
|
1332
|
written in python, very simplistic.
|
|
1333
|
1333
|
"""
|
|
1334
|
1334
|
|
|
1335
|
1335
|
# Ugly kludge, but calling curses.initscr() flat out crashes in emacs
|
|
1336
|
1336
|
TERM = os.environ.get('TERM','dumb')
|
|
1337
|
1337
|
if TERM in ['dumb','emacs'] and os.name != 'nt':
|
|
1338
|
1338
|
print strng
|
|
1339
|
1339
|
return
|
|
1340
|
1340
|
# chop off the topmost part of the string we don't want to see
|
|
1341
|
1341
|
str_lines = strng.split(os.linesep)[start:]
|
|
1342
|
1342
|
str_toprint = os.linesep.join(str_lines)
|
|
1343
|
1343
|
num_newlines = len(str_lines)
|
|
1344
|
1344
|
len_str = len(str_toprint)
|
|
1345
|
1345
|
|
|
1346
|
1346
|
# Dumb heuristics to guesstimate number of on-screen lines the string
|
|
1347
|
1347
|
# takes. Very basic, but good enough for docstrings in reasonable
|
|
1348
|
1348
|
# terminals. If someone later feels like refining it, it's not hard.
|
|
1349
|
1349
|
numlines = max(num_newlines,int(len_str/80)+1)
|
|
1350
|
1350
|
|
|
1351
|
1351
|
if os.name == "nt":
|
|
1352
|
1352
|
screen_lines_def = get_console_size(defaulty=25)[1]
|
|
1353
|
1353
|
else:
|
|
1354
|
1354
|
screen_lines_def = 25 # default value if we can't auto-determine
|
|
1355
|
1355
|
|
|
1356
|
1356
|
# auto-determine screen size
|
|
1357
|
1357
|
if screen_lines <= 0:
|
|
1358
|
1358
|
if TERM=='xterm':
|
|
1359
|
1359
|
try:
|
|
1360
|
1360
|
import curses
|
|
1361
|
1361
|
if hasattr(curses,'initscr'):
|
|
1362
|
1362
|
use_curses = 1
|
|
1363
|
1363
|
else:
|
|
1364
|
1364
|
use_curses = 0
|
|
1365
|
1365
|
except ImportError:
|
|
1366
|
1366
|
use_curses = 0
|
|
1367
|
1367
|
else:
|
|
1368
|
1368
|
# curses causes problems on many terminals other than xterm.
|
|
1369
|
1369
|
use_curses = 0
|
|
1370
|
1370
|
if use_curses:
|
|
1371
|
1371
|
scr = curses.initscr()
|
|
1372
|
1372
|
screen_lines_real,screen_cols = scr.getmaxyx()
|
|
1373
|
1373
|
curses.endwin()
|
|
1374
|
1374
|
screen_lines += screen_lines_real
|
|
1375
|
1375
|
#print '***Screen size:',screen_lines_real,'lines x',\
|
|
1376
|
1376
|
#screen_cols,'columns.' # dbg
|
|
1377
|
1377
|
else:
|
|
1378
|
1378
|
screen_lines += screen_lines_def
|
|
1379
|
1379
|
|
|
1380
|
1380
|
#print 'numlines',numlines,'screenlines',screen_lines # dbg
|
|
1381
|
1381
|
if numlines <= screen_lines :
|
|
1382
|
1382
|
#print '*** normal print' # dbg
|
|
1383
|
1383
|
print >>Term.cout, str_toprint
|
|
1384
|
1384
|
else:
|
|
1385
|
1385
|
# Try to open pager and default to internal one if that fails.
|
|
1386
|
1386
|
# All failure modes are tagged as 'retval=1', to match the return
|
|
1387
|
1387
|
# value of a failed system command. If any intermediate attempt
|
|
1388
|
1388
|
# sets retval to 1, at the end we resort to our own page_dumb() pager.
|
|
1389
|
1389
|
pager_cmd = get_pager_cmd(pager_cmd)
|
|
1390
|
1390
|
pager_cmd += ' ' + get_pager_start(pager_cmd,start)
|
|
1391
|
1391
|
if os.name == 'nt':
|
|
1392
|
1392
|
if pager_cmd.startswith('type'):
|
|
1393
|
1393
|
# The default WinXP 'type' command is failing on complex strings.
|
|
1394
|
1394
|
retval = 1
|
|
1395
|
1395
|
else:
|
|
1396
|
1396
|
tmpname = tempfile.mktemp('.txt')
|
|
1397
|
1397
|
tmpfile = file(tmpname,'wt')
|
|
1398
|
1398
|
tmpfile.write(strng)
|
|
1399
|
1399
|
tmpfile.close()
|
|
1400
|
1400
|
cmd = "%s < %s" % (pager_cmd,tmpname)
|
|
1401
|
1401
|
if os.system(cmd):
|
|
1402
|
1402
|
retval = 1
|
|
1403
|
1403
|
else:
|
|
1404
|
1404
|
retval = None
|
|
1405
|
1405
|
os.remove(tmpname)
|
|
1406
|
1406
|
else:
|
|
1407
|
1407
|
try:
|
|
1408
|
1408
|
retval = None
|
|
1409
|
1409
|
# if I use popen4, things hang. No idea why.
|
|
1410
|
1410
|
#pager,shell_out = os.popen4(pager_cmd)
|
|
1411
|
1411
|
pager = os.popen(pager_cmd,'w')
|
|
1412
|
1412
|
pager.write(strng)
|
|
1413
|
1413
|
pager.close()
|
|
1414
|
1414
|
retval = pager.close() # success returns None
|
|
1415
|
1415
|
except IOError,msg: # broken pipe when user quits
|
|
1416
|
1416
|
if msg.args == (32,'Broken pipe'):
|
|
1417
|
1417
|
retval = None
|
|
1418
|
1418
|
else:
|
|
1419
|
1419
|
retval = 1
|
|
1420
|
1420
|
except OSError:
|
|
1421
|
1421
|
# Other strange problems, sometimes seen in Win2k/cygwin
|
|
1422
|
1422
|
retval = 1
|
|
1423
|
1423
|
if retval is not None:
|
|
1424
|
1424
|
page_dumb(strng,screen_lines=screen_lines)
|
|
1425
|
1425
|
|
|
1426
|
1426
|
#----------------------------------------------------------------------------
|
|
1427
|
1427
|
def page_file(fname,start = 0, pager_cmd = None):
|
|
1428
|
1428
|
"""Page a file, using an optional pager command and starting line.
|
|
1429
|
1429
|
"""
|
|
1430
|
1430
|
|
|
1431
|
1431
|
pager_cmd = get_pager_cmd(pager_cmd)
|
|
1432
|
1432
|
pager_cmd += ' ' + get_pager_start(pager_cmd,start)
|
|
1433
|
1433
|
|
|
1434
|
1434
|
try:
|
|
1435
|
1435
|
if os.environ['TERM'] in ['emacs','dumb']:
|
|
1436
|
1436
|
raise EnvironmentError
|
|
1437
|
1437
|
xsys(pager_cmd + ' ' + fname)
|
|
1438
|
1438
|
except:
|
|
1439
|
1439
|
try:
|
|
1440
|
1440
|
if start > 0:
|
|
1441
|
1441
|
start -= 1
|
|
1442
|
1442
|
page(open(fname).read(),start)
|
|
1443
|
1443
|
except:
|
|
1444
|
1444
|
print 'Unable to show file',`fname`
|
|
1445
|
1445
|
|
|
1446
|
1446
|
#----------------------------------------------------------------------------
|
|
1447
|
1447
|
def snip_print(str,width = 75,print_full = 0,header = ''):
|
|
1448
|
1448
|
"""Print a string snipping the midsection to fit in width.
|
|
1449
|
1449
|
|
|
1450
|
1450
|
print_full: mode control:
|
|
1451
|
1451
|
- 0: only snip long strings
|
|
1452
|
1452
|
- 1: send to page() directly.
|
|
1453
|
1453
|
- 2: snip long strings and ask for full length viewing with page()
|
|
1454
|
1454
|
Return 1 if snipping was necessary, 0 otherwise."""
|
|
1455
|
1455
|
|
|
1456
|
1456
|
if print_full == 1:
|
|
1457
|
1457
|
page(header+str)
|
|
1458
|
1458
|
return 0
|
|
1459
|
1459
|
|
|
1460
|
1460
|
print header,
|
|
1461
|
1461
|
if len(str) < width:
|
|
1462
|
1462
|
print str
|
|
1463
|
1463
|
snip = 0
|
|
1464
|
1464
|
else:
|
|
1465
|
1465
|
whalf = int((width -5)/2)
|
|
1466
|
1466
|
print str[:whalf] + ' <...> ' + str[-whalf:]
|
|
1467
|
1467
|
snip = 1
|
|
1468
|
1468
|
if snip and print_full == 2:
|
|
1469
|
1469
|
if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
|
|
1470
|
1470
|
page(str)
|
|
1471
|
1471
|
return snip
|
|
1472
|
1472
|
|
|
1473
|
1473
|
#****************************************************************************
|
|
1474
|
1474
|
# lists, dicts and structures
|
|
1475
|
1475
|
|
|
1476
|
1476
|
def belong(candidates,checklist):
|
|
1477
|
1477
|
"""Check whether a list of items appear in a given list of options.
|
|
1478
|
1478
|
|
|
1479
|
1479
|
Returns a list of 1 and 0, one for each candidate given."""
|
|
1480
|
1480
|
|
|
1481
|
1481
|
return [x in checklist for x in candidates]
|
|
1482
|
1482
|
|
|
1483
|
1483
|
#----------------------------------------------------------------------------
|
|
1484
|
1484
|
def uniq_stable(elems):
|
|
1485
|
1485
|
"""uniq_stable(elems) -> list
|
|
1486
|
1486
|
|
|
1487
|
1487
|
Return from an iterable, a list of all the unique elements in the input,
|
|
1488
|
1488
|
but maintaining the order in which they first appear.
|
|
1489
|
1489
|
|
|
1490
|
1490
|
A naive solution to this problem which just makes a dictionary with the
|
|
1491
|
1491
|
elements as keys fails to respect the stability condition, since
|
|
1492
|
1492
|
dictionaries are unsorted by nature.
|
|
1493
|
1493
|
|
|
1494
|
1494
|
Note: All elements in the input must be valid dictionary keys for this
|
|
1495
|
1495
|
routine to work, as it internally uses a dictionary for efficiency
|
|
1496
|
1496
|
reasons."""
|
|
1497
|
1497
|
|
|
1498
|
1498
|
unique = []
|
|
1499
|
1499
|
unique_dict = {}
|
|
1500
|
1500
|
for nn in elems:
|
|
1501
|
1501
|
if nn not in unique_dict:
|
|
1502
|
1502
|
unique.append(nn)
|
|
1503
|
1503
|
unique_dict[nn] = None
|
|
1504
|
1504
|
return unique
|
|
1505
|
1505
|
|
|
1506
|
1506
|
#----------------------------------------------------------------------------
|
|
1507
|
1507
|
class NLprinter:
|
|
1508
|
1508
|
"""Print an arbitrarily nested list, indicating index numbers.
|
|
1509
|
1509
|
|
|
1510
|
1510
|
An instance of this class called nlprint is available and callable as a
|
|
1511
|
1511
|
function.
|
|
1512
|
1512
|
|
|
1513
|
1513
|
nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
|
|
1514
|
1514
|
and using 'sep' to separate the index from the value. """
|
|
1515
|
1515
|
|
|
1516
|
1516
|
def __init__(self):
|
|
1517
|
1517
|
self.depth = 0
|
|
1518
|
1518
|
|
|
1519
|
1519
|
def __call__(self,lst,pos='',**kw):
|
|
1520
|
1520
|
"""Prints the nested list numbering levels."""
|
|
1521
|
1521
|
kw.setdefault('indent',' ')
|
|
1522
|
1522
|
kw.setdefault('sep',': ')
|
|
1523
|
1523
|
kw.setdefault('start',0)
|
|
1524
|
1524
|
kw.setdefault('stop',len(lst))
|
|
1525
|
1525
|
# we need to remove start and stop from kw so they don't propagate
|
|
1526
|
1526
|
# into a recursive call for a nested list.
|
|
1527
|
1527
|
start = kw['start']; del kw['start']
|
|
1528
|
1528
|
stop = kw['stop']; del kw['stop']
|
|
1529
|
1529
|
if self.depth == 0 and 'header' in kw.keys():
|
|
1530
|
1530
|
print kw['header']
|
|
1531
|
1531
|
|
|
1532
|
1532
|
for idx in range(start,stop):
|
|
1533
|
1533
|
elem = lst[idx]
|
|
1534
|
1534
|
if type(elem)==type([]):
|
|
1535
|
1535
|
self.depth += 1
|
|
1536
|
1536
|
self.__call__(elem,itpl('$pos$idx,'),**kw)
|
|
1537
|
1537
|
self.depth -= 1
|
|
1538
|
1538
|
else:
|
|
1539
|
1539
|
printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
|
|
1540
|
1540
|
|
|
1541
|
1541
|
nlprint = NLprinter()
|
|
1542
|
1542
|
#----------------------------------------------------------------------------
|
|
1543
|
1543
|
def all_belong(candidates,checklist):
|
|
1544
|
1544
|
"""Check whether a list of items ALL appear in a given list of options.
|
|
1545
|
1545
|
|
|
1546
|
1546
|
Returns a single 1 or 0 value."""
|
|
1547
|
1547
|
|
|
1548
|
1548
|
return 1-(0 in [x in checklist for x in candidates])
|
|
1549
|
1549
|
|
|
1550
|
1550
|
#----------------------------------------------------------------------------
|
|
1551
|
1551
|
def sort_compare(lst1,lst2,inplace = 1):
|
|
1552
|
1552
|
"""Sort and compare two lists.
|
|
1553
|
1553
|
|
|
1554
|
1554
|
By default it does it in place, thus modifying the lists. Use inplace = 0
|
|
1555
|
1555
|
to avoid that (at the cost of temporary copy creation)."""
|
|
1556
|
1556
|
if not inplace:
|
|
1557
|
1557
|
lst1 = lst1[:]
|
|
1558
|
1558
|
lst2 = lst2[:]
|
|
1559
|
1559
|
lst1.sort(); lst2.sort()
|
|
1560
|
1560
|
return lst1 == lst2
|
|
1561
|
1561
|
|
|
1562
|
1562
|
#----------------------------------------------------------------------------
|
|
1563
|
1563
|
def mkdict(**kwargs):
|
|
1564
|
1564
|
"""Return a dict from a keyword list.
|
|
1565
|
1565
|
|
|
1566
|
1566
|
It's just syntactic sugar for making ditcionary creation more convenient:
|
|
1567
|
1567
|
# the standard way
|
|
1568
|
1568
|
>>>data = { 'red' : 1, 'green' : 2, 'blue' : 3 }
|
|
1569
|
1569
|
# a cleaner way
|
|
1570
|
1570
|
>>>data = dict(red=1, green=2, blue=3)
|
|
1571
|
1571
|
|
|
1572
|
1572
|
If you need more than this, look at the Struct() class."""
|
|
1573
|
1573
|
|
|
1574
|
1574
|
return kwargs
|
|
1575
|
1575
|
|
|
1576
|
1576
|
#----------------------------------------------------------------------------
|
|
1577
|
1577
|
def list2dict(lst):
|
|
1578
|
1578
|
"""Takes a list of (key,value) pairs and turns it into a dict."""
|
|
1579
|
1579
|
|
|
1580
|
1580
|
dic = {}
|
|
1581
|
1581
|
for k,v in lst: dic[k] = v
|
|
1582
|
1582
|
return dic
|
|
1583
|
1583
|
|
|
1584
|
1584
|
#----------------------------------------------------------------------------
|
|
1585
|
1585
|
def list2dict2(lst,default=''):
|
|
1586
|
1586
|
"""Takes a list and turns it into a dict.
|
|
1587
|
1587
|
Much slower than list2dict, but more versatile. This version can take
|
|
1588
|
1588
|
lists with sublists of arbitrary length (including sclars)."""
|
|
1589
|
1589
|
|
|
1590
|
1590
|
dic = {}
|
|
1591
|
1591
|
for elem in lst:
|
|
1592
|
1592
|
if type(elem) in (types.ListType,types.TupleType):
|
|
1593
|
1593
|
size = len(elem)
|
|
1594
|
1594
|
if size == 0:
|
|
1595
|
1595
|
pass
|
|
1596
|
1596
|
elif size == 1:
|
|
1597
|
1597
|
dic[elem] = default
|
|
1598
|
1598
|
else:
|
|
1599
|
1599
|
k,v = elem[0], elem[1:]
|
|
1600
|
1600
|
if len(v) == 1: v = v[0]
|
|
1601
|
1601
|
dic[k] = v
|
|
1602
|
1602
|
else:
|
|
1603
|
1603
|
dic[elem] = default
|
|
1604
|
1604
|
return dic
|
|
1605
|
1605
|
|
|
1606
|
1606
|
#----------------------------------------------------------------------------
|
|
1607
|
1607
|
def flatten(seq):
|
|
1608
|
1608
|
"""Flatten a list of lists (NOT recursive, only works for 2d lists)."""
|
|
1609
|
1609
|
|
|
1610
|
1610
|
# bug in python??? (YES. Fixed in 2.2, let's leave the kludgy fix in).
|
|
1611
|
1611
|
|
|
1612
|
1612
|
# if the x=0 isn't made, a *global* variable x is left over after calling
|
|
1613
|
1613
|
# this function, with the value of the last element in the return
|
|
1614
|
1614
|
# list. This does seem like a bug big time to me.
|
|
1615
|
1615
|
|
|
1616
|
1616
|
# the problem is fixed with the x=0, which seems to force the creation of
|
|
1617
|
1617
|
# a local name
|
|
1618
|
1618
|
|
|
1619
|
1619
|
x = 0
|
|
1620
|
1620
|
return [x for subseq in seq for x in subseq]
|
|
1621
|
1621
|
|
|
1622
|
1622
|
#----------------------------------------------------------------------------
|
|
1623
|
1623
|
def get_slice(seq,start=0,stop=None,step=1):
|
|
1624
|
1624
|
"""Get a slice of a sequence with variable step. Specify start,stop,step."""
|
|
1625
|
1625
|
if stop == None:
|
|
1626
|
1626
|
stop = len(seq)
|
|
1627
|
1627
|
item = lambda i: seq[i]
|
|
1628
|
1628
|
return map(item,xrange(start,stop,step))
|
|
1629
|
1629
|
|
|
1630
|
1630
|
#----------------------------------------------------------------------------
|
|
1631
|
1631
|
def chop(seq,size):
|
|
1632
|
1632
|
"""Chop a sequence into chunks of the given size."""
|
|
1633
|
1633
|
chunk = lambda i: seq[i:i+size]
|
|
1634
|
1634
|
return map(chunk,xrange(0,len(seq),size))
|
|
1635
|
1635
|
|
|
1636
|
1636
|
#----------------------------------------------------------------------------
|
|
1637
|
1637
|
def with(object, **args):
|
|
1638
|
1638
|
"""Set multiple attributes for an object, similar to Pascal's with.
|
|
1639
|
1639
|
|
|
1640
|
1640
|
Example:
|
|
1641
|
1641
|
with(jim,
|
|
1642
|
1642
|
born = 1960,
|
|
1643
|
1643
|
haircolour = 'Brown',
|
|
1644
|
1644
|
eyecolour = 'Green')
|
|
1645
|
1645
|
|
|
1646
|
1646
|
Credit: Greg Ewing, in
|
|
1647
|
1647
|
http://mail.python.org/pipermail/python-list/2001-May/040703.html"""
|
|
1648
|
1648
|
|
|
1649
|
1649
|
object.__dict__.update(args)
|
|
1650
|
1650
|
|
|
1651
|
1651
|
#----------------------------------------------------------------------------
|
|
1652
|
1652
|
def setattr_list(obj,alist,nspace = None):
|
|
1653
|
1653
|
"""Set a list of attributes for an object taken from a namespace.
|
|
1654
|
1654
|
|
|
1655
|
1655
|
setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
|
|
1656
|
1656
|
alist with their values taken from nspace, which must be a dict (something
|
|
1657
|
1657
|
like locals() will often do) If nspace isn't given, locals() of the
|
|
1658
|
1658
|
*caller* is used, so in most cases you can omit it.
|
|
1659
|
1659
|
|
|
1660
|
1660
|
Note that alist can be given as a string, which will be automatically
|
|
1661
|
1661
|
split into a list on whitespace. If given as a list, it must be a list of
|
|
1662
|
1662
|
*strings* (the variable names themselves), not of variables."""
|
|
1663
|
1663
|
|
|
1664
|
1664
|
# this grabs the local variables from the *previous* call frame -- that is
|
|
1665
|
1665
|
# the locals from the function that called setattr_list().
|
|
1666
|
1666
|
# - snipped from weave.inline()
|
|
1667
|
1667
|
if nspace is None:
|
|
1668
|
1668
|
call_frame = sys._getframe().f_back
|
|
1669
|
1669
|
nspace = call_frame.f_locals
|
|
1670
|
1670
|
|
|
1671
|
1671
|
if type(alist) in StringTypes:
|
|
1672
|
1672
|
alist = alist.split()
|
|
1673
|
1673
|
for attr in alist:
|
|
1674
|
1674
|
val = eval(attr,nspace)
|
|
1675
|
1675
|
setattr(obj,attr,val)
|
|
1676
|
1676
|
|
|
1677
|
1677
|
#----------------------------------------------------------------------------
|
|
1678
|
1678
|
def getattr_list(obj,alist,*args):
|
|
1679
|
1679
|
"""getattr_list(obj,alist[, default]) -> attribute list.
|
|
1680
|
1680
|
|
|
1681
|
1681
|
Get a list of named attributes for an object. When a default argument is
|
|
1682
|
1682
|
given, it is returned when the attribute doesn't exist; without it, an
|
|
1683
|
1683
|
exception is raised in that case.
|
|
1684
|
1684
|
|
|
1685
|
1685
|
Note that alist can be given as a string, which will be automatically
|
|
1686
|
1686
|
split into a list on whitespace. If given as a list, it must be a list of
|
|
1687
|
1687
|
*strings* (the variable names themselves), not of variables."""
|
|
1688
|
1688
|
|
|
1689
|
1689
|
if type(alist) in StringTypes:
|
|
1690
|
1690
|
alist = alist.split()
|
|
1691
|
1691
|
if args:
|
|
1692
|
1692
|
if len(args)==1:
|
|
1693
|
1693
|
default = args[0]
|
|
1694
|
1694
|
return map(lambda attr: getattr(obj,attr,default),alist)
|
|
1695
|
1695
|
else:
|
|
1696
|
1696
|
raise ValueError,'getattr_list() takes only one optional argument'
|
|
1697
|
1697
|
else:
|
|
1698
|
1698
|
return map(lambda attr: getattr(obj,attr),alist)
|
|
1699
|
1699
|
|
|
1700
|
1700
|
#----------------------------------------------------------------------------
|
|
1701
|
1701
|
def map_method(method,object_list,*argseq,**kw):
|
|
1702
|
1702
|
"""map_method(method,object_list,*args,**kw) -> list
|
|
1703
|
1703
|
|
|
1704
|
1704
|
Return a list of the results of applying the methods to the items of the
|
|
1705
|
1705
|
argument sequence(s). If more than one sequence is given, the method is
|
|
1706
|
1706
|
called with an argument list consisting of the corresponding item of each
|
|
1707
|
1707
|
sequence. All sequences must be of the same length.
|
|
1708
|
1708
|
|
|
1709
|
1709
|
Keyword arguments are passed verbatim to all objects called.
|
|
1710
|
1710
|
|
|
1711
|
1711
|
This is Python code, so it's not nearly as fast as the builtin map()."""
|
|
1712
|
1712
|
|
|
1713
|
1713
|
out_list = []
|
|
1714
|
1714
|
idx = 0
|
|
1715
|
1715
|
for object in object_list:
|
|
1716
|
1716
|
try:
|
|
1717
|
1717
|
handler = getattr(object, method)
|
|
1718
|
1718
|
except AttributeError:
|
|
1719
|
1719
|
out_list.append(None)
|
|
1720
|
1720
|
else:
|
|
1721
|
1721
|
if argseq:
|
|
1722
|
1722
|
args = map(lambda lst:lst[idx],argseq)
|
|
1723
|
1723
|
#print 'ob',object,'hand',handler,'ar',args # dbg
|
|
1724
|
1724
|
out_list.append(handler(args,**kw))
|
|
1725
|
1725
|
else:
|
|
1726
|
1726
|
out_list.append(handler(**kw))
|
|
1727
|
1727
|
idx += 1
|
|
1728
|
1728
|
return out_list
|
|
1729
|
1729
|
|
|
1730
|
1730
|
#----------------------------------------------------------------------------
|
|
1731
|
1731
|
def import_fail_info(mod_name,fns=None):
|
|
1732
|
1732
|
"""Inform load failure for a module."""
|
|
1733
|
1733
|
|
|
1734
|
1734
|
if fns == None:
|
|
1735
|
1735
|
warn("Loading of %s failed.\n" % (mod_name,))
|
|
1736
|
1736
|
else:
|
|
1737
|
1737
|
warn("Loading of %s from %s failed.\n" % (fns,mod_name))
|
|
1738
|
1738
|
|
|
1739
|
1739
|
#----------------------------------------------------------------------------
|
|
1740
|
1740
|
# Proposed popitem() extension, written as a method
|
|
1741
|
1741
|
|
|
1742
|
1742
|
class NotGiven: pass
|
|
1743
|
1743
|
|
|
1744
|
1744
|
def popkey(dct,key,default=NotGiven):
|
|
1745
|
1745
|
"""Return dct[key] and delete dct[key].
|
|
1746
|
1746
|
|
|
1747
|
1747
|
If default is given, return it if dct[key] doesn't exist, otherwise raise
|
|
1748
|
1748
|
KeyError. """
|
|
1749
|
1749
|
|
|
1750
|
1750
|
try:
|
|
1751
|
1751
|
val = dct[key]
|
|
1752
|
1752
|
except KeyError:
|
|
1753
|
1753
|
if default is NotGiven:
|
|
1754
|
1754
|
raise
|
|
1755
|
1755
|
else:
|
|
1756
|
1756
|
return default
|
|
1757
|
1757
|
else:
|
|
1758
|
1758
|
del dct[key]
|
|
1759
|
1759
|
return val
|
|
1760
|
1760
|
#*************************** end of file <genutils.py> **********************
|
|
1761
|
1761
|
|