1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """shell/term utilities, useful to write some python scripts instead of shell
19 scripts.
20 """
21
22 from __future__ import print_function
23
24 __docformat__ = "restructuredtext en"
25
26 import os
27 import glob
28 import shutil
29 import stat
30 import sys
31 import tempfile
32 import time
33 import fnmatch
34 import errno
35 import string
36 import random
37 import subprocess
38 from os.path import exists, isdir, islink, basename, join
39
40 from six import string_types
41 from six.moves import range, input as raw_input
42
43 from logilab.common import STD_BLACKLIST, _handle_blacklist
44 from logilab.common.compat import str_to_bytes
45 from logilab.common.deprecation import deprecated
46
47 try:
48 from logilab.common.proc import ProcInfo, NoSuchProcess
49 except ImportError:
50
52
55
56
58
60 self.path = tempfile.mkdtemp()
61 return self.path
62
63 - def __exit__(self, exctype, value, traceback):
64
65 shutil.rmtree(self.path)
66 return traceback is None
67
68
71 self.directory = directory
72
74 self.cwd = os.getcwd()
75 os.chdir(self.directory)
76 return self.directory
77
78 - def __exit__(self, exctype, value, traceback):
80
81
82 -def chown(path, login=None, group=None):
83 """Same as `os.chown` function but accepting user login or group name as
84 argument. If login or group is omitted, it's left unchanged.
85
86 Note: you must own the file to chown it (or be root). Otherwise OSError is raised.
87 """
88 if login is None:
89 uid = -1
90 else:
91 try:
92 uid = int(login)
93 except ValueError:
94 import pwd
95 uid = pwd.getpwnam(login).pw_uid
96 if group is None:
97 gid = -1
98 else:
99 try:
100 gid = int(group)
101 except ValueError:
102 import grp
103 gid = grp.getgrnam(group).gr_gid
104 os.chown(path, uid, gid)
105
106 -def mv(source, destination, _action=shutil.move):
107 """A shell-like mv, supporting wildcards.
108 """
109 sources = glob.glob(source)
110 if len(sources) > 1:
111 assert isdir(destination)
112 for filename in sources:
113 _action(filename, join(destination, basename(filename)))
114 else:
115 try:
116 source = sources[0]
117 except IndexError:
118 raise OSError('No file matching %s' % source)
119 if isdir(destination) and exists(destination):
120 destination = join(destination, basename(source))
121 try:
122 _action(source, destination)
123 except OSError as ex:
124 raise OSError('Unable to move %r to %r (%s)' % (
125 source, destination, ex))
126
128 """A shell-like rm, supporting wildcards.
129 """
130 for wfile in files:
131 for filename in glob.glob(wfile):
132 if islink(filename):
133 os.remove(filename)
134 elif isdir(filename):
135 shutil.rmtree(filename)
136 else:
137 os.remove(filename)
138
139 -def cp(source, destination):
140 """A shell-like cp, supporting wildcards.
141 """
142 mv(source, destination, _action=shutil.copy)
143
145 """Recursively find files ending with the given extensions from the directory.
146
147 :type directory: str
148 :param directory:
149 directory where the search should start
150
151 :type exts: basestring or list or tuple
152 :param exts:
153 extensions or lists or extensions to search
154
155 :type exclude: boolean
156 :param exts:
157 if this argument is True, returning files NOT ending with the given
158 extensions
159
160 :type blacklist: list or tuple
161 :param blacklist:
162 optional list of files or directory to ignore, default to the value of
163 `logilab.common.STD_BLACKLIST`
164
165 :rtype: list
166 :return:
167 the list of all matching files
168 """
169 if isinstance(exts, string_types):
170 exts = (exts,)
171 if exclude:
172 def match(filename, exts):
173 for ext in exts:
174 if filename.endswith(ext):
175 return False
176 return True
177 else:
178 def match(filename, exts):
179 for ext in exts:
180 if filename.endswith(ext):
181 return True
182 return False
183 files = []
184 for dirpath, dirnames, filenames in os.walk(directory):
185 _handle_blacklist(blacklist, dirnames, filenames)
186
187 dirname = basename(dirpath)
188 if dirname in blacklist:
189 continue
190 files.extend([join(dirpath, f) for f in filenames if match(f, exts)])
191 return files
192
193
195 """Recursively finds files matching glob `pattern` under `directory`.
196
197 This is an alternative to `logilab.common.shellutils.find`.
198
199 :type directory: str
200 :param directory:
201 directory where the search should start
202
203 :type pattern: basestring
204 :param pattern:
205 the glob pattern (e.g *.py, foo*.py, etc.)
206
207 :type blacklist: list or tuple
208 :param blacklist:
209 optional list of files or directory to ignore, default to the value of
210 `logilab.common.STD_BLACKLIST`
211
212 :rtype: iterator
213 :return:
214 iterator over the list of all matching files
215 """
216 for curdir, dirnames, filenames in os.walk(directory):
217 _handle_blacklist(blacklist, dirnames, filenames)
218 for fname in fnmatch.filter(filenames, pattern):
219 yield join(curdir, fname)
220
221 -def unzip(archive, destdir):
222 import zipfile
223 if not exists(destdir):
224 os.mkdir(destdir)
225 zfobj = zipfile.ZipFile(archive)
226 for name in zfobj.namelist():
227 if name.endswith('/'):
228 os.mkdir(join(destdir, name))
229 else:
230 outfile = open(join(destdir, name), 'wb')
231 outfile.write(zfobj.read(name))
232 outfile.close()
233
234
236 """This is a deadlock safe version of popen2 (no stdin), that returns
237 an object with errorlevel, out and err.
238 """
239
241 cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
242 self.out, self.err = cmd.communicate()
243 self.status = os.WEXITSTATUS(cmd.returncode)
244
245 Execute = deprecated('Use subprocess.Popen instead')(Execute)
246
247
248 -def acquire_lock(lock_file, max_try=10, delay=10, max_delay=3600):
249 """Acquire a lock represented by a file on the file system
250
251 If the process written in lock file doesn't exist anymore, we remove the
252 lock file immediately
253 If age of the lock_file is greater than max_delay, then we raise a UserWarning
254 """
255 count = abs(max_try)
256 while count:
257 try:
258 fd = os.open(lock_file, os.O_EXCL | os.O_RDWR | os.O_CREAT)
259 os.write(fd, str_to_bytes(str(os.getpid())) )
260 os.close(fd)
261 return True
262 except OSError as e:
263 if e.errno == errno.EEXIST:
264 try:
265 fd = open(lock_file, "r")
266 pid = int(fd.readline())
267 pi = ProcInfo(pid)
268 age = (time.time() - os.stat(lock_file)[stat.ST_MTIME])
269 if age / max_delay > 1 :
270 raise UserWarning("Command '%s' (pid %s) has locked the "
271 "file '%s' for %s minutes"
272 % (pi.name(), pid, lock_file, age/60))
273 except UserWarning:
274 raise
275 except NoSuchProcess:
276 os.remove(lock_file)
277 except Exception:
278
279
280
281
282 pass
283 else:
284 raise
285 count -= 1
286 time.sleep(delay)
287 else:
288 raise Exception('Unable to acquire %s' % lock_file)
289
291 """Release a lock represented by a file on the file system."""
292 os.remove(lock_file)
293
294
296 """A simple text progression bar."""
297
298 - def __init__(self, nbops, size=20, stream=sys.stdout, title=''):
299 if title:
300 self._fstr = '\r%s [%%-%ss]' % (title, int(size))
301 else:
302 self._fstr = '\r[%%-%ss]' % int(size)
303 self._stream = stream
304 self._total = nbops
305 self._size = size
306 self._current = 0
307 self._progress = 0
308 self._current_text = None
309 self._last_text_write_size = 0
310
311 - def _get_text(self):
312 return self._current_text
313
314 - def _set_text(self, text=None):
315 if text != self._current_text:
316 self._current_text = text
317 self.refresh()
318
319 - def _del_text(self):
321
322 text = property(_get_text, _set_text, _del_text)
323
324 - def update(self, offset=1, exact=False):
325 """Move FORWARD to new cursor position (cursor will never go backward).
326
327 :offset: fraction of ``size``
328
329 :exact:
330
331 - False: offset relative to current cursor position if True
332 - True: offset as an asbsolute position
333
334 """
335 if exact:
336 self._current = offset
337 else:
338 self._current += offset
339
340 progress = int((float(self._current)/float(self._total))*self._size)
341 if progress > self._progress:
342 self._progress = progress
343 self.refresh()
344
346 """Refresh the progression bar display."""
347 self._stream.write(self._fstr % ('=' * min(self._progress, self._size)) )
348 if self._last_text_write_size or self._current_text:
349 template = ' %%-%is' % (self._last_text_write_size)
350 text = self._current_text
351 if text is None:
352 text = ''
353 self._stream.write(template % text)
354 self._last_text_write_size = len(text.rstrip())
355 self._stream.flush()
356
358 self._stream.write('\n')
359 self._stream.flush()
360
361
371
372
373 _MARKER = object()
375
377 self.nbops = nbops
378 self.size = size
379 self.stream = stream
380 self.title = title
381 self.enabled = enabled
382
384 if self.enabled:
385 kwargs = {}
386 for attr in ('nbops', 'size', 'stream', 'title'):
387 value = getattr(self, attr)
388 if value is not _MARKER:
389 kwargs[attr] = value
390 self.pb = ProgressBar(**kwargs)
391 else:
392 self.pb = DummyProgressBar()
393 return self.pb
394
395 - def __exit__(self, exc_type, exc_val, exc_tb):
397
442
443 ASK = RawInput()
444
445
447 """avoid using os.getlogin() because of strange tty / stdin problems
448 (man 3 getlogin)
449 Another solution would be to use $LOGNAME, $USER or $USERNAME
450 """
451 if sys.platform != 'win32':
452 import pwd
453 return pwd.getpwuid(os.getuid())[0]
454 else:
455 return os.environ['USERNAME']
456
458 """dumb password generation function"""
459 pwd = ''
460 for i in range(length):
461 pwd += random.choice(vocab)
462 return pwd
463