Package logilab-common-0 ::
Package 39 ::
Package 0 ::
Module shellutils
|
|
1 """shell/term utilities, useful to write some python scripts instead of shell
2 scripts.
3
4 :author: Logilab
5 :copyright: 2000-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
7 :license: General Public License version 2 - http://www.gnu.org/licenses
8 """
9 __docformat__ = "restructuredtext en"
10
11 import os
12 import glob
13 import shutil
14 import stat
15 import sys
16 import tempfile
17 import time
18 import fnmatch
19 import errno
20 from os.path import exists, isdir, islink, basename, join, walk
21
22 from logilab.common import STD_BLACKLIST
23 try:
24 from logilab.common.proc import ProcInfo, NoSuchProcess
25 except ImportError:
26
28
31
32
33 -def chown(path, login=None, group=None):
34 """Same as `os.chown` function but accepting user login or group name as
35 argument. If login or group is omitted, it's left unchanged.
36
37 Note: you must own the file to chown it (or be root). Otherwise OSError is raised.
38 """
39 if login is None:
40 uid = -1
41 else:
42 try:
43 uid = int(login)
44 except ValueError:
45 import pwd
46 uid = pwd.getpwnam(login).pw_uid
47 if group is None:
48 gid = -1
49 else:
50 try:
51 gid = int(group)
52 except ValueError:
53 import grp
54 gid = grp.getgrname(group).gr_gid
55 os.chown(path, uid, gid)
56
57
58 -def mv(source, destination, _action=shutil.move):
59 """A shell-like mv, supporting wildcards.
60 """
61 sources = glob.glob(source)
62 if len(sources) > 1:
63 assert isdir(destination)
64 for filename in sources:
65 _action(filename, join(destination, basename(filename)))
66 else:
67 try:
68 source = sources[0]
69 except IndexError:
70 raise OSError('No file matching %s' % source)
71 if isdir(destination) and exists(destination):
72 destination = join(destination, basename(source))
73 try:
74 _action(source, destination)
75 except OSError, ex:
76 raise OSError('Unable to move %r to %r (%s)' % (
77 source, destination, ex))
78
80 """A shell-like rm, supporting wildcards.
81 """
82 for wfile in files:
83 for filename in glob.glob(wfile):
84 if islink(filename):
85 os.remove(filename)
86 elif isdir(filename):
87 shutil.rmtree(filename)
88 else:
89 os.remove(filename)
90
91 -def cp(source, destination):
92 """A shell-like cp, supporting wildcards.
93 """
94 mv(source, destination, _action=shutil.copy)
95
96
98 """Recursivly find files ending with the given extensions from the directory.
99
100 :type directory: str
101 :param directory:
102 directory where the search should start
103
104 :type exts: basestring or list or tuple
105 :param exts:
106 extensions or lists or extensions to search
107
108 :type exclude: boolean
109 :param exts:
110 if this argument is True, returning files NOT ending with the given
111 extensions
112
113 :type blacklist: list or tuple
114 :param blacklist:
115 optional list of files or directory to ignore, default to the value of
116 `logilab.common.STD_BLACKLIST`
117
118 :rtype: list
119 :return:
120 the list of all matching files
121 """
122 if isinstance(exts, basestring):
123 exts = (exts,)
124 if exclude:
125 def match(filename, exts):
126 for ext in exts:
127 if filename.endswith(ext):
128 return False
129 return True
130 else:
131 def match(filename, exts):
132 for ext in exts:
133 if filename.endswith(ext):
134 return True
135 return False
136 def func(files, directory, fnames):
137 """walk handler"""
138
139 for norecurs in blacklist:
140 try:
141 fnames.remove(norecurs)
142 except ValueError:
143 continue
144 for filename in fnames:
145 src = join(directory, filename)
146 if isdir(src):
147 continue
148 if match(filename, exts):
149 files.append(src)
150 files = []
151 walk(directory, func, files)
152 return files
153
154
156 """Recursively finds files matching glob `pattern` under `directory`.
157
158 This is an alternative to `logilab.common.shellutils.find`.
159
160 :type directory: str
161 :param directory:
162 directory where the search should start
163
164 :type pattern: basestring
165 :param pattern:
166 the glob pattern (e.g *.py, foo*.py, etc.)
167
168 :type blacklist: list or tuple
169 :param blacklist:
170 optional list of files or directory to ignore, default to the value of
171 `logilab.common.STD_BLACKLIST`
172
173 :rtype: iterator
174 :return:
175 iterator over the list of all matching files
176 """
177 for curdir, dirnames, filenames in os.walk(directory):
178 for fname in fnmatch.filter(filenames, pattern):
179 yield join(curdir, fname)
180 for skipped in blacklist:
181 if skipped in dirnames:
182 dirnames.remove(skipped)
183
184 -def unzip(archive, destdir):
185 import zipfile
186 if not exists(destdir):
187 os.mkdir(destdir)
188 zfobj = zipfile.ZipFile(archive)
189 for name in zfobj.namelist():
190 if name.endswith('/'):
191 os.mkdir(join(destdir, name))
192 else:
193 outfile = open(join(destdir, name), 'wb')
194 outfile.write(zfobj.read(name))
195 outfile.close()
196
198 """This is a deadlock safe version of popen2 (no stdin), that returns
199 an object with errorlevel, out and err.
200 """
201
203 outfile = tempfile.mktemp()
204 errfile = tempfile.mktemp()
205 self.status = os.system("( %s ) >%s 2>%s" %
206 (command, outfile, errfile)) >> 8
207 self.out = open(outfile,"r").read()
208 self.err = open(errfile,"r").read()
209 os.remove(outfile)
210 os.remove(errfile)
211
212 -def acquire_lock(lock_file, max_try=10, delay=10, max_delay=3600):
213 """Acquire a lock represented by a file on the file system
214
215 If the process written in lock file doesn't exist anymore, we remove the
216 lock file immediately
217 If age of the lock_file is greater than max_delay, then we raise a UserWarning
218 """
219 count = abs(max_try)
220 while count:
221 try:
222 fd = os.open(lock_file, os.O_EXCL | os.O_RDWR | os.O_CREAT)
223 os.write(fd, str(os.getpid()))
224 os.close(fd)
225 return True
226 except OSError, e:
227 if e.errno == errno.EEXIST:
228 try:
229 fd = open(lock_file, "r")
230 pid = int(fd.readline())
231 pi = ProcInfo(pid)
232 age = (time.time() - os.stat(lock_file)[stat.ST_MTIME])
233 if age / max_delay > 1 :
234 raise UserWarning("Command '%s' (pid %s) has locked the "
235 "file '%s' for %s minutes"
236 % (pi.name(), pid, lock_file, age/60))
237 except UserWarning:
238 raise
239 except NoSuchProcess:
240 os.remove(lock_file)
241 except Exception:
242
243
244
245
246 pass
247 else:
248 raise
249 count -= 1
250 time.sleep(delay)
251 else:
252 raise Exception('Unable to acquire %s' % lock_file)
253
255 """Release a lock represented by a file on the file system."""
256 os.remove(lock_file)
257
258
260 """A simple text progression bar."""
261
262 - def __init__(self, nbops, size=20, stream=sys.stdout):
263 self._fstr = '\r[%%-%ss]' % int(size)
264 self._stream = stream
265 self._total = nbops
266 self._size = size
267 self._current = 0
268 self._progress = 0
269
271 """Update the progression bar."""
272 self._current += 1
273 progress = int((float(self._current)/float(self._total))*self._size)
274 if progress > self._progress:
275 self._progress = progress
276 self.refresh()
277
279 """Refresh the progression bar display."""
280 self._stream.write(self._fstr % ('.' * min(self._progress, self._size)) )
281 self._stream.flush()
282