Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-unidiff for openSUSE:Factory 
checked in at 2021-08-23 10:07:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-unidiff (Old)
 and      /work/SRC/openSUSE:Factory/.python-unidiff.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-unidiff"

Mon Aug 23 10:07:50 2021 rev:7 rq:912667 version:0.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-unidiff/python-unidiff.changes    
2020-06-10 00:52:07.991527895 +0200
+++ /work/SRC/openSUSE:Factory/.python-unidiff.new.1899/python-unidiff.changes  
2021-08-23 10:08:30.420222809 +0200
@@ -1,0 +2,9 @@
+Sun Aug 15 19:15:09 UTC 2021 - Martin Li??ka <mli...@suse.cz>
+
+- Update to version 0.7.0
+  * Fixed issues handling multiple git renames.
+  * Renamed files return target filename as PatchedFile.path.
+  * Fixed error when first change is a binary file.
+  * Added source code type hints.
+
+-------------------------------------------------------------------

Old:
----
  v0.6.0.tar.gz

New:
----
  v0.7.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-unidiff.spec ++++++
--- /var/tmp/diff_new_pack.BOwgdd/_old  2021-08-23 10:08:31.560221479 +0200
+++ /var/tmp/diff_new_pack.BOwgdd/_new  2021-08-23 10:08:31.564221474 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-unidiff
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-unidiff
-Version:        0.6.0
+Version:        0.7.0
 Release:        0
 Summary:        Unified diff parsing/metadata extraction library
 License:        MIT
@@ -29,7 +29,7 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires(post): update-alternatives
-Requires(postun): update-alternatives
+Requires(postun):update-alternatives
 BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module pytest}

++++++ v0.6.0.tar.gz -> v0.7.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/.travis.yml 
new/python-unidiff-0.7.0/.travis.yml
--- old/python-unidiff-0.6.0/.travis.yml        2020-05-08 00:16:37.000000000 
+0200
+++ new/python-unidiff-0.7.0/.travis.yml        2021-08-15 20:46:23.000000000 
+0200
@@ -1,9 +1,8 @@
 language: python
 python:
   - "2.7"
-  - "3.4"
-  - "3.5"
   - "3.6"
   - "3.7"
   - "3.8"
+  - "3.9"
 script: ./run_tests.sh
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/AUTHORS 
new/python-unidiff-0.7.0/AUTHORS
--- old/python-unidiff-0.6.0/AUTHORS    2020-05-08 00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/AUTHORS    2021-08-15 20:46:23.000000000 +0200
@@ -26,3 +26,4 @@
   * Povilas Kanapickas (`@p12tic`_)
   * Snowhite (`@CirQ`_)
   * earonesty (`@earonesty`_)
+  * Ben Carlsson (`@glacials`_)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/HISTORY 
new/python-unidiff-0.7.0/HISTORY
--- old/python-unidiff-0.6.0/HISTORY    2020-05-08 00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/HISTORY    2021-08-15 20:46:23.000000000 +0200
@@ -1,6 +1,14 @@
 History
 -------
 
+0.7.0 - 2021-08-16
+------------------
+
+* Fixed issues handling multiple git renames.
+* Renamed files return target filename as PatchedFile.path.
+* Fixed error when first change is a binary file.
+* Added source code type hints.
+
 0.6.0 - 2020-05-07
 ----------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/README.rst 
new/python-unidiff-0.7.0/README.rst
--- old/python-unidiff-0.6.0/README.rst 2020-05-08 00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/README.rst 2021-08-15 20:46:23.000000000 +0200
@@ -3,8 +3,8 @@
 
 Simple Python library to parse and interact with unified diff data.
 
-.. image:: https://travis-ci.org/matiasb/python-unidiff.svg?branch=master
-    :target: https://travis-ci.org/matiasb/python-unidiff
+.. image:: https://www.travis-ci.com/matiasb/python-unidiff.svg?branch=master
+    :target: https://travis-ci.com/matiasb/python-unidiff
 
 Installing unidiff
 ------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/setup.py 
new/python-unidiff-0.7.0/setup.py
--- old/python-unidiff-0.6.0/setup.py   2020-05-08 00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/setup.py   2021-08-15 20:46:23.000000000 +0200
@@ -48,7 +48,6 @@
         "Programming Language :: Python :: 2",
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/tests/samples/git_rename.diff 
new/python-unidiff-0.7.0/tests/samples/git_rename.diff
--- old/python-unidiff-0.6.0/tests/samples/git_rename.diff      2020-05-08 
00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/tests/samples/git_rename.diff      2021-08-15 
20:46:23.000000000 +0200
@@ -11,3 +11,22 @@
  Some content
 -Some content
 +Some modified content
+
+diff --git a/oldfile b/newfile
+similarity index 85%
+rename from oldfile
+rename to newfile
+index a071991..4dbab21 100644
+--- a/oldfile
++++ b/newfile
+@@ -9,4 +9,4 @@ Some content
+ Some content
+ Some content
+ Some content
+-Some content
++Some modified content
+
+diff --git a/sub/onefile b/sub/otherfile
+similarity index 100%
+rename from onefile
+rename to otherfile
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/tests/test_parser.py 
new/python-unidiff-0.7.0/tests/test_parser.py
--- old/python-unidiff-0.6.0/tests/test_parser.py       2020-05-08 
00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/tests/test_parser.py       2021-08-15 
20:46:23.000000000 +0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 # The MIT License (MIT)
-# Copyright (c) 2014-2020 Matias Bordese
+# Copyright (c) 2014-2021 Matias Bordese
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -246,7 +246,7 @@
         self.assertTrue(res[0].is_added_file)
         self.assertTrue(res[0].is_binary_file)
 
-        # second file is added
+        # second file is modified
         self.assertTrue(res[1].is_modified_file)
         self.assertFalse(res[1].is_removed_file)
         self.assertFalse(res[1].is_added_file)
@@ -411,16 +411,27 @@
         with codecs.open(file_path, 'r', encoding='utf-8') as diff_file:
             res = PatchSet(diff_file)
 
-        self.assertEqual(len(res), 1)
-
-        patch = res[0]
-        self.assertTrue(patch.is_rename)
-        self.assertEqual(patch.added, 1)
-        self.assertEqual(patch.removed, 1)
-        self.assertEqual(len(res.modified_files), 1)
+        self.assertEqual(len(res), 3)
+        self.assertEqual(len(res.modified_files), 3)
         self.assertEqual(len(res.added_files), 0)
         self.assertEqual(len(res.removed_files), 0)
 
+        # renamed and modified files
+        for patch in res[:2]:
+            self.assertTrue(patch.is_rename)
+            self.assertEqual(patch.added, 1)
+            self.assertEqual(patch.removed, 1)
+        # renamed file under sub-path
+        patch = res[2]
+        self.assertTrue(patch.is_rename)
+        self.assertEqual(patch.added, 0)
+        self.assertEqual(patch.removed, 0)
+        # confirm the full path is in source/target filenames
+        self.assertEqual(patch.source_file, 'a/sub/onefile')
+        self.assertEqual(patch.target_file, 'b/sub/otherfile')
+        # check path is the target path
+        self.assertEqual(patch.path, 'sub/otherfile')
+
         # check that original diffs and those produced
         # by unidiff are the same
         with codecs.open(file_path, 'r', encoding='utf-8') as diff_file:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/unidiff/__version__.py 
new/python-unidiff-0.7.0/unidiff/__version__.py
--- old/python-unidiff-0.6.0/unidiff/__version__.py     2020-05-08 
00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/unidiff/__version__.py     2021-08-15 
20:46:23.000000000 +0200
@@ -21,4 +21,4 @@
 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 # OR OTHER DEALINGS IN THE SOFTWARE.
 
-__version__ = '0.6.0'
+__version__ = '0.7.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/unidiff/constants.py 
new/python-unidiff-0.7.0/unidiff/constants.py
--- old/python-unidiff-0.6.0/unidiff/constants.py       2020-05-08 
00:16:37.000000000 +0200
+++ new/python-unidiff-0.7.0/unidiff/constants.py       2021-08-15 
20:46:23.000000000 +0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 # The MIT License (MIT)
-# Copyright (c) 2014-2020 Matias Bordese
+# Copyright (c) 2014-2021 Matias Bordese
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -35,9 +35,9 @@
     r'^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?')
 
 
-# git renamed files support
-RE_RENAME_SOURCE_FILENAME = re.compile(r'^rename from (?P<filename>[^\t\n]+)')
-RE_RENAME_TARGET_FILENAME = re.compile(r'^rename to (?P<filename>[^\t\n]+)')
+# check diff git line for git renamed files support
+RE_DIFF_GIT_HEADER = re.compile(
+    r'^diff --git (?P<source>a/[^\t\n]+) (?P<target>b/[^\t\n]+)')
 
 
 # @@ (source offset, length) (target offset, length) @@ (section header)
@@ -63,6 +63,7 @@
 
 DEFAULT_ENCODING = 'UTF-8'
 
+DEV_NULL = '/dev/null'
 LINE_TYPE_ADDED = '+'
 LINE_TYPE_REMOVED = '-'
 LINE_TYPE_CONTEXT = ' '
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-unidiff-0.6.0/unidiff/patch.py 
new/python-unidiff-0.7.0/unidiff/patch.py
--- old/python-unidiff-0.6.0/unidiff/patch.py   2020-05-08 00:16:37.000000000 
+0200
+++ new/python-unidiff-0.7.0/unidiff/patch.py   2021-08-15 20:46:23.000000000 
+0200
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 # The MIT License (MIT)
-# Copyright (c) 2014-2020 Matias Bordese
+# Copyright (c) 2014-2021 Matias Bordese
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal
@@ -31,17 +31,17 @@
 
 from unidiff.constants import (
     DEFAULT_ENCODING,
+    DEV_NULL,
     LINE_TYPE_ADDED,
     LINE_TYPE_CONTEXT,
     LINE_TYPE_EMPTY,
     LINE_TYPE_REMOVED,
     LINE_TYPE_NO_NEWLINE,
     LINE_VALUE_NO_NEWLINE,
+    RE_DIFF_GIT_HEADER,
     RE_HUNK_BODY_LINE,
     RE_HUNK_EMPTY_BODY_LINE,
     RE_HUNK_HEADER,
-    RE_RENAME_SOURCE_FILENAME,
-    RE_RENAME_TARGET_FILENAME,
     RE_SOURCE_FILENAME,
     RE_TARGET_FILENAME,
     RE_NO_NEWLINE_MARKER,
@@ -62,6 +62,7 @@
         return cls
 else:
     from io import StringIO
+    from typing import Iterable, Optional, Union
     open_file = open
     make_str = str
     implements_to_string = lambda x: x
@@ -75,6 +76,7 @@
 
     def __init__(self, value, line_type,
                  source_line_no=None, target_line_no=None, diff_line_no=None):
+        # type: (str, str, Optional[int], Optional[int], Optional[int]) -> None
         super(Line, self).__init__()
         self.source_line_no = source_line_no
         self.target_line_no = target_line_no
@@ -83,12 +85,15 @@
         self.value = value
 
     def __repr__(self):
+        # type: () -> str
         return make_str("<Line: %s%s>") % (self.line_type, self.value)
 
     def __str__(self):
+        # type: () -> str
         return "%s%s" % (self.line_type, self.value)
 
     def __eq__(self, other):
+        # type: (Line) -> bool
         return (self.source_line_no == other.source_line_no and
                 self.target_line_no == other.target_line_no and
                 self.diff_line_no == other.diff_line_no and
@@ -97,14 +102,17 @@
 
     @property
     def is_added(self):
+        # type: () -> bool
         return self.line_type == LINE_TYPE_ADDED
 
     @property
     def is_removed(self):
+        # type: () -> bool
         return self.line_type == LINE_TYPE_REMOVED
 
     @property
     def is_context(self):
+        # type: () -> bool
         return self.line_type == LINE_TYPE_CONTEXT
 
 
@@ -118,10 +126,12 @@
     """
 
     def __repr__(self):
+        # type: () -> str
         value = "<PatchInfo: %s>" % self[0].strip()
         return make_str(value)
 
     def __str__(self):
+        # type: () -> str
         return ''.join(unicode(line) for line in self)
 
 
@@ -131,6 +141,7 @@
 
     def __init__(self, src_start=0, src_len=0, tgt_start=0, tgt_len=0,
                  section_header=''):
+        # type: (int, int, int, int, str) -> None
         super(Hunk, self).__init__()
         if src_len is None:
             src_len = 1
@@ -141,10 +152,11 @@
         self.target_start = int(tgt_start)
         self.target_length = int(tgt_len)
         self.section_header = section_header
-        self._added = None
-        self._removed = None
+        self._added = None  # Optional[int]
+        self._removed = None  # Optional[int]
 
     def __repr__(self):
+        # type: () -> str
         value = "<Hunk: @@ %d,%d %d,%d @@ %s>" % (self.source_start,
                                                   self.source_length,
                                                   self.target_start,
@@ -153,6 +165,7 @@
         return make_str(value)
 
     def __str__(self):
+        # type: () -> str
         # section header is optional and thus we output it only if it's present
         head = "@@ -%d,%d +%d,%d @@%s\n" % (
             self.source_start, self.source_length,
@@ -162,6 +175,7 @@
         return head + content
 
     def append(self, line):
+        # type: (Line) -> None
         """Append the line to hunk, and keep track of source/target lines."""
         # Make sure the line is encoded correctly. This is a no-op except for
         # potentially raising a UnicodeDecodeError.
@@ -170,6 +184,7 @@
 
     @property
     def added(self):
+        # type: () -> Optional[int]
         if self._added is not None:
             return self._added
         # re-calculate each time to allow for hunk modifications
@@ -178,6 +193,7 @@
 
     @property
     def removed(self):
+        # type: () -> Optional[int]
         if self._removed is not None:
             return self._removed
         # re-calculate each time to allow for hunk modifications
@@ -185,24 +201,29 @@
         return sum(1 for line in self if line.is_removed)
 
     def is_valid(self):
+        # type: () -> bool
         """Check hunk header data matches entered lines info."""
         return (len(self.source) == self.source_length and
                 len(self.target) == self.target_length)
 
     def source_lines(self):
+        # type: () -> Iterable[Line]
         """Hunk lines from source file (generator)."""
         return (l for l in self if l.is_context or l.is_removed)
 
     @property
     def source(self):
+        # type: () -> Iterable[str]
         return [str(l) for l in self.source_lines()]
 
     def target_lines(self):
+        # type: () -> Iterable[Line]
         """Hunk lines from target file (generator)."""
         return (l for l in self if l.is_context or l.is_added)
 
     @property
     def target(self):
+        # type: () -> Iterable[str]
         return [str(l) for l in self.target_lines()]
 
 
@@ -212,6 +233,7 @@
     def __init__(self, patch_info=None, source='', target='',
                  source_timestamp=None, target_timestamp=None,
                  is_binary_file=False, is_rename=False):
+        # type: (Optional[PatchInfo], str, str, Optional[str], Optional[str], 
bool, bool) -> None
         super(PatchedFile, self).__init__()
         self.patch_info = patch_info
         self.source_file = source
@@ -222,9 +244,11 @@
         self.is_rename = is_rename
 
     def __repr__(self):
+        # type: () -> str
         return make_str("<PatchedFile: %s>") % make_str(self.path)
 
     def __str__(self):
+        # type: () -> str
         source = ''
         target = ''
         # patch info is optional
@@ -240,6 +264,7 @@
         return info + source + target + hunks
 
     def _parse_hunk(self, header, diff, encoding, metadata_only):
+        # type: (str, enumerate[str], Optional[str], bool) -> None
         """Parse hunk details."""
         header_info = RE_HUNK_HEADER.match(header)
         hunk_info = header_info.groups()
@@ -293,7 +318,7 @@
                 if line_type == LINE_TYPE_EMPTY:
                     line_type = LINE_TYPE_CONTEXT
 
-                value = valid_line.group('value')
+                value = valid_line.group('value')  # type: str
                 original_line = Line(value, line_type=line_type)
 
                 if line_type == LINE_TYPE_ADDED:
@@ -339,6 +364,7 @@
         self.append(hunk)
 
     def _add_no_newline_marker_to_last_hunk(self):
+        # type: () -> None
         if not self:
             raise UnidiffParseError(
                 'Unexpected marker:' + LINE_VALUE_NO_NEWLINE)
@@ -347,6 +373,7 @@
             Line(LINE_VALUE_NO_NEWLINE + '\n', line_type=LINE_TYPE_NO_NEWLINE))
 
     def _append_trailing_empty_line(self):
+        # type: () -> None
         if not self:
             raise UnidiffParseError('Unexpected trailing newline character')
         last_hunk = self[-1]
@@ -354,49 +381,52 @@
 
     @property
     def path(self):
+        # type: () -> str
         """Return the file path abstracted from VCS."""
-        if (self.source_file.startswith('a/') and
-                self.target_file.startswith('b/')):
-            filepath = self.source_file[2:]
-        elif (self.source_file.startswith('a/') and
-              self.target_file == '/dev/null'):
-            filepath = self.source_file[2:]
-        elif (self.target_file is not None and
-              self.target_file.startswith('b/') and
-              self.source_file == '/dev/null'):
-            filepath = self.target_file[2:]
-        else:
-            filepath = self.source_file
+        filepath = self.source_file
+        if filepath in (None, DEV_NULL) or (
+                self.is_rename and self.target_file not in (None, DEV_NULL)):
+            # if this is a rename, prefer the target filename
+            filepath = self.target_file
+
+        if filepath.startswith('a/') or filepath.startswith('b/'):
+            filepath = filepath[2:]
+
         return filepath
 
     @property
     def added(self):
+        # type: () -> int
         """Return the file total added lines."""
         return sum([hunk.added for hunk in self])
 
     @property
     def removed(self):
+        # type: () -> int
         """Return the file total removed lines."""
         return sum([hunk.removed for hunk in self])
 
     @property
     def is_added_file(self):
+        # type: () -> bool
         """Return True if this patch adds the file."""
-        if self.source_file == '/dev/null':
+        if self.source_file == DEV_NULL:
             return True
         return (len(self) == 1 and self[0].source_start == 0 and
                 self[0].source_length == 0)
 
     @property
     def is_removed_file(self):
+        # type: () -> bool
         """Return True if this patch removes the file."""
-        if self.target_file == '/dev/null':
+        if self.target_file == DEV_NULL:
             return True
         return (len(self) == 1 and self[0].target_start == 0 and
                 self[0].target_length == 0)
 
     @property
     def is_modified_file(self):
+        # type: () -> bool
         """Return True if this patch modifies the file."""
         return not (self.is_added_file or self.is_removed_file)
 
@@ -406,11 +436,12 @@
     """A list of PatchedFiles."""
 
     def __init__(self, f, encoding=None, metadata_only=False):
+        # type: (Union[StringIO, str], Optional[str], bool) -> None
         super(PatchSet, self).__init__()
 
         # convert string inputs to StringIO objects
         if isinstance(f, basestring):
-            f = self._convert_string(f, encoding)
+            f = self._convert_string(f, encoding)  # type: StringIO
 
         # make sure we pass an iterator object to parse
         data = iter(f)
@@ -421,12 +452,15 @@
         self._parse(data, encoding=encoding, metadata_only=metadata_only)
 
     def __repr__(self):
+        # type: () -> str
         return make_str('<PatchSet: %s>') % super(PatchSet, self).__repr__()
 
     def __str__(self):
+        # type: () -> str
         return ''.join(unicode(patched_file) for patched_file in self)
 
     def _parse(self, diff, encoding, metadata_only):
+        # type: (StringIO, Optional[str], bool) -> None
         current_file = None
         patch_info = None
 
@@ -435,33 +469,22 @@
             if encoding is not None:
                 line = line.decode(encoding)
 
-            # check for a git rename, source file
-            is_rename_source_filename = RE_RENAME_SOURCE_FILENAME.match(line)
-            if is_rename_source_filename:
-                # prefix with 'a/' to match expected git source format
-                source_file = (
-                    'a/' + is_rename_source_filename.group('filename'))
-                # keep line as patch_info
-                patch_info.append(line)
-                # reset current file
-                current_file = None
-                continue
-
-            # check for a git rename, target file
-            is_rename_target_filename = RE_RENAME_TARGET_FILENAME.match(line)
-            if is_rename_target_filename:
-                if current_file is not None:
-                    raise UnidiffParseError('Target without source: %s' % line)
-                # prefix with 'b/' to match expected git source format
-                target_file = (
-                    'b/' + is_rename_target_filename.group('filename'))
-                # keep line as patch_info
+            # check for a git file rename
+            is_diff_git_header = RE_DIFF_GIT_HEADER.match(line)
+            if is_diff_git_header:
+                if patch_info is None:
+                    patch_info = PatchInfo()
+                source_file = is_diff_git_header.group('source')
+                target_file = is_diff_git_header.group('target')
+                if (source_file != DEV_NULL
+                        and target_file != DEV_NULL
+                        and source_file[2:] != target_file[2:]):
+                    # this is a renamed file
+                    current_file = PatchedFile(
+                        patch_info, source_file, target_file, None, None,
+                        is_rename=True)
+                    self.append(current_file)
                 patch_info.append(line)
-                # add current file to PatchSet
-                current_file = PatchedFile(
-                    patch_info, source_file, target_file, None, None,
-                    is_rename=True)
-                self.append(current_file)
                 continue
 
             # check for source file header
@@ -495,6 +518,7 @@
             # check for hunk header
             is_hunk_header = RE_HUNK_HEADER.match(line)
             if is_hunk_header:
+                patch_info = None
                 if current_file is None:
                     raise UnidiffParseError('Unexpected hunk found: %s' % line)
                 current_file._parse_hunk(line, diff, encoding, metadata_only)
@@ -513,6 +537,11 @@
                 current_file._append_trailing_empty_line()
                 continue
 
+            # if nothing has matched above then this line is a patch info
+            if patch_info is None:
+                current_file = None
+                patch_info = PatchInfo()
+
             is_binary_diff = RE_BINARY_DIFF.match(line)
             if is_binary_diff:
                 source_file = is_binary_diff.group('source_filename')
@@ -525,14 +554,11 @@
                 current_file = None
                 continue
 
-            # if nothing has matched above then this line is a patch info
-            if patch_info is None:
-                current_file = None
-                patch_info = PatchInfo()
             patch_info.append(line)
 
     @classmethod
     def from_filename(cls, filename, encoding=DEFAULT_ENCODING, errors=None):
+        # type: (str, str, Optional[str]) -> PatchSet
         """Return a PatchSet instance given a diff filename."""
         with open_file(filename, 'r', encoding=encoding, errors=errors) as f:
             instance = cls(f)
@@ -540,6 +566,7 @@
 
     @staticmethod
     def _convert_string(data, encoding=None, errors='strict'):
+        # type: (Union[str, bytes], str, str) -> StringIO
         if encoding is not None:
             # if encoding is given, assume bytes and decode
             data = unicode(data, encoding=encoding, errors=errors)
@@ -547,30 +574,36 @@
 
     @classmethod
     def from_string(cls, data, encoding=None, errors='strict'):
+        # type: (str, str, Optional[str]) -> PatchSet
         """Return a PatchSet instance given a diff string."""
         return cls(cls._convert_string(data, encoding, errors))
 
     @property
     def added_files(self):
+        # type: () -> list[PatchedFile]
         """Return patch added files as a list."""
         return [f for f in self if f.is_added_file]
 
     @property
     def removed_files(self):
+        # type: () -> list[PatchedFile]
         """Return patch removed files as a list."""
         return [f for f in self if f.is_removed_file]
 
     @property
     def modified_files(self):
+        # type: () -> list[PatchedFile]
         """Return patch modified files as a list."""
         return [f for f in self if f.is_modified_file]
 
     @property
     def added(self):
+        # type: () -> int
         """Return the patch total added lines."""
         return sum([f.added for f in self])
 
     @property
     def removed(self):
+        # type: () -> int
         """Return the patch total removed lines."""
         return sum([f.removed for f in self])

Reply via email to