Package logilab-common-0 :: Package 39 :: Package 0 :: Module shellutils
[frames] | no frames]

Source Code for Module logilab-common-0.39.0.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      # windows platform 
27 - class NoSuchProcess(Exception): pass
28
29 - def ProcInfo(pid):
30 raise NoSuchProcess()
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
79 -def rm(*files):
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
97 -def find(directory, exts, exclude=False, blacklist=STD_BLACKLIST):
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 # remove files/directories in the black list 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
155 -def globfind(directory, pattern, blacklist=STD_BLACKLIST):
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
197 -class Execute:
198 """This is a deadlock safe version of popen2 (no stdin), that returns 199 an object with errorlevel, out and err. 200 """ 201
202 - def __init__(self, command):
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 # The try block is not essential. can be skipped. 243 # Note: ProcInfo object is only available for linux 244 # process information are not accessible... 245 # or lock_file is no more present... 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
254 -def release_lock(lock_file):
255 """Release a lock represented by a file on the file system.""" 256 os.remove(lock_file)
257 258
259 -class ProgressBar(object):
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
270 - def update(self):
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
278 - def refresh(self):
279 """Refresh the progression bar display.""" 280 self._stream.write(self._fstr % ('.' * min(self._progress, self._size)) ) 281 self._stream.flush()
282