import sys import os import errno from glob import glob import tarfile from subprocess import Popen, PIPE from shutil import copyfileobj class Diffstat: def __init__(self, patch, ghost=False, patch_fh=None): self.patch = patch self.ghost = ghost if not ghost: i,o = os.popen2("diffstat -p1") i.write(str(patch)) i.close() self.output = o.readlines() else: popen = Popen("diffstat -p1 -t", shell=True, close_fds=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) popen.stderr.close() copyfileobj(patch_fh, popen.stdin) popen.stdin.close() popen.stdout.readline() self._stats = map(lambda x: map(lambda y: y.strip(), x.split(',', 3)), popen.stdout.readlines()) popen.wait() def stats(self): if not self.ghost: i,o = os.popen2("diffstat -p1 -t") i.write(str(self.patch)) i.close() o.readline() return [map(lambda x: x.strip(), l.split(",", 3)) for l in o.readlines()] else: return self._stats def lines(self): added, removed, modified = 0, 0, 0 for [added2, removed2, modified2, name] in self.stats(): added, removed, modified = added + int(added2), removed + int(removed2), modified + int(modified2) return (added, removed, modified) def summary(self): return self.output[-1] def __str__(self): return "".join(self.output) class Patch: def __init__(self, fh, level=1, ghost=False): self.ghost = ghost if not ghost: self.p = fh.readlines() else: self.p = [] self._diffstat = Diffstat(self, ghost=True, patch_fh=fh) self.lvl = level def __str__(self): return "".join(self.p) def lines(self): return len(self.p) def diffstat(self): if not self.ghost: return Diffstat(self) else: return self._diffstat class GenericPatchSeries (list): def blank(self): self.names = [] self.style = "unknown" self.patches = {} self.patchargs = {} # WTF am i doing this for, again? def iterpatches(self): for p in self.names: yield (p, self.patches[p]) def __iter__(self): return self.iterpatches() def __getitem__(self, y): return self.patches[self.names[y]] def __len__(self): return len(self.names) def fetch(self, name): return self.patches[name] def __str__(self): return "\n".join(self.names) # XXX this entire __init__ stuff is way to ugly class PatchSeries (GenericPatchSeries): def __init__(self, dir, ghost=False): self.ghost = ghost fd = None self.blank() self.style = "simple" try: fd = file(os.sep.join([dir, "00list"])) self.style = "dpatch" except IOError, e: if e.errno != errno.ENOENT: #print "ERROR: unable to open dpatch list..." self.blank() return try: fd = file(os.sep.join([dir, "series"])) self.style = "quilt" except IOError, e: if e.errno != errno.ENOENT: #print "ERROR: series file is a directory..." self.blank() return if fd: # remove blank lines for line in filter(None, [n.strip() for n in fd.readlines()]): stuff = line.split(' ') # skip comments if stuff[0][0] == "#": continue # here's the name name = stuff[0] #print "\t\t\t%s: %s"%(self.style,name) self.names.append(name) # anything else are either patch args or comments self.patchargs[name] = [] for rest in filter(None, stuff[1:]): if rest[0][0] == "#": break else: self.patchargs[name].append(rest) else: self.names = os.listdir(dir) self.names.sort() for n in self.names: self.patchargs[n] = [] # XXX this code is too ugly removelater=[] for p in self.names: try: self.patches[p] = Patch(file(os.sep.join([dir, p])), ghost=ghost) except IOError, e: if e.errno == errno.ENOENT and self.style == "dpatch": try: self.patches[p] = Patch(file(os.sep.join([dir, p+".dpatch"])), ghost=ghost) except: #print "ERROR: could not find patch",p self.blank() return elif e.errno == errno.EISDIR: #print "WARNING: directory %s in patch dir, patch list incomplete"%(p) removelater.append(p) else: #print "ERROR: could not find patch",p self.blank() return for p in removelater: self.names.remove(p) class Quilt30PatchSeries (GenericPatchSeries): def __init__(self, tarBall, ghost=False): self.blank() self.ghost = ghost self.style = "quilt (3.0)" self.tarfh = tarfile.open(tarBall, 'r:*') try: try: series_fh = self.tarfh.extractfile("debian/patches/debian.series") except KeyError: series_fh = self.tarfh.extractfile("debian/patches/series") except KeyError: series_fh = None if series_fh: for line in filter(None, [fn.strip() for fn in series_fh.readlines()]): stuff = line.split(' ') # skip comments if stuff[0][0] == "#": continue # here's the name name = stuff[0] self.names.append(name) # XXX to lazy eval this might be better for name in self.names: self.patches[name] = Patch(self.tarfh.extractfile("debian/patches/"+name), ghost=ghost) if __name__ == "__main__": print "Patch.py testing" try: p = Patch(file(sys.argv[1])) print "patch contents:" print p except IndexError: print "usage: %s "