From 51d0cacf42fd04f210a279ad4503f760de23c76d Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Tue, 29 Jul 2025 21:48:32 +0200 Subject: [PATCH 01/10] improve types according to test/test_nonunicode.py --- pygit2/_pygit2.pyi | 1 + test/test_nonunicode.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index acee4984..8ef2d72a 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -877,6 +877,7 @@ class Repository: def list_worktrees(self) -> list[str]: ... def listall_branches(self, flag: BranchType = BranchType.LOCAL) -> list[str]: ... def listall_mergeheads(self) -> list[Oid]: ... + def listall_references(self) -> list[str]: ... def listall_stashes(self) -> list[Stash]: ... def listall_submodules(self) -> list[str]: ... def lookup_branch( diff --git a/test/test_nonunicode.py b/test/test_nonunicode.py index 3daef1a2..cdf1ce16 100644 --- a/test/test_nonunicode.py +++ b/test/test_nonunicode.py @@ -32,6 +32,7 @@ import pytest import pygit2 +from pygit2 import Repository from . import utils @@ -44,7 +45,7 @@ @utils.requires_network @works_in_linux -def test_nonunicode_branchname(testrepo): +def test_nonunicode_branchname(testrepo: Repository) -> None: folderpath = 'temp_repo_nonutf' if os.path.exists(folderpath): shutil.rmtree(folderpath) From 5c57a7f344f78d9cd11953e0286daf50a3d01dc4 Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Tue, 29 Jul 2025 21:56:00 +0200 Subject: [PATCH 02/10] improve types according to test/test_note.py --- pygit2/_pygit2.pyi | 1 + test/test_note.py | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 8ef2d72a..b3340771 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -553,6 +553,7 @@ class Note: annotated_id: Oid id: Oid message: str + data: bytes def remove( self, author: Signature, committer: Signature, ref: str = 'refs/notes/commits' ) -> None: ... diff --git a/test/test_note.py b/test/test_note.py index 1f95e2a4..6a1846c0 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -27,7 +27,7 @@ import pytest -from pygit2 import Signature +from pygit2 import Blob, Repository, Signature NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') @@ -45,24 +45,26 @@ ] -def test_create_note(barerepo): +def test_create_note(barerepo: Repository) -> None: annotated_id = barerepo.revparse_single('HEAD~3').id author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) note_id = barerepo.create_note(NOTE[1], author, committer, str(annotated_id)) assert NOTE[0] == note_id + note = barerepo[note_id] + assert isinstance(note, Blob) # check the note blob - assert NOTE[1].encode() == barerepo[note_id].data + assert NOTE[1].encode() == note.data -def test_lookup_note(barerepo): +def test_lookup_note(barerepo: Repository) -> None: annotated_id = str(barerepo.head.target) note = barerepo.lookup_note(annotated_id) assert NOTES[0][0] == note.id assert NOTES[0][1] == note.message -def test_remove_note(barerepo): +def test_remove_note(barerepo: Repository) -> None: head = barerepo.head note = barerepo.lookup_note(str(head.target)) author = committer = Signature('Foo bar', 'foo@bar.com', 12346, 0) @@ -71,11 +73,14 @@ def test_remove_note(barerepo): barerepo.lookup_note(str(head.target)) -def test_iterate_notes(barerepo): +def test_iterate_notes(barerepo: Repository) -> None: for i, note in enumerate(barerepo.notes()): - assert NOTES[i] == (note.id, note.message, note.annotated_id) + note_id, message, annotated_id = NOTES[i] + assert note_id == note.id + assert message == note.message + assert annotated_id == note.annotated_id -def test_iterate_non_existing_ref(barerepo): +def test_iterate_non_existing_ref(barerepo: Repository) -> None: with pytest.raises(KeyError): - barerepo.notes('refs/notes/bad_ref') + barerepo.notes('refs/notes/bad_ref') # type: ignore From 285b82a6db250caa2b4fd954dd78629c67ded8e9 Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Tue, 29 Jul 2025 22:04:11 +0200 Subject: [PATCH 03/10] improve types according to test/test_object.py --- pygit2/_pygit2.pyi | 14 ++++++++++---- test/test_object.py | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index b3340771..3018204b 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -327,14 +327,20 @@ class _ObjectBase(Generic[T]): tree: Tree @overload def peel( - self, target_type: 'Literal[GIT_OBJ_COMMIT] | Type[Commit]' + self, target_type: 'Literal[GIT_OBJ_COMMIT, ObjectType.COMMIT] | Type[Commit]' ) -> 'Commit': ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_TREE] | Type[Tree]') -> 'Tree': ... + def peel( + self, target_type: 'Literal[GIT_OBJ_TREE, ObjectType.TREE] | Type[Tree]' + ) -> 'Tree': ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_TAG] | Type[Tag]') -> 'Tag': ... + def peel( + self, target_type: 'Literal[GIT_OBJ_TAG, ObjectType.TAG] | Type[Tag]' + ) -> 'Tag': ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_BLOB] | Type[Blob]') -> 'Blob': ... + def peel( + self, target_type: 'Literal[GIT_OBJ_BLOB, ObjectType.BLOB] | Type[Blob]' + ) -> 'Blob': ... @overload def peel(self, target_type: 'None') -> 'Commit|Tree|Tag|Blob': ... def read_raw(self) -> bytes: ... diff --git a/test/test_object.py b/test/test_object.py index 95b53458..fa1a8311 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -27,7 +27,7 @@ import pytest -from pygit2 import Tag, Tree +from pygit2 import Commit, Object, Oid, Repository, Tag, Tree from pygit2.enums import ObjectType BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' @@ -39,7 +39,7 @@ BLOB_FILE_CONTENT = b'bye world\n' -def test_equality(testrepo): +def test_equality(testrepo: Repository) -> None: # get a commit object twice and see if it equals ittestrepo commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] @@ -50,7 +50,7 @@ def test_equality(testrepo): assert not (commit_a != commit_b) -def test_hashing(testrepo): +def test_hashing(testrepo: Repository) -> None: # get a commit object twice and compare hashes commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] @@ -80,7 +80,7 @@ def test_hashing(testrepo): assert commit_b == commit_a -def test_peel_commit(testrepo): +def test_peel_commit(testrepo: Repository) -> None: # start by looking up the commit commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] @@ -91,7 +91,7 @@ def test_peel_commit(testrepo): assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' -def test_peel_commit_type(testrepo): +def test_peel_commit_type(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] tree = commit.peel(Tree) @@ -100,7 +100,7 @@ def test_peel_commit_type(testrepo): assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' -def test_invalid(testrepo): +def test_invalid(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] @@ -108,7 +108,7 @@ def test_invalid(testrepo): commit.peel(ObjectType.TAG) -def test_invalid_type(testrepo): +def test_invalid_type(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit = testrepo[commit_id] @@ -116,10 +116,10 @@ def test_invalid_type(testrepo): commit.peel(Tag) -def test_short_id(testrepo): - seen = {} # from short_id to full hex id +def test_short_id(testrepo: Repository) -> None: + seen: dict[str, Oid] = {} # from short_id to full hex id - def test_obj(obj, msg): + def test_obj(obj: Object | Commit, msg: str) -> None: short_id = obj.short_id msg = msg + f' short_id={short_id}' already = seen.get(short_id) @@ -138,7 +138,7 @@ def test_obj(obj, msg): test_obj(testrepo[entry.id], f'entry={entry.name}#{entry.id}') -def test_repr(testrepo): +def test_repr(testrepo: Repository) -> None: commit_id = testrepo.lookup_reference('refs/heads/master').target commit_a = testrepo[commit_id] assert repr(commit_a) == '' % commit_id From 0b93aab85984d3aef1903c1e196e913423bc8738 Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Tue, 29 Jul 2025 22:08:06 +0200 Subject: [PATCH 04/10] improve types according to test/test_odb.py --- pygit2/_pygit2.pyi | 4 ++-- test/test_odb.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 3018204b..4be51b3e 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -568,9 +568,9 @@ class Odb: backends: Iterator[OdbBackend] def __init__(self, *args, **kwargs) -> None: ... def add_backend(self, backend: OdbBackend, priority: int) -> None: ... - def add_disk_alternate(self, path: str) -> None: ... + def add_disk_alternate(self, path: str | Path) -> None: ... def exists(self, oid: _OidArg) -> bool: ... - def read(self, oid: _OidArg) -> tuple[int, int, bytes]: ... + def read(self, oid: _OidArg) -> tuple[int, bytes]: ... def write(self, type: int, data: bytes) -> Oid: ... def __contains__(self, other: _OidArg) -> bool: ... def __iter__(self) -> Iterator[Oid]: ... # Odb_as_iter diff --git a/test/test_odb.py b/test/test_odb.py index ce07c937..a9b33e18 100644 --- a/test/test_odb.py +++ b/test/test_odb.py @@ -28,11 +28,12 @@ # Standard Library import binascii from pathlib import Path +from typing import Generator import pytest # pygit2 -from pygit2 import Odb, Oid +from pygit2 import Odb, Oid, Repository from pygit2.enums import ObjectType from . import utils @@ -42,7 +43,7 @@ BLOB_OID = Oid(raw=BLOB_RAW) -def test_emptyodb(barerepo): +def test_emptyodb(barerepo: Repository) -> None: odb = Odb() assert len(list(odb)) == 0 @@ -53,22 +54,22 @@ def test_emptyodb(barerepo): @pytest.fixture -def odb(barerepo): +def odb(barerepo: Repository) -> Generator[Odb, None, None]: odb = barerepo.odb yield odb -def test_iterable(odb): +def test_iterable(odb: Odb) -> None: assert BLOB_HEX in odb -def test_contains(odb): +def test_contains(odb: Odb) -> None: assert BLOB_HEX in odb -def test_read(odb): +def test_read(odb: Odb) -> None: with pytest.raises(TypeError): - odb.read(123) + odb.read(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, odb.read, '1' * 40) ab = odb.read(BLOB_OID) @@ -84,7 +85,7 @@ def test_read(odb): assert (ObjectType.BLOB, b'a contents\n') == a3 -def test_write(odb): +def test_write(odb: Odb) -> None: data = b'hello world' # invalid object type with pytest.raises(ValueError): From 5424d2c4928a883845614017d1a364ec0b9c74fc Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Tue, 29 Jul 2025 22:22:13 +0200 Subject: [PATCH 05/10] add types to test/test_odb_backend.py --- test/test_odb_backend.py | 54 +++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/test/test_odb_backend.py b/test/test_odb_backend.py index c1995f26..ab1ea588 100644 --- a/test/test_odb_backend.py +++ b/test/test_odb_backend.py @@ -28,11 +28,13 @@ # Standard Library import binascii from pathlib import Path +from typing import Generator, Iterator import pytest # pygit2 import pygit2 +from pygit2 import Odb, Oid, Repository from pygit2.enums import ObjectType from . import utils @@ -43,12 +45,12 @@ @pytest.fixture -def odb(barerepo): +def odb_path(barerepo: Repository) -> Generator[tuple[Odb, Path], None, None]: yield barerepo.odb, Path(barerepo.path) / 'objects' -def test_pack(odb): - odb, path = odb +def test_pack(odb_path: tuple[Odb, Path]) -> None: + odb, path = odb_path pack = pygit2.OdbBackendPack(path) assert len(list(pack)) > 0 @@ -56,8 +58,8 @@ def test_pack(odb): assert obj in odb -def test_loose(odb): - odb, path = odb +def test_loose(odb_path: tuple[Odb, Path]) -> None: + odb, path = odb_path pack = pygit2.OdbBackendLoose(path, 5, False) assert len(list(pack)) > 0 @@ -66,30 +68,30 @@ def test_loose(odb): class ProxyBackend(pygit2.OdbBackend): - def __init__(self, source): + def __init__(self, source: pygit2.OdbBackend | pygit2.OdbBackendPack) -> None: super().__init__() self.source = source - def read_cb(self, oid): + def read_cb(self, oid: Oid | str) -> tuple[int, bytes]: return self.source.read(oid) - def read_prefix_cb(self, oid): + def read_prefix_cb(self, oid: Oid | str) -> tuple[int, bytes, Oid]: return self.source.read_prefix(oid) - def read_header_cb(self, oid): + def read_header_cb(self, oid: Oid | str) -> tuple[int, int]: typ, data = self.source.read(oid) return typ, len(data) - def exists_cb(self, oid): + def exists_cb(self, oid: Oid | str) -> bool: return self.source.exists(oid) - def exists_prefix_cb(self, oid): + def exists_prefix_cb(self, oid: Oid | str) -> Oid: return self.source.exists_prefix(oid) - def refresh_cb(self): + def refresh_cb(self) -> None: self.source.refresh() - def __iter__(self): + def __iter__(self) -> Iterator[Oid]: return iter(self.source) @@ -100,18 +102,18 @@ def __iter__(self): @pytest.fixture -def proxy(barerepo): +def proxy(barerepo: Repository) -> Generator[ProxyBackend, None, None]: path = Path(barerepo.path) / 'objects' yield ProxyBackend(pygit2.OdbBackendPack(path)) -def test_iterable(proxy): +def test_iterable(proxy: ProxyBackend) -> None: assert BLOB_HEX in [o for o in proxy] -def test_read(proxy): +def test_read(proxy: ProxyBackend) -> None: with pytest.raises(TypeError): - proxy.read(123) + proxy.read(123) # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, proxy.read, '1' * 40) ab = proxy.read(BLOB_OID) @@ -120,21 +122,21 @@ def test_read(proxy): assert (ObjectType.BLOB, b'a contents\n') == a -def test_read_prefix(proxy): +def test_read_prefix(proxy: ProxyBackend) -> None: a_hex_prefix = BLOB_HEX[:4] a3 = proxy.read_prefix(a_hex_prefix) assert (ObjectType.BLOB, b'a contents\n', BLOB_OID) == a3 -def test_exists(proxy): +def test_exists(proxy: ProxyBackend) -> None: with pytest.raises(TypeError): - proxy.exists(123) + proxy.exists(123) # type: ignore assert not proxy.exists('1' * 40) assert proxy.exists(BLOB_HEX) -def test_exists_prefix(proxy): +def test_exists_prefix(proxy: ProxyBackend) -> None: a_hex_prefix = BLOB_HEX[:4] assert BLOB_HEX == proxy.exists_prefix(a_hex_prefix) @@ -145,12 +147,12 @@ def test_exists_prefix(proxy): @pytest.fixture -def repo(barerepo): +def repo(barerepo: Repository) -> Generator[Repository, None, None]: odb = pygit2.Odb() path = Path(barerepo.path) / 'objects' - backend = pygit2.OdbBackendPack(path) - backend = ProxyBackend(backend) + backend_org = pygit2.OdbBackendPack(path) + backend = ProxyBackend(backend_org) odb.add_backend(backend, 1) repo = pygit2.Repository() @@ -158,9 +160,9 @@ def repo(barerepo): yield repo -def test_repo_read(repo): +def test_repo_read(repo: Repository) -> None: with pytest.raises(TypeError): - repo[123] + repo[123] # type: ignore utils.assertRaisesWithArg(KeyError, '1' * 40, repo.__getitem__, '1' * 40) From 197cf81c643a2280ca3880583399f1693ce7f82e Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Wed, 30 Jul 2025 20:20:25 +0200 Subject: [PATCH 06/10] add types to test/test_oid.py --- test/test_oid.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/test_oid.py b/test/test_oid.py index 41170226..d57432bf 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -36,42 +36,42 @@ RAW = unhexlify(HEX.encode('ascii')) -def test_raw(): +def test_raw() -> None: oid = Oid(raw=RAW) assert oid.raw == RAW assert oid == HEX -def test_hex(): +def test_hex() -> None: oid = Oid(hex=HEX) assert oid.raw == RAW assert oid == HEX -def test_hex_bytes(): +def test_hex_bytes() -> None: hex = bytes(HEX, 'ascii') with pytest.raises(TypeError): - Oid(hex=hex) + Oid(hex=hex) # type: ignore -def test_none(): +def test_none() -> None: with pytest.raises(ValueError): Oid() -def test_both(): +def test_both() -> None: with pytest.raises(ValueError): Oid(raw=RAW, hex=HEX) -def test_long(): +def test_long() -> None: with pytest.raises(ValueError): Oid(raw=RAW + b'a') with pytest.raises(ValueError): Oid(hex=HEX + 'a') -def test_cmp(): +def test_cmp() -> None: oid1 = Oid(raw=RAW) # Equal @@ -90,7 +90,7 @@ def test_cmp(): assert not oid1 >= oid2 -def test_hash(): +def test_hash() -> None: s = set() s.add(Oid(raw=RAW)) s.add(Oid(hex=HEX)) @@ -101,7 +101,7 @@ def test_hash(): assert len(s) == 3 -def test_bool(): +def test_bool() -> None: assert Oid(raw=RAW) assert Oid(hex=HEX) assert not Oid(raw=b'') From 551af8d33cde4fab7ba6f30250079f3ef2c47e27 Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Wed, 30 Jul 2025 22:01:16 +0200 Subject: [PATCH 07/10] improve types according to test/test_options.py --- pygit2/_pygit2.pyi | 75 +++++++++++++++++++++++++++++++++++++++++++- pygit2/settings.py | 68 ++++++++++++++++++++++++--------------- test/test_options.py | 40 +++++++++++------------ 3 files changed, 136 insertions(+), 47 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 4be51b3e..82819edd 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -36,6 +36,7 @@ from .enums import ( BlobFilter, BranchType, CheckoutStrategy, + ConfigLevel, DeltaStatus, DescribeStrategy, DiffFind, @@ -1078,7 +1079,79 @@ def discover_repository( def hash(data: bytes) -> Oid: ... def hashfile(path: str) -> Oid: ... def init_file_backend(path: str, flags: int = 0) -> object: ... -def option(opt: Option, *args) -> None: ... +@overload +def option( + opt: Literal[ + Option.GET_MWINDOW_FILE_LIMIT, + Option.GET_MWINDOW_MAPPED_LIMIT, + Option.GET_MWINDOW_SIZE, + ], +) -> int: ... +@overload +def option( + opt: Literal[ + Option.SET_MWINDOW_FILE_LIMIT, + Option.SET_MWINDOW_MAPPED_LIMIT, + Option.SET_MWINDOW_SIZE, + ], + value: int, +) -> None: ... +@overload +def option(opt: Literal[Option.GET_SEARCH_PATH], level: ConfigLevel) -> str: ... +@overload +def option( + opt: Literal[Option.SET_SEARCH_PATH], level: ConfigLevel, value: str +) -> None: ... +@overload +def option( + opt: Literal[Option.SET_CACHE_OBJECT_LIMIT], object_type: ObjectType, limit: int +) -> None: ... +@overload +def option(opt: Literal[Option.SET_CACHE_MAX_SIZE], max_size: int) -> None: ... +@overload +def option(opt: Literal[Option.GET_CACHED_MEMORY]) -> tuple[int, int]: ... + +# not implemented: +# Option.GET_TEMPLATE_PATH +# Option.SET_TEMPLATE_PATH + +@overload +def option( + opt: Literal[Option.SET_SSL_CERT_LOCATIONS], + file: str | bytes | None, + dir: str | bytes | None, +) -> None: ... + +# not implemented: +# Option.SET_USER_AGENT + +@overload +def option( + opt: Literal[ + Option.ENABLE_CACHING, + Option.ENABLE_STRICT_OBJECT_CREATION, + Option.ENABLE_STRICT_SYMBOLIC_REF_CREATION, + Option.ENABLE_OFS_DELTA, + Option.ENABLE_FSYNC_GITDIR, + Option.ENABLE_STRICT_HASH_VERIFICATION, + Option.ENABLE_UNSAVED_INDEX_SAFETY, + Option.DISABLE_PACK_KEEP_FILE_CHECKS, + Option.SET_OWNER_VALIDATION, + ], + value: bool, +) -> None: ... +@overload +def option(opt: Literal[Option.GET_OWNER_VALIDATION]) -> int: ... + +# not implemented: +# Option.SET_SSL_CIPHERS +# Option.GET_USER_AGENT +# Option.GET_WINDOWS_SHAREMODE +# Option.SET_WINDOWS_SHAREMODE +# Option.SET_ALLOCATOR +# Option.GET_PACK_MAX_OBJECTS +# Option.SET_PACK_MAX_OBJECTS + def reference_is_valid_name(refname: str) -> bool: ... def tree_entry_cmp(a: Object, b: Object) -> int: ... def _cache_enums() -> None: ... diff --git a/pygit2/settings.py b/pygit2/settings.py index 52eca6d9..da63d979 100644 --- a/pygit2/settings.py +++ b/pygit2/settings.py @@ -27,20 +27,21 @@ Settings mapping. """ -from ssl import get_default_verify_paths +from ssl import DefaultVerifyPaths, get_default_verify_paths +from typing import overload import pygit2.enums from ._pygit2 import option -from .enums import Option +from .enums import ConfigLevel, Option from .errors import GitError class SearchPathList: - def __getitem__(self, key): + def __getitem__(self, key: ConfigLevel) -> str: return option(Option.GET_SEARCH_PATH, key) - def __setitem__(self, key, value): + def __setitem__(self, key: ConfigLevel, value: str) -> None: option(Option.SET_SEARCH_PATH, key, value) @@ -50,12 +51,15 @@ class Settings: __slots__ = '_default_tls_verify_paths', '_ssl_cert_dir', '_ssl_cert_file' _search_path = SearchPathList() + _default_tls_verify_paths: DefaultVerifyPaths | None + _ssl_cert_file: str | bytes | None + _ssl_cert_dir: str | bytes | None - def __init__(self): + def __init__(self) -> None: """Initialize global pygit2 and libgit2 settings.""" self._initialize_tls_certificate_locations() - def _initialize_tls_certificate_locations(self): + def _initialize_tls_certificate_locations(self) -> None: """Set up initial TLS file and directory lookup locations.""" self._default_tls_verify_paths = get_default_verify_paths() try: @@ -72,7 +76,7 @@ def _initialize_tls_certificate_locations(self): self._ssl_cert_dir = None @property - def search_path(self): + def search_path(self) -> SearchPathList: """Configuration file search path. This behaves like an array whose indices correspond to ConfigLevel values. @@ -81,16 +85,16 @@ def search_path(self): return self._search_path @property - def mwindow_size(self): + def mwindow_size(self) -> int: """Get or set the maximum mmap window size""" return option(Option.GET_MWINDOW_SIZE) @mwindow_size.setter - def mwindow_size(self, value): + def mwindow_size(self, value: int) -> None: option(Option.SET_MWINDOW_SIZE, value) @property - def mwindow_mapped_limit(self): + def mwindow_mapped_limit(self) -> int: """ Get or set the maximum memory that will be mapped in total by the library @@ -98,18 +102,18 @@ def mwindow_mapped_limit(self): return option(Option.GET_MWINDOW_MAPPED_LIMIT) @mwindow_mapped_limit.setter - def mwindow_mapped_limit(self, value): + def mwindow_mapped_limit(self, value: int) -> None: option(Option.SET_MWINDOW_MAPPED_LIMIT, value) @property - def cached_memory(self): + def cached_memory(self) -> tuple[int, int]: """ Get the current bytes in cache and the maximum that would be allowed in the cache. """ return option(Option.GET_CACHED_MEMORY) - def enable_caching(self, value=True): + def enable_caching(self, value: bool = True) -> None: """ Enable or disable caching completely. @@ -119,7 +123,7 @@ def enable_caching(self, value=True): """ return option(Option.ENABLE_CACHING, value) - def disable_pack_keep_file_checks(self, value=True): + def disable_pack_keep_file_checks(self, value: bool = True) -> None: """ This will cause .keep file existence checks to be skipped when accessing packfiles, which can help performance with remote @@ -127,7 +131,7 @@ def disable_pack_keep_file_checks(self, value=True): """ return option(Option.DISABLE_PACK_KEEP_FILE_CHECKS, value) - def cache_max_size(self, value): + def cache_max_size(self, value: int) -> None: """ Set the maximum total data size that will be cached in memory across all repositories before libgit2 starts evicting objects @@ -137,7 +141,9 @@ def cache_max_size(self, value): """ return option(Option.SET_CACHE_MAX_SIZE, value) - def cache_object_limit(self, object_type: pygit2.enums.ObjectType, value): + def cache_object_limit( + self, object_type: pygit2.enums.ObjectType, value: int + ) -> None: """ Set the maximum data size for the given type of object to be considered eligible for caching in memory. Setting to value to @@ -148,36 +154,46 @@ def cache_object_limit(self, object_type: pygit2.enums.ObjectType, value): return option(Option.SET_CACHE_OBJECT_LIMIT, object_type, value) @property - def ssl_cert_file(self): + def ssl_cert_file(self) -> str | bytes | None: """TLS certificate file path.""" return self._ssl_cert_file @ssl_cert_file.setter - def ssl_cert_file(self, value): + def ssl_cert_file(self, value: str | bytes) -> None: """Set the TLS cert file path.""" self.set_ssl_cert_locations(value, self._ssl_cert_dir) @ssl_cert_file.deleter - def ssl_cert_file(self): + def ssl_cert_file(self) -> None: """Reset the TLS cert file path.""" - self.ssl_cert_file = self._default_tls_verify_paths.cafile + self.ssl_cert_file = self._default_tls_verify_paths.cafile # type: ignore[union-attr] @property - def ssl_cert_dir(self): + def ssl_cert_dir(self) -> str | bytes | None: """TLS certificates lookup directory path.""" return self._ssl_cert_dir @ssl_cert_dir.setter - def ssl_cert_dir(self, value): + def ssl_cert_dir(self, value: str | bytes) -> None: """Set the TLS certificate lookup folder.""" self.set_ssl_cert_locations(self._ssl_cert_file, value) @ssl_cert_dir.deleter - def ssl_cert_dir(self): + def ssl_cert_dir(self) -> None: """Reset the TLS certificate lookup folder.""" - self.ssl_cert_dir = self._default_tls_verify_paths.capath - - def set_ssl_cert_locations(self, cert_file, cert_dir): + self.ssl_cert_dir = self._default_tls_verify_paths.capath # type: ignore[union-attr] + + @overload + def set_ssl_cert_locations( + self, cert_file: str | bytes | None, cert_dir: str | bytes + ) -> None: ... + @overload + def set_ssl_cert_locations( + self, cert_file: str | bytes, cert_dir: str | bytes | None + ) -> None: ... + def set_ssl_cert_locations( + self, cert_file: str | bytes | None, cert_dir: str | bytes | None + ) -> None: """ Set the SSL certificate-authority locations. diff --git a/test/test_options.py b/test/test_options.py index 5510d714..790f459f 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -28,15 +28,15 @@ from pygit2.enums import ConfigLevel, ObjectType, Option -def __option(getter, setter, value): - old_value = option(getter) - option(setter, value) - assert value == option(getter) +def __option(getter: Option, setter: Option, value: object) -> None: + old_value = option(getter) # type: ignore[call-overload] + option(setter, value) # type: ignore[call-overload] + assert value == option(getter) # type: ignore[call-overload] # Reset to avoid side effects in later tests - option(setter, old_value) + option(setter, old_value) # type: ignore[call-overload] -def __proxy(name, value): +def __proxy(name: str, value: object) -> None: old_value = getattr(pygit2.settings, name) setattr(pygit2.settings, name, value) assert value == getattr(pygit2.settings, name) @@ -44,44 +44,44 @@ def __proxy(name, value): setattr(pygit2.settings, name, old_value) -def test_mwindow_size(): +def test_mwindow_size() -> None: __option(Option.GET_MWINDOW_SIZE, Option.SET_MWINDOW_SIZE, 200 * 1024) -def test_mwindow_size_proxy(): +def test_mwindow_size_proxy() -> None: __proxy('mwindow_size', 300 * 1024) -def test_mwindow_mapped_limit_200(): +def test_mwindow_mapped_limit_200() -> None: __option( Option.GET_MWINDOW_MAPPED_LIMIT, Option.SET_MWINDOW_MAPPED_LIMIT, 200 * 1024 ) -def test_mwindow_mapped_limit_300(): +def test_mwindow_mapped_limit_300() -> None: __proxy('mwindow_mapped_limit', 300 * 1024) -def test_cache_object_limit(): +def test_cache_object_limit() -> None: new_limit = 2 * 1024 option(Option.SET_CACHE_OBJECT_LIMIT, ObjectType.BLOB, new_limit) -def test_cache_object_limit_proxy(): +def test_cache_object_limit_proxy() -> None: new_limit = 4 * 1024 pygit2.settings.cache_object_limit(ObjectType.BLOB, new_limit) -def test_cached_memory(): +def test_cached_memory() -> None: value = option(Option.GET_CACHED_MEMORY) assert value[1] == 256 * 1024**2 -def test_cached_memory_proxy(): +def test_cached_memory_proxy() -> None: assert pygit2.settings.cached_memory[1] == 256 * 1024**2 -def test_enable_caching(): +def test_enable_caching() -> None: pygit2.settings.enable_caching(False) pygit2.settings.enable_caching(True) # Lower level API @@ -89,7 +89,7 @@ def test_enable_caching(): option(Option.ENABLE_CACHING, True) -def test_disable_pack_keep_file_checks(): +def test_disable_pack_keep_file_checks() -> None: pygit2.settings.disable_pack_keep_file_checks(False) pygit2.settings.disable_pack_keep_file_checks(True) # Lower level API @@ -97,14 +97,14 @@ def test_disable_pack_keep_file_checks(): option(Option.DISABLE_PACK_KEEP_FILE_CHECKS, True) -def test_cache_max_size_proxy(): +def test_cache_max_size_proxy() -> None: pygit2.settings.cache_max_size(128 * 1024**2) assert pygit2.settings.cached_memory[1] == 128 * 1024**2 pygit2.settings.cache_max_size(256 * 1024**2) assert pygit2.settings.cached_memory[1] == 256 * 1024**2 -def test_search_path(): +def test_search_path() -> None: paths = [ (ConfigLevel.GLOBAL, '/tmp/global'), (ConfigLevel.XDG, '/tmp/xdg'), @@ -116,7 +116,7 @@ def test_search_path(): assert path == option(Option.GET_SEARCH_PATH, level) -def test_search_path_proxy(): +def test_search_path_proxy() -> None: paths = [ (ConfigLevel.GLOBAL, '/tmp2/global'), (ConfigLevel.XDG, '/tmp2/xdg'), @@ -128,5 +128,5 @@ def test_search_path_proxy(): assert path == pygit2.settings.search_path[level] -def test_owner_validation(): +def test_owner_validation() -> None: __option(Option.GET_OWNER_VALIDATION, Option.SET_OWNER_VALIDATION, 0) From 6a4e860b043d2695fa5924418dd61238127227b5 Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Wed, 30 Jul 2025 22:25:02 +0200 Subject: [PATCH 08/10] improve types according to test/test_packbuilder.py --- pygit2/_libgit2/ffi.pyi | 5 +++++ pygit2/_pygit2.pyi | 9 +++++++++ pygit2/packbuilder.py | 29 +++++++++++++++++------------ test/test_packbuilder.py | 24 +++++++++++++++--------- test/utils.py | 2 +- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/pygit2/_libgit2/ffi.pyi b/pygit2/_libgit2/ffi.pyi index a911985f..4bf2637b 100644 --- a/pygit2/_libgit2/ffi.pyi +++ b/pygit2/_libgit2/ffi.pyi @@ -192,6 +192,9 @@ class GitRepositoryInitOptionsC: class GitCloneOptionsC: pass +class GitPackbuilderC: + pass + class GitProxyTC: pass @@ -275,6 +278,8 @@ def new(a: Literal['git_object *']) -> GitObjectC: ... @overload def new(a: Literal['git_object **']) -> _Pointer[GitObjectC]: ... @overload +def new(a: Literal['git_packbuilder **']) -> _Pointer[GitPackbuilderC]: ... +@overload def new(a: Literal['git_signature *']) -> GitSignatureC: ... @overload def new(a: Literal['git_signature **']) -> _Pointer[GitSignatureC]: ... diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 82819edd..1af40174 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -5,6 +5,7 @@ from pathlib import Path from queue import Queue from threading import Event from typing import ( + Callable, Generic, Iterator, Literal, @@ -58,6 +59,7 @@ from .enums import ( SortMode, ) from .filter import Filter +from .packbuilder import PackBuilder from .remotes import Remote from .repository import BaseRepository from .submodules import SubmoduleCollection @@ -757,6 +759,7 @@ class Repository: def _disown(self, *args, **kwargs) -> None: ... @classmethod def _from_c(cls, ptr: 'GitRepositoryC', owned: bool) -> 'Repository': ... + def __iter__(self) -> Iterator[Oid]: ... def __getitem__(self, key: str | Oid) -> Object: ... def add_worktree(self, name: str, path: str, ref: Reference = ...) -> Worktree: ... def amend_commit( @@ -934,6 +937,12 @@ class Repository: @property def message(self) -> str: ... def notes(self) -> Iterator[Note]: ... + def pack( + self, + path: str | Path | None = None, + pack_delegate: Callable[[PackBuilder], None] | None = None, + n_threads: int | None = None, + ) -> bool: ... def path_is_ignored(self, path: str) -> bool: ... def raw_listall_branches( self, flag: BranchType = BranchType.LOCAL diff --git a/pygit2/packbuilder.py b/pygit2/packbuilder.py index 5551e1fd..b7325c04 100644 --- a/pygit2/packbuilder.py +++ b/pygit2/packbuilder.py @@ -23,15 +23,18 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from os import PathLike # Import from pygit2 +from pygit2 import Oid, Repository + from .errors import check_error from .ffi import C, ffi from .utils import to_bytes class PackBuilder: - def __init__(self, repo): + def __init__(self, repo: Repository) -> None: cpackbuilder = ffi.new('git_packbuilder **') err = C.git_packbuilder_new(cpackbuilder, repo._repo) check_error(err) @@ -41,39 +44,41 @@ def __init__(self, repo): self._cpackbuilder = cpackbuilder @property - def _pointer(self): + def _pointer(self) -> bytes: return bytes(ffi.buffer(self._packbuilder)[:]) - def __del__(self): + def __del__(self) -> None: C.git_packbuilder_free(self._packbuilder) - def __len__(self): + def __len__(self) -> int: return C.git_packbuilder_object_count(self._packbuilder) @staticmethod - def __convert_object_to_oid(oid): + def __convert_object_to_oid(oid: Oid) -> 'ffi.GitOidC': git_oid = ffi.new('git_oid *') ffi.buffer(git_oid)[:] = oid.raw[:] return git_oid - def add(self, oid): + def add(self, oid: Oid) -> None: git_oid = self.__convert_object_to_oid(oid) err = C.git_packbuilder_insert(self._packbuilder, git_oid, ffi.NULL) check_error(err) - def add_recur(self, oid): + def add_recur(self, oid: Oid) -> None: git_oid = self.__convert_object_to_oid(oid) err = C.git_packbuilder_insert_recur(self._packbuilder, git_oid, ffi.NULL) check_error(err) - def set_threads(self, n_threads): + def set_threads(self, n_threads: int) -> int: return C.git_packbuilder_set_threads(self._packbuilder, n_threads) - def write(self, path=None): - path = ffi.NULL if path is None else to_bytes(path) - err = C.git_packbuilder_write(self._packbuilder, path, 0, ffi.NULL, ffi.NULL) + def write(self, path: str | bytes | PathLike[str] | None = None) -> None: + path_bytes = ffi.NULL if path is None else to_bytes(path) + err = C.git_packbuilder_write( + self._packbuilder, path_bytes, 0, ffi.NULL, ffi.NULL + ) check_error(err) @property - def written_objects_count(self): + def written_objects_count(self) -> int: return C.git_packbuilder_written(self._packbuilder) diff --git a/test/test_packbuilder.py b/test/test_packbuilder.py index cea99b39..d6b6a8e1 100644 --- a/test/test_packbuilder.py +++ b/test/test_packbuilder.py @@ -26,20 +26,21 @@ """Tests for Index files.""" from pathlib import Path +from typing import Callable import pygit2 -from pygit2 import PackBuilder +from pygit2 import Oid, PackBuilder, Repository from . import utils -def test_create_packbuilder(testrepo): +def test_create_packbuilder(testrepo: Repository) -> None: # simple test of PackBuilder creation packbuilder = PackBuilder(testrepo) assert len(packbuilder) == 0 -def test_add(testrepo): +def test_add(testrepo: Repository) -> None: # Add a few objects and confirm that the count is correct packbuilder = PackBuilder(testrepo) objects_to_add = [obj for obj in testrepo] @@ -49,9 +50,10 @@ def test_add(testrepo): assert len(packbuilder) == 2 -def test_add_recursively(testrepo): +def test_add_recursively(testrepo: Repository) -> None: # Add the head object and referenced objects recursively and confirm that the count is correct packbuilder = PackBuilder(testrepo) + assert isinstance(testrepo.head.target, Oid) packbuilder.add_recur(testrepo.head.target) # expect a count of 4 made up of the following referenced objects: @@ -63,14 +65,14 @@ def test_add_recursively(testrepo): assert len(packbuilder) == 4 -def test_repo_pack(testrepo, tmp_path): +def test_repo_pack(testrepo: Repository, tmp_path: Path) -> None: # pack the repo with the default strategy confirm_same_repo_after_packing(testrepo, tmp_path, None) -def test_pack_with_delegate(testrepo, tmp_path): +def test_pack_with_delegate(testrepo: Repository, tmp_path: Path) -> None: # loop through all branches and add each commit to the packbuilder - def pack_delegate(pb): + def pack_delegate(pb: PackBuilder) -> None: for branch in pb._repo.branches: br = pb._repo.branches.get(branch) for commit in br.log(): @@ -79,7 +81,7 @@ def pack_delegate(pb): confirm_same_repo_after_packing(testrepo, tmp_path, pack_delegate) -def setup_second_repo(tmp_path): +def setup_second_repo(tmp_path: Path) -> Repository: # helper method to set up a second repo for comparison tmp_path_2 = tmp_path / 'test_repo2' with utils.TemporaryRepository('testrepo.zip', tmp_path_2) as path: @@ -87,7 +89,11 @@ def setup_second_repo(tmp_path): return testrepo -def confirm_same_repo_after_packing(testrepo, tmp_path, pack_delegate): +def confirm_same_repo_after_packing( + testrepo: Repository, + tmp_path: Path, + pack_delegate: Callable[[PackBuilder], None] | None, +) -> None: # Helper method to confirm the contents of two repos before and after packing pack_repo = setup_second_repo(tmp_path) pack_repo_path = Path(pack_repo.path) diff --git a/test/utils.py b/test/utils.py index f4cadbc8..f4490ab4 100644 --- a/test/utils.py +++ b/test/utils.py @@ -89,7 +89,7 @@ def force_rm_handle(remove_path, path, excinfo): remove_path(path) -def rmtree(path): +def rmtree(path: str | Path) -> None: """In Windows a read-only file cannot be removed, and shutil.rmtree fails. So we implement our own version of rmtree to address this issue. """ From e7f0d4b1eab35dd87d28822d0f8c162a8497bddb Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Wed, 30 Jul 2025 22:29:19 +0200 Subject: [PATCH 09/10] add types to test/test_patch_encoding.py --- test/test_patch_encoding.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/test_patch_encoding.py b/test/test_patch_encoding.py index 30072cf4..4a151a70 100644 --- a/test/test_patch_encoding.py +++ b/test/test_patch_encoding.py @@ -24,6 +24,7 @@ # Boston, MA 02110-1301, USA. import pygit2 +from pygit2 import Blob, Repository expected_diff = b"""diff --git a/iso-8859-1.txt b/iso-8859-1.txt index e84e339..201e0c9 100644 @@ -35,7 +36,7 @@ """ -def test_patch_from_non_utf8(): +def test_patch_from_non_utf8() -> None: # blobs encoded in ISO-8859-1 old_content = b'Kristian H\xf8gsberg\n' new_content = old_content + b'foo\n' @@ -54,10 +55,14 @@ def test_patch_from_non_utf8(): assert patch.text.encode('utf-8') != expected_diff -def test_patch_create_from_blobs(encodingrepo): +def test_patch_create_from_blobs(encodingrepo: Repository) -> None: + old_content = encodingrepo['e84e339ac7fcc823106efa65a6972d7a20016c85'] + new_content = encodingrepo['201e0c908e3d9f526659df3e556c3d06384ef0df'] + assert isinstance(old_content, Blob) + assert isinstance(new_content, Blob) patch = pygit2.Patch.create_from( - encodingrepo['e84e339ac7fcc823106efa65a6972d7a20016c85'], - encodingrepo['201e0c908e3d9f526659df3e556c3d06384ef0df'], + old_content, + new_content, old_as_path='iso-8859-1.txt', new_as_path='iso-8859-1.txt', ) From ab2a5fe47eeb2403b5998e280c99377173768a7a Mon Sep 17 00:00:00 2001 From: Benedikt Seidl Date: Wed, 30 Jul 2025 22:33:15 +0200 Subject: [PATCH 10/10] add types to test/test_patch.py --- test/test_patch.py | 51 +++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/test/test_patch.py b/test/test_patch.py index b6b7c630..1c27986e 100644 --- a/test/test_patch.py +++ b/test/test_patch.py @@ -26,6 +26,7 @@ import pytest import pygit2 +from pygit2 import Blob, Repository BLOB_OLD_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_NEW_SHA = '3b18e512dba79e4c8300dd08aeb37f8e728b8dad' @@ -91,9 +92,11 @@ def test_patch_create_from_buffers(): assert patch.text == BLOB_PATCH -def test_patch_create_from_blobs(testrepo): +def test_patch_create_from_blobs(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] + assert isinstance(old_blob, Blob) + assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, @@ -105,8 +108,9 @@ def test_patch_create_from_blobs(testrepo): assert patch.text == BLOB_PATCH2 -def test_patch_create_from_blob_buffer(testrepo): +def test_patch_create_from_blob_buffer(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] + assert isinstance(old_blob, Blob) patch = pygit2.Patch.create_from( old_blob, BLOB_NEW_CONTENT, @@ -117,7 +121,7 @@ def test_patch_create_from_blob_buffer(testrepo): assert patch.text == BLOB_PATCH -def test_patch_create_from_blob_buffer_add(testrepo): +def test_patch_create_from_blob_buffer_add(testrepo: Repository) -> None: patch = pygit2.Patch.create_from( None, BLOB_NEW_CONTENT, @@ -128,8 +132,9 @@ def test_patch_create_from_blob_buffer_add(testrepo): assert patch.text == BLOB_PATCH_ADDED -def test_patch_create_from_blob_buffer_delete(testrepo): +def test_patch_create_from_blob_buffer_delete(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] + assert isinstance(old_blob, Blob) patch = pygit2.Patch.create_from( old_blob, @@ -141,19 +146,21 @@ def test_patch_create_from_blob_buffer_delete(testrepo): assert patch.text == BLOB_PATCH_DELETED -def test_patch_create_from_bad_old_type_arg(testrepo): +def test_patch_create_from_bad_old_type_arg(testrepo: Repository) -> None: with pytest.raises(TypeError): - pygit2.Patch.create_from(testrepo, BLOB_NEW_CONTENT) + pygit2.Patch.create_from(testrepo, BLOB_NEW_CONTENT) # type: ignore -def test_patch_create_from_bad_new_type_arg(testrepo): +def test_patch_create_from_bad_new_type_arg(testrepo: Repository) -> None: with pytest.raises(TypeError): - pygit2.Patch.create_from(None, testrepo) + pygit2.Patch.create_from(None, testrepo) # type: ignore -def test_context_lines(testrepo): +def test_context_lines(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] + assert isinstance(old_blob, Blob) + assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, @@ -162,6 +169,7 @@ def test_context_lines(testrepo): new_as_path=BLOB_NEW_PATH, ) + assert patch.text is not None context_count = len( [line for line in patch.text.splitlines() if line.startswith(' ')] ) @@ -169,9 +177,11 @@ def test_context_lines(testrepo): assert context_count != 0 -def test_no_context_lines(testrepo): +def test_no_context_lines(testrepo: Repository) -> None: old_blob = testrepo[BLOB_OLD_SHA] new_blob = testrepo[BLOB_NEW_SHA] + assert isinstance(old_blob, Blob) + assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, @@ -181,6 +191,7 @@ def test_no_context_lines(testrepo): context_lines=0, ) + assert patch.text is not None context_count = len( [line for line in patch.text.splitlines() if line.startswith(' ')] ) @@ -188,9 +199,11 @@ def test_no_context_lines(testrepo): assert context_count == 0 -def test_patch_create_blob_blobs(testrepo): +def test_patch_create_blob_blobs(testrepo: Repository) -> None: old_blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] new_blob = testrepo[testrepo.create_blob(BLOB_NEW_CONTENT)] + assert isinstance(old_blob, Blob) + assert isinstance(new_blob, Blob) patch = pygit2.Patch.create_from( old_blob, @@ -202,8 +215,9 @@ def test_patch_create_blob_blobs(testrepo): assert patch.text == BLOB_PATCH -def test_patch_create_blob_buffer(testrepo): +def test_patch_create_blob_buffer(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] + assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, BLOB_NEW_CONTENT, @@ -214,8 +228,9 @@ def test_patch_create_blob_buffer(testrepo): assert patch.text == BLOB_PATCH -def test_patch_create_blob_delete(testrepo): +def test_patch_create_blob_delete(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_OLD_CONTENT)] + assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, None, @@ -226,8 +241,9 @@ def test_patch_create_blob_delete(testrepo): assert patch.text == BLOB_PATCH_DELETED -def test_patch_create_blob_add(testrepo): +def test_patch_create_blob_add(testrepo: Repository) -> None: blob = testrepo[testrepo.create_blob(BLOB_NEW_CONTENT)] + assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( None, blob, @@ -238,8 +254,9 @@ def test_patch_create_blob_add(testrepo): assert patch.text == BLOB_PATCH_ADDED -def test_patch_delete_blob(testrepo): +def test_patch_delete_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_OLD_SHA] + assert isinstance(blob, Blob) patch = pygit2.Patch.create_from( blob, None, @@ -253,12 +270,14 @@ def test_patch_delete_blob(testrepo): assert patch.text == BLOB_PATCH_DELETED -def test_patch_multi_blob(testrepo): +def test_patch_multi_blob(testrepo: Repository) -> None: blob = testrepo[BLOB_OLD_SHA] + assert isinstance(blob, Blob) patch = pygit2.Patch.create_from(blob, None) patch_text = patch.text blob = testrepo[BLOB_OLD_SHA] + assert isinstance(blob, Blob) patch2 = pygit2.Patch.create_from(blob, None) patch_text2 = patch.text