##// END OF EJS Templates
Fixing subtle bug in expanduser(expandvars(path)) on Windows....
Brian Granger -
Show More
@@ -1,463 +1,464 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython cluster directory
4 The IPython cluster directory
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 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 __future__ import with_statement
18 from __future__ import with_statement
19
19
20 import os
20 import os
21 import shutil
21 import shutil
22 import sys
22 import sys
23
23
24 from twisted.python import log
24 from twisted.python import log
25
25
26 from IPython.core import release
26 from IPython.core import release
27 from IPython.config.loader import PyFileConfigLoader
27 from IPython.config.loader import PyFileConfigLoader
28 from IPython.core.application import Application
28 from IPython.core.application import Application
29 from IPython.core.component import Component
29 from IPython.core.component import Component
30 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
30 from IPython.config.loader import ArgParseConfigLoader, NoConfigDefault
31 from IPython.utils.traitlets import Unicode, Bool
31 from IPython.utils.traitlets import Unicode, Bool
32 from IPython.utils import genutils
32
33
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Imports
35 # Imports
35 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
36
37
37
38
38 class ClusterDirError(Exception):
39 class ClusterDirError(Exception):
39 pass
40 pass
40
41
41
42
42 class PIDFileError(Exception):
43 class PIDFileError(Exception):
43 pass
44 pass
44
45
45
46
46 class ClusterDir(Component):
47 class ClusterDir(Component):
47 """An object to manage the cluster directory and its resources.
48 """An object to manage the cluster directory and its resources.
48
49
49 The cluster directory is used by :command:`ipcontroller`,
50 The cluster directory is used by :command:`ipcontroller`,
50 :command:`ipcontroller` and :command:`ipcontroller` to manage the
51 :command:`ipcontroller` and :command:`ipcontroller` to manage the
51 configuration, logging and security of these applications.
52 configuration, logging and security of these applications.
52
53
53 This object knows how to find, create and manage these directories. This
54 This object knows how to find, create and manage these directories. This
54 should be used by any code that want's to handle cluster directories.
55 should be used by any code that want's to handle cluster directories.
55 """
56 """
56
57
57 security_dir_name = Unicode('security')
58 security_dir_name = Unicode('security')
58 log_dir_name = Unicode('log')
59 log_dir_name = Unicode('log')
59 pid_dir_name = Unicode('pid')
60 pid_dir_name = Unicode('pid')
60 security_dir = Unicode(u'')
61 security_dir = Unicode(u'')
61 log_dir = Unicode(u'')
62 log_dir = Unicode(u'')
62 pid_dir = Unicode(u'')
63 pid_dir = Unicode(u'')
63 location = Unicode(u'')
64 location = Unicode(u'')
64
65
65 def __init__(self, location):
66 def __init__(self, location):
66 super(ClusterDir, self).__init__(None)
67 super(ClusterDir, self).__init__(None)
67 self.location = location
68 self.location = location
68
69
69 def _location_changed(self, name, old, new):
70 def _location_changed(self, name, old, new):
70 if not os.path.isdir(new):
71 if not os.path.isdir(new):
71 os.makedirs(new, mode=0777)
72 os.makedirs(new, mode=0777)
72 else:
73 else:
73 os.chmod(new, 0777)
74 os.chmod(new, 0777)
74 self.security_dir = os.path.join(new, self.security_dir_name)
75 self.security_dir = os.path.join(new, self.security_dir_name)
75 self.log_dir = os.path.join(new, self.log_dir_name)
76 self.log_dir = os.path.join(new, self.log_dir_name)
76 self.pid_dir = os.path.join(new, self.pid_dir_name)
77 self.pid_dir = os.path.join(new, self.pid_dir_name)
77 self.check_dirs()
78 self.check_dirs()
78
79
79 def _log_dir_changed(self, name, old, new):
80 def _log_dir_changed(self, name, old, new):
80 self.check_log_dir()
81 self.check_log_dir()
81
82
82 def check_log_dir(self):
83 def check_log_dir(self):
83 if not os.path.isdir(self.log_dir):
84 if not os.path.isdir(self.log_dir):
84 os.mkdir(self.log_dir, 0777)
85 os.mkdir(self.log_dir, 0777)
85 else:
86 else:
86 os.chmod(self.log_dir, 0777)
87 os.chmod(self.log_dir, 0777)
87
88
88 def _security_dir_changed(self, name, old, new):
89 def _security_dir_changed(self, name, old, new):
89 self.check_security_dir()
90 self.check_security_dir()
90
91
91 def check_security_dir(self):
92 def check_security_dir(self):
92 if not os.path.isdir(self.security_dir):
93 if not os.path.isdir(self.security_dir):
93 os.mkdir(self.security_dir, 0700)
94 os.mkdir(self.security_dir, 0700)
94 else:
95 else:
95 os.chmod(self.security_dir, 0700)
96 os.chmod(self.security_dir, 0700)
96
97
97 def _pid_dir_changed(self, name, old, new):
98 def _pid_dir_changed(self, name, old, new):
98 self.check_pid_dir()
99 self.check_pid_dir()
99
100
100 def check_pid_dir(self):
101 def check_pid_dir(self):
101 if not os.path.isdir(self.pid_dir):
102 if not os.path.isdir(self.pid_dir):
102 os.mkdir(self.pid_dir, 0700)
103 os.mkdir(self.pid_dir, 0700)
103 else:
104 else:
104 os.chmod(self.pid_dir, 0700)
105 os.chmod(self.pid_dir, 0700)
105
106
106 def check_dirs(self):
107 def check_dirs(self):
107 self.check_security_dir()
108 self.check_security_dir()
108 self.check_log_dir()
109 self.check_log_dir()
109 self.check_pid_dir()
110 self.check_pid_dir()
110
111
111 def load_config_file(self, filename):
112 def load_config_file(self, filename):
112 """Load a config file from the top level of the cluster dir.
113 """Load a config file from the top level of the cluster dir.
113
114
114 Parameters
115 Parameters
115 ----------
116 ----------
116 filename : unicode or str
117 filename : unicode or str
117 The filename only of the config file that must be located in
118 The filename only of the config file that must be located in
118 the top-level of the cluster directory.
119 the top-level of the cluster directory.
119 """
120 """
120 loader = PyFileConfigLoader(filename, self.location)
121 loader = PyFileConfigLoader(filename, self.location)
121 return loader.load_config()
122 return loader.load_config()
122
123
123 def copy_config_file(self, config_file, path=None, overwrite=False):
124 def copy_config_file(self, config_file, path=None, overwrite=False):
124 """Copy a default config file into the active cluster directory.
125 """Copy a default config file into the active cluster directory.
125
126
126 Default configuration files are kept in :mod:`IPython.config.default`.
127 Default configuration files are kept in :mod:`IPython.config.default`.
127 This function moves these from that location to the working cluster
128 This function moves these from that location to the working cluster
128 directory.
129 directory.
129 """
130 """
130 if path is None:
131 if path is None:
131 import IPython.config.default
132 import IPython.config.default
132 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
133 path = IPython.config.default.__file__.split(os.path.sep)[:-1]
133 path = os.path.sep.join(path)
134 path = os.path.sep.join(path)
134 src = os.path.join(path, config_file)
135 src = os.path.join(path, config_file)
135 dst = os.path.join(self.location, config_file)
136 dst = os.path.join(self.location, config_file)
136 if not os.path.isfile(dst) or overwrite:
137 if not os.path.isfile(dst) or overwrite:
137 shutil.copy(src, dst)
138 shutil.copy(src, dst)
138
139
139 def copy_all_config_files(self, path=None, overwrite=False):
140 def copy_all_config_files(self, path=None, overwrite=False):
140 """Copy all config files into the active cluster directory."""
141 """Copy all config files into the active cluster directory."""
141 for f in ['ipcontroller_config.py', 'ipengine_config.py',
142 for f in ['ipcontroller_config.py', 'ipengine_config.py',
142 'ipcluster_config.py']:
143 'ipcluster_config.py']:
143 self.copy_config_file(f, path=path, overwrite=overwrite)
144 self.copy_config_file(f, path=path, overwrite=overwrite)
144
145
145 @classmethod
146 @classmethod
146 def create_cluster_dir(csl, cluster_dir):
147 def create_cluster_dir(csl, cluster_dir):
147 """Create a new cluster directory given a full path.
148 """Create a new cluster directory given a full path.
148
149
149 Parameters
150 Parameters
150 ----------
151 ----------
151 cluster_dir : str
152 cluster_dir : str
152 The full path to the cluster directory. If it does exist, it will
153 The full path to the cluster directory. If it does exist, it will
153 be used. If not, it will be created.
154 be used. If not, it will be created.
154 """
155 """
155 return ClusterDir(cluster_dir)
156 return ClusterDir(cluster_dir)
156
157
157 @classmethod
158 @classmethod
158 def create_cluster_dir_by_profile(cls, path, profile='default'):
159 def create_cluster_dir_by_profile(cls, path, profile='default'):
159 """Create a cluster dir by profile name and path.
160 """Create a cluster dir by profile name and path.
160
161
161 Parameters
162 Parameters
162 ----------
163 ----------
163 path : str
164 path : str
164 The path (directory) to put the cluster directory in.
165 The path (directory) to put the cluster directory in.
165 profile : str
166 profile : str
166 The name of the profile. The name of the cluster directory will
167 The name of the profile. The name of the cluster directory will
167 be "cluster_<profile>".
168 be "cluster_<profile>".
168 """
169 """
169 if not os.path.isdir(path):
170 if not os.path.isdir(path):
170 raise ClusterDirError('Directory not found: %s' % path)
171 raise ClusterDirError('Directory not found: %s' % path)
171 cluster_dir = os.path.join(path, 'cluster_' + profile)
172 cluster_dir = os.path.join(path, 'cluster_' + profile)
172 return ClusterDir(cluster_dir)
173 return ClusterDir(cluster_dir)
173
174
174 @classmethod
175 @classmethod
175 def find_cluster_dir_by_profile(cls, ipython_dir, profile='default'):
176 def find_cluster_dir_by_profile(cls, ipython_dir, profile='default'):
176 """Find an existing cluster dir by profile name, return its ClusterDir.
177 """Find an existing cluster dir by profile name, return its ClusterDir.
177
178
178 This searches through a sequence of paths for a cluster dir. If it
179 This searches through a sequence of paths for a cluster dir. If it
179 is not found, a :class:`ClusterDirError` exception will be raised.
180 is not found, a :class:`ClusterDirError` exception will be raised.
180
181
181 The search path algorithm is:
182 The search path algorithm is:
182 1. ``os.getcwd()``
183 1. ``os.getcwd()``
183 2. ``ipython_dir``
184 2. ``ipython_dir``
184 3. The directories found in the ":" separated
185 3. The directories found in the ":" separated
185 :env:`IPCLUSTER_DIR_PATH` environment variable.
186 :env:`IPCLUSTER_DIR_PATH` environment variable.
186
187
187 Parameters
188 Parameters
188 ----------
189 ----------
189 ipython_dir : unicode or str
190 ipython_dir : unicode or str
190 The IPython directory to use.
191 The IPython directory to use.
191 profile : unicode or str
192 profile : unicode or str
192 The name of the profile. The name of the cluster directory
193 The name of the profile. The name of the cluster directory
193 will be "cluster_<profile>".
194 will be "cluster_<profile>".
194 """
195 """
195 dirname = 'cluster_' + profile
196 dirname = 'cluster_' + profile
196 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
197 cluster_dir_paths = os.environ.get('IPCLUSTER_DIR_PATH','')
197 if cluster_dir_paths:
198 if cluster_dir_paths:
198 cluster_dir_paths = cluster_dir_paths.split(':')
199 cluster_dir_paths = cluster_dir_paths.split(':')
199 else:
200 else:
200 cluster_dir_paths = []
201 cluster_dir_paths = []
201 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
202 paths = [os.getcwd(), ipython_dir] + cluster_dir_paths
202 for p in paths:
203 for p in paths:
203 cluster_dir = os.path.join(p, dirname)
204 cluster_dir = os.path.join(p, dirname)
204 if os.path.isdir(cluster_dir):
205 if os.path.isdir(cluster_dir):
205 return ClusterDir(cluster_dir)
206 return ClusterDir(cluster_dir)
206 else:
207 else:
207 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
208 raise ClusterDirError('Cluster directory not found in paths: %s' % dirname)
208
209
209 @classmethod
210 @classmethod
210 def find_cluster_dir(cls, cluster_dir):
211 def find_cluster_dir(cls, cluster_dir):
211 """Find/create a cluster dir and return its ClusterDir.
212 """Find/create a cluster dir and return its ClusterDir.
212
213
213 This will create the cluster directory if it doesn't exist.
214 This will create the cluster directory if it doesn't exist.
214
215
215 Parameters
216 Parameters
216 ----------
217 ----------
217 cluster_dir : unicode or str
218 cluster_dir : unicode or str
218 The path of the cluster directory. This is expanded using
219 The path of the cluster directory. This is expanded using
219 :func:`os.path.expandvars` and :func:`os.path.expanduser`.
220 :func:`IPython.utils.genutils.expand_path`.
220 """
221 """
221 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
222 cluster_dir = genutils.expand_path(cluster_dir)
222 if not os.path.isdir(cluster_dir):
223 if not os.path.isdir(cluster_dir):
223 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
224 raise ClusterDirError('Cluster directory not found: %s' % cluster_dir)
224 return ClusterDir(cluster_dir)
225 return ClusterDir(cluster_dir)
225
226
226
227
227 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
228 class AppWithClusterDirArgParseConfigLoader(ArgParseConfigLoader):
228 """Default command line options for IPython cluster applications."""
229 """Default command line options for IPython cluster applications."""
229
230
230 def _add_other_arguments(self):
231 def _add_other_arguments(self):
231 self.parser.add_argument('--ipython-dir',
232 self.parser.add_argument('--ipython-dir',
232 dest='Global.ipython_dir',type=str,
233 dest='Global.ipython_dir',type=str,
233 help='Set to override default location of Global.ipython_dir.',
234 help='Set to override default location of Global.ipython_dir.',
234 default=NoConfigDefault,
235 default=NoConfigDefault,
235 metavar='Global.ipython_dir'
236 metavar='Global.ipython_dir'
236 )
237 )
237 self.parser.add_argument('-p', '--profile',
238 self.parser.add_argument('-p', '--profile',
238 dest='Global.profile',type=str,
239 dest='Global.profile',type=str,
239 help='The string name of the profile to be used. This determines '
240 help='The string name of the profile to be used. This determines '
240 'the name of the cluster dir as: cluster_<profile>. The default profile '
241 'the name of the cluster dir as: cluster_<profile>. The default profile '
241 'is named "default". The cluster directory is resolve this way '
242 'is named "default". The cluster directory is resolve this way '
242 'if the --cluster-dir option is not used.',
243 'if the --cluster-dir option is not used.',
243 default=NoConfigDefault,
244 default=NoConfigDefault,
244 metavar='Global.profile'
245 metavar='Global.profile'
245 )
246 )
246 self.parser.add_argument('--log-level',
247 self.parser.add_argument('--log-level',
247 dest="Global.log_level",type=int,
248 dest="Global.log_level",type=int,
248 help='Set the log level (0,10,20,30,40,50). Default is 30.',
249 help='Set the log level (0,10,20,30,40,50). Default is 30.',
249 default=NoConfigDefault,
250 default=NoConfigDefault,
250 metavar="Global.log_level"
251 metavar="Global.log_level"
251 )
252 )
252 self.parser.add_argument('--cluster-dir',
253 self.parser.add_argument('--cluster-dir',
253 dest='Global.cluster_dir',type=str,
254 dest='Global.cluster_dir',type=str,
254 help='Set the cluster dir. This overrides the logic used by the '
255 help='Set the cluster dir. This overrides the logic used by the '
255 '--profile option.',
256 '--profile option.',
256 default=NoConfigDefault,
257 default=NoConfigDefault,
257 metavar='Global.cluster_dir'
258 metavar='Global.cluster_dir'
258 )
259 )
259 self.parser.add_argument('--clean-logs',
260 self.parser.add_argument('--clean-logs',
260 dest='Global.clean_logs', action='store_true',
261 dest='Global.clean_logs', action='store_true',
261 help='Delete old log flies before starting.',
262 help='Delete old log flies before starting.',
262 default=NoConfigDefault
263 default=NoConfigDefault
263 )
264 )
264 self.parser.add_argument('--no-clean-logs',
265 self.parser.add_argument('--no-clean-logs',
265 dest='Global.clean_logs', action='store_false',
266 dest='Global.clean_logs', action='store_false',
266 help="Don't Delete old log flies before starting.",
267 help="Don't Delete old log flies before starting.",
267 default=NoConfigDefault
268 default=NoConfigDefault
268 )
269 )
269
270
270 class ApplicationWithClusterDir(Application):
271 class ApplicationWithClusterDir(Application):
271 """An application that puts everything into a cluster directory.
272 """An application that puts everything into a cluster directory.
272
273
273 Instead of looking for things in the ipython_dir, this type of application
274 Instead of looking for things in the ipython_dir, this type of application
274 will use its own private directory called the "cluster directory"
275 will use its own private directory called the "cluster directory"
275 for things like config files, log files, etc.
276 for things like config files, log files, etc.
276
277
277 The cluster directory is resolved as follows:
278 The cluster directory is resolved as follows:
278
279
279 * If the ``--cluster-dir`` option is given, it is used.
280 * If the ``--cluster-dir`` option is given, it is used.
280 * If ``--cluster-dir`` is not given, the application directory is
281 * If ``--cluster-dir`` is not given, the application directory is
281 resolve using the profile name as ``cluster_<profile>``. The search
282 resolve using the profile name as ``cluster_<profile>``. The search
282 path for this directory is then i) cwd if it is found there
283 path for this directory is then i) cwd if it is found there
283 and ii) in ipython_dir otherwise.
284 and ii) in ipython_dir otherwise.
284
285
285 The config file for the application is to be put in the cluster
286 The config file for the application is to be put in the cluster
286 dir and named the value of the ``config_file_name`` class attribute.
287 dir and named the value of the ``config_file_name`` class attribute.
287 """
288 """
288
289
289 auto_create_cluster_dir = True
290 auto_create_cluster_dir = True
290
291
291 def create_default_config(self):
292 def create_default_config(self):
292 super(ApplicationWithClusterDir, self).create_default_config()
293 super(ApplicationWithClusterDir, self).create_default_config()
293 self.default_config.Global.profile = 'default'
294 self.default_config.Global.profile = 'default'
294 self.default_config.Global.cluster_dir = ''
295 self.default_config.Global.cluster_dir = ''
295 self.default_config.Global.log_to_file = False
296 self.default_config.Global.log_to_file = False
296 self.default_config.Global.clean_logs = False
297 self.default_config.Global.clean_logs = False
297
298
298 def create_command_line_config(self):
299 def create_command_line_config(self):
299 """Create and return a command line config loader."""
300 """Create and return a command line config loader."""
300 return AppWithClusterDirArgParseConfigLoader(
301 return AppWithClusterDirArgParseConfigLoader(
301 description=self.description,
302 description=self.description,
302 version=release.version
303 version=release.version
303 )
304 )
304
305
305 def find_resources(self):
306 def find_resources(self):
306 """This resolves the cluster directory.
307 """This resolves the cluster directory.
307
308
308 This tries to find the cluster directory and if successful, it will
309 This tries to find the cluster directory and if successful, it will
309 have done:
310 have done:
310 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
311 * Sets ``self.cluster_dir_obj`` to the :class:`ClusterDir` object for
311 the application.
312 the application.
312 * Sets ``self.cluster_dir`` attribute of the application and config
313 * Sets ``self.cluster_dir`` attribute of the application and config
313 objects.
314 objects.
314
315
315 The algorithm used for this is as follows:
316 The algorithm used for this is as follows:
316 1. Try ``Global.cluster_dir``.
317 1. Try ``Global.cluster_dir``.
317 2. Try using ``Global.profile``.
318 2. Try using ``Global.profile``.
318 3. If both of these fail and ``self.auto_create_cluster_dir`` is
319 3. If both of these fail and ``self.auto_create_cluster_dir`` is
319 ``True``, then create the new cluster dir in the IPython directory.
320 ``True``, then create the new cluster dir in the IPython directory.
320 4. If all fails, then raise :class:`ClusterDirError`.
321 4. If all fails, then raise :class:`ClusterDirError`.
321 """
322 """
322
323
323 try:
324 try:
324 cluster_dir = self.command_line_config.Global.cluster_dir
325 cluster_dir = self.command_line_config.Global.cluster_dir
325 except AttributeError:
326 except AttributeError:
326 cluster_dir = self.default_config.Global.cluster_dir
327 cluster_dir = self.default_config.Global.cluster_dir
327 cluster_dir = os.path.expandvars(os.path.expanduser(cluster_dir))
328 cluster_dir = genutils.expand_path(cluster_dir)
328 try:
329 try:
329 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
330 self.cluster_dir_obj = ClusterDir.find_cluster_dir(cluster_dir)
330 except ClusterDirError:
331 except ClusterDirError:
331 pass
332 pass
332 else:
333 else:
333 self.log.info('Using existing cluster dir: %s' % \
334 self.log.info('Using existing cluster dir: %s' % \
334 self.cluster_dir_obj.location
335 self.cluster_dir_obj.location
335 )
336 )
336 self.finish_cluster_dir()
337 self.finish_cluster_dir()
337 return
338 return
338
339
339 try:
340 try:
340 self.profile = self.command_line_config.Global.profile
341 self.profile = self.command_line_config.Global.profile
341 except AttributeError:
342 except AttributeError:
342 self.profile = self.default_config.Global.profile
343 self.profile = self.default_config.Global.profile
343 try:
344 try:
344 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
345 self.cluster_dir_obj = ClusterDir.find_cluster_dir_by_profile(
345 self.ipython_dir, self.profile)
346 self.ipython_dir, self.profile)
346 except ClusterDirError:
347 except ClusterDirError:
347 pass
348 pass
348 else:
349 else:
349 self.log.info('Using existing cluster dir: %s' % \
350 self.log.info('Using existing cluster dir: %s' % \
350 self.cluster_dir_obj.location
351 self.cluster_dir_obj.location
351 )
352 )
352 self.finish_cluster_dir()
353 self.finish_cluster_dir()
353 return
354 return
354
355
355 if self.auto_create_cluster_dir:
356 if self.auto_create_cluster_dir:
356 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
357 self.cluster_dir_obj = ClusterDir.create_cluster_dir_by_profile(
357 self.ipython_dir, self.profile
358 self.ipython_dir, self.profile
358 )
359 )
359 self.log.info('Creating new cluster dir: %s' % \
360 self.log.info('Creating new cluster dir: %s' % \
360 self.cluster_dir_obj.location
361 self.cluster_dir_obj.location
361 )
362 )
362 self.finish_cluster_dir()
363 self.finish_cluster_dir()
363 else:
364 else:
364 raise ClusterDirError('Could not find a valid cluster directory.')
365 raise ClusterDirError('Could not find a valid cluster directory.')
365
366
366 def finish_cluster_dir(self):
367 def finish_cluster_dir(self):
367 # Set the cluster directory
368 # Set the cluster directory
368 self.cluster_dir = self.cluster_dir_obj.location
369 self.cluster_dir = self.cluster_dir_obj.location
369
370
370 # These have to be set because they could be different from the one
371 # These have to be set because they could be different from the one
371 # that we just computed. Because command line has the highest
372 # that we just computed. Because command line has the highest
372 # priority, this will always end up in the master_config.
373 # priority, this will always end up in the master_config.
373 self.default_config.Global.cluster_dir = self.cluster_dir
374 self.default_config.Global.cluster_dir = self.cluster_dir
374 self.command_line_config.Global.cluster_dir = self.cluster_dir
375 self.command_line_config.Global.cluster_dir = self.cluster_dir
375
376
376 # Set the search path to the cluster directory
377 # Set the search path to the cluster directory
377 self.config_file_paths = (self.cluster_dir,)
378 self.config_file_paths = (self.cluster_dir,)
378
379
379 def find_config_file_name(self):
380 def find_config_file_name(self):
380 """Find the config file name for this application."""
381 """Find the config file name for this application."""
381 # For this type of Application it should be set as a class attribute.
382 # For this type of Application it should be set as a class attribute.
382 if not hasattr(self, 'config_file_name'):
383 if not hasattr(self, 'config_file_name'):
383 self.log.critical("No config filename found")
384 self.log.critical("No config filename found")
384
385
385 def find_config_file_paths(self):
386 def find_config_file_paths(self):
386 # Set the search path to the cluster directory
387 # Set the search path to the cluster directory
387 self.config_file_paths = (self.cluster_dir,)
388 self.config_file_paths = (self.cluster_dir,)
388
389
389 def pre_construct(self):
390 def pre_construct(self):
390 # The log and security dirs were set earlier, but here we put them
391 # The log and security dirs were set earlier, but here we put them
391 # into the config and log them.
392 # into the config and log them.
392 config = self.master_config
393 config = self.master_config
393 sdir = self.cluster_dir_obj.security_dir
394 sdir = self.cluster_dir_obj.security_dir
394 self.security_dir = config.Global.security_dir = sdir
395 self.security_dir = config.Global.security_dir = sdir
395 ldir = self.cluster_dir_obj.log_dir
396 ldir = self.cluster_dir_obj.log_dir
396 self.log_dir = config.Global.log_dir = ldir
397 self.log_dir = config.Global.log_dir = ldir
397 pdir = self.cluster_dir_obj.pid_dir
398 pdir = self.cluster_dir_obj.pid_dir
398 self.pid_dir = config.Global.pid_dir = pdir
399 self.pid_dir = config.Global.pid_dir = pdir
399 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
400 self.log.info("Cluster directory set to: %s" % self.cluster_dir)
400
401
401 def start_logging(self):
402 def start_logging(self):
402 # Remove old log files
403 # Remove old log files
403 if self.master_config.Global.clean_logs:
404 if self.master_config.Global.clean_logs:
404 log_dir = self.master_config.Global.log_dir
405 log_dir = self.master_config.Global.log_dir
405 for f in os.listdir(log_dir):
406 for f in os.listdir(log_dir):
406 if f.startswith(self.name + '-') and f.endswith('.log'):
407 if f.startswith(self.name + '-') and f.endswith('.log'):
407 os.remove(os.path.join(log_dir, f))
408 os.remove(os.path.join(log_dir, f))
408 # Start logging to the new log file
409 # Start logging to the new log file
409 if self.master_config.Global.log_to_file:
410 if self.master_config.Global.log_to_file:
410 log_filename = self.name + '-' + str(os.getpid()) + '.log'
411 log_filename = self.name + '-' + str(os.getpid()) + '.log'
411 logfile = os.path.join(self.log_dir, log_filename)
412 logfile = os.path.join(self.log_dir, log_filename)
412 open_log_file = open(logfile, 'w')
413 open_log_file = open(logfile, 'w')
413 else:
414 else:
414 open_log_file = sys.stdout
415 open_log_file = sys.stdout
415 log.startLogging(open_log_file)
416 log.startLogging(open_log_file)
416
417
417 def write_pid_file(self, overwrite=False):
418 def write_pid_file(self, overwrite=False):
418 """Create a .pid file in the pid_dir with my pid.
419 """Create a .pid file in the pid_dir with my pid.
419
420
420 This must be called after pre_construct, which sets `self.pid_dir`.
421 This must be called after pre_construct, which sets `self.pid_dir`.
421 This raises :exc:`PIDFileError` if the pid file exists already.
422 This raises :exc:`PIDFileError` if the pid file exists already.
422 """
423 """
423 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
424 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
424 if os.path.isfile(pid_file):
425 if os.path.isfile(pid_file):
425 pid = self.get_pid_from_file()
426 pid = self.get_pid_from_file()
426 if not overwrite:
427 if not overwrite:
427 raise PIDFileError(
428 raise PIDFileError(
428 'The pid file [%s] already exists. \nThis could mean that this '
429 'The pid file [%s] already exists. \nThis could mean that this '
429 'server is already running with [pid=%s].' % (pid_file, pid)
430 'server is already running with [pid=%s].' % (pid_file, pid)
430 )
431 )
431 with open(pid_file, 'w') as f:
432 with open(pid_file, 'w') as f:
432 self.log.info("Creating pid file: %s" % pid_file)
433 self.log.info("Creating pid file: %s" % pid_file)
433 f.write(repr(os.getpid())+'\n')
434 f.write(repr(os.getpid())+'\n')
434
435
435 def remove_pid_file(self):
436 def remove_pid_file(self):
436 """Remove the pid file.
437 """Remove the pid file.
437
438
438 This should be called at shutdown by registering a callback with
439 This should be called at shutdown by registering a callback with
439 :func:`reactor.addSystemEventTrigger`.
440 :func:`reactor.addSystemEventTrigger`.
440 """
441 """
441 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
442 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
442 if os.path.isfile(pid_file):
443 if os.path.isfile(pid_file):
443 try:
444 try:
444 self.log.info("Removing pid file: %s" % pid_file)
445 self.log.info("Removing pid file: %s" % pid_file)
445 os.remove(pid_file)
446 os.remove(pid_file)
446 except:
447 except:
447 self.log.warn("Error removing the pid file: %s" % pid_file)
448 self.log.warn("Error removing the pid file: %s" % pid_file)
448 raise
449 raise
449
450
450 def get_pid_from_file(self):
451 def get_pid_from_file(self):
451 """Get the pid from the pid file.
452 """Get the pid from the pid file.
452
453
453 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
454 If the pid file doesn't exist a :exc:`PIDFileError` is raised.
454 """
455 """
455 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
456 pid_file = os.path.join(self.pid_dir, self.name + '.pid')
456 if os.path.isfile(pid_file):
457 if os.path.isfile(pid_file):
457 with open(pid_file, 'r') as f:
458 with open(pid_file, 'r') as f:
458 pid = int(f.read().strip())
459 pid = int(f.read().strip())
459 return pid
460 return pid
460 else:
461 else:
461 raise PIDFileError('pid file not found: %s' % pid_file)
462 raise PIDFileError('pid file not found: %s' % pid_file)
462
463
463
464
@@ -1,1799 +1,1806 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """General purpose utilities.
2 """General purpose utilities.
3
3
4 This is a grab-bag of stuff I find useful in most programs I write. Some of
4 This is a grab-bag of stuff I find useful in most programs I write. Some of
5 these things are also convenient when working at the command line.
5 these things are also convenient when working at the command line.
6 """
6 """
7
7
8 #*****************************************************************************
8 #*****************************************************************************
9 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
9 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #*****************************************************************************
13 #*****************************************************************************
14
14
15 #****************************************************************************
15 #****************************************************************************
16 # required modules from the Python standard library
16 # required modules from the Python standard library
17 import __main__
17 import __main__
18
18
19 import os
19 import os
20 import platform
20 import platform
21 import re
21 import re
22 import shlex
22 import shlex
23 import shutil
23 import shutil
24 import subprocess
24 import subprocess
25 import sys
25 import sys
26 import time
26 import time
27 import types
27 import types
28 import warnings
28 import warnings
29
29
30 # Curses and termios are Unix-only modules
30 # Curses and termios are Unix-only modules
31 try:
31 try:
32 import curses
32 import curses
33 # We need termios as well, so if its import happens to raise, we bail on
33 # We need termios as well, so if its import happens to raise, we bail on
34 # using curses altogether.
34 # using curses altogether.
35 import termios
35 import termios
36 except ImportError:
36 except ImportError:
37 USE_CURSES = False
37 USE_CURSES = False
38 else:
38 else:
39 # Curses on Solaris may not be complete, so we can't use it there
39 # Curses on Solaris may not be complete, so we can't use it there
40 USE_CURSES = hasattr(curses,'initscr')
40 USE_CURSES = hasattr(curses,'initscr')
41
41
42 # Other IPython utilities
42 # Other IPython utilities
43 import IPython
43 import IPython
44 from IPython.external.Itpl import itpl,printpl
44 from IPython.external.Itpl import itpl,printpl
45 from IPython.utils import platutils
45 from IPython.utils import platutils
46 from IPython.utils.generics import result_display
46 from IPython.utils.generics import result_display
47 from IPython.external.path import path
47 from IPython.external.path import path
48
48
49 try:
49 try:
50 set
50 set
51 except:
51 except:
52 from sets import Set as set
52 from sets import Set as set
53
53
54
54
55 #****************************************************************************
55 #****************************************************************************
56 # Exceptions
56 # Exceptions
57 class Error(Exception):
57 class Error(Exception):
58 """Base class for exceptions in this module."""
58 """Base class for exceptions in this module."""
59 pass
59 pass
60
60
61 #----------------------------------------------------------------------------
61 #----------------------------------------------------------------------------
62 class IOStream:
62 class IOStream:
63 def __init__(self,stream,fallback):
63 def __init__(self,stream,fallback):
64 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
64 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
65 stream = fallback
65 stream = fallback
66 self.stream = stream
66 self.stream = stream
67 self._swrite = stream.write
67 self._swrite = stream.write
68 self.flush = stream.flush
68 self.flush = stream.flush
69
69
70 def write(self,data):
70 def write(self,data):
71 try:
71 try:
72 self._swrite(data)
72 self._swrite(data)
73 except:
73 except:
74 try:
74 try:
75 # print handles some unicode issues which may trip a plain
75 # print handles some unicode issues which may trip a plain
76 # write() call. Attempt to emulate write() by using a
76 # write() call. Attempt to emulate write() by using a
77 # trailing comma
77 # trailing comma
78 print >> self.stream, data,
78 print >> self.stream, data,
79 except:
79 except:
80 # if we get here, something is seriously broken.
80 # if we get here, something is seriously broken.
81 print >> sys.stderr, \
81 print >> sys.stderr, \
82 'ERROR - failed to write data to stream:', self.stream
82 'ERROR - failed to write data to stream:', self.stream
83
83
84 def close(self):
84 def close(self):
85 pass
85 pass
86
86
87
87
88 class IOTerm:
88 class IOTerm:
89 """ Term holds the file or file-like objects for handling I/O operations.
89 """ Term holds the file or file-like objects for handling I/O operations.
90
90
91 These are normally just sys.stdin, sys.stdout and sys.stderr but for
91 These are normally just sys.stdin, sys.stdout and sys.stderr but for
92 Windows they can can replaced to allow editing the strings before they are
92 Windows they can can replaced to allow editing the strings before they are
93 displayed."""
93 displayed."""
94
94
95 # In the future, having IPython channel all its I/O operations through
95 # In the future, having IPython channel all its I/O operations through
96 # this class will make it easier to embed it into other environments which
96 # this class will make it easier to embed it into other environments which
97 # are not a normal terminal (such as a GUI-based shell)
97 # are not a normal terminal (such as a GUI-based shell)
98 def __init__(self,cin=None,cout=None,cerr=None):
98 def __init__(self,cin=None,cout=None,cerr=None):
99 self.cin = IOStream(cin,sys.stdin)
99 self.cin = IOStream(cin,sys.stdin)
100 self.cout = IOStream(cout,sys.stdout)
100 self.cout = IOStream(cout,sys.stdout)
101 self.cerr = IOStream(cerr,sys.stderr)
101 self.cerr = IOStream(cerr,sys.stderr)
102
102
103 # Global variable to be used for all I/O
103 # Global variable to be used for all I/O
104 Term = IOTerm()
104 Term = IOTerm()
105
105
106 import IPython.utils.rlineimpl as readline
106 import IPython.utils.rlineimpl as readline
107 # Remake Term to use the readline i/o facilities
107 # Remake Term to use the readline i/o facilities
108 if sys.platform == 'win32' and readline.have_readline:
108 if sys.platform == 'win32' and readline.have_readline:
109
109
110 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
110 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
111
111
112
112
113 #****************************************************************************
113 #****************************************************************************
114 # Generic warning/error printer, used by everything else
114 # Generic warning/error printer, used by everything else
115 def warn(msg,level=2,exit_val=1):
115 def warn(msg,level=2,exit_val=1):
116 """Standard warning printer. Gives formatting consistency.
116 """Standard warning printer. Gives formatting consistency.
117
117
118 Output is sent to Term.cerr (sys.stderr by default).
118 Output is sent to Term.cerr (sys.stderr by default).
119
119
120 Options:
120 Options:
121
121
122 -level(2): allows finer control:
122 -level(2): allows finer control:
123 0 -> Do nothing, dummy function.
123 0 -> Do nothing, dummy function.
124 1 -> Print message.
124 1 -> Print message.
125 2 -> Print 'WARNING:' + message. (Default level).
125 2 -> Print 'WARNING:' + message. (Default level).
126 3 -> Print 'ERROR:' + message.
126 3 -> Print 'ERROR:' + message.
127 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
127 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
128
128
129 -exit_val (1): exit value returned by sys.exit() for a level 4
129 -exit_val (1): exit value returned by sys.exit() for a level 4
130 warning. Ignored for all other levels."""
130 warning. Ignored for all other levels."""
131
131
132 if level>0:
132 if level>0:
133 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
133 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
134 print >> Term.cerr, '%s%s' % (header[level],msg)
134 print >> Term.cerr, '%s%s' % (header[level],msg)
135 if level == 4:
135 if level == 4:
136 print >> Term.cerr,'Exiting.\n'
136 print >> Term.cerr,'Exiting.\n'
137 sys.exit(exit_val)
137 sys.exit(exit_val)
138
138
139 def info(msg):
139 def info(msg):
140 """Equivalent to warn(msg,level=1)."""
140 """Equivalent to warn(msg,level=1)."""
141
141
142 warn(msg,level=1)
142 warn(msg,level=1)
143
143
144 def error(msg):
144 def error(msg):
145 """Equivalent to warn(msg,level=3)."""
145 """Equivalent to warn(msg,level=3)."""
146
146
147 warn(msg,level=3)
147 warn(msg,level=3)
148
148
149 def fatal(msg,exit_val=1):
149 def fatal(msg,exit_val=1):
150 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
150 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
151
151
152 warn(msg,exit_val=exit_val,level=4)
152 warn(msg,exit_val=exit_val,level=4)
153
153
154 #---------------------------------------------------------------------------
154 #---------------------------------------------------------------------------
155 # Debugging routines
155 # Debugging routines
156 #
156 #
157 def debugx(expr,pre_msg=''):
157 def debugx(expr,pre_msg=''):
158 """Print the value of an expression from the caller's frame.
158 """Print the value of an expression from the caller's frame.
159
159
160 Takes an expression, evaluates it in the caller's frame and prints both
160 Takes an expression, evaluates it in the caller's frame and prints both
161 the given expression and the resulting value (as well as a debug mark
161 the given expression and the resulting value (as well as a debug mark
162 indicating the name of the calling function. The input must be of a form
162 indicating the name of the calling function. The input must be of a form
163 suitable for eval().
163 suitable for eval().
164
164
165 An optional message can be passed, which will be prepended to the printed
165 An optional message can be passed, which will be prepended to the printed
166 expr->value pair."""
166 expr->value pair."""
167
167
168 cf = sys._getframe(1)
168 cf = sys._getframe(1)
169 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
169 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
170 eval(expr,cf.f_globals,cf.f_locals))
170 eval(expr,cf.f_globals,cf.f_locals))
171
171
172 # deactivate it by uncommenting the following line, which makes it a no-op
172 # deactivate it by uncommenting the following line, which makes it a no-op
173 #def debugx(expr,pre_msg=''): pass
173 #def debugx(expr,pre_msg=''): pass
174
174
175 #----------------------------------------------------------------------------
175 #----------------------------------------------------------------------------
176 StringTypes = types.StringTypes
176 StringTypes = types.StringTypes
177
177
178 # Basic timing functionality
178 # Basic timing functionality
179
179
180 # If possible (Unix), use the resource module instead of time.clock()
180 # If possible (Unix), use the resource module instead of time.clock()
181 try:
181 try:
182 import resource
182 import resource
183 def clocku():
183 def clocku():
184 """clocku() -> floating point number
184 """clocku() -> floating point number
185
185
186 Return the *USER* CPU time in seconds since the start of the process.
186 Return the *USER* CPU time in seconds since the start of the process.
187 This is done via a call to resource.getrusage, so it avoids the
187 This is done via a call to resource.getrusage, so it avoids the
188 wraparound problems in time.clock()."""
188 wraparound problems in time.clock()."""
189
189
190 return resource.getrusage(resource.RUSAGE_SELF)[0]
190 return resource.getrusage(resource.RUSAGE_SELF)[0]
191
191
192 def clocks():
192 def clocks():
193 """clocks() -> floating point number
193 """clocks() -> floating point number
194
194
195 Return the *SYSTEM* CPU time in seconds since the start of the process.
195 Return the *SYSTEM* CPU time in seconds since the start of the process.
196 This is done via a call to resource.getrusage, so it avoids the
196 This is done via a call to resource.getrusage, so it avoids the
197 wraparound problems in time.clock()."""
197 wraparound problems in time.clock()."""
198
198
199 return resource.getrusage(resource.RUSAGE_SELF)[1]
199 return resource.getrusage(resource.RUSAGE_SELF)[1]
200
200
201 def clock():
201 def clock():
202 """clock() -> floating point number
202 """clock() -> floating point number
203
203
204 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
204 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
205 the process. This is done via a call to resource.getrusage, so it
205 the process. This is done via a call to resource.getrusage, so it
206 avoids the wraparound problems in time.clock()."""
206 avoids the wraparound problems in time.clock()."""
207
207
208 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
208 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
209 return u+s
209 return u+s
210
210
211 def clock2():
211 def clock2():
212 """clock2() -> (t_user,t_system)
212 """clock2() -> (t_user,t_system)
213
213
214 Similar to clock(), but return a tuple of user/system times."""
214 Similar to clock(), but return a tuple of user/system times."""
215 return resource.getrusage(resource.RUSAGE_SELF)[:2]
215 return resource.getrusage(resource.RUSAGE_SELF)[:2]
216
216
217 except ImportError:
217 except ImportError:
218 # There is no distinction of user/system time under windows, so we just use
218 # There is no distinction of user/system time under windows, so we just use
219 # time.clock() for everything...
219 # time.clock() for everything...
220 clocku = clocks = clock = time.clock
220 clocku = clocks = clock = time.clock
221 def clock2():
221 def clock2():
222 """Under windows, system CPU time can't be measured.
222 """Under windows, system CPU time can't be measured.
223
223
224 This just returns clock() and zero."""
224 This just returns clock() and zero."""
225 return time.clock(),0.0
225 return time.clock(),0.0
226
226
227 def timings_out(reps,func,*args,**kw):
227 def timings_out(reps,func,*args,**kw):
228 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
228 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
229
229
230 Execute a function reps times, return a tuple with the elapsed total
230 Execute a function reps times, return a tuple with the elapsed total
231 CPU time in seconds, the time per call and the function's output.
231 CPU time in seconds, the time per call and the function's output.
232
232
233 Under Unix, the return value is the sum of user+system time consumed by
233 Under Unix, the return value is the sum of user+system time consumed by
234 the process, computed via the resource module. This prevents problems
234 the process, computed via the resource module. This prevents problems
235 related to the wraparound effect which the time.clock() function has.
235 related to the wraparound effect which the time.clock() function has.
236
236
237 Under Windows the return value is in wall clock seconds. See the
237 Under Windows the return value is in wall clock seconds. See the
238 documentation for the time module for more details."""
238 documentation for the time module for more details."""
239
239
240 reps = int(reps)
240 reps = int(reps)
241 assert reps >=1, 'reps must be >= 1'
241 assert reps >=1, 'reps must be >= 1'
242 if reps==1:
242 if reps==1:
243 start = clock()
243 start = clock()
244 out = func(*args,**kw)
244 out = func(*args,**kw)
245 tot_time = clock()-start
245 tot_time = clock()-start
246 else:
246 else:
247 rng = xrange(reps-1) # the last time is executed separately to store output
247 rng = xrange(reps-1) # the last time is executed separately to store output
248 start = clock()
248 start = clock()
249 for dummy in rng: func(*args,**kw)
249 for dummy in rng: func(*args,**kw)
250 out = func(*args,**kw) # one last time
250 out = func(*args,**kw) # one last time
251 tot_time = clock()-start
251 tot_time = clock()-start
252 av_time = tot_time / reps
252 av_time = tot_time / reps
253 return tot_time,av_time,out
253 return tot_time,av_time,out
254
254
255 def timings(reps,func,*args,**kw):
255 def timings(reps,func,*args,**kw):
256 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
256 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
257
257
258 Execute a function reps times, return a tuple with the elapsed total CPU
258 Execute a function reps times, return a tuple with the elapsed total CPU
259 time in seconds and the time per call. These are just the first two values
259 time in seconds and the time per call. These are just the first two values
260 in timings_out()."""
260 in timings_out()."""
261
261
262 return timings_out(reps,func,*args,**kw)[0:2]
262 return timings_out(reps,func,*args,**kw)[0:2]
263
263
264 def timing(func,*args,**kw):
264 def timing(func,*args,**kw):
265 """timing(func,*args,**kw) -> t_total
265 """timing(func,*args,**kw) -> t_total
266
266
267 Execute a function once, return the elapsed total CPU time in
267 Execute a function once, return the elapsed total CPU time in
268 seconds. This is just the first value in timings_out()."""
268 seconds. This is just the first value in timings_out()."""
269
269
270 return timings_out(1,func,*args,**kw)[0]
270 return timings_out(1,func,*args,**kw)[0]
271
271
272 #****************************************************************************
272 #****************************************************************************
273 # file and system
273 # file and system
274
274
275 def arg_split(s,posix=False):
275 def arg_split(s,posix=False):
276 """Split a command line's arguments in a shell-like manner.
276 """Split a command line's arguments in a shell-like manner.
277
277
278 This is a modified version of the standard library's shlex.split()
278 This is a modified version of the standard library's shlex.split()
279 function, but with a default of posix=False for splitting, so that quotes
279 function, but with a default of posix=False for splitting, so that quotes
280 in inputs are respected."""
280 in inputs are respected."""
281
281
282 # XXX - there may be unicode-related problems here!!! I'm not sure that
282 # XXX - there may be unicode-related problems here!!! I'm not sure that
283 # shlex is truly unicode-safe, so it might be necessary to do
283 # shlex is truly unicode-safe, so it might be necessary to do
284 #
284 #
285 # s = s.encode(sys.stdin.encoding)
285 # s = s.encode(sys.stdin.encoding)
286 #
286 #
287 # first, to ensure that shlex gets a normal string. Input from anyone who
287 # first, to ensure that shlex gets a normal string. Input from anyone who
288 # knows more about unicode and shlex than I would be good to have here...
288 # knows more about unicode and shlex than I would be good to have here...
289 lex = shlex.shlex(s, posix=posix)
289 lex = shlex.shlex(s, posix=posix)
290 lex.whitespace_split = True
290 lex.whitespace_split = True
291 return list(lex)
291 return list(lex)
292
292
293 def system(cmd,verbose=0,debug=0,header=''):
293 def system(cmd,verbose=0,debug=0,header=''):
294 """Execute a system command, return its exit status.
294 """Execute a system command, return its exit status.
295
295
296 Options:
296 Options:
297
297
298 - verbose (0): print the command to be executed.
298 - verbose (0): print the command to be executed.
299
299
300 - debug (0): only print, do not actually execute.
300 - debug (0): only print, do not actually execute.
301
301
302 - header (''): Header to print on screen prior to the executed command (it
302 - header (''): Header to print on screen prior to the executed command (it
303 is only prepended to the command, no newlines are added).
303 is only prepended to the command, no newlines are added).
304
304
305 Note: a stateful version of this function is available through the
305 Note: a stateful version of this function is available through the
306 SystemExec class."""
306 SystemExec class."""
307
307
308 stat = 0
308 stat = 0
309 if verbose or debug: print header+cmd
309 if verbose or debug: print header+cmd
310 sys.stdout.flush()
310 sys.stdout.flush()
311 if not debug: stat = os.system(cmd)
311 if not debug: stat = os.system(cmd)
312 return stat
312 return stat
313
313
314 def abbrev_cwd():
314 def abbrev_cwd():
315 """ Return abbreviated version of cwd, e.g. d:mydir """
315 """ Return abbreviated version of cwd, e.g. d:mydir """
316 cwd = os.getcwd().replace('\\','/')
316 cwd = os.getcwd().replace('\\','/')
317 drivepart = ''
317 drivepart = ''
318 tail = cwd
318 tail = cwd
319 if sys.platform == 'win32':
319 if sys.platform == 'win32':
320 if len(cwd) < 4:
320 if len(cwd) < 4:
321 return cwd
321 return cwd
322 drivepart,tail = os.path.splitdrive(cwd)
322 drivepart,tail = os.path.splitdrive(cwd)
323
323
324
324
325 parts = tail.split('/')
325 parts = tail.split('/')
326 if len(parts) > 2:
326 if len(parts) > 2:
327 tail = '/'.join(parts[-2:])
327 tail = '/'.join(parts[-2:])
328
328
329 return (drivepart + (
329 return (drivepart + (
330 cwd == '/' and '/' or tail))
330 cwd == '/' and '/' or tail))
331
331
332
332
333 # This function is used by ipython in a lot of places to make system calls.
333 # This function is used by ipython in a lot of places to make system calls.
334 # We need it to be slightly different under win32, due to the vagaries of
334 # We need it to be slightly different under win32, due to the vagaries of
335 # 'network shares'. A win32 override is below.
335 # 'network shares'. A win32 override is below.
336
336
337 def shell(cmd,verbose=0,debug=0,header=''):
337 def shell(cmd,verbose=0,debug=0,header=''):
338 """Execute a command in the system shell, always return None.
338 """Execute a command in the system shell, always return None.
339
339
340 Options:
340 Options:
341
341
342 - verbose (0): print the command to be executed.
342 - verbose (0): print the command to be executed.
343
343
344 - debug (0): only print, do not actually execute.
344 - debug (0): only print, do not actually execute.
345
345
346 - header (''): Header to print on screen prior to the executed command (it
346 - header (''): Header to print on screen prior to the executed command (it
347 is only prepended to the command, no newlines are added).
347 is only prepended to the command, no newlines are added).
348
348
349 Note: this is similar to genutils.system(), but it returns None so it can
349 Note: this is similar to genutils.system(), but it returns None so it can
350 be conveniently used in interactive loops without getting the return value
350 be conveniently used in interactive loops without getting the return value
351 (typically 0) printed many times."""
351 (typically 0) printed many times."""
352
352
353 stat = 0
353 stat = 0
354 if verbose or debug: print header+cmd
354 if verbose or debug: print header+cmd
355 # flush stdout so we don't mangle python's buffering
355 # flush stdout so we don't mangle python's buffering
356 sys.stdout.flush()
356 sys.stdout.flush()
357
357
358 if not debug:
358 if not debug:
359 platutils.set_term_title("IPy " + cmd)
359 platutils.set_term_title("IPy " + cmd)
360 os.system(cmd)
360 os.system(cmd)
361 platutils.set_term_title("IPy " + abbrev_cwd())
361 platutils.set_term_title("IPy " + abbrev_cwd())
362
362
363 # override shell() for win32 to deal with network shares
363 # override shell() for win32 to deal with network shares
364 if os.name in ('nt','dos'):
364 if os.name in ('nt','dos'):
365
365
366 shell_ori = shell
366 shell_ori = shell
367
367
368 def shell(cmd,verbose=0,debug=0,header=''):
368 def shell(cmd,verbose=0,debug=0,header=''):
369 if os.getcwd().startswith(r"\\"):
369 if os.getcwd().startswith(r"\\"):
370 path = os.getcwd()
370 path = os.getcwd()
371 # change to c drive (cannot be on UNC-share when issuing os.system,
371 # change to c drive (cannot be on UNC-share when issuing os.system,
372 # as cmd.exe cannot handle UNC addresses)
372 # as cmd.exe cannot handle UNC addresses)
373 os.chdir("c:")
373 os.chdir("c:")
374 # issue pushd to the UNC-share and then run the command
374 # issue pushd to the UNC-share and then run the command
375 try:
375 try:
376 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
376 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
377 finally:
377 finally:
378 os.chdir(path)
378 os.chdir(path)
379 else:
379 else:
380 shell_ori(cmd,verbose,debug,header)
380 shell_ori(cmd,verbose,debug,header)
381
381
382 shell.__doc__ = shell_ori.__doc__
382 shell.__doc__ = shell_ori.__doc__
383
383
384 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
384 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
385 """Dummy substitute for perl's backquotes.
385 """Dummy substitute for perl's backquotes.
386
386
387 Executes a command and returns the output.
387 Executes a command and returns the output.
388
388
389 Accepts the same arguments as system(), plus:
389 Accepts the same arguments as system(), plus:
390
390
391 - split(0): if true, the output is returned as a list split on newlines.
391 - split(0): if true, the output is returned as a list split on newlines.
392
392
393 Note: a stateful version of this function is available through the
393 Note: a stateful version of this function is available through the
394 SystemExec class.
394 SystemExec class.
395
395
396 This is pretty much deprecated and rarely used,
396 This is pretty much deprecated and rarely used,
397 genutils.getoutputerror may be what you need.
397 genutils.getoutputerror may be what you need.
398
398
399 """
399 """
400
400
401 if verbose or debug: print header+cmd
401 if verbose or debug: print header+cmd
402 if not debug:
402 if not debug:
403 output = os.popen(cmd).read()
403 output = os.popen(cmd).read()
404 # stipping last \n is here for backwards compat.
404 # stipping last \n is here for backwards compat.
405 if output.endswith('\n'):
405 if output.endswith('\n'):
406 output = output[:-1]
406 output = output[:-1]
407 if split:
407 if split:
408 return output.split('\n')
408 return output.split('\n')
409 else:
409 else:
410 return output
410 return output
411
411
412 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
412 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
413 """Return (standard output,standard error) of executing cmd in a shell.
413 """Return (standard output,standard error) of executing cmd in a shell.
414
414
415 Accepts the same arguments as system(), plus:
415 Accepts the same arguments as system(), plus:
416
416
417 - split(0): if true, each of stdout/err is returned as a list split on
417 - split(0): if true, each of stdout/err is returned as a list split on
418 newlines.
418 newlines.
419
419
420 Note: a stateful version of this function is available through the
420 Note: a stateful version of this function is available through the
421 SystemExec class."""
421 SystemExec class."""
422
422
423 if verbose or debug: print header+cmd
423 if verbose or debug: print header+cmd
424 if not cmd:
424 if not cmd:
425 if split:
425 if split:
426 return [],[]
426 return [],[]
427 else:
427 else:
428 return '',''
428 return '',''
429 if not debug:
429 if not debug:
430 pin,pout,perr = os.popen3(cmd)
430 pin,pout,perr = os.popen3(cmd)
431 tout = pout.read().rstrip()
431 tout = pout.read().rstrip()
432 terr = perr.read().rstrip()
432 terr = perr.read().rstrip()
433 pin.close()
433 pin.close()
434 pout.close()
434 pout.close()
435 perr.close()
435 perr.close()
436 if split:
436 if split:
437 return tout.split('\n'),terr.split('\n')
437 return tout.split('\n'),terr.split('\n')
438 else:
438 else:
439 return tout,terr
439 return tout,terr
440
440
441 # for compatibility with older naming conventions
441 # for compatibility with older naming conventions
442 xsys = system
442 xsys = system
443 bq = getoutput
443 bq = getoutput
444
444
445 class SystemExec:
445 class SystemExec:
446 """Access the system and getoutput functions through a stateful interface.
446 """Access the system and getoutput functions through a stateful interface.
447
447
448 Note: here we refer to the system and getoutput functions from this
448 Note: here we refer to the system and getoutput functions from this
449 library, not the ones from the standard python library.
449 library, not the ones from the standard python library.
450
450
451 This class offers the system and getoutput functions as methods, but the
451 This class offers the system and getoutput functions as methods, but the
452 verbose, debug and header parameters can be set for the instance (at
452 verbose, debug and header parameters can be set for the instance (at
453 creation time or later) so that they don't need to be specified on each
453 creation time or later) so that they don't need to be specified on each
454 call.
454 call.
455
455
456 For efficiency reasons, there's no way to override the parameters on a
456 For efficiency reasons, there's no way to override the parameters on a
457 per-call basis other than by setting instance attributes. If you need
457 per-call basis other than by setting instance attributes. If you need
458 local overrides, it's best to directly call system() or getoutput().
458 local overrides, it's best to directly call system() or getoutput().
459
459
460 The following names are provided as alternate options:
460 The following names are provided as alternate options:
461 - xsys: alias to system
461 - xsys: alias to system
462 - bq: alias to getoutput
462 - bq: alias to getoutput
463
463
464 An instance can then be created as:
464 An instance can then be created as:
465 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
465 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
466 """
466 """
467
467
468 def __init__(self,verbose=0,debug=0,header='',split=0):
468 def __init__(self,verbose=0,debug=0,header='',split=0):
469 """Specify the instance's values for verbose, debug and header."""
469 """Specify the instance's values for verbose, debug and header."""
470 setattr_list(self,'verbose debug header split')
470 setattr_list(self,'verbose debug header split')
471
471
472 def system(self,cmd):
472 def system(self,cmd):
473 """Stateful interface to system(), with the same keyword parameters."""
473 """Stateful interface to system(), with the same keyword parameters."""
474
474
475 system(cmd,self.verbose,self.debug,self.header)
475 system(cmd,self.verbose,self.debug,self.header)
476
476
477 def shell(self,cmd):
477 def shell(self,cmd):
478 """Stateful interface to shell(), with the same keyword parameters."""
478 """Stateful interface to shell(), with the same keyword parameters."""
479
479
480 shell(cmd,self.verbose,self.debug,self.header)
480 shell(cmd,self.verbose,self.debug,self.header)
481
481
482 xsys = system # alias
482 xsys = system # alias
483
483
484 def getoutput(self,cmd):
484 def getoutput(self,cmd):
485 """Stateful interface to getoutput()."""
485 """Stateful interface to getoutput()."""
486
486
487 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
487 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
488
488
489 def getoutputerror(self,cmd):
489 def getoutputerror(self,cmd):
490 """Stateful interface to getoutputerror()."""
490 """Stateful interface to getoutputerror()."""
491
491
492 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
492 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
493
493
494 bq = getoutput # alias
494 bq = getoutput # alias
495
495
496 #-----------------------------------------------------------------------------
496 #-----------------------------------------------------------------------------
497 def mutex_opts(dict,ex_op):
497 def mutex_opts(dict,ex_op):
498 """Check for presence of mutually exclusive keys in a dict.
498 """Check for presence of mutually exclusive keys in a dict.
499
499
500 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
500 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
501 for op1,op2 in ex_op:
501 for op1,op2 in ex_op:
502 if op1 in dict and op2 in dict:
502 if op1 in dict and op2 in dict:
503 raise ValueError,'\n*** ERROR in Arguments *** '\
503 raise ValueError,'\n*** ERROR in Arguments *** '\
504 'Options '+op1+' and '+op2+' are mutually exclusive.'
504 'Options '+op1+' and '+op2+' are mutually exclusive.'
505
505
506 #-----------------------------------------------------------------------------
506 #-----------------------------------------------------------------------------
507 def get_py_filename(name):
507 def get_py_filename(name):
508 """Return a valid python filename in the current directory.
508 """Return a valid python filename in the current directory.
509
509
510 If the given name is not a file, it adds '.py' and searches again.
510 If the given name is not a file, it adds '.py' and searches again.
511 Raises IOError with an informative message if the file isn't found."""
511 Raises IOError with an informative message if the file isn't found."""
512
512
513 name = os.path.expanduser(name)
513 name = os.path.expanduser(name)
514 if not os.path.isfile(name) and not name.endswith('.py'):
514 if not os.path.isfile(name) and not name.endswith('.py'):
515 name += '.py'
515 name += '.py'
516 if os.path.isfile(name):
516 if os.path.isfile(name):
517 return name
517 return name
518 else:
518 else:
519 raise IOError,'File `%s` not found.' % name
519 raise IOError,'File `%s` not found.' % name
520
520
521 #-----------------------------------------------------------------------------
521 #-----------------------------------------------------------------------------
522
522
523
523
524 def filefind(filename, path_dirs=None):
524 def filefind(filename, path_dirs=None):
525 """Find a file by looking through a sequence of paths.
525 """Find a file by looking through a sequence of paths.
526
526
527 This iterates through a sequence of paths looking for a file and returns
527 This iterates through a sequence of paths looking for a file and returns
528 the full, absolute path of the first occurence of the file. If no set of
528 the full, absolute path of the first occurence of the file. If no set of
529 path dirs is given, the filename is tested as is, after running through
529 path dirs is given, the filename is tested as is, after running through
530 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
530 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
531
531
532 filefind('myfile.txt')
532 filefind('myfile.txt')
533
533
534 will find the file in the current working dir, but::
534 will find the file in the current working dir, but::
535
535
536 filefind('~/myfile.txt')
536 filefind('~/myfile.txt')
537
537
538 Will find the file in the users home directory. This function does not
538 Will find the file in the users home directory. This function does not
539 automatically try any paths, such as the cwd or the user's home directory.
539 automatically try any paths, such as the cwd or the user's home directory.
540
540
541 Parameters
541 Parameters
542 ----------
542 ----------
543 filename : str
543 filename : str
544 The filename to look for.
544 The filename to look for.
545 path_dirs : str, None or sequence of str
545 path_dirs : str, None or sequence of str
546 The sequence of paths to look for the file in. If None, the filename
546 The sequence of paths to look for the file in. If None, the filename
547 need to be absolute or be in the cwd. If a string, the string is
547 need to be absolute or be in the cwd. If a string, the string is
548 put into a sequence and the searched. If a sequence, walk through
548 put into a sequence and the searched. If a sequence, walk through
549 each element and join with ``filename``, calling :func:`expandvars`
549 each element and join with ``filename``, calling :func:`expandvars`
550 and :func:`expanduser` before testing for existence.
550 and :func:`expanduser` before testing for existence.
551
551
552 Returns
552 Returns
553 -------
553 -------
554 Raises :exc:`IOError` or returns absolute path to file.
554 Raises :exc:`IOError` or returns absolute path to file.
555 """
555 """
556 if path_dirs is None:
556 if path_dirs is None:
557 path_dirs = ("",)
557 path_dirs = ("",)
558 elif isinstance(path_dirs, basestring):
558 elif isinstance(path_dirs, basestring):
559 path_dirs = (path_dirs,)
559 path_dirs = (path_dirs,)
560 for path in path_dirs:
560 for path in path_dirs:
561 if path == '.': path = os.getcwd()
561 if path == '.': path = os.getcwd()
562 testname = os.path.expandvars(
562 testname = expand_path(os.path.join(path, filename))
563 os.path.expanduser(
564 os.path.join(path, filename)))
565 if os.path.isfile(testname):
563 if os.path.isfile(testname):
566 return os.path.abspath(testname)
564 return os.path.abspath(testname)
567 raise IOError("File does not exist in any "
565 raise IOError("File does not exist in any "
568 "of the search paths: %r, %r" % \
566 "of the search paths: %r, %r" % \
569 (filename, path_dirs))
567 (filename, path_dirs))
570
568
571
569
572 #----------------------------------------------------------------------------
570 #----------------------------------------------------------------------------
573 def file_read(filename):
571 def file_read(filename):
574 """Read a file and close it. Returns the file source."""
572 """Read a file and close it. Returns the file source."""
575 fobj = open(filename,'r');
573 fobj = open(filename,'r');
576 source = fobj.read();
574 source = fobj.read();
577 fobj.close()
575 fobj.close()
578 return source
576 return source
579
577
580 def file_readlines(filename):
578 def file_readlines(filename):
581 """Read a file and close it. Returns the file source using readlines()."""
579 """Read a file and close it. Returns the file source using readlines()."""
582 fobj = open(filename,'r');
580 fobj = open(filename,'r');
583 lines = fobj.readlines();
581 lines = fobj.readlines();
584 fobj.close()
582 fobj.close()
585 return lines
583 return lines
586
584
587 #----------------------------------------------------------------------------
585 #----------------------------------------------------------------------------
588 def target_outdated(target,deps):
586 def target_outdated(target,deps):
589 """Determine whether a target is out of date.
587 """Determine whether a target is out of date.
590
588
591 target_outdated(target,deps) -> 1/0
589 target_outdated(target,deps) -> 1/0
592
590
593 deps: list of filenames which MUST exist.
591 deps: list of filenames which MUST exist.
594 target: single filename which may or may not exist.
592 target: single filename which may or may not exist.
595
593
596 If target doesn't exist or is older than any file listed in deps, return
594 If target doesn't exist or is older than any file listed in deps, return
597 true, otherwise return false.
595 true, otherwise return false.
598 """
596 """
599 try:
597 try:
600 target_time = os.path.getmtime(target)
598 target_time = os.path.getmtime(target)
601 except os.error:
599 except os.error:
602 return 1
600 return 1
603 for dep in deps:
601 for dep in deps:
604 dep_time = os.path.getmtime(dep)
602 dep_time = os.path.getmtime(dep)
605 if dep_time > target_time:
603 if dep_time > target_time:
606 #print "For target",target,"Dep failed:",dep # dbg
604 #print "For target",target,"Dep failed:",dep # dbg
607 #print "times (dep,tar):",dep_time,target_time # dbg
605 #print "times (dep,tar):",dep_time,target_time # dbg
608 return 1
606 return 1
609 return 0
607 return 0
610
608
611 #-----------------------------------------------------------------------------
609 #-----------------------------------------------------------------------------
612 def target_update(target,deps,cmd):
610 def target_update(target,deps,cmd):
613 """Update a target with a given command given a list of dependencies.
611 """Update a target with a given command given a list of dependencies.
614
612
615 target_update(target,deps,cmd) -> runs cmd if target is outdated.
613 target_update(target,deps,cmd) -> runs cmd if target is outdated.
616
614
617 This is just a wrapper around target_outdated() which calls the given
615 This is just a wrapper around target_outdated() which calls the given
618 command if target is outdated."""
616 command if target is outdated."""
619
617
620 if target_outdated(target,deps):
618 if target_outdated(target,deps):
621 xsys(cmd)
619 xsys(cmd)
622
620
623 #----------------------------------------------------------------------------
621 #----------------------------------------------------------------------------
624 def unquote_ends(istr):
622 def unquote_ends(istr):
625 """Remove a single pair of quotes from the endpoints of a string."""
623 """Remove a single pair of quotes from the endpoints of a string."""
626
624
627 if not istr:
625 if not istr:
628 return istr
626 return istr
629 if (istr[0]=="'" and istr[-1]=="'") or \
627 if (istr[0]=="'" and istr[-1]=="'") or \
630 (istr[0]=='"' and istr[-1]=='"'):
628 (istr[0]=='"' and istr[-1]=='"'):
631 return istr[1:-1]
629 return istr[1:-1]
632 else:
630 else:
633 return istr
631 return istr
634
632
635 #----------------------------------------------------------------------------
633 #----------------------------------------------------------------------------
636 def flag_calls(func):
634 def flag_calls(func):
637 """Wrap a function to detect and flag when it gets called.
635 """Wrap a function to detect and flag when it gets called.
638
636
639 This is a decorator which takes a function and wraps it in a function with
637 This is a decorator which takes a function and wraps it in a function with
640 a 'called' attribute. wrapper.called is initialized to False.
638 a 'called' attribute. wrapper.called is initialized to False.
641
639
642 The wrapper.called attribute is set to False right before each call to the
640 The wrapper.called attribute is set to False right before each call to the
643 wrapped function, so if the call fails it remains False. After the call
641 wrapped function, so if the call fails it remains False. After the call
644 completes, wrapper.called is set to True and the output is returned.
642 completes, wrapper.called is set to True and the output is returned.
645
643
646 Testing for truth in wrapper.called allows you to determine if a call to
644 Testing for truth in wrapper.called allows you to determine if a call to
647 func() was attempted and succeeded."""
645 func() was attempted and succeeded."""
648
646
649 def wrapper(*args,**kw):
647 def wrapper(*args,**kw):
650 wrapper.called = False
648 wrapper.called = False
651 out = func(*args,**kw)
649 out = func(*args,**kw)
652 wrapper.called = True
650 wrapper.called = True
653 return out
651 return out
654
652
655 wrapper.called = False
653 wrapper.called = False
656 wrapper.__doc__ = func.__doc__
654 wrapper.__doc__ = func.__doc__
657 return wrapper
655 return wrapper
658
656
659 #----------------------------------------------------------------------------
657 #----------------------------------------------------------------------------
660 def dhook_wrap(func,*a,**k):
658 def dhook_wrap(func,*a,**k):
661 """Wrap a function call in a sys.displayhook controller.
659 """Wrap a function call in a sys.displayhook controller.
662
660
663 Returns a wrapper around func which calls func, with all its arguments and
661 Returns a wrapper around func which calls func, with all its arguments and
664 keywords unmodified, using the default sys.displayhook. Since IPython
662 keywords unmodified, using the default sys.displayhook. Since IPython
665 modifies sys.displayhook, it breaks the behavior of certain systems that
663 modifies sys.displayhook, it breaks the behavior of certain systems that
666 rely on the default behavior, notably doctest.
664 rely on the default behavior, notably doctest.
667 """
665 """
668
666
669 def f(*a,**k):
667 def f(*a,**k):
670
668
671 dhook_s = sys.displayhook
669 dhook_s = sys.displayhook
672 sys.displayhook = sys.__displayhook__
670 sys.displayhook = sys.__displayhook__
673 try:
671 try:
674 out = func(*a,**k)
672 out = func(*a,**k)
675 finally:
673 finally:
676 sys.displayhook = dhook_s
674 sys.displayhook = dhook_s
677
675
678 return out
676 return out
679
677
680 f.__doc__ = func.__doc__
678 f.__doc__ = func.__doc__
681 return f
679 return f
682
680
683 #----------------------------------------------------------------------------
681 #----------------------------------------------------------------------------
684 def doctest_reload():
682 def doctest_reload():
685 """Properly reload doctest to reuse it interactively.
683 """Properly reload doctest to reuse it interactively.
686
684
687 This routine:
685 This routine:
688
686
689 - imports doctest but does NOT reload it (see below).
687 - imports doctest but does NOT reload it (see below).
690
688
691 - resets its global 'master' attribute to None, so that multiple uses of
689 - resets its global 'master' attribute to None, so that multiple uses of
692 the module interactively don't produce cumulative reports.
690 the module interactively don't produce cumulative reports.
693
691
694 - Monkeypatches its core test runner method to protect it from IPython's
692 - Monkeypatches its core test runner method to protect it from IPython's
695 modified displayhook. Doctest expects the default displayhook behavior
693 modified displayhook. Doctest expects the default displayhook behavior
696 deep down, so our modification breaks it completely. For this reason, a
694 deep down, so our modification breaks it completely. For this reason, a
697 hard monkeypatch seems like a reasonable solution rather than asking
695 hard monkeypatch seems like a reasonable solution rather than asking
698 users to manually use a different doctest runner when under IPython.
696 users to manually use a different doctest runner when under IPython.
699
697
700 Notes
698 Notes
701 -----
699 -----
702
700
703 This function *used to* reload doctest, but this has been disabled because
701 This function *used to* reload doctest, but this has been disabled because
704 reloading doctest unconditionally can cause massive breakage of other
702 reloading doctest unconditionally can cause massive breakage of other
705 doctest-dependent modules already in memory, such as those for IPython's
703 doctest-dependent modules already in memory, such as those for IPython's
706 own testing system. The name wasn't changed to avoid breaking people's
704 own testing system. The name wasn't changed to avoid breaking people's
707 code, but the reload call isn't actually made anymore."""
705 code, but the reload call isn't actually made anymore."""
708
706
709 import doctest
707 import doctest
710 doctest.master = None
708 doctest.master = None
711 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
709 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
712
710
713 #----------------------------------------------------------------------------
711 #----------------------------------------------------------------------------
714 class HomeDirError(Error):
712 class HomeDirError(Error):
715 pass
713 pass
716
714
717 def get_home_dir():
715 def get_home_dir():
718 """Return the closest possible equivalent to a 'home' directory.
716 """Return the closest possible equivalent to a 'home' directory.
719
717
720 * On POSIX, we try $HOME.
718 * On POSIX, we try $HOME.
721 * On Windows we try:
719 * On Windows we try:
722 - %HOMESHARE%
720 - %HOMESHARE%
723 - %HOMEDRIVE\%HOMEPATH%
721 - %HOMEDRIVE\%HOMEPATH%
724 - %USERPROFILE%
722 - %USERPROFILE%
725 - Registry hack
723 - Registry hack
726 * On Dos C:\
724 * On Dos C:\
727
725
728 Currently only Posix and NT are implemented, a HomeDirError exception is
726 Currently only Posix and NT are implemented, a HomeDirError exception is
729 raised for all other OSes.
727 raised for all other OSes.
730 """
728 """
731
729
732 isdir = os.path.isdir
730 isdir = os.path.isdir
733 env = os.environ
731 env = os.environ
734
732
735 # first, check py2exe distribution root directory for _ipython.
733 # first, check py2exe distribution root directory for _ipython.
736 # This overrides all. Normally does not exist.
734 # This overrides all. Normally does not exist.
737
735
738 if hasattr(sys, "frozen"): #Is frozen by py2exe
736 if hasattr(sys, "frozen"): #Is frozen by py2exe
739 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
737 if '\\library.zip\\' in IPython.__file__.lower():#libraries compressed to zip-file
740 root, rest = IPython.__file__.lower().split('library.zip')
738 root, rest = IPython.__file__.lower().split('library.zip')
741 else:
739 else:
742 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
740 root=os.path.join(os.path.split(IPython.__file__)[0],"../../")
743 root=os.path.abspath(root).rstrip('\\')
741 root=os.path.abspath(root).rstrip('\\')
744 if isdir(os.path.join(root, '_ipython')):
742 if isdir(os.path.join(root, '_ipython')):
745 os.environ["IPYKITROOT"] = root
743 os.environ["IPYKITROOT"] = root
746 return root.decode(sys.getfilesystemencoding())
744 return root.decode(sys.getfilesystemencoding())
747
745
748 if os.name == 'posix':
746 if os.name == 'posix':
749 # Linux, Unix, AIX, OS X
747 # Linux, Unix, AIX, OS X
750 try:
748 try:
751 homedir = env['HOME']
749 homedir = env['HOME']
752 except KeyError:
750 except KeyError:
753 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
751 raise HomeDirError('Undefined $HOME, IPython cannot proceed.')
754 else:
752 else:
755 return homedir.decode(sys.getfilesystemencoding())
753 return homedir.decode(sys.getfilesystemencoding())
756 elif os.name == 'nt':
754 elif os.name == 'nt':
757 # Now for win9x, XP, Vista, 7?
755 # Now for win9x, XP, Vista, 7?
758 # For some strange reason all of these return 'nt' for os.name.
756 # For some strange reason all of these return 'nt' for os.name.
759 # First look for a network home directory. This will return the UNC
757 # First look for a network home directory. This will return the UNC
760 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
758 # path (\\server\\Users\%username%) not the mapped path (Z:\). This
761 # is needed when running IPython on cluster where all paths have to
759 # is needed when running IPython on cluster where all paths have to
762 # be UNC.
760 # be UNC.
763 try:
761 try:
764 homedir = env['HOMESHARE']
762 homedir = env['HOMESHARE']
765 except KeyError:
763 except KeyError:
766 pass
764 pass
767 else:
765 else:
768 if isdir(homedir):
766 if isdir(homedir):
769 return homedir.decode(sys.getfilesystemencoding())
767 return homedir.decode(sys.getfilesystemencoding())
770
768
771 # Now look for a local home directory
769 # Now look for a local home directory
772 try:
770 try:
773 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
771 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
774 except KeyError:
772 except KeyError:
775 pass
773 pass
776 else:
774 else:
777 if isdir(homedir):
775 if isdir(homedir):
778 return homedir.decode(sys.getfilesystemencoding())
776 return homedir.decode(sys.getfilesystemencoding())
779
777
780 # Now the users profile directory
778 # Now the users profile directory
781 try:
779 try:
782 homedir = os.path.join(env['USERPROFILE'])
780 homedir = os.path.join(env['USERPROFILE'])
783 except KeyError:
781 except KeyError:
784 pass
782 pass
785 else:
783 else:
786 if isdir(homedir):
784 if isdir(homedir):
787 return homedir.decode(sys.getfilesystemencoding())
785 return homedir.decode(sys.getfilesystemencoding())
788
786
789 # Use the registry to get the 'My Documents' folder.
787 # Use the registry to get the 'My Documents' folder.
790 try:
788 try:
791 import _winreg as wreg
789 import _winreg as wreg
792 key = wreg.OpenKey(
790 key = wreg.OpenKey(
793 wreg.HKEY_CURRENT_USER,
791 wreg.HKEY_CURRENT_USER,
794 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
792 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
795 )
793 )
796 homedir = wreg.QueryValueEx(key,'Personal')[0]
794 homedir = wreg.QueryValueEx(key,'Personal')[0]
797 key.Close()
795 key.Close()
798 except:
796 except:
799 pass
797 pass
800 else:
798 else:
801 if isdir(homedir):
799 if isdir(homedir):
802 return homedir.decode(sys.getfilesystemencoding())
800 return homedir.decode(sys.getfilesystemencoding())
803
801
804 # If all else fails, raise HomeDirError
802 # If all else fails, raise HomeDirError
805 raise HomeDirError('No valid home directory could be found')
803 raise HomeDirError('No valid home directory could be found')
806 elif os.name == 'dos':
804 elif os.name == 'dos':
807 # Desperate, may do absurd things in classic MacOS. May work under DOS.
805 # Desperate, may do absurd things in classic MacOS. May work under DOS.
808 return 'C:\\'.decode(sys.getfilesystemencoding())
806 return 'C:\\'.decode(sys.getfilesystemencoding())
809 else:
807 else:
810 raise HomeDirError('No valid home directory could be found for your OS')
808 raise HomeDirError('No valid home directory could be found for your OS')
811
809
812
810
813 def get_ipython_dir():
811 def get_ipython_dir():
814 """Get the IPython directory for this platform and user.
812 """Get the IPython directory for this platform and user.
815
813
816 This uses the logic in `get_home_dir` to find the home directory
814 This uses the logic in `get_home_dir` to find the home directory
817 and the adds .ipython to the end of the path.
815 and the adds .ipython to the end of the path.
818 """
816 """
819 ipdir_def = '.ipython'
817 ipdir_def = '.ipython'
820 home_dir = get_home_dir()
818 home_dir = get_home_dir()
821 ipdir = os.environ.get(
819 ipdir = os.environ.get(
822 'IPYTHON_DIR', os.environ.get(
820 'IPYTHON_DIR', os.environ.get(
823 'IPYTHONDIR', os.path.join(home_dir, ipdir_def)
821 'IPYTHONDIR', os.path.join(home_dir, ipdir_def)
824 )
822 )
825 )
823 )
826 return ipdir.decode(sys.getfilesystemencoding())
824 return ipdir.decode(sys.getfilesystemencoding())
827
825
828
826
829 #****************************************************************************
827 #****************************************************************************
830 # strings and text
828 # strings and text
831
829
832 class LSString(str):
830 class LSString(str):
833 """String derivative with a special access attributes.
831 """String derivative with a special access attributes.
834
832
835 These are normal strings, but with the special attributes:
833 These are normal strings, but with the special attributes:
836
834
837 .l (or .list) : value as list (split on newlines).
835 .l (or .list) : value as list (split on newlines).
838 .n (or .nlstr): original value (the string itself).
836 .n (or .nlstr): original value (the string itself).
839 .s (or .spstr): value as whitespace-separated string.
837 .s (or .spstr): value as whitespace-separated string.
840 .p (or .paths): list of path objects
838 .p (or .paths): list of path objects
841
839
842 Any values which require transformations are computed only once and
840 Any values which require transformations are computed only once and
843 cached.
841 cached.
844
842
845 Such strings are very useful to efficiently interact with the shell, which
843 Such strings are very useful to efficiently interact with the shell, which
846 typically only understands whitespace-separated options for commands."""
844 typically only understands whitespace-separated options for commands."""
847
845
848 def get_list(self):
846 def get_list(self):
849 try:
847 try:
850 return self.__list
848 return self.__list
851 except AttributeError:
849 except AttributeError:
852 self.__list = self.split('\n')
850 self.__list = self.split('\n')
853 return self.__list
851 return self.__list
854
852
855 l = list = property(get_list)
853 l = list = property(get_list)
856
854
857 def get_spstr(self):
855 def get_spstr(self):
858 try:
856 try:
859 return self.__spstr
857 return self.__spstr
860 except AttributeError:
858 except AttributeError:
861 self.__spstr = self.replace('\n',' ')
859 self.__spstr = self.replace('\n',' ')
862 return self.__spstr
860 return self.__spstr
863
861
864 s = spstr = property(get_spstr)
862 s = spstr = property(get_spstr)
865
863
866 def get_nlstr(self):
864 def get_nlstr(self):
867 return self
865 return self
868
866
869 n = nlstr = property(get_nlstr)
867 n = nlstr = property(get_nlstr)
870
868
871 def get_paths(self):
869 def get_paths(self):
872 try:
870 try:
873 return self.__paths
871 return self.__paths
874 except AttributeError:
872 except AttributeError:
875 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
873 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
876 return self.__paths
874 return self.__paths
877
875
878 p = paths = property(get_paths)
876 p = paths = property(get_paths)
879
877
880 def print_lsstring(arg):
878 def print_lsstring(arg):
881 """ Prettier (non-repr-like) and more informative printer for LSString """
879 """ Prettier (non-repr-like) and more informative printer for LSString """
882 print "LSString (.p, .n, .l, .s available). Value:"
880 print "LSString (.p, .n, .l, .s available). Value:"
883 print arg
881 print arg
884
882
885 print_lsstring = result_display.when_type(LSString)(print_lsstring)
883 print_lsstring = result_display.when_type(LSString)(print_lsstring)
886
884
887 #----------------------------------------------------------------------------
885 #----------------------------------------------------------------------------
888 class SList(list):
886 class SList(list):
889 """List derivative with a special access attributes.
887 """List derivative with a special access attributes.
890
888
891 These are normal lists, but with the special attributes:
889 These are normal lists, but with the special attributes:
892
890
893 .l (or .list) : value as list (the list itself).
891 .l (or .list) : value as list (the list itself).
894 .n (or .nlstr): value as a string, joined on newlines.
892 .n (or .nlstr): value as a string, joined on newlines.
895 .s (or .spstr): value as a string, joined on spaces.
893 .s (or .spstr): value as a string, joined on spaces.
896 .p (or .paths): list of path objects
894 .p (or .paths): list of path objects
897
895
898 Any values which require transformations are computed only once and
896 Any values which require transformations are computed only once and
899 cached."""
897 cached."""
900
898
901 def get_list(self):
899 def get_list(self):
902 return self
900 return self
903
901
904 l = list = property(get_list)
902 l = list = property(get_list)
905
903
906 def get_spstr(self):
904 def get_spstr(self):
907 try:
905 try:
908 return self.__spstr
906 return self.__spstr
909 except AttributeError:
907 except AttributeError:
910 self.__spstr = ' '.join(self)
908 self.__spstr = ' '.join(self)
911 return self.__spstr
909 return self.__spstr
912
910
913 s = spstr = property(get_spstr)
911 s = spstr = property(get_spstr)
914
912
915 def get_nlstr(self):
913 def get_nlstr(self):
916 try:
914 try:
917 return self.__nlstr
915 return self.__nlstr
918 except AttributeError:
916 except AttributeError:
919 self.__nlstr = '\n'.join(self)
917 self.__nlstr = '\n'.join(self)
920 return self.__nlstr
918 return self.__nlstr
921
919
922 n = nlstr = property(get_nlstr)
920 n = nlstr = property(get_nlstr)
923
921
924 def get_paths(self):
922 def get_paths(self):
925 try:
923 try:
926 return self.__paths
924 return self.__paths
927 except AttributeError:
925 except AttributeError:
928 self.__paths = [path(p) for p in self if os.path.exists(p)]
926 self.__paths = [path(p) for p in self if os.path.exists(p)]
929 return self.__paths
927 return self.__paths
930
928
931 p = paths = property(get_paths)
929 p = paths = property(get_paths)
932
930
933 def grep(self, pattern, prune = False, field = None):
931 def grep(self, pattern, prune = False, field = None):
934 """ Return all strings matching 'pattern' (a regex or callable)
932 """ Return all strings matching 'pattern' (a regex or callable)
935
933
936 This is case-insensitive. If prune is true, return all items
934 This is case-insensitive. If prune is true, return all items
937 NOT matching the pattern.
935 NOT matching the pattern.
938
936
939 If field is specified, the match must occur in the specified
937 If field is specified, the match must occur in the specified
940 whitespace-separated field.
938 whitespace-separated field.
941
939
942 Examples::
940 Examples::
943
941
944 a.grep( lambda x: x.startswith('C') )
942 a.grep( lambda x: x.startswith('C') )
945 a.grep('Cha.*log', prune=1)
943 a.grep('Cha.*log', prune=1)
946 a.grep('chm', field=-1)
944 a.grep('chm', field=-1)
947 """
945 """
948
946
949 def match_target(s):
947 def match_target(s):
950 if field is None:
948 if field is None:
951 return s
949 return s
952 parts = s.split()
950 parts = s.split()
953 try:
951 try:
954 tgt = parts[field]
952 tgt = parts[field]
955 return tgt
953 return tgt
956 except IndexError:
954 except IndexError:
957 return ""
955 return ""
958
956
959 if isinstance(pattern, basestring):
957 if isinstance(pattern, basestring):
960 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
958 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
961 else:
959 else:
962 pred = pattern
960 pred = pattern
963 if not prune:
961 if not prune:
964 return SList([el for el in self if pred(match_target(el))])
962 return SList([el for el in self if pred(match_target(el))])
965 else:
963 else:
966 return SList([el for el in self if not pred(match_target(el))])
964 return SList([el for el in self if not pred(match_target(el))])
967 def fields(self, *fields):
965 def fields(self, *fields):
968 """ Collect whitespace-separated fields from string list
966 """ Collect whitespace-separated fields from string list
969
967
970 Allows quick awk-like usage of string lists.
968 Allows quick awk-like usage of string lists.
971
969
972 Example data (in var a, created by 'a = !ls -l')::
970 Example data (in var a, created by 'a = !ls -l')::
973 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
971 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
974 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
972 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
975
973
976 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
974 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
977 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
975 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
978 (note the joining by space).
976 (note the joining by space).
979 a.fields(-1) is ['ChangeLog', 'IPython']
977 a.fields(-1) is ['ChangeLog', 'IPython']
980
978
981 IndexErrors are ignored.
979 IndexErrors are ignored.
982
980
983 Without args, fields() just split()'s the strings.
981 Without args, fields() just split()'s the strings.
984 """
982 """
985 if len(fields) == 0:
983 if len(fields) == 0:
986 return [el.split() for el in self]
984 return [el.split() for el in self]
987
985
988 res = SList()
986 res = SList()
989 for el in [f.split() for f in self]:
987 for el in [f.split() for f in self]:
990 lineparts = []
988 lineparts = []
991
989
992 for fd in fields:
990 for fd in fields:
993 try:
991 try:
994 lineparts.append(el[fd])
992 lineparts.append(el[fd])
995 except IndexError:
993 except IndexError:
996 pass
994 pass
997 if lineparts:
995 if lineparts:
998 res.append(" ".join(lineparts))
996 res.append(" ".join(lineparts))
999
997
1000 return res
998 return res
1001 def sort(self,field= None, nums = False):
999 def sort(self,field= None, nums = False):
1002 """ sort by specified fields (see fields())
1000 """ sort by specified fields (see fields())
1003
1001
1004 Example::
1002 Example::
1005 a.sort(1, nums = True)
1003 a.sort(1, nums = True)
1006
1004
1007 Sorts a by second field, in numerical order (so that 21 > 3)
1005 Sorts a by second field, in numerical order (so that 21 > 3)
1008
1006
1009 """
1007 """
1010
1008
1011 #decorate, sort, undecorate
1009 #decorate, sort, undecorate
1012 if field is not None:
1010 if field is not None:
1013 dsu = [[SList([line]).fields(field), line] for line in self]
1011 dsu = [[SList([line]).fields(field), line] for line in self]
1014 else:
1012 else:
1015 dsu = [[line, line] for line in self]
1013 dsu = [[line, line] for line in self]
1016 if nums:
1014 if nums:
1017 for i in range(len(dsu)):
1015 for i in range(len(dsu)):
1018 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1016 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
1019 try:
1017 try:
1020 n = int(numstr)
1018 n = int(numstr)
1021 except ValueError:
1019 except ValueError:
1022 n = 0;
1020 n = 0;
1023 dsu[i][0] = n
1021 dsu[i][0] = n
1024
1022
1025
1023
1026 dsu.sort()
1024 dsu.sort()
1027 return SList([t[1] for t in dsu])
1025 return SList([t[1] for t in dsu])
1028
1026
1029 def print_slist(arg):
1027 def print_slist(arg):
1030 """ Prettier (non-repr-like) and more informative printer for SList """
1028 """ Prettier (non-repr-like) and more informative printer for SList """
1031 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1029 print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
1032 if hasattr(arg, 'hideonce') and arg.hideonce:
1030 if hasattr(arg, 'hideonce') and arg.hideonce:
1033 arg.hideonce = False
1031 arg.hideonce = False
1034 return
1032 return
1035
1033
1036 nlprint(arg)
1034 nlprint(arg)
1037
1035
1038 print_slist = result_display.when_type(SList)(print_slist)
1036 print_slist = result_display.when_type(SList)(print_slist)
1039
1037
1040
1038
1041
1039
1042 #----------------------------------------------------------------------------
1040 #----------------------------------------------------------------------------
1043 def esc_quotes(strng):
1041 def esc_quotes(strng):
1044 """Return the input string with single and double quotes escaped out"""
1042 """Return the input string with single and double quotes escaped out"""
1045
1043
1046 return strng.replace('"','\\"').replace("'","\\'")
1044 return strng.replace('"','\\"').replace("'","\\'")
1047
1045
1048 #----------------------------------------------------------------------------
1046 #----------------------------------------------------------------------------
1049 def make_quoted_expr(s):
1047 def make_quoted_expr(s):
1050 """Return string s in appropriate quotes, using raw string if possible.
1048 """Return string s in appropriate quotes, using raw string if possible.
1051
1049
1052 XXX - example removed because it caused encoding errors in documentation
1050 XXX - example removed because it caused encoding errors in documentation
1053 generation. We need a new example that doesn't contain invalid chars.
1051 generation. We need a new example that doesn't contain invalid chars.
1054
1052
1055 Note the use of raw string and padding at the end to allow trailing
1053 Note the use of raw string and padding at the end to allow trailing
1056 backslash.
1054 backslash.
1057 """
1055 """
1058
1056
1059 tail = ''
1057 tail = ''
1060 tailpadding = ''
1058 tailpadding = ''
1061 raw = ''
1059 raw = ''
1062 if "\\" in s:
1060 if "\\" in s:
1063 raw = 'r'
1061 raw = 'r'
1064 if s.endswith('\\'):
1062 if s.endswith('\\'):
1065 tail = '[:-1]'
1063 tail = '[:-1]'
1066 tailpadding = '_'
1064 tailpadding = '_'
1067 if '"' not in s:
1065 if '"' not in s:
1068 quote = '"'
1066 quote = '"'
1069 elif "'" not in s:
1067 elif "'" not in s:
1070 quote = "'"
1068 quote = "'"
1071 elif '"""' not in s and not s.endswith('"'):
1069 elif '"""' not in s and not s.endswith('"'):
1072 quote = '"""'
1070 quote = '"""'
1073 elif "'''" not in s and not s.endswith("'"):
1071 elif "'''" not in s and not s.endswith("'"):
1074 quote = "'''"
1072 quote = "'''"
1075 else:
1073 else:
1076 # give up, backslash-escaped string will do
1074 # give up, backslash-escaped string will do
1077 return '"%s"' % esc_quotes(s)
1075 return '"%s"' % esc_quotes(s)
1078 res = raw + quote + s + tailpadding + quote + tail
1076 res = raw + quote + s + tailpadding + quote + tail
1079 return res
1077 return res
1080
1078
1081
1079
1082 #----------------------------------------------------------------------------
1080 #----------------------------------------------------------------------------
1083 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1081 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1084 """Take multiple lines of input.
1082 """Take multiple lines of input.
1085
1083
1086 A list with each line of input as a separate element is returned when a
1084 A list with each line of input as a separate element is returned when a
1087 termination string is entered (defaults to a single '.'). Input can also
1085 termination string is entered (defaults to a single '.'). Input can also
1088 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1086 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1089
1087
1090 Lines of input which end in \\ are joined into single entries (and a
1088 Lines of input which end in \\ are joined into single entries (and a
1091 secondary continuation prompt is issued as long as the user terminates
1089 secondary continuation prompt is issued as long as the user terminates
1092 lines with \\). This allows entering very long strings which are still
1090 lines with \\). This allows entering very long strings which are still
1093 meant to be treated as single entities.
1091 meant to be treated as single entities.
1094 """
1092 """
1095
1093
1096 try:
1094 try:
1097 if header:
1095 if header:
1098 header += '\n'
1096 header += '\n'
1099 lines = [raw_input(header + ps1)]
1097 lines = [raw_input(header + ps1)]
1100 except EOFError:
1098 except EOFError:
1101 return []
1099 return []
1102 terminate = [terminate_str]
1100 terminate = [terminate_str]
1103 try:
1101 try:
1104 while lines[-1:] != terminate:
1102 while lines[-1:] != terminate:
1105 new_line = raw_input(ps1)
1103 new_line = raw_input(ps1)
1106 while new_line.endswith('\\'):
1104 while new_line.endswith('\\'):
1107 new_line = new_line[:-1] + raw_input(ps2)
1105 new_line = new_line[:-1] + raw_input(ps2)
1108 lines.append(new_line)
1106 lines.append(new_line)
1109
1107
1110 return lines[:-1] # don't return the termination command
1108 return lines[:-1] # don't return the termination command
1111 except EOFError:
1109 except EOFError:
1112 print
1110 print
1113 return lines
1111 return lines
1114
1112
1115 #----------------------------------------------------------------------------
1113 #----------------------------------------------------------------------------
1116 def raw_input_ext(prompt='', ps2='... '):
1114 def raw_input_ext(prompt='', ps2='... '):
1117 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1115 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1118
1116
1119 line = raw_input(prompt)
1117 line = raw_input(prompt)
1120 while line.endswith('\\'):
1118 while line.endswith('\\'):
1121 line = line[:-1] + raw_input(ps2)
1119 line = line[:-1] + raw_input(ps2)
1122 return line
1120 return line
1123
1121
1124 #----------------------------------------------------------------------------
1122 #----------------------------------------------------------------------------
1125 def ask_yes_no(prompt,default=None):
1123 def ask_yes_no(prompt,default=None):
1126 """Asks a question and returns a boolean (y/n) answer.
1124 """Asks a question and returns a boolean (y/n) answer.
1127
1125
1128 If default is given (one of 'y','n'), it is used if the user input is
1126 If default is given (one of 'y','n'), it is used if the user input is
1129 empty. Otherwise the question is repeated until an answer is given.
1127 empty. Otherwise the question is repeated until an answer is given.
1130
1128
1131 An EOF is treated as the default answer. If there is no default, an
1129 An EOF is treated as the default answer. If there is no default, an
1132 exception is raised to prevent infinite loops.
1130 exception is raised to prevent infinite loops.
1133
1131
1134 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1132 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1135
1133
1136 answers = {'y':True,'n':False,'yes':True,'no':False}
1134 answers = {'y':True,'n':False,'yes':True,'no':False}
1137 ans = None
1135 ans = None
1138 while ans not in answers.keys():
1136 while ans not in answers.keys():
1139 try:
1137 try:
1140 ans = raw_input(prompt+' ').lower()
1138 ans = raw_input(prompt+' ').lower()
1141 if not ans: # response was an empty string
1139 if not ans: # response was an empty string
1142 ans = default
1140 ans = default
1143 except KeyboardInterrupt:
1141 except KeyboardInterrupt:
1144 pass
1142 pass
1145 except EOFError:
1143 except EOFError:
1146 if default in answers.keys():
1144 if default in answers.keys():
1147 ans = default
1145 ans = default
1148 print
1146 print
1149 else:
1147 else:
1150 raise
1148 raise
1151
1149
1152 return answers[ans]
1150 return answers[ans]
1153
1151
1154 #----------------------------------------------------------------------------
1152 #----------------------------------------------------------------------------
1155 class EvalDict:
1153 class EvalDict:
1156 """
1154 """
1157 Emulate a dict which evaluates its contents in the caller's frame.
1155 Emulate a dict which evaluates its contents in the caller's frame.
1158
1156
1159 Usage:
1157 Usage:
1160 >>> number = 19
1158 >>> number = 19
1161
1159
1162 >>> text = "python"
1160 >>> text = "python"
1163
1161
1164 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1162 >>> print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1165 Python 2.1 rules!
1163 Python 2.1 rules!
1166 """
1164 """
1167
1165
1168 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1166 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1169 # modified (shorter) version of:
1167 # modified (shorter) version of:
1170 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1168 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1171 # Skip Montanaro (skip@pobox.com).
1169 # Skip Montanaro (skip@pobox.com).
1172
1170
1173 def __getitem__(self, name):
1171 def __getitem__(self, name):
1174 frame = sys._getframe(1)
1172 frame = sys._getframe(1)
1175 return eval(name, frame.f_globals, frame.f_locals)
1173 return eval(name, frame.f_globals, frame.f_locals)
1176
1174
1177 EvalString = EvalDict # for backwards compatibility
1175 EvalString = EvalDict # for backwards compatibility
1178 #----------------------------------------------------------------------------
1176 #----------------------------------------------------------------------------
1179 def qw(words,flat=0,sep=None,maxsplit=-1):
1177 def qw(words,flat=0,sep=None,maxsplit=-1):
1180 """Similar to Perl's qw() operator, but with some more options.
1178 """Similar to Perl's qw() operator, but with some more options.
1181
1179
1182 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1180 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1183
1181
1184 words can also be a list itself, and with flat=1, the output will be
1182 words can also be a list itself, and with flat=1, the output will be
1185 recursively flattened.
1183 recursively flattened.
1186
1184
1187 Examples:
1185 Examples:
1188
1186
1189 >>> qw('1 2')
1187 >>> qw('1 2')
1190 ['1', '2']
1188 ['1', '2']
1191
1189
1192 >>> qw(['a b','1 2',['m n','p q']])
1190 >>> qw(['a b','1 2',['m n','p q']])
1193 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1191 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1194
1192
1195 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1193 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1196 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1194 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
1197 """
1195 """
1198
1196
1199 if type(words) in StringTypes:
1197 if type(words) in StringTypes:
1200 return [word.strip() for word in words.split(sep,maxsplit)
1198 return [word.strip() for word in words.split(sep,maxsplit)
1201 if word and not word.isspace() ]
1199 if word and not word.isspace() ]
1202 if flat:
1200 if flat:
1203 return flatten(map(qw,words,[1]*len(words)))
1201 return flatten(map(qw,words,[1]*len(words)))
1204 return map(qw,words)
1202 return map(qw,words)
1205
1203
1206 #----------------------------------------------------------------------------
1204 #----------------------------------------------------------------------------
1207 def qwflat(words,sep=None,maxsplit=-1):
1205 def qwflat(words,sep=None,maxsplit=-1):
1208 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1206 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1209 return qw(words,1,sep,maxsplit)
1207 return qw(words,1,sep,maxsplit)
1210
1208
1211 #----------------------------------------------------------------------------
1209 #----------------------------------------------------------------------------
1212 def qw_lol(indata):
1210 def qw_lol(indata):
1213 """qw_lol('a b') -> [['a','b']],
1211 """qw_lol('a b') -> [['a','b']],
1214 otherwise it's just a call to qw().
1212 otherwise it's just a call to qw().
1215
1213
1216 We need this to make sure the modules_some keys *always* end up as a
1214 We need this to make sure the modules_some keys *always* end up as a
1217 list of lists."""
1215 list of lists."""
1218
1216
1219 if type(indata) in StringTypes:
1217 if type(indata) in StringTypes:
1220 return [qw(indata)]
1218 return [qw(indata)]
1221 else:
1219 else:
1222 return qw(indata)
1220 return qw(indata)
1223
1221
1224 #----------------------------------------------------------------------------
1222 #----------------------------------------------------------------------------
1225 def grep(pat,list,case=1):
1223 def grep(pat,list,case=1):
1226 """Simple minded grep-like function.
1224 """Simple minded grep-like function.
1227 grep(pat,list) returns occurrences of pat in list, None on failure.
1225 grep(pat,list) returns occurrences of pat in list, None on failure.
1228
1226
1229 It only does simple string matching, with no support for regexps. Use the
1227 It only does simple string matching, with no support for regexps. Use the
1230 option case=0 for case-insensitive matching."""
1228 option case=0 for case-insensitive matching."""
1231
1229
1232 # This is pretty crude. At least it should implement copying only references
1230 # This is pretty crude. At least it should implement copying only references
1233 # to the original data in case it's big. Now it copies the data for output.
1231 # to the original data in case it's big. Now it copies the data for output.
1234 out=[]
1232 out=[]
1235 if case:
1233 if case:
1236 for term in list:
1234 for term in list:
1237 if term.find(pat)>-1: out.append(term)
1235 if term.find(pat)>-1: out.append(term)
1238 else:
1236 else:
1239 lpat=pat.lower()
1237 lpat=pat.lower()
1240 for term in list:
1238 for term in list:
1241 if term.lower().find(lpat)>-1: out.append(term)
1239 if term.lower().find(lpat)>-1: out.append(term)
1242
1240
1243 if len(out): return out
1241 if len(out): return out
1244 else: return None
1242 else: return None
1245
1243
1246 #----------------------------------------------------------------------------
1244 #----------------------------------------------------------------------------
1247 def dgrep(pat,*opts):
1245 def dgrep(pat,*opts):
1248 """Return grep() on dir()+dir(__builtins__).
1246 """Return grep() on dir()+dir(__builtins__).
1249
1247
1250 A very common use of grep() when working interactively."""
1248 A very common use of grep() when working interactively."""
1251
1249
1252 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1250 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1253
1251
1254 #----------------------------------------------------------------------------
1252 #----------------------------------------------------------------------------
1255 def idgrep(pat):
1253 def idgrep(pat):
1256 """Case-insensitive dgrep()"""
1254 """Case-insensitive dgrep()"""
1257
1255
1258 return dgrep(pat,0)
1256 return dgrep(pat,0)
1259
1257
1260 #----------------------------------------------------------------------------
1258 #----------------------------------------------------------------------------
1261 def igrep(pat,list):
1259 def igrep(pat,list):
1262 """Synonym for case-insensitive grep."""
1260 """Synonym for case-insensitive grep."""
1263
1261
1264 return grep(pat,list,case=0)
1262 return grep(pat,list,case=0)
1265
1263
1266 #----------------------------------------------------------------------------
1264 #----------------------------------------------------------------------------
1267 def indent(str,nspaces=4,ntabs=0):
1265 def indent(str,nspaces=4,ntabs=0):
1268 """Indent a string a given number of spaces or tabstops.
1266 """Indent a string a given number of spaces or tabstops.
1269
1267
1270 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1268 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1271 """
1269 """
1272 if str is None:
1270 if str is None:
1273 return
1271 return
1274 ind = '\t'*ntabs+' '*nspaces
1272 ind = '\t'*ntabs+' '*nspaces
1275 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1273 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1276 if outstr.endswith(os.linesep+ind):
1274 if outstr.endswith(os.linesep+ind):
1277 return outstr[:-len(ind)]
1275 return outstr[:-len(ind)]
1278 else:
1276 else:
1279 return outstr
1277 return outstr
1280
1278
1281 #-----------------------------------------------------------------------------
1279 #-----------------------------------------------------------------------------
1282 def native_line_ends(filename,backup=1):
1280 def native_line_ends(filename,backup=1):
1283 """Convert (in-place) a file to line-ends native to the current OS.
1281 """Convert (in-place) a file to line-ends native to the current OS.
1284
1282
1285 If the optional backup argument is given as false, no backup of the
1283 If the optional backup argument is given as false, no backup of the
1286 original file is left. """
1284 original file is left. """
1287
1285
1288 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1286 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1289
1287
1290 bak_filename = filename + backup_suffixes[os.name]
1288 bak_filename = filename + backup_suffixes[os.name]
1291
1289
1292 original = open(filename).read()
1290 original = open(filename).read()
1293 shutil.copy2(filename,bak_filename)
1291 shutil.copy2(filename,bak_filename)
1294 try:
1292 try:
1295 new = open(filename,'wb')
1293 new = open(filename,'wb')
1296 new.write(os.linesep.join(original.splitlines()))
1294 new.write(os.linesep.join(original.splitlines()))
1297 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1295 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1298 new.close()
1296 new.close()
1299 except:
1297 except:
1300 os.rename(bak_filename,filename)
1298 os.rename(bak_filename,filename)
1301 if not backup:
1299 if not backup:
1302 try:
1300 try:
1303 os.remove(bak_filename)
1301 os.remove(bak_filename)
1304 except:
1302 except:
1305 pass
1303 pass
1306
1304
1307 #****************************************************************************
1305 #****************************************************************************
1308 # lists, dicts and structures
1306 # lists, dicts and structures
1309
1307
1310 def belong(candidates,checklist):
1308 def belong(candidates,checklist):
1311 """Check whether a list of items appear in a given list of options.
1309 """Check whether a list of items appear in a given list of options.
1312
1310
1313 Returns a list of 1 and 0, one for each candidate given."""
1311 Returns a list of 1 and 0, one for each candidate given."""
1314
1312
1315 return [x in checklist for x in candidates]
1313 return [x in checklist for x in candidates]
1316
1314
1317 #----------------------------------------------------------------------------
1315 #----------------------------------------------------------------------------
1318 def uniq_stable(elems):
1316 def uniq_stable(elems):
1319 """uniq_stable(elems) -> list
1317 """uniq_stable(elems) -> list
1320
1318
1321 Return from an iterable, a list of all the unique elements in the input,
1319 Return from an iterable, a list of all the unique elements in the input,
1322 but maintaining the order in which they first appear.
1320 but maintaining the order in which they first appear.
1323
1321
1324 A naive solution to this problem which just makes a dictionary with the
1322 A naive solution to this problem which just makes a dictionary with the
1325 elements as keys fails to respect the stability condition, since
1323 elements as keys fails to respect the stability condition, since
1326 dictionaries are unsorted by nature.
1324 dictionaries are unsorted by nature.
1327
1325
1328 Note: All elements in the input must be valid dictionary keys for this
1326 Note: All elements in the input must be valid dictionary keys for this
1329 routine to work, as it internally uses a dictionary for efficiency
1327 routine to work, as it internally uses a dictionary for efficiency
1330 reasons."""
1328 reasons."""
1331
1329
1332 unique = []
1330 unique = []
1333 unique_dict = {}
1331 unique_dict = {}
1334 for nn in elems:
1332 for nn in elems:
1335 if nn not in unique_dict:
1333 if nn not in unique_dict:
1336 unique.append(nn)
1334 unique.append(nn)
1337 unique_dict[nn] = None
1335 unique_dict[nn] = None
1338 return unique
1336 return unique
1339
1337
1340 #----------------------------------------------------------------------------
1338 #----------------------------------------------------------------------------
1341 class NLprinter:
1339 class NLprinter:
1342 """Print an arbitrarily nested list, indicating index numbers.
1340 """Print an arbitrarily nested list, indicating index numbers.
1343
1341
1344 An instance of this class called nlprint is available and callable as a
1342 An instance of this class called nlprint is available and callable as a
1345 function.
1343 function.
1346
1344
1347 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1345 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1348 and using 'sep' to separate the index from the value. """
1346 and using 'sep' to separate the index from the value. """
1349
1347
1350 def __init__(self):
1348 def __init__(self):
1351 self.depth = 0
1349 self.depth = 0
1352
1350
1353 def __call__(self,lst,pos='',**kw):
1351 def __call__(self,lst,pos='',**kw):
1354 """Prints the nested list numbering levels."""
1352 """Prints the nested list numbering levels."""
1355 kw.setdefault('indent',' ')
1353 kw.setdefault('indent',' ')
1356 kw.setdefault('sep',': ')
1354 kw.setdefault('sep',': ')
1357 kw.setdefault('start',0)
1355 kw.setdefault('start',0)
1358 kw.setdefault('stop',len(lst))
1356 kw.setdefault('stop',len(lst))
1359 # we need to remove start and stop from kw so they don't propagate
1357 # we need to remove start and stop from kw so they don't propagate
1360 # into a recursive call for a nested list.
1358 # into a recursive call for a nested list.
1361 start = kw['start']; del kw['start']
1359 start = kw['start']; del kw['start']
1362 stop = kw['stop']; del kw['stop']
1360 stop = kw['stop']; del kw['stop']
1363 if self.depth == 0 and 'header' in kw.keys():
1361 if self.depth == 0 and 'header' in kw.keys():
1364 print kw['header']
1362 print kw['header']
1365
1363
1366 for idx in range(start,stop):
1364 for idx in range(start,stop):
1367 elem = lst[idx]
1365 elem = lst[idx]
1368 if type(elem)==type([]):
1366 if type(elem)==type([]):
1369 self.depth += 1
1367 self.depth += 1
1370 self.__call__(elem,itpl('$pos$idx,'),**kw)
1368 self.__call__(elem,itpl('$pos$idx,'),**kw)
1371 self.depth -= 1
1369 self.depth -= 1
1372 else:
1370 else:
1373 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1371 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1374
1372
1375 nlprint = NLprinter()
1373 nlprint = NLprinter()
1376 #----------------------------------------------------------------------------
1374 #----------------------------------------------------------------------------
1377 def all_belong(candidates,checklist):
1375 def all_belong(candidates,checklist):
1378 """Check whether a list of items ALL appear in a given list of options.
1376 """Check whether a list of items ALL appear in a given list of options.
1379
1377
1380 Returns a single 1 or 0 value."""
1378 Returns a single 1 or 0 value."""
1381
1379
1382 return 1-(0 in [x in checklist for x in candidates])
1380 return 1-(0 in [x in checklist for x in candidates])
1383
1381
1384 #----------------------------------------------------------------------------
1382 #----------------------------------------------------------------------------
1385 def sort_compare(lst1,lst2,inplace = 1):
1383 def sort_compare(lst1,lst2,inplace = 1):
1386 """Sort and compare two lists.
1384 """Sort and compare two lists.
1387
1385
1388 By default it does it in place, thus modifying the lists. Use inplace = 0
1386 By default it does it in place, thus modifying the lists. Use inplace = 0
1389 to avoid that (at the cost of temporary copy creation)."""
1387 to avoid that (at the cost of temporary copy creation)."""
1390 if not inplace:
1388 if not inplace:
1391 lst1 = lst1[:]
1389 lst1 = lst1[:]
1392 lst2 = lst2[:]
1390 lst2 = lst2[:]
1393 lst1.sort(); lst2.sort()
1391 lst1.sort(); lst2.sort()
1394 return lst1 == lst2
1392 return lst1 == lst2
1395
1393
1396 #----------------------------------------------------------------------------
1394 #----------------------------------------------------------------------------
1397 def list2dict(lst):
1395 def list2dict(lst):
1398 """Takes a list of (key,value) pairs and turns it into a dict."""
1396 """Takes a list of (key,value) pairs and turns it into a dict."""
1399
1397
1400 dic = {}
1398 dic = {}
1401 for k,v in lst: dic[k] = v
1399 for k,v in lst: dic[k] = v
1402 return dic
1400 return dic
1403
1401
1404 #----------------------------------------------------------------------------
1402 #----------------------------------------------------------------------------
1405 def list2dict2(lst,default=''):
1403 def list2dict2(lst,default=''):
1406 """Takes a list and turns it into a dict.
1404 """Takes a list and turns it into a dict.
1407 Much slower than list2dict, but more versatile. This version can take
1405 Much slower than list2dict, but more versatile. This version can take
1408 lists with sublists of arbitrary length (including sclars)."""
1406 lists with sublists of arbitrary length (including sclars)."""
1409
1407
1410 dic = {}
1408 dic = {}
1411 for elem in lst:
1409 for elem in lst:
1412 if type(elem) in (types.ListType,types.TupleType):
1410 if type(elem) in (types.ListType,types.TupleType):
1413 size = len(elem)
1411 size = len(elem)
1414 if size == 0:
1412 if size == 0:
1415 pass
1413 pass
1416 elif size == 1:
1414 elif size == 1:
1417 dic[elem] = default
1415 dic[elem] = default
1418 else:
1416 else:
1419 k,v = elem[0], elem[1:]
1417 k,v = elem[0], elem[1:]
1420 if len(v) == 1: v = v[0]
1418 if len(v) == 1: v = v[0]
1421 dic[k] = v
1419 dic[k] = v
1422 else:
1420 else:
1423 dic[elem] = default
1421 dic[elem] = default
1424 return dic
1422 return dic
1425
1423
1426 #----------------------------------------------------------------------------
1424 #----------------------------------------------------------------------------
1427 def flatten(seq):
1425 def flatten(seq):
1428 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1426 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1429
1427
1430 return [x for subseq in seq for x in subseq]
1428 return [x for subseq in seq for x in subseq]
1431
1429
1432 #----------------------------------------------------------------------------
1430 #----------------------------------------------------------------------------
1433 def get_slice(seq,start=0,stop=None,step=1):
1431 def get_slice(seq,start=0,stop=None,step=1):
1434 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1432 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1435 if stop == None:
1433 if stop == None:
1436 stop = len(seq)
1434 stop = len(seq)
1437 item = lambda i: seq[i]
1435 item = lambda i: seq[i]
1438 return map(item,xrange(start,stop,step))
1436 return map(item,xrange(start,stop,step))
1439
1437
1440 #----------------------------------------------------------------------------
1438 #----------------------------------------------------------------------------
1441 def chop(seq,size):
1439 def chop(seq,size):
1442 """Chop a sequence into chunks of the given size."""
1440 """Chop a sequence into chunks of the given size."""
1443 chunk = lambda i: seq[i:i+size]
1441 chunk = lambda i: seq[i:i+size]
1444 return map(chunk,xrange(0,len(seq),size))
1442 return map(chunk,xrange(0,len(seq),size))
1445
1443
1446 #----------------------------------------------------------------------------
1444 #----------------------------------------------------------------------------
1447 # with is a keyword as of python 2.5, so this function is renamed to withobj
1445 # with is a keyword as of python 2.5, so this function is renamed to withobj
1448 # from its old 'with' name.
1446 # from its old 'with' name.
1449 def with_obj(object, **args):
1447 def with_obj(object, **args):
1450 """Set multiple attributes for an object, similar to Pascal's with.
1448 """Set multiple attributes for an object, similar to Pascal's with.
1451
1449
1452 Example:
1450 Example:
1453 with_obj(jim,
1451 with_obj(jim,
1454 born = 1960,
1452 born = 1960,
1455 haircolour = 'Brown',
1453 haircolour = 'Brown',
1456 eyecolour = 'Green')
1454 eyecolour = 'Green')
1457
1455
1458 Credit: Greg Ewing, in
1456 Credit: Greg Ewing, in
1459 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1457 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1460
1458
1461 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1459 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1462 has become a keyword for Python 2.5, so we had to rename it."""
1460 has become a keyword for Python 2.5, so we had to rename it."""
1463
1461
1464 object.__dict__.update(args)
1462 object.__dict__.update(args)
1465
1463
1466 #----------------------------------------------------------------------------
1464 #----------------------------------------------------------------------------
1467 def setattr_list(obj,alist,nspace = None):
1465 def setattr_list(obj,alist,nspace = None):
1468 """Set a list of attributes for an object taken from a namespace.
1466 """Set a list of attributes for an object taken from a namespace.
1469
1467
1470 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1468 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1471 alist with their values taken from nspace, which must be a dict (something
1469 alist with their values taken from nspace, which must be a dict (something
1472 like locals() will often do) If nspace isn't given, locals() of the
1470 like locals() will often do) If nspace isn't given, locals() of the
1473 *caller* is used, so in most cases you can omit it.
1471 *caller* is used, so in most cases you can omit it.
1474
1472
1475 Note that alist can be given as a string, which will be automatically
1473 Note that alist can be given as a string, which will be automatically
1476 split into a list on whitespace. If given as a list, it must be a list of
1474 split into a list on whitespace. If given as a list, it must be a list of
1477 *strings* (the variable names themselves), not of variables."""
1475 *strings* (the variable names themselves), not of variables."""
1478
1476
1479 # this grabs the local variables from the *previous* call frame -- that is
1477 # this grabs the local variables from the *previous* call frame -- that is
1480 # the locals from the function that called setattr_list().
1478 # the locals from the function that called setattr_list().
1481 # - snipped from weave.inline()
1479 # - snipped from weave.inline()
1482 if nspace is None:
1480 if nspace is None:
1483 call_frame = sys._getframe().f_back
1481 call_frame = sys._getframe().f_back
1484 nspace = call_frame.f_locals
1482 nspace = call_frame.f_locals
1485
1483
1486 if type(alist) in StringTypes:
1484 if type(alist) in StringTypes:
1487 alist = alist.split()
1485 alist = alist.split()
1488 for attr in alist:
1486 for attr in alist:
1489 val = eval(attr,nspace)
1487 val = eval(attr,nspace)
1490 setattr(obj,attr,val)
1488 setattr(obj,attr,val)
1491
1489
1492 #----------------------------------------------------------------------------
1490 #----------------------------------------------------------------------------
1493 def getattr_list(obj,alist,*args):
1491 def getattr_list(obj,alist,*args):
1494 """getattr_list(obj,alist[, default]) -> attribute list.
1492 """getattr_list(obj,alist[, default]) -> attribute list.
1495
1493
1496 Get a list of named attributes for an object. When a default argument is
1494 Get a list of named attributes for an object. When a default argument is
1497 given, it is returned when the attribute doesn't exist; without it, an
1495 given, it is returned when the attribute doesn't exist; without it, an
1498 exception is raised in that case.
1496 exception is raised in that case.
1499
1497
1500 Note that alist can be given as a string, which will be automatically
1498 Note that alist can be given as a string, which will be automatically
1501 split into a list on whitespace. If given as a list, it must be a list of
1499 split into a list on whitespace. If given as a list, it must be a list of
1502 *strings* (the variable names themselves), not of variables."""
1500 *strings* (the variable names themselves), not of variables."""
1503
1501
1504 if type(alist) in StringTypes:
1502 if type(alist) in StringTypes:
1505 alist = alist.split()
1503 alist = alist.split()
1506 if args:
1504 if args:
1507 if len(args)==1:
1505 if len(args)==1:
1508 default = args[0]
1506 default = args[0]
1509 return map(lambda attr: getattr(obj,attr,default),alist)
1507 return map(lambda attr: getattr(obj,attr,default),alist)
1510 else:
1508 else:
1511 raise ValueError,'getattr_list() takes only one optional argument'
1509 raise ValueError,'getattr_list() takes only one optional argument'
1512 else:
1510 else:
1513 return map(lambda attr: getattr(obj,attr),alist)
1511 return map(lambda attr: getattr(obj,attr),alist)
1514
1512
1515 #----------------------------------------------------------------------------
1513 #----------------------------------------------------------------------------
1516 def map_method(method,object_list,*argseq,**kw):
1514 def map_method(method,object_list,*argseq,**kw):
1517 """map_method(method,object_list,*args,**kw) -> list
1515 """map_method(method,object_list,*args,**kw) -> list
1518
1516
1519 Return a list of the results of applying the methods to the items of the
1517 Return a list of the results of applying the methods to the items of the
1520 argument sequence(s). If more than one sequence is given, the method is
1518 argument sequence(s). If more than one sequence is given, the method is
1521 called with an argument list consisting of the corresponding item of each
1519 called with an argument list consisting of the corresponding item of each
1522 sequence. All sequences must be of the same length.
1520 sequence. All sequences must be of the same length.
1523
1521
1524 Keyword arguments are passed verbatim to all objects called.
1522 Keyword arguments are passed verbatim to all objects called.
1525
1523
1526 This is Python code, so it's not nearly as fast as the builtin map()."""
1524 This is Python code, so it's not nearly as fast as the builtin map()."""
1527
1525
1528 out_list = []
1526 out_list = []
1529 idx = 0
1527 idx = 0
1530 for object in object_list:
1528 for object in object_list:
1531 try:
1529 try:
1532 handler = getattr(object, method)
1530 handler = getattr(object, method)
1533 except AttributeError:
1531 except AttributeError:
1534 out_list.append(None)
1532 out_list.append(None)
1535 else:
1533 else:
1536 if argseq:
1534 if argseq:
1537 args = map(lambda lst:lst[idx],argseq)
1535 args = map(lambda lst:lst[idx],argseq)
1538 #print 'ob',object,'hand',handler,'ar',args # dbg
1536 #print 'ob',object,'hand',handler,'ar',args # dbg
1539 out_list.append(handler(args,**kw))
1537 out_list.append(handler(args,**kw))
1540 else:
1538 else:
1541 out_list.append(handler(**kw))
1539 out_list.append(handler(**kw))
1542 idx += 1
1540 idx += 1
1543 return out_list
1541 return out_list
1544
1542
1545 #----------------------------------------------------------------------------
1543 #----------------------------------------------------------------------------
1546 def get_class_members(cls):
1544 def get_class_members(cls):
1547 ret = dir(cls)
1545 ret = dir(cls)
1548 if hasattr(cls,'__bases__'):
1546 if hasattr(cls,'__bases__'):
1549 for base in cls.__bases__:
1547 for base in cls.__bases__:
1550 ret.extend(get_class_members(base))
1548 ret.extend(get_class_members(base))
1551 return ret
1549 return ret
1552
1550
1553 #----------------------------------------------------------------------------
1551 #----------------------------------------------------------------------------
1554 def dir2(obj):
1552 def dir2(obj):
1555 """dir2(obj) -> list of strings
1553 """dir2(obj) -> list of strings
1556
1554
1557 Extended version of the Python builtin dir(), which does a few extra
1555 Extended version of the Python builtin dir(), which does a few extra
1558 checks, and supports common objects with unusual internals that confuse
1556 checks, and supports common objects with unusual internals that confuse
1559 dir(), such as Traits and PyCrust.
1557 dir(), such as Traits and PyCrust.
1560
1558
1561 This version is guaranteed to return only a list of true strings, whereas
1559 This version is guaranteed to return only a list of true strings, whereas
1562 dir() returns anything that objects inject into themselves, even if they
1560 dir() returns anything that objects inject into themselves, even if they
1563 are later not really valid for attribute access (many extension libraries
1561 are later not really valid for attribute access (many extension libraries
1564 have such bugs).
1562 have such bugs).
1565 """
1563 """
1566
1564
1567 # Start building the attribute list via dir(), and then complete it
1565 # Start building the attribute list via dir(), and then complete it
1568 # with a few extra special-purpose calls.
1566 # with a few extra special-purpose calls.
1569 words = dir(obj)
1567 words = dir(obj)
1570
1568
1571 if hasattr(obj,'__class__'):
1569 if hasattr(obj,'__class__'):
1572 words.append('__class__')
1570 words.append('__class__')
1573 words.extend(get_class_members(obj.__class__))
1571 words.extend(get_class_members(obj.__class__))
1574 #if '__base__' in words: 1/0
1572 #if '__base__' in words: 1/0
1575
1573
1576 # Some libraries (such as traits) may introduce duplicates, we want to
1574 # Some libraries (such as traits) may introduce duplicates, we want to
1577 # track and clean this up if it happens
1575 # track and clean this up if it happens
1578 may_have_dupes = False
1576 may_have_dupes = False
1579
1577
1580 # this is the 'dir' function for objects with Enthought's traits
1578 # this is the 'dir' function for objects with Enthought's traits
1581 if hasattr(obj, 'trait_names'):
1579 if hasattr(obj, 'trait_names'):
1582 try:
1580 try:
1583 words.extend(obj.trait_names())
1581 words.extend(obj.trait_names())
1584 may_have_dupes = True
1582 may_have_dupes = True
1585 except TypeError:
1583 except TypeError:
1586 # This will happen if `obj` is a class and not an instance.
1584 # This will happen if `obj` is a class and not an instance.
1587 pass
1585 pass
1588
1586
1589 # Support for PyCrust-style _getAttributeNames magic method.
1587 # Support for PyCrust-style _getAttributeNames magic method.
1590 if hasattr(obj, '_getAttributeNames'):
1588 if hasattr(obj, '_getAttributeNames'):
1591 try:
1589 try:
1592 words.extend(obj._getAttributeNames())
1590 words.extend(obj._getAttributeNames())
1593 may_have_dupes = True
1591 may_have_dupes = True
1594 except TypeError:
1592 except TypeError:
1595 # `obj` is a class and not an instance. Ignore
1593 # `obj` is a class and not an instance. Ignore
1596 # this error.
1594 # this error.
1597 pass
1595 pass
1598
1596
1599 if may_have_dupes:
1597 if may_have_dupes:
1600 # eliminate possible duplicates, as some traits may also
1598 # eliminate possible duplicates, as some traits may also
1601 # appear as normal attributes in the dir() call.
1599 # appear as normal attributes in the dir() call.
1602 words = list(set(words))
1600 words = list(set(words))
1603 words.sort()
1601 words.sort()
1604
1602
1605 # filter out non-string attributes which may be stuffed by dir() calls
1603 # filter out non-string attributes which may be stuffed by dir() calls
1606 # and poor coding in third-party modules
1604 # and poor coding in third-party modules
1607 return [w for w in words if isinstance(w, basestring)]
1605 return [w for w in words if isinstance(w, basestring)]
1608
1606
1609 #----------------------------------------------------------------------------
1607 #----------------------------------------------------------------------------
1610 def import_fail_info(mod_name,fns=None):
1608 def import_fail_info(mod_name,fns=None):
1611 """Inform load failure for a module."""
1609 """Inform load failure for a module."""
1612
1610
1613 if fns == None:
1611 if fns == None:
1614 warn("Loading of %s failed.\n" % (mod_name,))
1612 warn("Loading of %s failed.\n" % (mod_name,))
1615 else:
1613 else:
1616 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1614 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1617
1615
1618 #----------------------------------------------------------------------------
1616 #----------------------------------------------------------------------------
1619 # Proposed popitem() extension, written as a method
1617 # Proposed popitem() extension, written as a method
1620
1618
1621
1619
1622 class NotGiven: pass
1620 class NotGiven: pass
1623
1621
1624 def popkey(dct,key,default=NotGiven):
1622 def popkey(dct,key,default=NotGiven):
1625 """Return dct[key] and delete dct[key].
1623 """Return dct[key] and delete dct[key].
1626
1624
1627 If default is given, return it if dct[key] doesn't exist, otherwise raise
1625 If default is given, return it if dct[key] doesn't exist, otherwise raise
1628 KeyError. """
1626 KeyError. """
1629
1627
1630 try:
1628 try:
1631 val = dct[key]
1629 val = dct[key]
1632 except KeyError:
1630 except KeyError:
1633 if default is NotGiven:
1631 if default is NotGiven:
1634 raise
1632 raise
1635 else:
1633 else:
1636 return default
1634 return default
1637 else:
1635 else:
1638 del dct[key]
1636 del dct[key]
1639 return val
1637 return val
1640
1638
1641 def wrap_deprecated(func, suggest = '<nothing>'):
1639 def wrap_deprecated(func, suggest = '<nothing>'):
1642 def newFunc(*args, **kwargs):
1640 def newFunc(*args, **kwargs):
1643 warnings.warn("Call to deprecated function %s, use %s instead" %
1641 warnings.warn("Call to deprecated function %s, use %s instead" %
1644 ( func.__name__, suggest),
1642 ( func.__name__, suggest),
1645 category=DeprecationWarning,
1643 category=DeprecationWarning,
1646 stacklevel = 2)
1644 stacklevel = 2)
1647 return func(*args, **kwargs)
1645 return func(*args, **kwargs)
1648 return newFunc
1646 return newFunc
1649
1647
1650
1648
1651 def _num_cpus_unix():
1649 def _num_cpus_unix():
1652 """Return the number of active CPUs on a Unix system."""
1650 """Return the number of active CPUs on a Unix system."""
1653 return os.sysconf("SC_NPROCESSORS_ONLN")
1651 return os.sysconf("SC_NPROCESSORS_ONLN")
1654
1652
1655
1653
1656 def _num_cpus_darwin():
1654 def _num_cpus_darwin():
1657 """Return the number of active CPUs on a Darwin system."""
1655 """Return the number of active CPUs on a Darwin system."""
1658 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
1656 p = subprocess.Popen(['sysctl','-n','hw.ncpu'],stdout=subprocess.PIPE)
1659 return p.stdout.read()
1657 return p.stdout.read()
1660
1658
1661
1659
1662 def _num_cpus_windows():
1660 def _num_cpus_windows():
1663 """Return the number of active CPUs on a Windows system."""
1661 """Return the number of active CPUs on a Windows system."""
1664 return os.environ.get("NUMBER_OF_PROCESSORS")
1662 return os.environ.get("NUMBER_OF_PROCESSORS")
1665
1663
1666
1664
1667 def num_cpus():
1665 def num_cpus():
1668 """Return the effective number of CPUs in the system as an integer.
1666 """Return the effective number of CPUs in the system as an integer.
1669
1667
1670 This cross-platform function makes an attempt at finding the total number of
1668 This cross-platform function makes an attempt at finding the total number of
1671 available CPUs in the system, as returned by various underlying system and
1669 available CPUs in the system, as returned by various underlying system and
1672 python calls.
1670 python calls.
1673
1671
1674 If it can't find a sensible answer, it returns 1 (though an error *may* make
1672 If it can't find a sensible answer, it returns 1 (though an error *may* make
1675 it return a large positive number that's actually incorrect).
1673 it return a large positive number that's actually incorrect).
1676 """
1674 """
1677
1675
1678 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
1676 # Many thanks to the Parallel Python project (http://www.parallelpython.com)
1679 # for the names of the keys we needed to look up for this function. This
1677 # for the names of the keys we needed to look up for this function. This
1680 # code was inspired by their equivalent function.
1678 # code was inspired by their equivalent function.
1681
1679
1682 ncpufuncs = {'Linux':_num_cpus_unix,
1680 ncpufuncs = {'Linux':_num_cpus_unix,
1683 'Darwin':_num_cpus_darwin,
1681 'Darwin':_num_cpus_darwin,
1684 'Windows':_num_cpus_windows,
1682 'Windows':_num_cpus_windows,
1685 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
1683 # On Vista, python < 2.5.2 has a bug and returns 'Microsoft'
1686 # See http://bugs.python.org/issue1082 for details.
1684 # See http://bugs.python.org/issue1082 for details.
1687 'Microsoft':_num_cpus_windows,
1685 'Microsoft':_num_cpus_windows,
1688 }
1686 }
1689
1687
1690 ncpufunc = ncpufuncs.get(platform.system(),
1688 ncpufunc = ncpufuncs.get(platform.system(),
1691 # default to unix version (Solaris, AIX, etc)
1689 # default to unix version (Solaris, AIX, etc)
1692 _num_cpus_unix)
1690 _num_cpus_unix)
1693
1691
1694 try:
1692 try:
1695 ncpus = max(1,int(ncpufunc()))
1693 ncpus = max(1,int(ncpufunc()))
1696 except:
1694 except:
1697 ncpus = 1
1695 ncpus = 1
1698 return ncpus
1696 return ncpus
1699
1697
1700 def extract_vars(*names,**kw):
1698 def extract_vars(*names,**kw):
1701 """Extract a set of variables by name from another frame.
1699 """Extract a set of variables by name from another frame.
1702
1700
1703 :Parameters:
1701 :Parameters:
1704 - `*names`: strings
1702 - `*names`: strings
1705 One or more variable names which will be extracted from the caller's
1703 One or more variable names which will be extracted from the caller's
1706 frame.
1704 frame.
1707
1705
1708 :Keywords:
1706 :Keywords:
1709 - `depth`: integer (0)
1707 - `depth`: integer (0)
1710 How many frames in the stack to walk when looking for your variables.
1708 How many frames in the stack to walk when looking for your variables.
1711
1709
1712
1710
1713 Examples:
1711 Examples:
1714
1712
1715 In [2]: def func(x):
1713 In [2]: def func(x):
1716 ...: y = 1
1714 ...: y = 1
1717 ...: print extract_vars('x','y')
1715 ...: print extract_vars('x','y')
1718 ...:
1716 ...:
1719
1717
1720 In [3]: func('hello')
1718 In [3]: func('hello')
1721 {'y': 1, 'x': 'hello'}
1719 {'y': 1, 'x': 'hello'}
1722 """
1720 """
1723
1721
1724 depth = kw.get('depth',0)
1722 depth = kw.get('depth',0)
1725
1723
1726 callerNS = sys._getframe(depth+1).f_locals
1724 callerNS = sys._getframe(depth+1).f_locals
1727 return dict((k,callerNS[k]) for k in names)
1725 return dict((k,callerNS[k]) for k in names)
1728
1726
1729
1727
1730 def extract_vars_above(*names):
1728 def extract_vars_above(*names):
1731 """Extract a set of variables by name from another frame.
1729 """Extract a set of variables by name from another frame.
1732
1730
1733 Similar to extractVars(), but with a specified depth of 1, so that names
1731 Similar to extractVars(), but with a specified depth of 1, so that names
1734 are exctracted exactly from above the caller.
1732 are exctracted exactly from above the caller.
1735
1733
1736 This is simply a convenience function so that the very common case (for us)
1734 This is simply a convenience function so that the very common case (for us)
1737 of skipping exactly 1 frame doesn't have to construct a special dict for
1735 of skipping exactly 1 frame doesn't have to construct a special dict for
1738 keyword passing."""
1736 keyword passing."""
1739
1737
1740 callerNS = sys._getframe(2).f_locals
1738 callerNS = sys._getframe(2).f_locals
1741 return dict((k,callerNS[k]) for k in names)
1739 return dict((k,callerNS[k]) for k in names)
1742
1740
1743 def shexp(s):
1741 def expand_path(s):
1744 """Expand $VARS and ~names in a string, like a shell
1742 """Expand $VARS and ~names in a string, like a shell
1745
1743
1746 :Examples:
1744 :Examples:
1747
1745
1748 In [2]: os.environ['FOO']='test'
1746 In [2]: os.environ['FOO']='test'
1749
1747
1750 In [3]: shexp('variable FOO is $FOO')
1748 In [3]: shexp('variable FOO is $FOO')
1751 Out[3]: 'variable FOO is test'
1749 Out[3]: 'variable FOO is test'
1752 """
1750 """
1753 return os.path.expandvars(os.path.expanduser(s))
1751 # This is a pretty subtle hack. When expand user is given a UNC path
1754
1752 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
1753 # the $ to get (\\server\share\%username%). I think it considered $
1754 # alone an empty var. But, we need the $ to remains there (it indicates
1755 # a hidden share).
1756 if os.name=='nt':
1757 s.replace('$\\', 'IPYTHON_TEMP')
1758 s2 = os.path.expandvars(os.path.expanduser(s))
1759 if os.name=='nt':
1760 s2.replace('IPYTHON_TEMP', '$\\')
1761 return s2
1755
1762
1756 def list_strings(arg):
1763 def list_strings(arg):
1757 """Always return a list of strings, given a string or list of strings
1764 """Always return a list of strings, given a string or list of strings
1758 as input.
1765 as input.
1759
1766
1760 :Examples:
1767 :Examples:
1761
1768
1762 In [7]: list_strings('A single string')
1769 In [7]: list_strings('A single string')
1763 Out[7]: ['A single string']
1770 Out[7]: ['A single string']
1764
1771
1765 In [8]: list_strings(['A single string in a list'])
1772 In [8]: list_strings(['A single string in a list'])
1766 Out[8]: ['A single string in a list']
1773 Out[8]: ['A single string in a list']
1767
1774
1768 In [9]: list_strings(['A','list','of','strings'])
1775 In [9]: list_strings(['A','list','of','strings'])
1769 Out[9]: ['A', 'list', 'of', 'strings']
1776 Out[9]: ['A', 'list', 'of', 'strings']
1770 """
1777 """
1771
1778
1772 if isinstance(arg,basestring): return [arg]
1779 if isinstance(arg,basestring): return [arg]
1773 else: return arg
1780 else: return arg
1774
1781
1775
1782
1776 #----------------------------------------------------------------------------
1783 #----------------------------------------------------------------------------
1777 def marquee(txt='',width=78,mark='*'):
1784 def marquee(txt='',width=78,mark='*'):
1778 """Return the input string centered in a 'marquee'.
1785 """Return the input string centered in a 'marquee'.
1779
1786
1780 :Examples:
1787 :Examples:
1781
1788
1782 In [16]: marquee('A test',40)
1789 In [16]: marquee('A test',40)
1783 Out[16]: '**************** A test ****************'
1790 Out[16]: '**************** A test ****************'
1784
1791
1785 In [17]: marquee('A test',40,'-')
1792 In [17]: marquee('A test',40,'-')
1786 Out[17]: '---------------- A test ----------------'
1793 Out[17]: '---------------- A test ----------------'
1787
1794
1788 In [18]: marquee('A test',40,' ')
1795 In [18]: marquee('A test',40,' ')
1789 Out[18]: ' A test '
1796 Out[18]: ' A test '
1790
1797
1791 """
1798 """
1792 if not txt:
1799 if not txt:
1793 return (mark*width)[:width]
1800 return (mark*width)[:width]
1794 nmark = (width-len(txt)-2)/len(mark)/2
1801 nmark = (width-len(txt)-2)/len(mark)/2
1795 if nmark < 0: nmark =0
1802 if nmark < 0: nmark =0
1796 marks = mark*nmark
1803 marks = mark*nmark
1797 return '%s %s %s' % (marks,txt,marks)
1804 return '%s %s %s' % (marks,txt,marks)
1798
1805
1799 #*************************** end of file <genutils.py> **********************
1806 #*************************** end of file <genutils.py> **********************
General Comments 0
You need to be logged in to leave comments. Login now