Script 'mail_helper' called by obssrc
Hello community,

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

Package is "python-dulwich"

Tue Aug 24 10:54:02 2021 rev:41 rq:913271 version:0.20.24

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-dulwich/python-dulwich.changes    
2021-06-11 22:30:34.230119442 +0200
+++ /work/SRC/openSUSE:Factory/.python-dulwich.new.1899/python-dulwich.changes  
2021-08-24 10:54:26.204355604 +0200
@@ -1,0 +2,11 @@
+Thu Aug 19 15:45:44 UTC 2021 - Bernhard Wiedemann <[email protected]>
+
+- update to 0.20.24
+  * https://github.com/dulwich/dulwich/commits/0.20.24
+  * fix build after 2023 (boo#1102840)
+  * config: disregard UTF-8 BOM when reading file
+  * Split out a AbstractHTTPGitClient class
+  * Set zip_safe=false
+  * Add separate HTTPProxyUnauthorized exception
+
+-------------------------------------------------------------------

Old:
----
  dulwich-0.20.23.tar.gz

New:
----
  dulwich-0.20.24.tar.gz

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

Other differences:
------------------
++++++ python-dulwich.spec ++++++
--- /var/tmp/diff_new_pack.XjaIlA/_old  2021-08-24 10:54:26.764354862 +0200
+++ /var/tmp/diff_new_pack.XjaIlA/_new  2021-08-24 10:54:26.768354857 +0200
@@ -20,7 +20,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-dulwich
-Version:        0.20.23
+Version:        0.20.24
 Release:        0
 Summary:        Pure-Python Git Library
 License:        Apache-2.0 OR GPL-2.0-or-later

++++++ dulwich-0.20.23.tar.gz -> dulwich-0.20.24.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/.github/workflows/pythonpackage.yml 
new/dulwich-0.20.24/.github/workflows/pythonpackage.yml
--- old/dulwich-0.20.23/.github/workflows/pythonpackage.yml     2021-05-24 
22:39:10.000000000 +0200
+++ new/dulwich-0.20.24/.github/workflows/pythonpackage.yml     2021-07-18 
14:58:17.000000000 +0200
@@ -46,7 +46,7 @@
       if: "matrix.os != 'windows-latest' && matrix.python-version != 'pypy3'"
     - name: Install mypy
       run: |
-        pip install -U mypy
+        pip install -U mypy types-paramiko types-certifi
       if: "matrix.python-version != 'pypy3'"
     - name: Style checks
       run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/NEWS new/dulwich-0.20.24/NEWS
--- old/dulwich-0.20.23/NEWS    2021-05-24 22:40:45.000000000 +0200
+++ new/dulwich-0.20.24/NEWS    2021-07-18 14:59:47.000000000 +0200
@@ -1,3 +1,16 @@
+0.20.24        2021-07-18
+
+ * config: disregard UTF-8 BOM when reading file.
+   (Dan Villiom Podlaski Christiansen)
+
+ * Skip lines with spaces only in .gitignore. (Andrey Torsunov, #878)
+
+ * Add a separate HTTPProxyUnauthorized exception for 407 errors.
+   (Jelmer Vernoo??, #822)
+
+ * Split out a AbstractHTTPGitClient class.
+   (Jelmer Vernoo??)
+
 0.20.23        2021-05-24
 
  * Fix installation of GPG during package publishing.
@@ -282,7 +295,7 @@
  BUG FIXES
 
  * Avoid ``PermissionError``, since it is Python3-specific.
-  (Jelmer Vernoo??)
+   (Jelmer Vernoo??)
 
  * Fix regression that added a dependency on C git for the
    test suite. (Jelmer Vernoo??, #720)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/PKG-INFO new/dulwich-0.20.24/PKG-INFO
--- old/dulwich-0.20.23/PKG-INFO        2021-05-24 22:40:48.656779500 +0200
+++ new/dulwich-0.20.24/PKG-INFO        2021-07-18 14:59:56.916478600 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.20.23
+Version: 0.20.24
 Summary: Python Git Library
 Home-page: https://www.dulwich.io/
 Author: Jelmer Vernooij
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/build.cmd 
new/dulwich-0.20.24/build.cmd
--- old/dulwich-0.20.23/build.cmd       2021-05-24 22:39:10.000000000 +0200
+++ new/dulwich-0.20.24/build.cmd       1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
-@echo off
-:: To build extensions for 64 bit Python 3, we need to configure environment
-:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of:
-:: MS Windows SDK for Windows 7 and .NET Framework 4
-::
-:: More details at:
-:: https://github.com/cython/cython/wiki/CythonExtensionsOnWindows
-
-IF "%DISTUTILS_USE_SDK%"=="1" (
-    ECHO Configuring environment to build with MSVC on a 64bit architecture
-    ECHO Using Windows SDK 7.1
-    "C:\Program Files\Microsoft SDKs\Windows\v7.1\Setup\WindowsSdkVer.exe" -q 
-version:v7.1
-    CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 
/release
-    SET MSSdk=1
-    REM Need the following to allow tox to see the SDK compiler
-    SET TOX_TESTENV_PASSENV=DISTUTILS_USE_SDK MSSdk INCLUDE LIB
-) ELSE (
-    ECHO Using default MSVC build environment
-)
-
-CALL %*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/__init__.py 
new/dulwich-0.20.24/dulwich/__init__.py
--- old/dulwich-0.20.23/dulwich/__init__.py     2021-05-24 22:40:46.000000000 
+0200
+++ new/dulwich-0.20.24/dulwich/__init__.py     2021-07-18 14:59:47.000000000 
+0200
@@ -22,4 +22,4 @@
 
 """Python implementation of the Git file formats and protocols."""
 
-__version__ = (0, 20, 23)
+__version__ = (0, 20, 24)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/client.py 
new/dulwich-0.20.24/dulwich/client.py
--- old/dulwich-0.20.23/dulwich/client.py       2021-05-24 22:39:10.000000000 
+0200
+++ new/dulwich-0.20.24/dulwich/client.py       2021-07-18 14:58:17.000000000 
+0200
@@ -133,6 +133,15 @@
         self.url = url
 
 
+class HTTPProxyUnauthorized(Exception):
+    """Raised when proxy authentication fails."""
+
+    def __init__(self, proxy_authenticate, url):
+        Exception.__init__(self, "No valid proxy credentials provided")
+        self.proxy_authenticate = proxy_authenticate
+        self.url = url
+
+
 def _fileno_can_read(fileno):
     """Check if a file descriptor is readable."""
     return len(select.select([fileno], [], [], 0)[0]) > 0
@@ -549,8 +558,8 @@
         Args:
           path: Remote path to fetch from
           determine_wants: Function determine what refs
-        to fetch. Receives dictionary of name->sha, should return
-        list of shas to fetch.
+            to fetch. Receives dictionary of name->sha, should return
+            list of shas to fetch.
           graph_walker: Object with next() and ack().
           pack_data: Callback called for each bit of data in the pack
           progress: Callback for progress reports (strings)
@@ -901,10 +910,10 @@
         Args:
           path: Repository path (as bytestring)
           update_refs: Function to determine changes to remote refs.
-        Receive dict with existing remote refs, returns dict with
-        changed refs (name -> sha, where sha=ZERO_SHA for deletions)
+            Receive dict with existing remote refs, returns dict with
+            changed refs (name -> sha, where sha=ZERO_SHA for deletions)
           generate_pack_data: Function that can return a tuple with
-        number of objects and pack data to upload.
+            number of objects and pack data to upload.
           progress: Optional callback called with progress updates
 
         Returns:
@@ -995,8 +1004,8 @@
         Args:
           path: Remote path to fetch from
           determine_wants: Function determine what refs
-        to fetch. Receives dictionary of name->sha, should return
-        list of shas to fetch.
+            to fetch. Receives dictionary of name->sha, should return
+            list of shas to fetch.
           graph_walker: Object with next() and ack().
           pack_data: Callback called for each bit of data in the pack
           progress: Callback for progress reports (strings)
@@ -1292,9 +1301,9 @@
         Args:
           path: Repository path (as bytestring)
           update_refs: Function to determine changes to remote refs.
-        Receive dict with existing remote refs, returns dict with
-        changed refs (name -> sha, where sha=ZERO_SHA for deletions)
-        with number of items and pack data to upload.
+            Receive dict with existing remote refs, returns dict with
+            changed refs (name -> sha, where sha=ZERO_SHA for deletions)
+            with number of items and pack data to upload.
           progress: Optional progress function
 
         Returns:
@@ -1353,8 +1362,8 @@
           path: Path to fetch from (as bytestring)
           target: Target repository to fetch into
           determine_wants: Optional function determine what refs
-        to fetch. Receives dictionary of name->sha, should return
-        list of shas to fetch. Defaults to all shas.
+            to fetch. Receives dictionary of name->sha, should return
+            list of shas to fetch. Defaults to all shas.
           progress: Optional progress function
           depth: Shallow fetch depth
 
@@ -1385,8 +1394,8 @@
         Args:
           path: Remote path to fetch from
           determine_wants: Function determine what refs
-        to fetch. Receives dictionary of name->sha, should return
-        list of shas to fetch.
+            to fetch. Receives dictionary of name->sha, should return
+            list of shas to fetch.
           graph_walker: Object with next() and ack().
           pack_data: Callback called for each bit of data in the pack
           progress: Callback for progress reports (strings)
@@ -1759,8 +1768,6 @@
     if proxy_server is not None:
         if proxy_manager_cls is None:
             proxy_manager_cls = urllib3.ProxyManager
-        # `urllib3` requires a `str` object in both Python 2 and 3, while
-        # `ConfigDict` coerces entries to `bytes` on Python 3. Compensate.
         if not isinstance(proxy_server, str):
             proxy_server = proxy_server.decode()
         manager = proxy_manager_cls(proxy_server, headers=headers, **kwargs)
@@ -1772,71 +1779,20 @@
     return manager
 
 
-class HttpGitClient(GitClient):
-    def __init__(
-        self,
-        base_url,
-        dumb=None,
-        pool_manager=None,
-        config=None,
-        username=None,
-        password=None,
-        **kwargs
-    ):
-        self._base_url = base_url.rstrip("/") + "/"
-        self._username = username
-        self._password = password
-        self.dumb = dumb
-
-        if pool_manager is None:
-            self.pool_manager = default_urllib3_manager(config)
-        else:
-            self.pool_manager = pool_manager
+class AbstractHttpGitClient(GitClient):
+    """Abstract base class for HTTP Git Clients.
 
-        if username is not None:
-            # No escaping needed: ":" is not allowed in username:
-            # https://tools.ietf.org/html/rfc2617#section-2
-            credentials = "%s:%s" % (username, password)
-            import urllib3.util
+    This is agonistic of the actual HTTP implementation.
 
-            basic_auth = urllib3.util.make_headers(basic_auth=credentials)
-            self.pool_manager.headers.update(basic_auth)
+    Subclasses should provide an implementation of the
+    _http_request method.
+    """
 
+    def __init__(self, base_url, dumb=False, **kwargs):
+        self._base_url = base_url.rstrip("/") + "/"
+        self.dumb = dumb
         GitClient.__init__(self, **kwargs)
 
-    def get_url(self, path):
-        return self._get_url(path).rstrip("/")
-
-    @classmethod
-    def from_parsedurl(cls, parsedurl, **kwargs):
-        password = parsedurl.password
-        if password is not None:
-            kwargs["password"] = urlunquote(password)
-        username = parsedurl.username
-        if username is not None:
-            kwargs["username"] = urlunquote(username)
-        netloc = parsedurl.hostname
-        if parsedurl.port:
-            netloc = "%s:%s" % (netloc, parsedurl.port)
-        if parsedurl.username:
-            netloc = "%s@%s" % (parsedurl.username, netloc)
-        parsedurl = parsedurl._replace(netloc=netloc)
-        return cls(urlunparse(parsedurl), **kwargs)
-
-    def __repr__(self):
-        return "%s(%r, dumb=%r)" % (
-            type(self).__name__,
-            self._base_url,
-            self.dumb,
-        )
-
-    def _get_url(self, path):
-        if not isinstance(path, str):
-            # urllib3.util.url._encode_invalid_chars() converts the path back
-            # to bytes using the utf-8 codec.
-            path = path.decode("utf-8")
-        return urljoin(self._base_url, path).rstrip("/") + "/"
-
     def _http_request(self, url, headers=None, data=None, 
allow_compression=False):
         """Perform HTTP request.
 
@@ -1853,48 +1809,8 @@
           method for the response data.
 
         """
-        req_headers = self.pool_manager.headers.copy()
-        if headers is not None:
-            req_headers.update(headers)
-        req_headers["Pragma"] = "no-cache"
-        if allow_compression:
-            req_headers["Accept-Encoding"] = "gzip"
-        else:
-            req_headers["Accept-Encoding"] = "identity"
-
-        if data is None:
-            resp = self.pool_manager.request("GET", url, headers=req_headers)
-        else:
-            resp = self.pool_manager.request(
-                "POST", url, headers=req_headers, body=data
-            )
 
-        if resp.status == 404:
-            raise NotGitRepository()
-        if resp.status == 401:
-            raise HTTPUnauthorized(resp.getheader("WWW-Authenticate"), url)
-        if resp.status != 200:
-            raise GitProtocolError(
-                "unexpected http resp %d for %s" % (resp.status, url)
-            )
-
-        # TODO: Optimization available by adding `preload_content=False` to the
-        # request and just passing the `read` method on instead of going via
-        # `BytesIO`, if we can guarantee that the entire response is consumed
-        # before issuing the next to still allow for connection reuse from the
-        # pool.
-        read = BytesIO(resp.data).read
-
-        resp.content_type = resp.getheader("Content-Type")
-        # Check if geturl() is available (urllib3 version >= 1.23)
-        try:
-            resp_url = resp.geturl()
-        except AttributeError:
-            # get_redirect_location() is available for urllib3 >= 1.1
-            resp.redirect_location = resp.get_redirect_location()
-        else:
-            resp.redirect_location = resp_url if resp_url != url else ""
-        return resp, read
+        raise NotImplementedError(self._http_request)
 
     def _discover_references(self, service, base_url):
         assert base_url[-1] == "/"
@@ -1934,6 +1850,11 @@
             resp.close()
 
     def _smart_request(self, service, url, data):
+        """Send a 'smart' HTTP request.
+
+        This is a simple wrapper around _http_request that sets
+        a couple of extra headers.
+        """
         assert url[-1] == "/"
         url = urljoin(url, service)
         result_content_type = "application/x-%s-result" % service
@@ -1955,10 +1876,10 @@
         Args:
           path: Repository path (as bytestring)
           update_refs: Function to determine changes to remote refs.
-        Receives dict with existing remote refs, returns dict with
-        changed refs (name -> sha, where sha=ZERO_SHA for deletions)
+            Receives dict with existing remote refs, returns dict with
+            changed refs (name -> sha, where sha=ZERO_SHA for deletions)
           generate_pack_data: Function that can return a tuple
-        with number of elements and pack data to upload.
+            with number of elements and pack data to upload.
           progress: Optional progress function
 
         Returns:
@@ -2089,6 +2010,123 @@
         refs, _, _ = self._discover_references(b"git-upload-pack", url)
         return refs
 
+    def get_url(self, path):
+        return self._get_url(path).rstrip("/")
+
+    def _get_url(self, path):
+        return urljoin(self._base_url, path).rstrip("/") + "/"
+
+    @classmethod
+    def from_parsedurl(cls, parsedurl, **kwargs):
+        password = parsedurl.password
+        if password is not None:
+            kwargs["password"] = urlunquote(password)
+        username = parsedurl.username
+        if username is not None:
+            kwargs["username"] = urlunquote(username)
+        netloc = parsedurl.hostname
+        if parsedurl.port:
+            netloc = "%s:%s" % (netloc, parsedurl.port)
+        if parsedurl.username:
+            netloc = "%s@%s" % (parsedurl.username, netloc)
+        parsedurl = parsedurl._replace(netloc=netloc)
+        return cls(urlunparse(parsedurl), **kwargs)
+
+    def __repr__(self):
+        return "%s(%r, dumb=%r)" % (
+            type(self).__name__,
+            self._base_url,
+            self.dumb,
+        )
+
+
+class Urllib3HttpGitClient(AbstractHttpGitClient):
+    def __init__(
+        self,
+        base_url,
+        dumb=None,
+        pool_manager=None,
+        config=None,
+        username=None,
+        password=None,
+        **kwargs
+    ):
+        self._username = username
+        self._password = password
+
+        if pool_manager is None:
+            self.pool_manager = default_urllib3_manager(config)
+        else:
+            self.pool_manager = pool_manager
+
+        if username is not None:
+            # No escaping needed: ":" is not allowed in username:
+            # https://tools.ietf.org/html/rfc2617#section-2
+            credentials = "%s:%s" % (username, password)
+            import urllib3.util
+
+            basic_auth = urllib3.util.make_headers(basic_auth=credentials)
+            self.pool_manager.headers.update(basic_auth)
+
+        super(Urllib3HttpGitClient, self).__init__(
+            base_url=base_url, dumb=dumb, **kwargs)
+
+    def _get_url(self, path):
+        if not isinstance(path, str):
+            # urllib3.util.url._encode_invalid_chars() converts the path back
+            # to bytes using the utf-8 codec.
+            path = path.decode("utf-8")
+        return urljoin(self._base_url, path).rstrip("/") + "/"
+
+    def _http_request(self, url, headers=None, data=None, 
allow_compression=False):
+        req_headers = self.pool_manager.headers.copy()
+        if headers is not None:
+            req_headers.update(headers)
+        req_headers["Pragma"] = "no-cache"
+        if allow_compression:
+            req_headers["Accept-Encoding"] = "gzip"
+        else:
+            req_headers["Accept-Encoding"] = "identity"
+
+        if data is None:
+            resp = self.pool_manager.request("GET", url, headers=req_headers)
+        else:
+            resp = self.pool_manager.request(
+                "POST", url, headers=req_headers, body=data
+            )
+
+        if resp.status == 404:
+            raise NotGitRepository()
+        if resp.status == 401:
+            raise HTTPUnauthorized(resp.getheader("WWW-Authenticate"), url)
+        if resp.status == 407:
+            raise HTTPProxyUnauthorized(resp.getheader("Proxy-Authenticate"), 
url)
+        if resp.status != 200:
+            raise GitProtocolError(
+                "unexpected http resp %d for %s" % (resp.status, url)
+            )
+
+        # TODO: Optimization available by adding `preload_content=False` to the
+        # request and just passing the `read` method on instead of going via
+        # `BytesIO`, if we can guarantee that the entire response is consumed
+        # before issuing the next to still allow for connection reuse from the
+        # pool.
+        read = BytesIO(resp.data).read
+
+        resp.content_type = resp.getheader("Content-Type")
+        # Check if geturl() is available (urllib3 version >= 1.23)
+        try:
+            resp_url = resp.geturl()
+        except AttributeError:
+            # get_redirect_location() is available for urllib3 >= 1.1
+            resp.redirect_location = resp.get_redirect_location()
+        else:
+            resp.redirect_location = resp_url if resp_url != url else ""
+        return resp, read
+
+
+HttpGitClient = Urllib3HttpGitClient
+
 
 def get_transport_and_path_from_url(url, config=None, **kwargs):
     """Obtain a git client from a URL.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/config.py 
new/dulwich-0.20.24/dulwich/config.py
--- old/dulwich-0.20.23/dulwich/config.py       2021-05-24 22:39:10.000000000 
+0200
+++ new/dulwich-0.20.24/dulwich/config.py       2021-07-18 14:58:17.000000000 
+0200
@@ -387,14 +387,16 @@
         super(ConfigFile, self).__init__(values=values, encoding=encoding)
         self.path = None
 
-    @classmethod
-    def from_file(cls, f: BinaryIO) -> "ConfigFile":
+    @classmethod  # noqa: C901
+    def from_file(cls, f: BinaryIO) -> "ConfigFile":  # noqa: C901
         """Read configuration from a file-like object."""
         ret = cls()
         section = None  # type: Optional[Tuple[bytes, ...]]
         setting = None
         continuation = None
         for lineno, line in enumerate(f.readlines()):
+            if lineno == 0 and line.startswith(b'\xef\xbb\xbf'):
+                line = line[3:]
             line = line.lstrip()
             if setting is None:
                 # Parse section header ("[bla]")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/ignore.py 
new/dulwich-0.20.24/dulwich/ignore.py
--- old/dulwich-0.20.23/dulwich/ignore.py       2021-05-24 22:39:10.000000000 
+0200
+++ new/dulwich-0.20.24/dulwich/ignore.py       2021-07-18 14:58:17.000000000 
+0200
@@ -123,7 +123,7 @@
         line = line.rstrip(b"\r\n")
 
         # Ignore blank lines, they're used for readability.
-        if not line:
+        if not line.strip():
             continue
 
         if line.startswith(b"#"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/porcelain.py 
new/dulwich-0.20.24/dulwich/porcelain.py
--- old/dulwich-0.20.23/dulwich/porcelain.py    2021-05-24 22:39:10.000000000 
+0200
+++ new/dulwich-0.20.24/dulwich/porcelain.py    2021-07-18 14:58:17.000000000 
+0200
@@ -1808,13 +1808,22 @@
         stash.push()
 
 
-def stash_pop(repo):
-    """Pop a new stash from the stack."""
+def stash_pop(repo, index):
+    """Pop a stash from the stack."""
     with open_repo_closing(repo) as r:
         from dulwich.stash import Stash
 
         stash = Stash.from_repo(r)
-        stash.pop()
+        stash.pop(index)
+
+
+def stash_drop(repo, index):
+    """Drop a stash from the stack."""
+    with open_repo_closing(repo) as r:
+        from dulwich.stash import Stash
+
+        stash = Stash.from_repo(r)
+        stash.drop(index)
 
 
 def ls_files(repo):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/tests/test_config.py 
new/dulwich-0.20.24/dulwich/tests/test_config.py
--- old/dulwich-0.20.23/dulwich/tests/test_config.py    2021-05-24 
22:39:10.000000000 +0200
+++ new/dulwich-0.20.24/dulwich/tests/test_config.py    2021-07-18 
14:58:17.000000000 +0200
@@ -108,6 +108,11 @@
         self.assertEqual(b"bar", cf.get((b"core",), b"foo"))
         self.assertEqual(b"bar", cf.get((b"core", b"foo"), b"foo"))
 
+    def test_from_file_utf8_bom(self):
+        text = "[core]\nfoo = b\u00e4r\n".encode("utf-8-sig")
+        cf = self.from_file(text)
+        self.assertEqual(b"b\xc3\xa4r", cf.get((b"core",), b"foo"))
+
     def test_from_file_section_case_insensitive_lower(self):
         cf = self.from_file(b"[cOre]\nfOo = bar\n")
         self.assertEqual(b"bar", cf.get((b"core",), b"foo"))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/tests/test_ignore.py 
new/dulwich-0.20.24/dulwich/tests/test_ignore.py
--- old/dulwich-0.20.23/dulwich/tests/test_ignore.py    2021-05-24 
22:39:10.000000000 +0200
+++ new/dulwich-0.20.24/dulwich/tests/test_ignore.py    2021-07-18 
14:58:17.000000000 +0200
@@ -105,7 +105,7 @@
         f = BytesIO(
             b"""
 # a comment
-
+\x20\x20
 # and an empty line:
 
 \\#not a comment
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich/tests/test_porcelain.py 
new/dulwich-0.20.24/dulwich/tests/test_porcelain.py
--- old/dulwich-0.20.23/dulwich/tests/test_porcelain.py 2021-05-24 
22:39:10.000000000 +0200
+++ new/dulwich-0.20.24/dulwich/tests/test_porcelain.py 2021-07-18 
14:58:17.000000000 +0200
@@ -111,55 +111,55 @@
 MOMZHMSVBDqyyIx3assGlxSX8BSFW0lhKyT7i0XqnAgCJ9f/5oq0SbFGq+01VQb7
 jIx9PbcYJORxsE0JG/CXXPv27bRtQXsudkWGSYvC0NLOgk4z8+kQpQtyFh16lujq
 WRwMeriu0qNDjCa1/eHIKDovhAZ3GyO5/9m1tBlUZXN0IFVzZXIgPHRlc3RAdGVz
-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
-=YPjc
+dC5jb20+iQHOBBMBCAA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEEjrR8
+MQ4fJK44PYMvfN2AClLmXiYFAmDcEZEACgkQfN2AClLmXibZzgv/ZfeTpTuqQE1W
+C1jT5KpQExnt0BizTX0U7BvSn8Fr6VXTyol6kYc3u71GLUuJyawCLtIzOXqOXJvz
+bjcZqymcMADuftKcfMy513FhbF6MhdVd6QoeBP6+7/xXOFJCi+QVYF7SQ2h7K1Qm
++yXOiAMgSxhCZQGPBNJLlDUOd47nSIMANvlumFtmLY/1FD7RpG7WQWjeX1mnxNTw
+hUU+Yv7GuFc/JprXCIYqHbhWfvXyVtae2ZK4xuVi5eqwA2RfggOVM7drb+CgPhG0
++9aEDDLOZqVi65wK7J73Puo3rFTbPQMljxw5s27rWqF+vB6hhVdJOPNomWy3naPi
+k5MW0mhsacASz1WYndpZz+XaQTq/wJF5HUyyeUWJ0vlOEdwx021PHcqSTyfNnkjD
+KncrE21t2sxWRsgGDETxIwkd2b2HNGAvveUD0ffFK/oJHGSXjAERFGc3wuiDj3mQ
+BvKm4wt4QF9ZMrCdhMAA6ax5kfEUqQR4ntmrJk/khp/mV7TILaI4nQVYBGBjIyIB
+DADghIo9wXnRxzfdDTvwnP8dHpLAIaPokgdpyLswqUCixJWiW2xcV6weUjEWwH6n
+eN/t1uZYVehbrotxVPla+MPvzhxp6/cmG+2lhzEBOp6zRwnL1wIB6HoKJfpREhyM
+c8rLR0zMso1L1bJTyydvnu07a7BWo3VWKjilb0rEZZUSD/2hidx5HxMOJSoidLWe
+d/PPuv6yht3NtA4UThlcfldm9G6PbqCdm1kMEKAkq0wVJvhPJ6gEFRNJimgygfUw
+MDFXEIhQtxjgdV5Uoz3O5452VLoRsDlgpi3E0WDGj7WXDaO5uSU0T5aJgVgHCP/f
+xZhHuQFk2YYIl5nCBpOZyWWI0IKmscTuEwzpkhICQDQFvcMZ5ibsl7wA2P7YTrQf
+FDMjjzuaK80GYPfxDFlyKUyLqFt8w/QzsZLDLX7+jxIEpbRAaMw/JsWqm5BMxxbS
+3CIQiS5S3oSKDsNINelqWFfwvLhvlQra8gIxyNTlek25OdgG66BiiX+seH8A/ql+
+F+MAEQEAAQAL/1jrNSLjMt9pwo6qFKClVQZP2vf7+sH7v7LeHIDXr3EnYUnVYnOq
+B1FU5PspTp/+J9W25DB9CZLx7Gj8qeslFdiuLSOoIBB4RCToB3kAoeTH0DHqW/Gs
+hFTrmJkuDp9zpo/ek6SIXJx5rHAyR9KVw0fizQprH2f6PcgLbTWeM61dJuqowmg3
+7eCOyIKv7VQvFqEhYokLD+JNmrvg+Htg0DXGvdjRjAwPf/NezEXpj67a6cHTp1/C
+hwp7pevG+3fTxaCJFesl5/TxxtnaBLE8m2uo/S6Hxgn9l0edonroe1QlTjEqGLy2
+7qi2z5Rem+v6GWNDRgvAWur13v8FNdyduHlioG/NgRsU9mE2MYeFsfi3cfNpJQp/
+wC9PSCIXrb/45mkS8KyjZpCrIPB9RV/m0MREq01TPom7rstZc4A1pD0Ot7AtUYS3
+e95zLyEmeLziPJ9fV4fgPmEudDr1uItnmV0LOskKlpg5sc0hhdrwYoobfkKt2dx6
+DqfMlcM1ZkUbLQYA4jwfpFJG4HmYvjL2xCJxM0ycjvMbqFN+4UjgYWVlRfOrm1V4
+Op86FjbRbV6OOCNhznotAg7mul4xtzrrTkK8o3YLBeJseDgl4AWuzXtNa9hE0XpK
+9gJoEHUuBOOsamVh2HpXESFyE5CclOV7JSh541TlZKfnqfZYCg4JSbp0UijkawCL
+5bJJUiGGMD9rZUxIAKQO1DvUEzptS7Jl6S3y5sbIIhilp4KfYWbSk3PPu9CnZD5b
+LhEQp0elxnb/IL8PBgD+DpTeC8unkGKXUpbe9x0ISI6V1D6FmJq/FxNg7fMa3QCh
+fGiAyoTm80ZETynj+blRaDO3gY4lTLa3Opubof1EqK2QmwXmpyvXEZNYcQfQ2CCS
+GOWUCK8jEQamUPf1PWndZXJUmROI1WukhlL71V/ir6zQeVCv1wcwPwclJPnAe87u
+pEklnCYpvsEldwHUX9u0BWzoULIEsi+ddtHmT0KTeF/DHRy0W15jIHbjFqhqckj1
+/6fmr7l7kIi/kN4vWe0F/0Q8IXX+cVMgbl3aIuaGcvENLGcoAsAtPGx88SfRgmfu
+HK64Y7hx1m+Bo215rxJzZRjqHTBPp0BmCi+JKkaavIBrYRbsx20gveI4dzhLcUhB
+kiT4Q7oz0/VbGHS1CEf9KFeS/YOGj57s4yHauSVI0XdP9kBRTWmXvBkzsooB2cKH
+hwhUN7iiT1k717CiTNUT6Q/pcPFCyNuMoBBGQTU206JEgIjQvI3f8xMUMGmGVVQz
+9/k716ycnhb2JZ/Q/AyQIeHJiQG2BBgBCAAgAhsMFiEEjrR8MQ4fJK44PYMvfN2A
+ClLmXiYFAmDcEa4ACgkQfN2AClLmXiZxxQv/XaMN0hPCygtrQMbCsTNb34JbvJzh
+hngPuUAfTbRHrR3YeATyQofNbL0DD3fvfzeFF8qESqvzCSZxS6dYsXPd4MCJTzlp
+zYBZ2X0sOrgDqZvqCZKN72RKgdk0KvthdzAxsIm2dfcQOxxowXMxhJEXZmsFpusx
+jKJxOcrfVRjXJnh9isY0NpCoqMQ+3k3wDJ3VGEHV7G+A+vFkWfbLJF5huQ96uaH9
+Uc+jUsREUH9G82ZBqpoioEN8Ith4VXpYnKdTMonK/+ZcyeraJZhXrvbjnEomKdzU
+0pu4bt1HlLR3dcnpjN7b009MBf2xLgEfQk2nPZ4zzY+tDkxygtPllaB4dldFjBpT
+j7Q+t49sWMjmlJUbLlHfuJ7nUUK5+cGjBsWVObAEcyfemHWCTVFnEa2BJslGC08X
+rFcjRRcMEr9ct4551QFBHsv3O/Wp3/wqczYgE9itSnGT05w+4vLt4smG+dnEHjRJ
+brMb2upTHa+kjktjdO96/BgSnKYqmNmPB/qB
+=ivA/
 -----END PGP PRIVATE KEY BLOCK-----
     """
 
@@ -197,55 +197,55 @@
 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
-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
-=kOKh
+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
+=9zU5
 -----END PGP PRIVATE KEY BLOCK-----
 """
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich.egg-info/PKG-INFO 
new/dulwich-0.20.24/dulwich.egg-info/PKG-INFO
--- old/dulwich-0.20.23/dulwich.egg-info/PKG-INFO       2021-05-24 
22:40:48.000000000 +0200
+++ new/dulwich-0.20.24/dulwich.egg-info/PKG-INFO       2021-07-18 
14:59:56.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: dulwich
-Version: 0.20.23
+Version: 0.20.24
 Summary: Python Git Library
 Home-page: https://www.dulwich.io/
 Author: Jelmer Vernooij
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich.egg-info/SOURCES.txt 
new/dulwich-0.20.24/dulwich.egg-info/SOURCES.txt
--- old/dulwich-0.20.23/dulwich.egg-info/SOURCES.txt    2021-05-24 
22:40:48.000000000 +0200
+++ new/dulwich-0.20.24/dulwich.egg-info/SOURCES.txt    2021-07-18 
14:59:56.000000000 +0200
@@ -15,7 +15,6 @@
 README.swift.rst
 SECURITY.md
 TODO
-build.cmd
 dulwich.cfg
 releaser.conf
 requirements.txt
@@ -93,6 +92,7 @@
 dulwich.egg-info/SOURCES.txt
 dulwich.egg-info/dependency_links.txt
 dulwich.egg-info/entry_points.txt
+dulwich.egg-info/not-zip-safe
 dulwich.egg-info/requires.txt
 dulwich.egg-info/top_level.txt
 dulwich/cloud/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/dulwich.egg-info/not-zip-safe 
new/dulwich-0.20.24/dulwich.egg-info/not-zip-safe
--- old/dulwich-0.20.23/dulwich.egg-info/not-zip-safe   1970-01-01 
01:00:00.000000000 +0100
+++ new/dulwich-0.20.24/dulwich.egg-info/not-zip-safe   2021-07-18 
14:58:18.000000000 +0200
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dulwich-0.20.23/setup.py new/dulwich-0.20.24/setup.py
--- old/dulwich-0.20.23/setup.py        2021-05-24 22:40:45.000000000 +0200
+++ new/dulwich-0.20.24/setup.py        2021-07-18 14:59:47.000000000 +0200
@@ -23,7 +23,7 @@
         'For 2.7 support, please install a version prior to 0.20')
 
 
-dulwich_version_string = '0.20.23'
+dulwich_version_string = '0.20.24'
 
 
 class DulwichDistribution(Distribution):
@@ -116,6 +116,7 @@
       package_data={'': ['../docs/tutorial/*.txt', 'py.typed']},
       scripts=scripts,
       ext_modules=ext_modules,
+      zip_safe=False,
       distclass=DulwichDistribution,
       classifiers=[
           'Development Status :: 4 - Beta',

Reply via email to