def read_patch(filename, fp=None): """Iterates over all patches contained in a file, and returns PatchObject objects.""" header = {} with _PatchReader(filename, fp) as fp: while True: line = fp.peek() if line is None: break elif line.startswith("From: "): header['author'], header['email'] = _parse_author(line[6:]) header.pop('signedoffby', None) fp.read() elif line.startswith("Subject: "): subject = line[9:].rstrip("\r\n") fp.read() while True: line = fp.peek() if not line.startswith(" "): break subject += line.rstrip("\r\n") fp.read() subject, revision = _parse_subject(subject) if not subject.endswith("."): subject += "." subject = re.sub('^([^:]*: *)([a-z])', lambda x: "%s%s" % (x.group(1), x.group(2).upper()), subject, 1) header['subject'], header['revision'] = subject, revision header.pop('signedoffby', None) elif line.startswith("Signed-off-by: "): if 'signedoffby' not in header: header['signedoffby'] = [] header['signedoffby'].append(_parse_author(line[15:])) fp.read() elif line.startswith("diff --git "): tmp = line.strip().split(" ") if len(tmp) != 4: raise PatchParserError("Unable to parse git diff header line '%s'." % line) yield _read_single_patch(fp, header, tmp[2].strip(), tmp[3].strip()) elif line.startswith("--- "): yield _read_single_patch(fp, header) elif line.startswith("@@ -") or line.startswith("+++ "): raise PatchParserError("Patch didn't start with a git or diff header.") else: fp.read()