| 1 | # This program is free software; you can redistribute it and/or modify it under |
|---|
| 2 | # the terms of the GNU General Public License as published by the Free Software |
|---|
| 3 | # Foundation; either version 2 of the License, or (at your option) any later |
|---|
| 4 | # version. |
|---|
| 5 | # |
|---|
| 6 | # This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 7 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|---|
| 8 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
|---|
| 9 | # |
|---|
| 10 | # You should have received a copy of the GNU General Public License along with |
|---|
| 11 | # this program; if not, write to the Free Software Foundation, Inc., |
|---|
| 12 | # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 13 | """ |
|---|
| 14 | Some shell/term utilities, useful to write some python scripts instead of shell |
|---|
| 15 | scripts |
|---|
| 16 | |
|---|
| 17 | :author: Logilab |
|---|
| 18 | :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE) |
|---|
| 19 | :contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org |
|---|
| 20 | """ |
|---|
| 21 | __docformat__ = "restructuredtext en" |
|---|
| 22 | |
|---|
| 23 | import os |
|---|
| 24 | import glob |
|---|
| 25 | import shutil |
|---|
| 26 | import sys |
|---|
| 27 | import tempfile |
|---|
| 28 | import time |
|---|
| 29 | from os.path import exists, isdir, islink, basename, join, walk |
|---|
| 30 | |
|---|
| 31 | from logilab.common import STD_BLACKLIST |
|---|
| 32 | |
|---|
| 33 | |
|---|
| 34 | def chown(path, login=None, group=None): |
|---|
| 35 | """same as `os.chown` function but accepting user login or group name as |
|---|
| 36 | argument. If login or group is omitted, it's left unchanged. |
|---|
| 37 | |
|---|
| 38 | Note: you must own the file to chown it (or be root). Otherwise OSError is raised. |
|---|
| 39 | """ |
|---|
| 40 | if login is None: |
|---|
| 41 | uid = -1 |
|---|
| 42 | else: |
|---|
| 43 | try: |
|---|
| 44 | uid = int(login) |
|---|
| 45 | except ValueError: |
|---|
| 46 | import pwd |
|---|
| 47 | uid = pwd.getpwname(login).pw_uid |
|---|
| 48 | if group is None: |
|---|
| 49 | gid = -1 |
|---|
| 50 | else: |
|---|
| 51 | try: |
|---|
| 52 | gid = int(group) |
|---|
| 53 | except ValueError: |
|---|
| 54 | import grp |
|---|
| 55 | gid = grp.getgrname(group).gr_gid |
|---|
| 56 | os.chown(path, uid, gid) |
|---|
| 57 | |
|---|
| 58 | |
|---|
| 59 | def mv(source, destination, _action=shutil.move): |
|---|
| 60 | """a shell like mv, supporting wildcards |
|---|
| 61 | """ |
|---|
| 62 | sources = glob.glob(source) |
|---|
| 63 | if len(sources) > 1: |
|---|
| 64 | assert isdir(destination) |
|---|
| 65 | for filename in sources: |
|---|
| 66 | _action(filename, join(destination, basename(filename))) |
|---|
| 67 | else: |
|---|
| 68 | try: |
|---|
| 69 | source = sources[0] |
|---|
| 70 | except IndexError: |
|---|
| 71 | raise OSError('No file matching %s' % source) |
|---|
| 72 | if isdir(destination) and exists(destination): |
|---|
| 73 | destination = join(destination, basename(source)) |
|---|
| 74 | try: |
|---|
| 75 | _action(source, destination) |
|---|
| 76 | except OSError, ex: |
|---|
| 77 | raise OSError('Unable to move %r to %r (%s)' % ( |
|---|
| 78 | source, destination, ex)) |
|---|
| 79 | |
|---|
| 80 | def rm(*files): |
|---|
| 81 | """a shell like rm, supporting wildcards |
|---|
| 82 | """ |
|---|
| 83 | for wfile in files: |
|---|
| 84 | for filename in glob.glob(wfile): |
|---|
| 85 | if islink(filename): |
|---|
| 86 | os.remove(filename) |
|---|
| 87 | elif isdir(filename): |
|---|
| 88 | shutil.rmtree(filename) |
|---|
| 89 | else: |
|---|
| 90 | os.remove(filename) |
|---|
| 91 | |
|---|
| 92 | def cp(source, destination): |
|---|
| 93 | """a shell like cp, supporting wildcards |
|---|
| 94 | """ |
|---|
| 95 | mv(source, destination, _action=shutil.copy) |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | def find(directory, exts, exclude=False, blacklist=STD_BLACKLIST): |
|---|
| 99 | """recursivly find files ending with the given extensions from the directory |
|---|
| 100 | |
|---|
| 101 | :type directory: str |
|---|
| 102 | :param directory: |
|---|
| 103 | directory where the search should start |
|---|
| 104 | |
|---|
| 105 | :type exts: basestring or list or tuple |
|---|
| 106 | :param exts: |
|---|
| 107 | extensions or lists or extensions to search |
|---|
| 108 | |
|---|
| 109 | :type exclude: boolean |
|---|
| 110 | :param exts: |
|---|
| 111 | if this argument is True, returning files NOT ending with the given |
|---|
| 112 | extensions |
|---|
| 113 | |
|---|
| 114 | :type blacklist: list or tuple |
|---|
| 115 | :param blacklist: |
|---|
| 116 | optional list of files or directory to ignore, default to the value of |
|---|
| 117 | `logilab.common.STD_BLACKLIST` |
|---|
| 118 | |
|---|
| 119 | :rtype: list |
|---|
| 120 | :return: |
|---|
| 121 | the list of all matching files |
|---|
| 122 | """ |
|---|
| 123 | if isinstance(exts, basestring): |
|---|
| 124 | exts = (exts,) |
|---|
| 125 | if exclude: |
|---|
| 126 | def match(filename, exts): |
|---|
| 127 | for ext in exts: |
|---|
| 128 | if filename.endswith(ext): |
|---|
| 129 | return False |
|---|
| 130 | return True |
|---|
| 131 | else: |
|---|
| 132 | def match(filename, exts): |
|---|
| 133 | for ext in exts: |
|---|
| 134 | if filename.endswith(ext): |
|---|
| 135 | return True |
|---|
| 136 | return False |
|---|
| 137 | def func(files, directory, fnames): |
|---|
| 138 | """walk handler""" |
|---|
| 139 | # remove files/directories in the black list |
|---|
| 140 | for norecurs in blacklist: |
|---|
| 141 | try: |
|---|
| 142 | fnames.remove(norecurs) |
|---|
| 143 | except ValueError: |
|---|
| 144 | continue |
|---|
| 145 | for filename in fnames: |
|---|
| 146 | src = join(directory, filename) |
|---|
| 147 | if isdir(src): |
|---|
| 148 | continue |
|---|
| 149 | if match(filename, exts): |
|---|
| 150 | files.append(src) |
|---|
| 151 | files = [] |
|---|
| 152 | walk(directory, func, files) |
|---|
| 153 | return files |
|---|
| 154 | |
|---|
| 155 | |
|---|
| 156 | class Execute: |
|---|
| 157 | """This is a deadlock safe version of popen2 (no stdin), that returns |
|---|
| 158 | an object with errorlevel, out and err |
|---|
| 159 | """ |
|---|
| 160 | |
|---|
| 161 | def __init__(self, command): |
|---|
| 162 | outfile = tempfile.mktemp() |
|---|
| 163 | errfile = tempfile.mktemp() |
|---|
| 164 | self.status = os.system("( %s ) >%s 2>%s" % |
|---|
| 165 | (command, outfile, errfile)) >> 8 |
|---|
| 166 | self.out = open(outfile,"r").read() |
|---|
| 167 | self.err = open(errfile,"r").read() |
|---|
| 168 | os.remove(outfile) |
|---|
| 169 | os.remove(errfile) |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | def acquire_lock(lock_file, max_try=10, delay=10): |
|---|
| 173 | """acquire a lock represented by a file on the file system""" |
|---|
| 174 | count = 0 |
|---|
| 175 | while max_try <= 0 or count < max_try: |
|---|
| 176 | if not exists(lock_file): |
|---|
| 177 | break |
|---|
| 178 | count += 1 |
|---|
| 179 | time.sleep(delay) |
|---|
| 180 | else: |
|---|
| 181 | raise Exception('Unable to acquire %s' % lock_file) |
|---|
| 182 | stream = open(lock_file, 'w') |
|---|
| 183 | stream.write(str(os.getpid())) |
|---|
| 184 | stream.close() |
|---|
| 185 | |
|---|
| 186 | def release_lock(lock_file): |
|---|
| 187 | """release a lock represented by a file on the file system""" |
|---|
| 188 | os.remove(lock_file) |
|---|
| 189 | |
|---|
| 190 | |
|---|
| 191 | class ProgressBar(object): |
|---|
| 192 | """a simple text progression bar""" |
|---|
| 193 | |
|---|
| 194 | def __init__(self, nbops, size=20., stream=sys.stdout): |
|---|
| 195 | self._dotevery = max(nbops / size, 1) |
|---|
| 196 | self._fstr = '\r[%-20s]' |
|---|
| 197 | self._dotcount, self._dots = 1, [] |
|---|
| 198 | self._stream = stream |
|---|
| 199 | |
|---|
| 200 | def update(self): |
|---|
| 201 | """update the progression bar""" |
|---|
| 202 | self._dotcount += 1 |
|---|
| 203 | if self._dotcount >= self._dotevery: |
|---|
| 204 | self._dotcount = 1 |
|---|
| 205 | self._dots.append('.') |
|---|
| 206 | self._stream.write(self._fstr % ''.join(self._dots)) |
|---|
| 207 | self._stream.flush() |
|---|