Jelmer Vernooij 1 неделя назад
Родитель
Сommit
08b1971741

+ 1 - 6
dulwich/client.py

@@ -1478,12 +1478,7 @@ class GitClient:
 
 
     @staticmethod
     @staticmethod
     def _warn_filter_objects() -> None:
     def _warn_filter_objects() -> None:
-        import warnings
-
-        warnings.warn(
-            "object filtering not recognized by server, ignoring",
-            UserWarning,
-        )
+        logging.warning("object filtering not recognized by server, ignoring")
 
 
 
 
 def check_wants(wants: Set[bytes], refs: Mapping[bytes, bytes]) -> None:
 def check_wants(wants: Set[bytes], refs: Mapping[bytes, bytes]) -> None:

+ 7 - 6
dulwich/signature.py

@@ -1293,12 +1293,13 @@ class SSHCliSignatureVendor(SignatureSigner, SignatureVerifier):
                 args.extend(["-r", self.revocation_file])
                 args.extend(["-r", self.revocation_file])
 
 
             try:
             try:
-                subprocess.run(
-                    args,
-                    stdin=open(data_filename, "rb"),
-                    capture_output=True,
-                    check=True,
-                )
+                with open(data_filename, "rb") as data_file:
+                    subprocess.run(
+                        args,
+                        stdin=data_file,
+                        capture_output=True,
+                        check=True,
+                    )
             except subprocess.CalledProcessError as e:
             except subprocess.CalledProcessError as e:
                 raise BadSignature(
                 raise BadSignature(
                     f"SSH signature verification failed: {e.stderr.decode('utf-8', errors='replace')}"
                     f"SSH signature verification failed: {e.stderr.decode('utf-8', errors='replace')}"

+ 22 - 15
tests/compat/test_partial_clone.py

@@ -390,14 +390,15 @@ class PartialCloneClientTestCase(CompatTestCase):
             # Get all refs
             # Get all refs
             return list(refs.values())
             return list(refs.values())
 
 
-        # Fetch with filter
-        result = client.fetch(
-            path,
-            dest_repo,
-            determine_wants=determine_wants,
-            progress=None,
-            filter_spec=b"blob:none",
-        )
+        # Fetch with filter (may warn if server doesn't support filtering)
+        with self.assertLogs(level="WARNING"):
+            result = client.fetch(
+                path,
+                dest_repo,
+                determine_wants=determine_wants,
+                progress=None,
+                filter_spec=b"blob:none",
+            )
 
 
         # The fetch should succeed with partial clone
         # The fetch should succeed with partial clone
         self.assertIsNotNone(result)
         self.assertIsNotNone(result)
@@ -430,13 +431,14 @@ class PartialCloneClientTestCase(CompatTestCase):
 
 
         client, path = get_transport_and_path(f"git://localhost:{daemon_port}/")
         client, path = get_transport_and_path(f"git://localhost:{daemon_port}/")
 
 
-        # Clone with blob:limit filter
-        cloned_repo = client.clone(
-            path,
-            dest_path,
-            mkdir=False,
-            filter_spec=b"blob:limit=100",
-        )
+        # Clone with blob:limit filter (may warn if server doesn't support filtering)
+        with self.assertLogs(level="WARNING"):
+            cloned_repo = client.clone(
+                path,
+                dest_path,
+                mkdir=False,
+                filter_spec=b"blob:limit=100",
+            )
         self.addCleanup(cloned_repo.close)
         self.addCleanup(cloned_repo.close)
 
 
         # Verify clone succeeded
         # Verify clone succeeded
@@ -483,6 +485,11 @@ class PartialCloneClientTestCase(CompatTestCase):
         def cleanup_daemon():
         def cleanup_daemon():
             daemon_process.terminate()
             daemon_process.terminate()
             daemon_process.wait(timeout=2)
             daemon_process.wait(timeout=2)
+            # Close pipes to avoid ResourceWarning
+            if daemon_process.stdout:
+                daemon_process.stdout.close()
+            if daemon_process.stderr:
+                daemon_process.stderr.close()
 
 
         self.addCleanup(cleanup_daemon)
         self.addCleanup(cleanup_daemon)
 
 

+ 4 - 12
tests/porcelain/test_ignore.py

@@ -59,14 +59,10 @@ class CheckIgnoreQuotePathTests(TestCase):
 
 
         # Test with quote_path=True (default)
         # Test with quote_path=True (default)
         abs_paths = [os.path.join(self.test_dir, f) for f in test_files]
         abs_paths = [os.path.join(self.test_dir, f) for f in test_files]
-        ignored_quoted = set(
-            check_ignore(self.test_dir, abs_paths, quote_path=True)
-        )
+        ignored_quoted = set(check_ignore(self.test_dir, abs_paths, quote_path=True))
 
 
         # Test with quote_path=False
         # Test with quote_path=False
-        ignored_unquoted = set(
-            check_ignore(self.test_dir, abs_paths, quote_path=False)
-        )
+        ignored_unquoted = set(check_ignore(self.test_dir, abs_paths, quote_path=False))
 
 
         # Verify quoted results
         # Verify quoted results
         expected_quoted = {
         expected_quoted = {
@@ -100,12 +96,8 @@ class CheckIgnoreQuotePathTests(TestCase):
 
 
         # Test both settings
         # Test both settings
         abs_paths = [os.path.join(self.test_dir, f) for f in test_files]
         abs_paths = [os.path.join(self.test_dir, f) for f in test_files]
-        ignored_quoted = set(
-            check_ignore(self.test_dir, abs_paths, quote_path=True)
-        )
-        ignored_unquoted = set(
-            check_ignore(self.test_dir, abs_paths, quote_path=False)
-        )
+        ignored_quoted = set(check_ignore(self.test_dir, abs_paths, quote_path=True))
+        ignored_unquoted = set(check_ignore(self.test_dir, abs_paths, quote_path=False))
 
 
         # Both should return the same results for ASCII filenames
         # Both should return the same results for ASCII filenames
         expected = {"test.txt", "file.tmp"}
         expected = {"test.txt", "file.tmp"}

+ 3 - 2
tests/porcelain/test_lfs.py

@@ -276,8 +276,9 @@ class LFSPorcelainTestCase(TestCase):
         with self.assertRaises(KeyError):
         with self.assertRaises(KeyError):
             config.get((b"filter", b"lfs"), b"smudge")
             config.get((b"filter", b"lfs"), b"smudge")
 
 
-        # Clone the repository
-        cloned_repo = porcelain.clone(source_dir, clone_dir)
+        # Clone the repository (may warn about missing LFS objects)
+        with self.assertLogs("dulwich.lfs", level="WARNING"):
+            cloned_repo = porcelain.clone(source_dir, clone_dir)
 
 
         # Verify that built-in LFS filter was used
         # Verify that built-in LFS filter was used
         normalizer = cloned_repo.get_blob_normalizer()
         normalizer = cloned_repo.get_blob_normalizer()

+ 0 - 2
tests/test_annotate.py

@@ -18,9 +18,7 @@
 
 
 """Tests for annotate support."""
 """Tests for annotate support."""
 
 
-import os
 import tempfile
 import tempfile
-import unittest
 from typing import Any
 from typing import Any
 from unittest import TestCase
 from unittest import TestCase
 
 

+ 0 - 1
tests/test_bisect.py

@@ -26,7 +26,6 @@ import shutil
 import tempfile
 import tempfile
 
 
 from dulwich.bisect import BisectState
 from dulwich.bisect import BisectState
-from dulwich.objects import Tree
 from dulwich.repo import Repo
 from dulwich.repo import Repo
 from dulwich.tests.utils import make_commit
 from dulwich.tests.utils import make_commit
 
 

+ 6 - 3
tests/test_filters.py

@@ -385,7 +385,8 @@ sys.stdout.buffer.write(result)
             f.write(b"test content\n")
             f.write(b"test content\n")
 
 
         # Adding file should work and fallback to original content
         # Adding file should work and fallback to original content
-        worktree.stage(["test.txt"])
+        with self.assertLogs(level="WARNING"):
+            worktree.stage(["test.txt"])
 
 
         # Check that original content was preserved
         # Check that original content was preserved
         index = self.repo.open_index()
         index = self.repo.open_index()
@@ -579,7 +580,8 @@ while True:
         )
         )
 
 
         test_data = b"hello world\n"
         test_data = b"hello world\n"
-        result = driver.clean(test_data)
+        with self.assertLogs(level="WARNING"):
+            result = driver.clean(test_data)
 
 
         # Should fallback to tr command and uppercase
         # Should fallback to tr command and uppercase
         self.assertEqual(result, b"HELLO WORLD\n")
         self.assertEqual(result, b"HELLO WORLD\n")
@@ -1156,7 +1158,8 @@ protocol.write_pkt_line(None)
             )
             )
 
 
             # Should fallback to clean_cmd when process fails
             # Should fallback to clean_cmd when process fails
-            result = driver.clean(b"test data")
+            with self.assertLogs(level="WARNING"):
+                result = driver.clean(b"test data")
             self.assertEqual(result, b"test data")
             self.assertEqual(result, b"test data")
 
 
         finally:
         finally:

+ 2 - 1
tests/test_lfs_integration.py

@@ -145,5 +145,6 @@ class LFSFilterIntegrationTests(TestCase):
         blob.data = pointer.to_bytes()
         blob.data = pointer.to_bytes()
 
 
         # Checkout should return the pointer as-is when object is missing
         # Checkout should return the pointer as-is when object is missing
-        checked_out = self.normalizer.checkout_normalize(blob, b"missing.bin")
+        with self.assertLogs("dulwich.lfs", level="WARNING"):
+            checked_out = self.normalizer.checkout_normalize(blob, b"missing.bin")
         self.assertEqual(checked_out.data, blob.data)
         self.assertEqual(checked_out.data, blob.data)

+ 1 - 3
tests/test_rebase.py

@@ -22,8 +22,6 @@
 """Tests for dulwich.rebase."""
 """Tests for dulwich.rebase."""
 
 
 import importlib.util
 import importlib.util
-import os
-import tempfile
 
 
 from dulwich.objects import Blob, Commit, Tree
 from dulwich.objects import Blob, Commit, Tree
 from dulwich.rebase import (
 from dulwich.rebase import (
@@ -36,7 +34,7 @@ from dulwich.rebase import (
     rebase,
     rebase,
     start_interactive,
     start_interactive,
 )
 )
-from dulwich.repo import MemoryRepo, Repo
+from dulwich.repo import MemoryRepo
 from dulwich.tests.utils import make_commit
 from dulwich.tests.utils import make_commit
 
 
 from . import DependencyMissing, TestCase
 from . import DependencyMissing, TestCase

+ 3 - 2
tests/test_repository.py

@@ -548,7 +548,8 @@ class RepositoryRootTests(TestCase):
         self.addCleanup(shutil.rmtree, tmp_dir)
         self.addCleanup(shutil.rmtree, tmp_dir)
         t = Repo.init(tmp_dir)
         t = Repo.init(tmp_dir)
         self.addCleanup(t.close)
         self.addCleanup(t.close)
-        r.fetch(t)
+        with self.assertLogs(level="WARNING"):
+            r.fetch(t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
         self.assertIn(b"a90fa2d900a17e99b433217e988c4eb4a2e9a097", t)
@@ -1726,7 +1727,7 @@ class BuildRepoRootTests(TestCase):
         wt.unstage(["new_dir/foo"])
         wt.unstage(["new_dir/foo"])
 
 
         unstaged = get_unstaged_changes(self._repo)
         unstaged = get_unstaged_changes(self._repo)
-        self.assertEqual([os.fsencode(os.path.join("new_dir", "foo"))], unstaged)
+        self.assertEqual([b"new_dir/foo"], unstaged)
 
 
     def test_unstage_while_no_commit(self) -> None:
     def test_unstage_while_no_commit(self) -> None:
         file = "foo"
         file = "foo"

+ 51 - 17
tests/test_rerere.py

@@ -594,12 +594,18 @@ class RerereEndToEndTests(unittest.TestCase):
         gitattributes = self.repo.get_gitattributes()
         gitattributes = self.repo.get_gitattributes()
         config = self.repo.get_config()
         config = self.repo.get_config()
         merged_tree, conflicts = recursive_merge(
         merged_tree, conflicts = recursive_merge(
-            self.repo.object_store, merge_bases, head_commit, merge_commit,
-            gitattributes, config
+            self.repo.object_store,
+            merge_bases,
+            head_commit,
+            merge_commit,
+            gitattributes,
+            config,
         )
         )
         self.repo.object_store.add_object(merged_tree)
         self.repo.object_store.add_object(merged_tree)
         changes = tree_changes(self.repo.object_store, head_commit.tree, merged_tree.id)
         changes = tree_changes(self.repo.object_store, head_commit.tree, merged_tree.id)
-        update_working_tree(self.repo, head_commit.tree, merged_tree.id, change_iterator=changes)
+        update_working_tree(
+            self.repo, head_commit.tree, merged_tree.id, change_iterator=changes
+        )
 
 
         # Should have conflicts
         # Should have conflicts
         self.assertEqual([b"file.txt"], conflicts)
         self.assertEqual([b"file.txt"], conflicts)
@@ -635,14 +641,24 @@ class RerereEndToEndTests(unittest.TestCase):
             f.write(b"line 1\nbranch1 change\nline 3\n")
             f.write(b"line 1\nbranch1 change\nline 3\n")
 
 
         # Merge again - should create same conflict
         # Merge again - should create same conflict
-        merge_bases2 = find_merge_base(self.repo, [commit_branch1.id, commit_branch2.id])
+        merge_bases2 = find_merge_base(
+            self.repo, [commit_branch1.id, commit_branch2.id]
+        )
         merged_tree2, conflicts2 = recursive_merge(
         merged_tree2, conflicts2 = recursive_merge(
-            self.repo.object_store, merge_bases2, commit_branch1, commit_branch2,
-            gitattributes, config
+            self.repo.object_store,
+            merge_bases2,
+            commit_branch1,
+            commit_branch2,
+            gitattributes,
+            config,
         )
         )
         self.repo.object_store.add_object(merged_tree2)
         self.repo.object_store.add_object(merged_tree2)
-        changes2 = tree_changes(self.repo.object_store, commit_branch1.tree, merged_tree2.id)
-        update_working_tree(self.repo, commit_branch1.tree, merged_tree2.id, change_iterator=changes2)
+        changes2 = tree_changes(
+            self.repo.object_store, commit_branch1.tree, merged_tree2.id
+        )
+        update_working_tree(
+            self.repo, commit_branch1.tree, merged_tree2.id, change_iterator=changes2
+        )
         self.assertEqual([b"file.txt"], conflicts2)
         self.assertEqual([b"file.txt"], conflicts2)
 
 
         # Now rerere should recognize the conflict
         # Now rerere should recognize the conflict
@@ -718,12 +734,20 @@ class RerereEndToEndTests(unittest.TestCase):
         gitattributes = self.repo.get_gitattributes()
         gitattributes = self.repo.get_gitattributes()
         config = self.repo.get_config()
         config = self.repo.get_config()
         merged_tree, conflicts = recursive_merge(
         merged_tree, conflicts = recursive_merge(
-            self.repo.object_store, merge_bases, commit_branch1, commit_branch2,
-            gitattributes, config
+            self.repo.object_store,
+            merge_bases,
+            commit_branch1,
+            commit_branch2,
+            gitattributes,
+            config,
         )
         )
         self.repo.object_store.add_object(merged_tree)
         self.repo.object_store.add_object(merged_tree)
-        changes = tree_changes(self.repo.object_store, commit_branch1.tree, merged_tree.id)
-        update_working_tree(self.repo, commit_branch1.tree, merged_tree.id, change_iterator=changes)
+        changes = tree_changes(
+            self.repo.object_store, commit_branch1.tree, merged_tree.id
+        )
+        update_working_tree(
+            self.repo, commit_branch1.tree, merged_tree.id, change_iterator=changes
+        )
 
 
         # Record conflict and resolution
         # Record conflict and resolution
         recorded, _ = rerere_auto(self.repo, self.tempdir, conflicts)
         recorded, _ = rerere_auto(self.repo, self.tempdir, conflicts)
@@ -742,14 +766,24 @@ class RerereEndToEndTests(unittest.TestCase):
             f.write(b"line 1\nbranch1 change\nline 3\n")
             f.write(b"line 1\nbranch1 change\nline 3\n")
 
 
         # Perform merge again using lower-level APIs
         # Perform merge again using lower-level APIs
-        merge_bases2 = find_merge_base(self.repo, [commit_branch1.id, commit_branch2.id])
+        merge_bases2 = find_merge_base(
+            self.repo, [commit_branch1.id, commit_branch2.id]
+        )
         merged_tree2, conflicts2 = recursive_merge(
         merged_tree2, conflicts2 = recursive_merge(
-            self.repo.object_store, merge_bases2, commit_branch1, commit_branch2,
-            gitattributes, config
+            self.repo.object_store,
+            merge_bases2,
+            commit_branch1,
+            commit_branch2,
+            gitattributes,
+            config,
         )
         )
         self.repo.object_store.add_object(merged_tree2)
         self.repo.object_store.add_object(merged_tree2)
-        changes2 = tree_changes(self.repo.object_store, commit_branch1.tree, merged_tree2.id)
-        update_working_tree(self.repo, commit_branch1.tree, merged_tree2.id, change_iterator=changes2)
+        changes2 = tree_changes(
+            self.repo.object_store, commit_branch1.tree, merged_tree2.id
+        )
+        update_working_tree(
+            self.repo, commit_branch1.tree, merged_tree2.id, change_iterator=changes2
+        )
 
 
         # With autoupdate, rerere should auto-apply the resolution
         # With autoupdate, rerere should auto-apply the resolution
         recorded2, resolved2 = rerere_auto(self.repo, self.tempdir, conflicts2)
         recorded2, resolved2 = rerere_auto(self.repo, self.tempdir, conflicts2)

+ 7 - 3
tests/test_source.py

@@ -111,19 +111,23 @@ def _imports_module(file_path, module_name):
         # Check "import dulwich.porcelain" or "import dulwich.porcelain.lfs"
         # Check "import dulwich.porcelain" or "import dulwich.porcelain.lfs"
         if isinstance(node, ast.Import):
         if isinstance(node, ast.Import):
             for alias in node.names:
             for alias in node.names:
-                if alias.name == module_name or alias.name.startswith(f"{module_name}."):
+                if alias.name == module_name or alias.name.startswith(
+                    f"{module_name}."
+                ):
                     return True
                     return True
 
 
         # Check "from dulwich.porcelain import ..." or "from dulwich import porcelain"
         # Check "from dulwich.porcelain import ..." or "from dulwich import porcelain"
         if isinstance(node, ast.ImportFrom):
         if isinstance(node, ast.ImportFrom):
             # "from dulwich.porcelain import something"
             # "from dulwich.porcelain import something"
             # "from dulwich.porcelain.lfs import something"
             # "from dulwich.porcelain.lfs import something"
-            if node.module == module_name or (node.module and node.module.startswith(f"{module_name}.")):
+            if node.module == module_name or (
+                node.module and node.module.startswith(f"{module_name}.")
+            ):
                 return True
                 return True
             # Handle "from dulwich import porcelain"
             # Handle "from dulwich import porcelain"
             if node.module and module_name.startswith(f"{node.module}."):
             if node.module and module_name.startswith(f"{node.module}."):
                 # e.g., module="dulwich", module_name="dulwich.porcelain"
                 # e.g., module="dulwich", module_name="dulwich.porcelain"
-                suffix = module_name[len(node.module) + 1:]
+                suffix = module_name[len(node.module) + 1 :]
                 for alias in node.names:
                 for alias in node.names:
                     if alias.name == suffix:
                     if alias.name == suffix:
                         return True
                         return True

+ 1 - 2
tests/test_worktree.py

@@ -29,7 +29,6 @@ from unittest import skipIf
 
 
 from dulwich.errors import CommitError
 from dulwich.errors import CommitError
 from dulwich.index import get_unstaged_changes as _get_unstaged_changes
 from dulwich.index import get_unstaged_changes as _get_unstaged_changes
-from dulwich.objects import Commit
 from dulwich.object_store import tree_lookup_path
 from dulwich.object_store import tree_lookup_path
 from dulwich.repo import Repo
 from dulwich.repo import Repo
 from dulwich.worktree import (
 from dulwich.worktree import (
@@ -186,7 +185,7 @@ class WorkTreeUnstagingTests(WorkTreeTestCase):
         self.worktree.unstage(["new_dir/foo"])
         self.worktree.unstage(["new_dir/foo"])
 
 
         unstaged = get_unstaged_changes(self.repo)
         unstaged = get_unstaged_changes(self.repo)
-        self.assertEqual([os.fsencode(os.path.join("new_dir", "foo"))], unstaged)
+        self.assertEqual([b"new_dir/foo"], unstaged)
 
 
     def test_unstage_while_no_commit(self):
     def test_unstage_while_no_commit(self):
         """Test unstaging when there are no commits."""
         """Test unstaging when there are no commits."""