]> Git Repo - J-u-boot.git/blobdiff - tools/patman/patchstream.py
patman: Add a test for PatchStream tags
[J-u-boot.git] / tools / patman / patchstream.py
index 6d3c41f49ec0c6f617ec7f98cb2d5c65e20091dc..d6f6ae9251303778f33e8a50cdc3205455541dab 100644 (file)
@@ -1,49 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0+
 # Copyright (c) 2011 The Chromium OS Authors.
 #
-# SPDX-License-Identifier:     GPL-2.0+
-#
 
+"""Handles parsing a stream of commits/emails from 'git log' or other source"""
+
+import datetime
+import io
 import math
 import os
 import re
 import shutil
 import tempfile
 
-import command
-import commit
-import gitutil
-from series import Series
+from patman import command
+from patman import commit
+from patman import gitutil
+from patman.series import Series
 
 # Tags that we detect and remove
-re_remove = re.compile('^BUG=|^TEST=|^BRANCH=|^Change-Id:|^Review URL:'
-    '|Reviewed-on:|Commit-\w*:')
+RE_REMOVE = re.compile(r'^BUG=|^TEST=|^BRANCH=|^Review URL:'
+                       r'|Reviewed-on:|Commit-\w*:')
 
 # Lines which are allowed after a TEST= line
-re_allowed_after_test = re.compile('^Signed-off-by:')
+RE_ALLOWED_AFTER_TEST = re.compile('^Signed-off-by:')
 
 # Signoffs
-re_signoff = re.compile('^Signed-off-by: *(.*)')
-
-# The start of the cover letter
-re_cover = re.compile('^Cover-letter:')
+RE_SIGNOFF = re.compile('^Signed-off-by: *(.*)')
 
-# A cover letter Cc
-re_cover_cc = re.compile('^Cover-letter-cc: *(.*)')
+# Cover letter tag
+RE_COVER = re.compile('^Cover-([a-z-]*): *(.*)')
 
 # Patch series tag
-re_series_tag = re.compile('^Series-([a-z-]*): *(.*)')
+RE_SERIES_TAG = re.compile('^Series-([a-z-]*): *(.*)')
+
+# Change-Id will be used to generate the Message-Id and then be stripped
+RE_CHANGE_ID = re.compile('^Change-Id: *(.*)')
 
 # Commit series tag
-re_commit_tag = re.compile('^Commit-([a-z-]*): *(.*)')
+RE_COMMIT_TAG = re.compile('^Commit-([a-z-]*): *(.*)')
 
 # Commit tags that we want to collect and keep
-re_tag = re.compile('^(Tested-by|Acked-by|Reviewed-by|Patch-cc): (.*)')
+RE_TAG = re.compile('^(Tested-by|Acked-by|Reviewed-by|Patch-cc|Fixes): (.*)')
 
 # The start of a new commit in the git log
-re_commit = re.compile('^commit ([0-9a-f]*)$')
+RE_COMMIT = re.compile('^commit ([0-9a-f]*)$')
 
 # We detect these since checkpatch doesn't always do it
-re_space_before_tab = re.compile('^[+].* \t')
+RE_SPACE_BEFORE_TAB = re.compile('^[+].* \t')
+
+# Match indented lines for changes
+RE_LEADING_WHITESPACE = re.compile(r'^\s')
 
 # States we can be in - can we use range() and still have comments?
 STATE_MSG_HEADER = 0        # Still in the message header
@@ -59,61 +65,151 @@ class PatchStream:
     unwanted tags or inject additional ones. These correspond to the two
     phases of processing.
     """
-    def __init__(self, series, name=None, is_log=False):
+    def __init__(self, series, is_log=False):
         self.skip_blank = False          # True to skip a single blank line
         self.found_test = False          # Found a TEST= line
-        self.lines_after_test = 0        # MNumber of lines found after TEST=
-        self.warn = []                   # List of warnings we have collected
+        self.lines_after_test = 0        # Number of lines found after TEST=
         self.linenum = 1                 # Output line number we are up to
         self.in_section = None           # Name of start...END section we are in
         self.notes = []                  # Series notes
         self.section = []                # The current section...END section
         self.series = series             # Info about the patch series
         self.is_log = is_log             # True if indent like git log
-        self.in_change = 0               # Non-zero if we are in a change list
+        self.in_change = None            # Name of the change list we are in
+        self.change_version = 0          # Non-zero if we are in a change list
+        self.change_lines = []           # Lines of the current change
         self.blank_count = 0             # Number of blank lines stored up
         self.state = STATE_MSG_HEADER    # What state are we in?
-        self.signoff = []                # Contents of signoff line
         self.commit = None               # Current commit
 
-    def AddToSeries(self, line, name, value):
+    @staticmethod
+    def process_text(text, is_comment=False):
+        """Process some text through this class using a default Commit/Series
+
+        Args:
+            text (str): Text to parse
+            is_comment (bool): True if this is a comment rather than a patch.
+                If True, PatchStream doesn't expect a patch subject at the
+                start, but jumps straight into the body
+
+        Returns:
+            PatchStream: object with results
+        """
+        pstrm = PatchStream(Series())
+        pstrm.commit = commit.Commit(None)
+        infd = io.StringIO(text)
+        outfd = io.StringIO()
+        if is_comment:
+            pstrm.state = STATE_PATCH_HEADER
+        pstrm.process_stream(infd, outfd)
+        return pstrm
+
+    def _add_warn(self, warn):
+        """Add a new warning to report to the user about the current commit
+
+        The new warning is added to the current commit if not already present.
+
+        Args:
+            warn (str): Warning to report
+
+        Raises:
+            ValueError: Warning is generated with no commit associated
+        """
+        if not self.commit:
+            raise ValueError('Warning outside commit: %s' % warn)
+        if warn not in self.commit.warn:
+            self.commit.warn.append(warn)
+
+    def _add_to_series(self, line, name, value):
         """Add a new Series-xxx tag.
 
         When a Series-xxx tag is detected, we come here to record it, if we
         are scanning a 'git log'.
 
         Args:
-            line: Source line containing tag (useful for debug/error messages)
-            name: Tag name (part after 'Series-')
-            value: Tag value (part after 'Series-xxx: ')
+            line (str): Source line containing tag (useful for debug/error
+                messages)
+            name (str): Tag name (part after 'Series-')
+            value (str): Tag value (part after 'Series-xxx: ')
         """
         if name == 'notes':
             self.in_section = name
             self.skip_blank = False
         if self.is_log:
-            self.series.AddTag(self.commit, line, name, value)
+            warn = self.series.AddTag(self.commit, line, name, value)
+            if warn:
+                self.commit.warn.append(warn)
 
-    def AddToCommit(self, line, name, value):
+    def _add_to_commit(self, name):
         """Add a new Commit-xxx tag.
 
         When a Commit-xxx tag is detected, we come here to record it.
 
         Args:
-            line: Source line containing tag (useful for debug/error messages)
-            name: Tag name (part after 'Commit-')
-            value: Tag value (part after 'Commit-xxx: ')
+            name (str): Tag name (part after 'Commit-')
         """
         if name == 'notes':
             self.in_section = 'commit-' + name
             self.skip_blank = False
 
-    def CloseCommit(self):
+    def _add_commit_rtag(self, rtag_type, who):
+        """Add a response tag to the current commit
+
+        Args:
+            rtag_type (str): rtag type (e.g. 'Reviewed-by')
+            who (str): Person who gave that rtag, e.g.
+                 'Fred Bloggs <[email protected]>'
+        """
+        self.commit.AddRtag(rtag_type, who)
+
+    def _close_commit(self):
         """Save the current commit into our commit list, and reset our state"""
         if self.commit and self.is_log:
             self.series.AddCommit(self.commit)
             self.commit = None
+        # If 'END' is missing in a 'Cover-letter' section, and that section
+        # happens to show up at the very end of the commit message, this is
+        # the chance for us to fix it up.
+        if self.in_section == 'cover' and self.is_log:
+            self.series.cover = self.section
+            self.in_section = None
+            self.skip_blank = True
+            self.section = []
+
+    def _parse_version(self, value, line):
+        """Parse a version from a *-changes tag
+
+        Args:
+            value (str): Tag value (part after 'xxx-changes: '
+            line (str): Source line containing tag
 
-    def ProcessLine(self, line):
+        Returns:
+            int: The version as an integer
+
+        Raises:
+            ValueError: the value cannot be converted
+        """
+        try:
+            return int(value)
+        except ValueError:
+            raise ValueError("%s: Cannot decode version info '%s'" %
+                             (self.commit.hash, line))
+
+    def _finalise_change(self):
+        """_finalise a (multi-line) change and add it to the series or commit"""
+        if not self.change_lines:
+            return
+        change = '\n'.join(self.change_lines)
+
+        if self.in_change == 'Series':
+            self.series.AddChange(self.change_version, self.commit, change)
+        elif self.in_change == 'Cover':
+            self.series.AddChange(self.change_version, None, change)
+        elif self.in_change == 'Commit':
+            self.commit.AddChange(self.change_version, change)
+        self.change_lines = []
+
+    def process_line(self, line):
         """Process a single line of a patch file or commit log
 
         This process a line and returns a list of lines to output. The list
@@ -132,29 +228,35 @@ class PatchStream:
                 don't want, and add things we think are required.
 
         Args:
-            line: text line to process
+            line (str): text line to process
 
         Returns:
-            list of output lines, or [] if nothing should be output
+            list: list of output lines, or [] if nothing should be output
+
+        Raises:
+            ValueError: a fatal error occurred while parsing, e.g. an END
+                without a starting tag, or two commits with two change IDs
         """
         # Initially we have no output. Prepare the input line string
         out = []
         line = line.rstrip('\n')
 
-        commit_match = re_commit.match(line) if self.is_log else None
+        commit_match = RE_COMMIT.match(line) if self.is_log else None
 
         if self.is_log:
             if line[:4] == '    ':
                 line = line[4:]
 
         # Handle state transition and skipping blank lines
-        series_tag_match = re_series_tag.match(line)
-        commit_tag_match = re_commit_tag.match(line)
-        cover_cc_match = re_cover_cc.match(line)
-        signoff_match = re_signoff.match(line)
+        series_tag_match = RE_SERIES_TAG.match(line)
+        change_id_match = RE_CHANGE_ID.match(line)
+        commit_tag_match = RE_COMMIT_TAG.match(line)
+        cover_match = RE_COVER.match(line)
+        signoff_match = RE_SIGNOFF.match(line)
+        leading_whitespace_match = RE_LEADING_WHITESPACE.match(line)
         tag_match = None
         if self.state == STATE_PATCH_HEADER:
-            tag_match = re_tag.match(line)
+            tag_match = RE_TAG.match(line)
         is_blank = not line.strip()
         if is_blank:
             if (self.state == STATE_MSG_HEADER
@@ -168,6 +270,36 @@ class PatchStream:
         elif commit_match:
             self.state = STATE_MSG_HEADER
 
+        # If a tag is detected, or a new commit starts
+        if series_tag_match or commit_tag_match or change_id_match or \
+           cover_match or signoff_match or self.state == STATE_MSG_HEADER:
+            # but we are already in a section, this means 'END' is missing
+            # for that section, fix it up.
+            if self.in_section:
+                self._add_warn("Missing 'END' in section '%s'" % self.in_section)
+                if self.in_section == 'cover':
+                    self.series.cover = self.section
+                elif self.in_section == 'notes':
+                    if self.is_log:
+                        self.series.notes += self.section
+                elif self.in_section == 'commit-notes':
+                    if self.is_log:
+                        self.commit.notes += self.section
+                else:
+                    # This should not happen
+                    raise ValueError("Unknown section '%s'" % self.in_section)
+                self.in_section = None
+                self.skip_blank = True
+                self.section = []
+            # but we are already in a change list, that means a blank line
+            # is missing, fix it up.
+            if self.in_change:
+                self._add_warn("Missing 'blank line' in section '%s-changes'" %
+                               self.in_change)
+                self._finalise_change()
+                self.in_change = None
+                self.change_version = 0
+
         # If we are in a section, keep collecting lines until we see END
         if self.in_section:
             if line == 'END':
@@ -180,19 +312,24 @@ class PatchStream:
                     if self.is_log:
                         self.commit.notes += self.section
                 else:
-                    self.warn.append("Unknown section '%s'" % self.in_section)
+                    # This should not happen
+                    raise ValueError("Unknown section '%s'" % self.in_section)
                 self.in_section = None
                 self.skip_blank = True
                 self.section = []
             else:
                 self.section.append(line)
 
+        # If we are not in a section, it is an unexpected END
+        elif line == 'END':
+            raise ValueError("'END' wihout section")
+
         # Detect the commit subject
         elif not is_blank and self.state == STATE_PATCH_SUBJECT:
             self.commit.subject = line
 
         # Detect the tags we want to remove, and skip blank lines
-        elif re_remove.match(line) and not commit_tag_match:
+        elif RE_REMOVE.match(line) and not commit_tag_match:
             self.skip_blank = True
 
             # TEST= should be the last thing in the commit, so remove
@@ -202,26 +339,35 @@ class PatchStream:
         elif self.skip_blank and is_blank:
             self.skip_blank = False
 
-        # Detect the start of a cover letter section
-        elif re_cover.match(line):
-            self.in_section = 'cover'
-            self.skip_blank = False
-
-        elif cover_cc_match:
-            value = cover_cc_match.group(1)
-            self.AddToSeries(line, 'cover-cc', value)
+        # Detect Cover-xxx tags
+        elif cover_match:
+            name = cover_match.group(1)
+            value = cover_match.group(2)
+            if name == 'letter':
+                self.in_section = 'cover'
+                self.skip_blank = False
+            elif name == 'letter-cc':
+                self._add_to_series(line, 'cover-cc', value)
+            elif name == 'changes':
+                self.in_change = 'Cover'
+                self.change_version = self._parse_version(value, line)
 
         # If we are in a change list, key collected lines until a blank one
         elif self.in_change:
             if is_blank:
                 # Blank line ends this change list
-                self.in_change = 0
+                self._finalise_change()
+                self.in_change = None
+                self.change_version = 0
             elif line == '---':
-                self.in_change = 0
-                out = self.ProcessLine(line)
-            else:
-                if self.is_log:
-                    self.series.AddChange(self.in_change, self.commit, line)
+                self._finalise_change()
+                self.in_change = None
+                self.change_version = 0
+                out = self.process_line(line)
+            elif self.is_log:
+                if not leading_whitespace_match:
+                    self._finalise_change()
+                self.change_lines.append(line)
             self.skip_blank = False
 
         # Detect Series-xxx tags
@@ -230,62 +376,68 @@ class PatchStream:
             value = series_tag_match.group(2)
             if name == 'changes':
                 # value is the version number: e.g. 1, or 2
-                try:
-                    value = int(value)
-                except ValueError as str:
-                    raise ValueError("%s: Cannot decode version info '%s'" %
-                        (self.commit.hash, line))
-                self.in_change = int(value)
+                self.in_change = 'Series'
+                self.change_version = self._parse_version(value, line)
             else:
-                self.AddToSeries(line, name, value)
+                self._add_to_series(line, name, value)
                 self.skip_blank = True
 
+        # Detect Change-Id tags
+        elif change_id_match:
+            value = change_id_match.group(1)
+            if self.is_log:
+                if self.commit.change_id:
+                    raise ValueError(
+                        "%s: Two Change-Ids: '%s' vs. '%s'" % self.commit.hash,
+                        self.commit.change_id, value)
+                self.commit.change_id = value
+            self.skip_blank = True
+
         # Detect Commit-xxx tags
         elif commit_tag_match:
             name = commit_tag_match.group(1)
             value = commit_tag_match.group(2)
             if name == 'notes':
-                self.AddToCommit(line, name, value)
+                self._add_to_commit(name)
                 self.skip_blank = True
+            elif name == 'changes':
+                self.in_change = 'Commit'
+                self.change_version = self._parse_version(value, line)
+            else:
+                self._add_warn('Line %d: Ignoring Commit-%s' %
+                               (self.linenum, name))
 
         # Detect the start of a new commit
         elif commit_match:
-            self.CloseCommit()
+            self._close_commit()
             self.commit = commit.Commit(commit_match.group(1))
 
         # Detect tags in the commit message
         elif tag_match:
+            rtag_type, who = tag_match.groups()
+            self._add_commit_rtag(rtag_type, who)
             # Remove Tested-by self, since few will take much notice
-            if (tag_match.group(1) == 'Tested-by' and
-                    tag_match.group(2).find(os.getenv('USER') + '@') != -1):
-                self.warn.append("Ignoring %s" % line)
-            elif tag_match.group(1) == 'Patch-cc':
-                self.commit.AddCc(tag_match.group(2).split(','))
+            if (rtag_type == 'Tested-by' and
+                    who.find(os.getenv('USER') + '@') != -1):
+                self._add_warn("Ignoring %s" % line)
+            elif rtag_type == 'Patch-cc':
+                self.commit.AddCc(who.split(','))
             else:
                 out = [line]
 
         # Suppress duplicate signoffs
         elif signoff_match:
             if (self.is_log or not self.commit or
-                self.commit.CheckDuplicateSignoff(signoff_match.group(1))):
+                    self.commit.CheckDuplicateSignoff(signoff_match.group(1))):
                 out = [line]
 
         # Well that means this is an ordinary line
         else:
-            pos = 1
-            # Look for ugly ASCII characters
-            for ch in line:
-                # TODO: Would be nicer to report source filename and line
-                if ord(ch) > 0x80:
-                    self.warn.append("Line %d/%d ('%s') has funny ascii char" %
-                        (self.linenum, pos, line))
-                pos += 1
-
             # Look for space before tab
-            m = re_space_before_tab.match(line)
-            if m:
-                self.warn.append('Line %d/%d has space before tab' %
-                    (self.linenum, m.start()))
+            mat = RE_SPACE_BEFORE_TAB.match(line)
+            if mat:
+                self._add_warn('Line %d/%d has space before tab' %
+                               (self.linenum, mat.start()))
 
             # OK, we have a valid non-blank line
             out = [line]
@@ -299,7 +451,7 @@ class PatchStream:
             elif line == '---':
                 self.state = STATE_DIFFS
 
-                # Output the tags (signeoff first), then change list
+                # Output the tags (signoff first), then change list
                 out = []
                 log = self.series.MakeChangeLog(self.commit)
                 out += [line]
@@ -307,36 +459,80 @@ class PatchStream:
                     out += self.commit.notes
                 out += [''] + log
             elif self.found_test:
-                if not re_allowed_after_test.match(line):
+                if not RE_ALLOWED_AFTER_TEST.match(line):
                     self.lines_after_test += 1
 
         return out
 
-    def Finalize(self):
+    def finalise(self):
         """Close out processing of this patch stream"""
-        self.CloseCommit()
+        self._finalise_change()
+        self._close_commit()
         if self.lines_after_test:
-            self.warn.append('Found %d lines after TEST=' %
-                    self.lines_after_test)
+            self._add_warn('Found %d lines after TEST=' % self.lines_after_test)
+
+    def _write_message_id(self, outfd):
+        """Write the Message-Id into the output.
+
+        This is based on the Change-Id in the original patch, the version,
+        and the prefix.
+
+        Args:
+            outfd (io.IOBase): Output stream file object
+        """
+        if not self.commit.change_id:
+            return
+
+        # If the count is -1 we're testing, so use a fixed time
+        if self.commit.count == -1:
+            time_now = datetime.datetime(1999, 12, 31, 23, 59, 59)
+        else:
+            time_now = datetime.datetime.now()
+
+        # In theory there is email.utils.make_msgid() which would be nice
+        # to use, but it already produces something way too long and thus
+        # will produce ugly commit lines if someone throws this into
+        # a "Link:" tag in the final commit.  So (sigh) roll our own.
 
-    def ProcessStream(self, infd, outfd):
+        # Start with the time; presumably we wouldn't send the same series
+        # with the same Change-Id at the exact same second.
+        parts = [time_now.strftime("%Y%m%d%H%M%S")]
+
+        # These seem like they would be nice to include.
+        if 'prefix' in self.series:
+            parts.append(self.series['prefix'])
+        if 'version' in self.series:
+            parts.append("v%s" % self.series['version'])
+
+        parts.append(str(self.commit.count + 1))
+
+        # The Change-Id must be last, right before the @
+        parts.append(self.commit.change_id)
+
+        # Join parts together with "." and write it out.
+        outfd.write('Message-Id: <%s@changeid>\n' % '.'.join(parts))
+
+    def process_stream(self, infd, outfd):
         """Copy a stream from infd to outfd, filtering out unwanting things.
 
         This is used to process patch files one at a time.
 
         Args:
-            infd: Input stream file object
-            outfd: Output stream file object
+            infd (io.IOBase): Input stream file object
+            outfd (io.IOBase): Output stream file object
         """
         # Extract the filename from each diff, for nice warnings
         fname = None
         last_fname = None
         re_fname = re.compile('diff --git a/(.*) b/.*')
+
+        self._write_message_id(outfd)
+
         while True:
             line = infd.readline()
             if not line:
                 break
-            out = self.ProcessLine(line)
+            out = self.process_line(line)
 
             # Try to detect blank lines at EOF
             for line in out:
@@ -348,56 +544,78 @@ class PatchStream:
                     self.blank_count += 1
                 else:
                     if self.blank_count and (line == '-- ' or match):
-                        self.warn.append("Found possible blank line(s) at "
-                                "end of file '%s'" % last_fname)
+                        self._add_warn("Found possible blank line(s) at end of file '%s'" %
+                                       last_fname)
                     outfd.write('+\n' * self.blank_count)
                     outfd.write(line + '\n')
                     self.blank_count = 0
-        self.Finalize()
+        self.finalise()
 
 
-def GetMetaDataForList(commit_range, git_dir=None, count=None,
-                       series = None, allow_overwrite=False):
+def get_metadata_for_list(commit_range, git_dir=None, count=None,
+                          series=None, allow_overwrite=False):
     """Reads out patch series metadata from the commits
 
     This does a 'git log' on the relevant commits and pulls out the tags we
     are interested in.
 
     Args:
-        commit_range: Range of commits to count (e.g. 'HEAD..base')
-        git_dir: Path to git repositiory (None to use default)
-        count: Number of commits to list, or None for no limit
-        series: Series object to add information into. By default a new series
+        commit_range (str): Range of commits to count (e.g. 'HEAD..base')
+        git_dir (str): Path to git repositiory (None to use default)
+        count (int): Number of commits to list, or None for no limit
+        series (Series): Object to add information into. By default a new series
             is started.
-        allow_overwrite: Allow tags to overwrite an existing tag
+        allow_overwrite (bool): Allow tags to overwrite an existing tag
+
     Returns:
-        A Series object containing information about the commits.
+        Series: Object containing information about the commits.
     """
     if not series:
         series = Series()
     series.allow_overwrite = allow_overwrite
-    params = gitutil.LogCmd(commit_range,reverse=True, count=count,
+    params = gitutil.LogCmd(commit_range, reverse=True, count=count,
                             git_dir=git_dir)
     stdout = command.RunPipe([params], capture=True).stdout
-    ps = PatchStream(series, is_log=True)
+    pst = PatchStream(series, is_log=True)
     for line in stdout.splitlines():
-        ps.ProcessLine(line)
-    ps.Finalize()
+        pst.process_line(line)
+    pst.finalise()
     return series
 
-def GetMetaData(start, count):
+def get_metadata(branch, start, count):
     """Reads out patch series metadata from the commits
 
     This does a 'git log' on the relevant commits and pulls out the tags we
     are interested in.
 
     Args:
-        start: Commit to start from: 0=HEAD, 1=next one, etc.
-        count: Number of commits to list
+        branch (str): Branch to use (None for current branch)
+        start (int): Commit to start from: 0=branch HEAD, 1=next one, etc.
+        count (int): Number of commits to list
+
+    Returns:
+        Series: Object containing information about the commits.
     """
-    return GetMetaDataForList('HEAD~%d' % start, None, count)
+    return get_metadata_for_list(
+        '%s~%d' % (branch if branch else 'HEAD', start), None, count)
 
-def FixPatch(backup_dir, fname, series, commit):
+def get_metadata_for_test(text):
+    """Process metadata from a file containing a git log. Used for tests
+
+    Args:
+        text:
+
+    Returns:
+        Series: Object containing information about the commits.
+    """
+    series = Series()
+    pst = PatchStream(series, is_log=True)
+    for line in text.splitlines():
+        pst.process_line(line)
+    pst.finalise()
+    return series
+
+def fix_patch(backup_dir, fname, series, cmt):
     """Fix up a patch file, by adding/removing as required.
 
     We remove our tags from the patch file, insert changes lists, etc.
@@ -406,18 +624,20 @@ def FixPatch(backup_dir, fname, series, commit):
     A backup file is put into backup_dir (if not None).
 
     Args:
-        fname: Filename to patch file to process
-        series: Series information about this patch set
-        commit: Commit object for this patch file
+        backup_dir (str): Path to directory to use to backup the file
+        fname (str): Filename to patch file to process
+        series (Series): Series information about this patch set
+        cmt (Commit): Commit object for this patch file
+
     Return:
-        A list of errors, or [] if all ok.
+        list: A list of errors, each str, or [] if all ok.
     """
     handle, tmpname = tempfile.mkstemp()
-    outfd = os.fdopen(handle, 'w')
-    infd = open(fname, 'r')
-    ps = PatchStream(series)
-    ps.commit = commit
-    ps.ProcessStream(infd, outfd)
+    outfd = os.fdopen(handle, 'w', encoding='utf-8')
+    infd = open(fname, 'r', encoding='utf-8')
+    pst = PatchStream(series)
+    pst.commit = cmt
+    pst.process_stream(infd, outfd)
     infd.close()
     outfd.close()
 
@@ -425,46 +645,46 @@ def FixPatch(backup_dir, fname, series, commit):
     if backup_dir:
         shutil.copy(fname, os.path.join(backup_dir, os.path.basename(fname)))
     shutil.move(tmpname, fname)
-    return ps.warn
+    return cmt.warn
 
-def FixPatches(series, fnames):
+def fix_patches(series, fnames):
     """Fix up a list of patches identified by filenames
 
     The patch files are processed in place, and overwritten.
 
     Args:
-        series: The series object
-        fnames: List of patch files to process
+        series (Series): The Series object
+        fnames (:type: list of str): List of patch files to process
     """
     # Current workflow creates patches, so we shouldn't need a backup
     backup_dir = None  #tempfile.mkdtemp('clean-patch')
     count = 0
     for fname in fnames:
-        commit = series.commits[count]
-        commit.patch = fname
-        result = FixPatch(backup_dir, fname, series, commit)
+        cmt = series.commits[count]
+        cmt.patch = fname
+        cmt.count = count
+        result = fix_patch(backup_dir, fname, series, cmt)
         if result:
-            print '%d warnings for %s:' % (len(result), fname)
+            print('%d warnings for %s:' % (len(result), fname))
             for warn in result:
-                print '\t', warn
+                print('\t', warn)
             print
         count += 1
-    print 'Cleaned %d patches' % count
-    return series
+    print('Cleaned %d patches' % count)
 
-def InsertCoverLetter(fname, series, count):
+def insert_cover_letter(fname, series, count):
     """Inserts a cover letter with the required info into patch 0
 
     Args:
-        fname: Input / output filename of the cover letter file
-        series: Series object
-        count: Number of patches in the series
+        fname (str): Input / output filename of the cover letter file
+        series (Series): Series object
+        count (int): Number of patches in the series
     """
-    fd = open(fname, 'r')
-    lines = fd.readlines()
-    fd.close()
+    fil = open(fname, 'r')
+    lines = fil.readlines()
+    fil.close()
 
-    fd = open(fname, 'w')
+    fil = open(fname, 'w')
     text = series.cover
     prefix = series.GetPatchPrefix()
     for line in lines:
@@ -484,5 +704,5 @@ def InsertCoverLetter(fname, series, count):
             # Now the change list
             out = series.MakeChangeLog(None)
             line += '\n' + '\n'.join(out)
-        fd.write(line)
-    fd.close()
+        fil.write(line)
+    fil.close()
This page took 0.052274 seconds and 4 git commands to generate.