From 55bc2861cdd014ee454c29cae2a1d6c7eec3312e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 18:13:32 +0700 Subject: [PATCH 01/44] add some rules to ruff --- pyproject.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 20d4b61a..b5e4e2f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,18 @@ extend-exclude = [ "build", "venv*", ] +lint.select = ["C", "E", "W", "F", "I", "B", "C4", "ARG", "SIM", "PTH", "PL", "TID"] +lint.ignore = [ + "W291", # Trailing whitespace + "E501", # Line too long + "W293", # Blank line contains whitespace + "PLR0912", # Too many branches + "PLR2004", # Magic values + "PLR0915", # Too many statements + "PLW0603", # Global statement + "PLR0913", # Too many arguments + "B010", # setattr +] [tool.ruff.format] quote-style = "single" From 2c06530cf746e8d230b9c156e5187d84fd369859 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 18:28:48 +0700 Subject: [PATCH 02/44] ignore unused imports --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b5e4e2f2..50c037af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ lint.ignore = [ "PLW0603", # Global statement "PLR0913", # Too many arguments "B010", # setattr + "F401" # unused imports ] [tool.ruff.format] From 484886a335628d691bb971343e0699666f2b71b6 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 18:32:42 +0700 Subject: [PATCH 03/44] Initial formatting and liniting fix of all files --- build_tag.py | 3 +- pygit2/__init__.py | 26 +++++++++------- pygit2/_pygit2.pyi | 3 +- pygit2/_run.py | 2 +- pygit2/blame.py | 4 +-- pygit2/blob.py | 2 +- pygit2/branches.py | 3 +- pygit2/callbacks.py | 18 ++++-------- pygit2/config.py | 8 ++--- pygit2/credentials.py | 3 +- pygit2/errors.py | 7 ++--- pygit2/ffi.py | 3 +- pygit2/index.py | 9 +++--- pygit2/packbuilder.py | 2 +- pygit2/references.py | 1 + pygit2/refspec.py | 2 +- pygit2/remotes.py | 13 ++++---- pygit2/repository.py | 42 ++++++++++++++------------ pygit2/submodules.py | 7 +++-- pygit2/utils.py | 2 +- setup.py | 12 ++++---- test/conftest.py | 4 ++- test/test_apply_diff.py | 9 +++--- test/test_archive.py | 5 ++-- test/test_attributes.py | 2 +- test/test_blame.py | 5 ++-- test/test_blob.py | 38 ++++++++++++------------ test/test_branch.py | 7 +++-- test/test_branch_empty.py | 2 +- test/test_cherrypick.py | 1 + test/test_commit.py | 42 +++++++++++++------------- test/test_commit_gpg.py | 8 ++--- test/test_commit_trailer.py | 3 +- test/test_config.py | 30 +++++++++---------- test/test_credentials.py | 6 ++-- test/test_describe.py | 38 ++++++++++++------------ test/test_diff.py | 49 +++++++++++++++--------------- test/test_diff_binary.py | 8 ++--- test/test_filter.py | 27 +++++++++-------- test/test_index.py | 11 +++---- test/test_mailmap.py | 1 - test/test_merge.py | 26 ++++++++-------- test/test_note.py | 2 +- test/test_object.py | 5 ++-- test/test_odb.py | 8 ++--- test/test_odb_backend.py | 10 +++---- test/test_oid.py | 4 +-- test/test_packbuilder.py | 11 +++---- test/test_patch.py | 2 +- test/test_patch_encoding.py | 1 - test/test_refdb_backend.py | 3 +- test/test_refs.py | 32 ++++++++++++-------- test/test_remote.py | 54 +++++++++++++++++----------------- test/test_remote_utf8.py | 4 ++- test/test_repository.py | 36 +++++++++++------------ test/test_repository_bare.py | 35 +++++++++++----------- test/test_repository_custom.py | 3 +- test/test_revparse.py | 3 +- test/test_revwalk.py | 5 ++-- test/test_signature.py | 6 ++-- test/test_submodule.py | 15 +++++----- test/test_tag.py | 13 ++++---- test/test_tree.py | 10 +++---- test/utils.py | 3 +- 64 files changed, 391 insertions(+), 368 deletions(-) diff --git a/build_tag.py b/build_tag.py index 66c61b68..5051f997 100644 --- a/build_tag.py +++ b/build_tag.py @@ -1,4 +1,5 @@ -import platform, sys +import platform +import sys py = {'CPython': 'cp', 'PyPy': 'pp'}[platform.python_implementation()] print(f'{py}{sys.version_info.major}{sys.version_info.minor}') diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 48857f96..e245184c 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -25,24 +25,31 @@ # Standard Library import functools -from os import PathLike import typing - -# Low level API -from ._pygit2 import * -from ._pygit2 import _cache_enums +from os import PathLike # High level API from . import enums from ._build import __version__ + +# Low level API +from ._pygit2 import * +from ._pygit2 import _cache_enums from .blame import Blame, BlameHunk from .blob import BlobIO -from .callbacks import Payload, RemoteCallbacks, CheckoutCallbacks, StashApplyCallbacks -from .callbacks import git_clone_options, git_fetch_options, get_credentials +from .callbacks import ( + CheckoutCallbacks, + Payload, + RemoteCallbacks, + StashApplyCallbacks, + get_credentials, + git_clone_options, + git_fetch_options, +) from .config import Config from .credentials import * -from .errors import check_error, Passthrough -from .ffi import ffi, C +from .errors import Passthrough, check_error +from .ffi import C, ffi from .filter import Filter from .index import Index, IndexEntry from .legacyenums import * @@ -53,7 +60,6 @@ from .submodules import Submodule from .utils import to_bytes, to_str - # Features features = enums.Feature(C.git_libgit2_features()) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index a73da2a3..5c3c879c 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -1,5 +1,6 @@ -from typing import Iterator, Literal, Optional, overload from io import IOBase +from typing import Iterator, Literal, Optional, overload + from . import Index from .enums import ( ApplyLocation, diff --git a/pygit2/_run.py b/pygit2/_run.py index 78b52a29..94f99426 100644 --- a/pygit2/_run.py +++ b/pygit2/_run.py @@ -29,8 +29,8 @@ # Import from the Standard Library import codecs -from pathlib import Path import sys +from pathlib import Path # Import from cffi from cffi import FFI diff --git a/pygit2/blame.py b/pygit2/blame.py index 3b7ef748..8a156362 100644 --- a/pygit2/blame.py +++ b/pygit2/blame.py @@ -24,9 +24,9 @@ # Boston, MA 02110-1301, USA. # Import from pygit2 -from .ffi import ffi, C +from ._pygit2 import Oid, Signature +from .ffi import C, ffi from .utils import GenericIterator -from ._pygit2 import Signature, Oid def wrap_signature(csig): diff --git a/pygit2/blob.py b/pygit2/blob.py index d9f4de89..19e3f3c4 100644 --- a/pygit2/blob.py +++ b/pygit2/blob.py @@ -2,8 +2,8 @@ import threading import time from contextlib import AbstractContextManager -from typing import Optional from queue import Queue +from typing import Optional from ._pygit2 import Blob, Oid from .enums import BlobFilter diff --git a/pygit2/branches.py b/pygit2/branches.py index 99630d31..21d887ad 100644 --- a/pygit2/branches.py +++ b/pygit2/branches.py @@ -24,10 +24,11 @@ # Boston, MA 02110-1301, USA. from __future__ import annotations + from typing import TYPE_CHECKING -from .enums import BranchType, ReferenceType from ._pygit2 import Commit, Oid +from .enums import BranchType, ReferenceType # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: diff --git a/pygit2/callbacks.py b/pygit2/callbacks.py index a01268b6..5ebe475c 100644 --- a/pygit2/callbacks.py +++ b/pygit2/callbacks.py @@ -68,12 +68,11 @@ from typing import Optional, Union # pygit2 -from ._pygit2 import Oid, DiffFile +from ._pygit2 import DiffFile, Oid from .enums import CheckoutNotify, CheckoutStrategy, CredentialType, StashApplyProgress -from .errors import check_error, Passthrough -from .ffi import ffi, C -from .utils import maybe_string, to_bytes, ptr_to_bytes, StrArray - +from .errors import Passthrough, check_error +from .ffi import C, ffi +from .utils import StrArray, maybe_string, ptr_to_bytes, to_bytes # # The payload is the way to pass information from the pygit2 API, through @@ -471,9 +470,7 @@ def _certificate_check_cb(cert_i, valid, host, data): if not val: return C.GIT_ECERTIFICATE except Passthrough: - if is_ssh: - return 0 - elif valid: + if is_ssh or valid: return 0 else: return C.GIT_ECERTIFICATE @@ -669,10 +666,7 @@ def _git_checkout_options( paths=None, c_checkout_options_ptr=None, ): - if callbacks is None: - payload = CheckoutCallbacks() - else: - payload = callbacks + payload = CheckoutCallbacks() if callbacks is None else callbacks # Get handle to payload handle = ffi.new_handle(payload) diff --git a/pygit2/config.py b/pygit2/config.py index 8143a1f1..e5bce33b 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -29,8 +29,10 @@ from cached_property import cached_property # Import from pygit2 +import contextlib + from .errors import check_error -from .ffi import ffi, C +from .ffi import C, ffi from .utils import to_bytes @@ -96,10 +98,8 @@ def from_c(cls, repo, ptr): return config def __del__(self): - try: + with contextlib.suppress(AttributeError): C.git_config_free(self._config) - except AttributeError: - pass def _get(self, key): key = str_to_bytes(key, 'key') diff --git a/pygit2/credentials.py b/pygit2/credentials.py index fb5ae81e..f16aac4a 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -23,9 +23,8 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -from .ffi import C - from .enums import CredentialType +from .ffi import C class Username: diff --git a/pygit2/errors.py b/pygit2/errors.py index d95bfaa6..53108072 100644 --- a/pygit2/errors.py +++ b/pygit2/errors.py @@ -24,11 +24,10 @@ # Boston, MA 02110-1301, USA. # Import from pygit2 -from .ffi import ffi, C from ._pygit2 import GitError +from .ffi import C, ffi - -value_errors = set([C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS]) +value_errors = {C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS} def check_error(err, io=False): @@ -36,7 +35,7 @@ def check_error(err, io=False): return # These are special error codes, they should never reach here - test = err != C.GIT_EUSER and err != C.GIT_PASSTHROUGH + test = err not in (C.GIT_EUSER, C.GIT_PASSTHROUGH) assert test, f'Unexpected error code {err}' # Error message diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 7a60faf7..25814e0e 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -24,4 +24,5 @@ # Boston, MA 02110-1301, USA. # Import from pygit2 -from ._libgit2 import ffi, lib as C +from ._libgit2 import ffi +from ._libgit2 import lib as C diff --git a/pygit2/index.py b/pygit2/index.py index b06ae684..df164c0f 100644 --- a/pygit2/index.py +++ b/pygit2/index.py @@ -27,12 +27,11 @@ import weakref # Import from pygit2 -from ._pygit2 import Oid, Tree, Diff +from ._pygit2 import Diff, Oid, Tree from .enums import DiffOption, FileMode from .errors import check_error -from .ffi import ffi, C -from .utils import to_bytes, to_str -from .utils import GenericIterator, StrArray +from .ffi import C, ffi +from .utils import GenericIterator, StrArray, to_bytes, to_str class Index: @@ -367,7 +366,7 @@ def oid(self): @property def hex(self): """The id of the referenced object as a hex string""" - warnings.warn('Use str(entry.id)', DeprecationWarning) + warnings.warn('Use str(entry.id)', DeprecationWarning, stacklevel=2) return str(self.id) def __str__(self): diff --git a/pygit2/packbuilder.py b/pygit2/packbuilder.py index 0bee41ac..8fefcd89 100644 --- a/pygit2/packbuilder.py +++ b/pygit2/packbuilder.py @@ -26,7 +26,7 @@ # Import from pygit2 from .errors import check_error -from .ffi import ffi, C +from .ffi import C, ffi from .utils import to_bytes diff --git a/pygit2/references.py b/pygit2/references.py index f36d7be2..45784ca7 100644 --- a/pygit2/references.py +++ b/pygit2/references.py @@ -24,6 +24,7 @@ # Boston, MA 02110-1301, USA. from __future__ import annotations + from typing import TYPE_CHECKING from .enums import ReferenceFilter diff --git a/pygit2/refspec.py b/pygit2/refspec.py index 423d820a..0af6aa73 100644 --- a/pygit2/refspec.py +++ b/pygit2/refspec.py @@ -25,7 +25,7 @@ # Import from pygit2 from .errors import check_error -from .ffi import ffi, C +from .ffi import C, ffi from .utils import to_bytes diff --git a/pygit2/remotes.py b/pygit2/remotes.py index 02a4dbe2..b26bc917 100644 --- a/pygit2/remotes.py +++ b/pygit2/remotes.py @@ -24,17 +24,19 @@ # Boston, MA 02110-1301, USA. from __future__ import annotations + from typing import TYPE_CHECKING +from . import utils + # Import from pygit2 from ._pygit2 import Oid from .callbacks import git_fetch_options, git_push_options, git_remote_callbacks from .enums import FetchPrune from .errors import check_error -from .ffi import ffi, C +from .ffi import C, ffi from .refspec import Refspec -from . import utils -from .utils import maybe_string, to_bytes, strarray_to_strings, StrArray +from .utils import StrArray, maybe_string, strarray_to_strings, to_bytes # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: @@ -185,10 +187,7 @@ def ls_remotes(self, callbacks=None, proxy=None): for i in range(int(refs_len[0])): ref = refs[0][i] local = bool(ref.local) - if local: - loid = Oid(raw=bytes(ffi.buffer(ref.loid.id)[:])) - else: - loid = None + loid = Oid(raw=bytes(ffi.buffer(ref.loid.id)[:])) if local else None remote = { 'local': local, diff --git a/pygit2/repository.py b/pygit2/repository.py index 38a969d3..fb2d7038 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -23,19 +23,28 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +import tarfile +import typing from io import BytesIO from os import PathLike from string import hexdigits from time import time -import tarfile -import typing -# Import from pygit2 -from ._pygit2 import Repository as _Repository, init_file_backend -from ._pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN -from ._pygit2 import Reference, Tree, Commit, Blob, Signature -from ._pygit2 import InvalidSpecError +from ._pygit2 import ( + GIT_OID_HEXSZ, + GIT_OID_MINPREFIXLEN, + Blob, + Commit, + InvalidSpecError, + Oid, + Reference, + Signature, + Tree, + init_file_backend, +) +# Import from pygit2 +from ._pygit2 import Repository as _Repository from .blame import Blame from .branches import Branches from .callbacks import git_checkout_options, git_stash_apply_options @@ -56,13 +65,13 @@ RepositoryState, ) from .errors import check_error -from .ffi import ffi, C +from .ffi import C, ffi from .index import Index, IndexEntry from .packbuilder import PackBuilder from .references import References from .remotes import RemoteCollection from .submodules import SubmoduleCollection -from .utils import to_bytes, StrArray +from .utils import StrArray, to_bytes class BaseRepository(_Repository): @@ -167,10 +176,7 @@ def hashfile( """ c_path = to_bytes(path) - if as_path is None: - c_as_path = ffi.NULL - else: - c_as_path = to_bytes(as_path) + c_as_path = ffi.NULL if as_path is None else to_bytes(as_path) c_oid = ffi.new('git_oid *') @@ -271,11 +277,11 @@ def create_reference(self, name, target, force=False, message=None): def listall_references(self) -> typing.List[str]: """Return a list with all the references in the repository.""" - return list(x.name for x in self.references.iterator()) + return [x.name for x in self.references.iterator()] def listall_reference_objects(self) -> typing.List[Reference]: """Return a list with all the reference objects in the repository.""" - return list(x for x in self.references.iterator()) + return list(self.references.iterator()) def resolve_refish(self, refish): """Convert a reference-like short name "ref-ish" to a valid @@ -435,7 +441,7 @@ def __whatever_to_tree_or_blob(self, obj): return None # If it's a string, then it has to be valid revspec - if isinstance(obj, str) or isinstance(obj, bytes): + if isinstance(obj, (str, bytes)): obj = self.revparse_single(obj) elif isinstance(obj, Oid): obj = self[obj] @@ -527,7 +533,7 @@ def diff( # Case 1: Diff tree to tree if isinstance(a, Tree) and isinstance(b, Tree): - return a.diff_to_tree(b, **dict(zip(opt_keys, opt_values))) + return a.diff_to_tree(b, **dict(zip(opt_keys, opt_values, strict=False))) # Case 2: Index to workdir elif a is None and b is None: @@ -1370,7 +1376,7 @@ def get_attr( elif attr_kind == C.GIT_ATTR_VALUE_STRING: return ffi.string(cvalue[0]).decode('utf-8') - assert False, 'the attribute value from libgit2 is invalid' + raise AssertionError('the attribute value from libgit2 is invalid') # # Identity for reference operations diff --git a/pygit2/submodules.py b/pygit2/submodules.py index 08b1a9a5..257ef00c 100644 --- a/pygit2/submodules.py +++ b/pygit2/submodules.py @@ -24,14 +24,15 @@ # Boston, MA 02110-1301, USA. from __future__ import annotations + from typing import TYPE_CHECKING, Iterable, Iterator, Optional, Union from ._pygit2 import Oid -from .callbacks import git_fetch_options, RemoteCallbacks +from .callbacks import RemoteCallbacks, git_fetch_options from .enums import SubmoduleIgnore, SubmoduleStatus from .errors import check_error -from .ffi import ffi, C -from .utils import to_bytes, maybe_string +from .ffi import C, ffi +from .utils import maybe_string, to_bytes # Need BaseRepository for type hints, but don't let it cause a circular dependency if TYPE_CHECKING: diff --git a/pygit2/utils.py b/pygit2/utils.py index 0660cc42..bf6d7ead 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -27,7 +27,7 @@ import os # Import from pygit2 -from .ffi import ffi, C +from .ffi import C, ffi def maybe_string(ptr): diff --git a/setup.py b/setup.py index 7c158eff..7751bdfc 100644 --- a/setup.py +++ b/setup.py @@ -24,15 +24,15 @@ # Boston, MA 02110-1301, USA. # Import setuptools before distutils to avoid user warning -from setuptools import setup, Extension - +import os +import sys +from distutils import log from distutils.command.build import build from distutils.command.sdist import sdist -from distutils import log -import os from pathlib import Path -from subprocess import Popen, PIPE -import sys +from subprocess import PIPE, Popen + +from setuptools import Extension, setup # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') diff --git a/test/conftest.py b/test/conftest.py index 1c6d7b8f..6feb9d23 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,8 +1,10 @@ -from pathlib import Path import platform +from pathlib import Path import pytest + import pygit2 + from . import utils diff --git a/test/test_apply_diff.py b/test/test_apply_diff.py index 915d4368..7dc6da16 100644 --- a/test/test_apply_diff.py +++ b/test/test_apply_diff.py @@ -23,13 +23,14 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -import pygit2 -from pygit2.enums import ApplyLocation, CheckoutStrategy, FileStatus -import pytest - import os from pathlib import Path +import pytest + +import pygit2 +from pygit2.enums import ApplyLocation, CheckoutStrategy, FileStatus + def read_content(testrepo): with (Path(testrepo.workdir) / 'hello.txt').open('rb') as f: diff --git a/test/test_archive.py b/test/test_archive.py index b6977714..bb41bc22 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -23,11 +23,10 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -from pathlib import Path import tarfile +from pathlib import Path -from pygit2 import Index, Oid, Tree, Object - +from pygit2 import Index, Object, Oid, Tree TREE_HASH = 'fd937514cb799514d4b81bb24c5fcfeb6472b245' COMMIT_HASH = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' diff --git a/test/test_attributes.py b/test/test_attributes.py index 73b0cbe4..f5df0c6b 100644 --- a/test/test_attributes.py +++ b/test/test_attributes.py @@ -38,7 +38,7 @@ def test_no_attr(testrepo): assert testrepo.get_attr('file.py', 'foo') is None assert testrepo.get_attr('file.py', 'text') assert not testrepo.get_attr('file.jpg', 'text') - assert 'lf' == testrepo.get_attr('file.sh', 'eol') + assert testrepo.get_attr('file.sh', 'eol') == 'lf' def test_no_attr_aspath(testrepo): diff --git a/test/test_blame.py b/test/test_blame.py index 8bd7fca3..e0a6a2d1 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -27,10 +27,9 @@ import pytest -from pygit2 import Signature, Oid +from pygit2 import Oid, Signature from pygit2.enums import BlameFlag - PATH = 'hello.txt' HUNKS = [ @@ -109,7 +108,7 @@ def test(): def test_blame_for_line(testrepo): blame = testrepo.blame(PATH) - for i, line in zip(range(0, 2), range(1, 3)): + for i, line in zip(range(0, 2), range(1, 3), strict=False): hunk = blame.for_line(line) assert hunk.lines_in_hunk == 1 diff --git a/test/test_blob.py b/test/test_blob.py index 78cec628..afec399c 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -27,15 +27,15 @@ import io from pathlib import Path -from threading import Event from queue import Queue +from threading import Event import pytest import pygit2 from pygit2.enums import ObjectType -from . import utils +from . import utils BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_CONTENT = """hello world @@ -86,10 +86,10 @@ def test_read_blob(testrepo): assert blob.id == BLOB_SHA assert isinstance(blob, pygit2.Blob) assert not blob.is_binary - assert ObjectType.BLOB == blob.type - assert BLOB_CONTENT == blob.data + assert blob.type == ObjectType.BLOB + assert blob.data == BLOB_CONTENT assert len(BLOB_CONTENT) == blob.size - assert BLOB_CONTENT == blob.read_raw() + assert blob.read_raw() == BLOB_CONTENT def test_create_blob(testrepo): @@ -97,17 +97,17 @@ def test_create_blob(testrepo): blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) - assert ObjectType.BLOB == blob.type + assert blob.type == ObjectType.BLOB assert blob_oid == blob.id assert utils.gen_blob_sha1(BLOB_NEW_CONTENT) == blob_oid - assert BLOB_NEW_CONTENT == blob.data + assert blob.data == BLOB_NEW_CONTENT assert len(BLOB_NEW_CONTENT) == blob.size - assert BLOB_NEW_CONTENT == blob.read_raw() + assert blob.read_raw() == BLOB_NEW_CONTENT blob_buffer = memoryview(blob) assert len(BLOB_NEW_CONTENT) == len(blob_buffer) - assert BLOB_NEW_CONTENT == blob_buffer + assert blob_buffer == BLOB_NEW_CONTENT def set_content(): blob_buffer[:2] = b'hi' @@ -121,14 +121,14 @@ def test_create_blob_fromworkdir(testrepo): blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) - assert ObjectType.BLOB == blob.type + assert blob.type == ObjectType.BLOB assert blob_oid == blob.id assert utils.gen_blob_sha1(BLOB_FILE_CONTENT) == blob_oid - assert BLOB_FILE_CONTENT == blob.data + assert blob.data == BLOB_FILE_CONTENT assert len(BLOB_FILE_CONTENT) == blob.size - assert BLOB_FILE_CONTENT == blob.read_raw() + assert blob.read_raw() == BLOB_FILE_CONTENT def test_create_blob_fromworkdir_aspath(testrepo): @@ -148,7 +148,7 @@ def test_create_blob_fromdisk(testrepo): blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) - assert ObjectType.BLOB == blob.type + assert blob.type == ObjectType.BLOB def test_create_blob_fromiobase(testrepo): @@ -160,10 +160,10 @@ def test_create_blob_fromiobase(testrepo): blob = testrepo[blob_oid] assert isinstance(blob, pygit2.Blob) - assert ObjectType.BLOB == blob.type + assert blob.type == ObjectType.BLOB assert blob_oid == blob.id - assert BLOB_SHA == blob_oid + assert blob_oid == BLOB_SHA def test_diff_blob(testrepo): @@ -220,7 +220,7 @@ def test_blob_write_to_queue(testrepo): chunks = [] while not queue.empty(): chunks.append(queue.get()) - assert BLOB_CONTENT == b''.join(chunks) + assert b''.join(chunks) == BLOB_CONTENT def test_blob_write_to_queue_filtered(testrepo): @@ -235,14 +235,14 @@ def test_blob_write_to_queue_filtered(testrepo): chunks = [] while not queue.empty(): chunks.append(queue.get()) - assert b'bye world\n' == b''.join(chunks) + assert b''.join(chunks) == b'bye world\n' def test_blobio(testrepo): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] with pygit2.BlobIO(blob) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' assert not reader.raw._thread.is_alive() @@ -250,5 +250,5 @@ def test_blobio_filtered(testrepo): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] with pygit2.BlobIO(blob, as_path='bye.txt') as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' assert not reader.raw._thread.is_alive() diff --git a/test/test_branch.py b/test/test_branch.py index 4f4903a4..82f7799c 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -25,11 +25,12 @@ """Tests for branch methods.""" -import pygit2 -import pytest import os -from pygit2.enums import BranchType +import pytest + +import pygit2 +from pygit2.enums import BranchType LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' I18N_LAST_COMMIT = '5470a671a80ac3789f1a6a8cefbcf43ce7af0563' diff --git a/test/test_branch_empty.py b/test/test_branch_empty.py index 2afd749f..fe33f812 100644 --- a/test/test_branch_empty.py +++ b/test/test_branch_empty.py @@ -24,8 +24,8 @@ # Boston, MA 02110-1301, USA. import pytest -from pygit2.enums import BranchType +from pygit2.enums import BranchType ORIGIN_MASTER_COMMIT = '784855caf26449a1914d2cf62d12b9374d76ae78' diff --git a/test/test_cherrypick.py b/test/test_cherrypick.py index a5e5c558..5d6da70c 100644 --- a/test/test_cherrypick.py +++ b/test/test_cherrypick.py @@ -26,6 +26,7 @@ """Tests for merging and information about it.""" from pathlib import Path + import pytest import pygit2 diff --git a/test/test_commit.py b/test/test_commit.py index 521059b9..b3254128 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -29,10 +29,10 @@ import pytest -from pygit2 import Signature, Oid, GitError +from pygit2 import GitError, Oid, Signature from pygit2.enums import ObjectType -from . import utils +from . import utils COMMIT_SHA = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA_TO_AMEND = ( @@ -52,10 +52,10 @@ def test_commit_refcount(barerepo): def test_read_commit(barerepo): commit = barerepo[COMMIT_SHA] - assert COMMIT_SHA == commit.id + assert commit.id == COMMIT_SHA parents = commit.parents - assert 1 == len(parents) - assert 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' == parents[0].id + assert len(parents) == 1 + assert parents[0].id == 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' assert commit.message_encoding is None assert commit.message == ( 'Second test data commit.\n\n' 'This commit has some additional text.\n' @@ -68,7 +68,7 @@ def test_read_commit(barerepo): assert commit.author == Signature( 'Dave Borowitz', 'dborowitz@google.com', 1288477363, -420 ) - assert '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' == commit.tree.id + assert commit.tree.id == '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' def test_new_commit(barerepo): @@ -89,17 +89,17 @@ def test_new_commit(barerepo): sha = repo.create_commit(None, author, committer, message, tree_prefix, parents) commit = repo[sha] - assert ObjectType.COMMIT == commit.type - assert '98286caaab3f1fde5bf52c8369b2b0423bad743b' == commit.id + assert commit.type == ObjectType.COMMIT + assert commit.id == '98286caaab3f1fde5bf52c8369b2b0423bad743b' assert commit.message_encoding is None assert message == commit.message - assert 12346 == commit.commit_time + assert commit.commit_time == 12346 assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id - assert 1 == len(commit.parents) - assert COMMIT_SHA == commit.parents[0].id + assert len(commit.parents) == 1 + assert commit.parents[0].id == COMMIT_SHA assert Oid(hex=COMMIT_SHA) == commit.parent_ids[0] @@ -118,16 +118,16 @@ def test_new_commit_encoding(barerepo): ) commit = repo[sha] - assert ObjectType.COMMIT == commit.type - assert 'iso-8859-1' == commit.message_encoding + assert commit.type == ObjectType.COMMIT + assert commit.message_encoding == 'iso-8859-1' assert message.encode(encoding) == commit.raw_message - assert 12346 == commit.commit_time + assert commit.commit_time == 12346 assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id - assert 1 == len(commit.parents) - assert COMMIT_SHA == commit.parents[0].id + assert len(commit.parents) == 1 + assert commit.parents[0].id == COMMIT_SHA assert Oid(hex=COMMIT_SHA) == commit.parent_ids[0] @@ -175,7 +175,7 @@ def test_amend_commit_metadata(barerepo): amended_commit = repo[amended_oid] assert repo.head.target == amended_oid - assert ObjectType.COMMIT == amended_commit.type + assert amended_commit.type == ObjectType.COMMIT assert amended_committer == amended_commit.committer assert amended_author == amended_commit.author assert amended_message.encode(encoding) == amended_commit.raw_message @@ -196,7 +196,7 @@ def test_amend_commit_tree(barerepo): amended_commit = repo[amended_oid] assert repo.head.target == amended_oid - assert ObjectType.COMMIT == amended_commit.type + assert amended_commit.type == ObjectType.COMMIT assert commit.message == amended_commit.message assert commit.author == amended_commit.author assert commit.committer == amended_commit.committer @@ -267,13 +267,13 @@ def test_amend_commit_argument_types(barerepo): # Pass an Oid for the commit amended_oid = repo.amend_commit(alt_commit1, None, message='Hello') amended_commit = repo[amended_oid] - assert ObjectType.COMMIT == amended_commit.type + assert amended_commit.type == ObjectType.COMMIT assert amended_oid != COMMIT_SHA_TO_AMEND # Pass a str for the commit amended_oid = repo.amend_commit(alt_commit2, None, message='Hello', tree=alt_tree) amended_commit = repo[amended_oid] - assert ObjectType.COMMIT == amended_commit.type + assert amended_commit.type == ObjectType.COMMIT assert amended_oid != COMMIT_SHA_TO_AMEND assert repo[COMMIT_SHA_TO_AMEND].tree != amended_commit.tree assert alt_tree.id == amended_commit.tree_id @@ -282,6 +282,6 @@ def test_amend_commit_argument_types(barerepo): # (Warning: the tip of the branch will be altered after this test!) amended_oid = repo.amend_commit(alt_commit2, alt_refname, message='Hello') amended_commit = repo[amended_oid] - assert ObjectType.COMMIT == amended_commit.type + assert amended_commit.type == ObjectType.COMMIT assert amended_oid != COMMIT_SHA_TO_AMEND assert repo.head.target == amended_oid diff --git a/test/test_commit_gpg.py b/test/test_commit_gpg.py index cb1c812e..58675639 100644 --- a/test/test_commit_gpg.py +++ b/test/test_commit_gpg.py @@ -119,16 +119,16 @@ def test_commit_signing(gpgsigned): assert gpgsig_content == commit.read_raw().decode('utf-8') # perform sanity checks - assert ObjectType.COMMIT == commit.type - assert '6569fdf71dbd99081891154641869c537784a3ba' == commit.id + assert commit.type == ObjectType.COMMIT + assert commit.id == '6569fdf71dbd99081891154641869c537784a3ba' assert commit.message_encoding is None assert message == commit.message - assert 1358451456 == commit.commit_time + assert commit.commit_time == 1358451456 assert committer == commit.committer assert author == commit.author assert tree == commit.tree.id assert Oid(hex=tree) == commit.tree_id - assert 1 == len(commit.parents) + assert len(commit.parents) == 1 assert parent == commit.parents[0].id assert Oid(hex=parent) == commit.parent_ids[0] diff --git a/test/test_commit_trailer.py b/test/test_commit_trailer.py index 7f07825e..b176122d 100644 --- a/test/test_commit_trailer.py +++ b/test/test_commit_trailer.py @@ -23,9 +23,10 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -import pygit2 import pytest +import pygit2 + from . import utils diff --git a/test/test_config.py b/test/test_config.py index c89c7d38..06e5a8a2 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -23,13 +23,14 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +import contextlib from pathlib import Path import pytest from pygit2 import Config -from . import utils +from . import utils CONFIG_FILENAME = 'test_config' @@ -37,10 +38,8 @@ @pytest.fixture def config(testrepo): yield testrepo.config - try: + with contextlib.suppress(OSError): Path(CONFIG_FILENAME).unlink() - except OSError: - pass def test_config(config): @@ -155,23 +154,24 @@ def test_multivar(): config.add_file(CONFIG_FILENAME, 6) assert 'this.that' in config - assert ['foobar', 'foobeer'] == list(config.get_multivar('this.that')) - assert ['foobar'] == list(config.get_multivar('this.that', 'bar')) - assert ['foobar', 'foobeer'] == list(config.get_multivar('this.that', 'foo.*')) + assert list(config.get_multivar('this.that')) == ['foobar', 'foobeer'] + assert list(config.get_multivar('this.that', 'bar')) == ['foobar'] + assert list(config.get_multivar('this.that', 'foo.*')) == ['foobar', 'foobeer'] config.set_multivar('this.that', '^.*beer', 'fool') - assert ['fool'] == list(config.get_multivar('this.that', 'fool')) + assert list(config.get_multivar('this.that', 'fool')) == ['fool'] config.set_multivar('this.that', 'foo.*', 'foo-123456') - assert ['foo-123456', 'foo-123456'] == list( - config.get_multivar('this.that', 'foo.*') - ) + assert list(config.get_multivar('this.that', 'foo.*')) == [ + 'foo-123456', + 'foo-123456', + ] config.delete_multivar('this.that', 'bar') - assert ['foo-123456', 'foo-123456'] == list(config.get_multivar('this.that', '')) + assert list(config.get_multivar('this.that', '')) == ['foo-123456', 'foo-123456'] config.delete_multivar('this.that', 'foo-[0-9]+') - assert [] == list(config.get_multivar('this.that', '')) + assert list(config.get_multivar('this.that', '')) == [] def test_iterator(config): @@ -188,5 +188,5 @@ def test_parsing(): assert Config.parse_bool('on') assert Config.parse_bool('1') - assert 5 == Config.parse_int('5') - assert 1024 == Config.parse_int('1k') + assert Config.parse_int('5') == 5 + assert Config.parse_int('1k') == 1024 diff --git a/test/test_credentials.py b/test/test_credentials.py index 0e9adf66..90b870d3 100644 --- a/test/test_credentials.py +++ b/test/test_credentials.py @@ -25,16 +25,16 @@ """Tests for credentials""" -from pathlib import Path import platform +from pathlib import Path import pytest import pygit2 -from pygit2 import Username, UserPass, Keypair, KeypairFromAgent, KeypairFromMemory +from pygit2 import Keypair, KeypairFromAgent, KeypairFromMemory, Username, UserPass from pygit2.enums import CredentialType -from . import utils +from . import utils REMOTE_NAME = 'origin' REMOTE_URL = 'git://github.com/libgit2/pygit2.git' diff --git a/test/test_describe.py b/test/test_describe.py index 7ae41c28..65a2b02a 100644 --- a/test/test_describe.py +++ b/test/test_describe.py @@ -27,8 +27,8 @@ import pytest -from pygit2.enums import DescribeStrategy, ObjectType import pygit2 +from pygit2.enums import DescribeStrategy, ObjectType def add_tag(repo, name, target): @@ -41,7 +41,7 @@ def add_tag(repo, name, target): def test_describe(testrepo): add_tag(testrepo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') - assert 'thetag-2-g2be5719' == testrepo.describe() + assert testrepo.describe() == 'thetag-2-g2be5719' def test_describe_without_ref(testrepo): @@ -50,19 +50,20 @@ def test_describe_without_ref(testrepo): def test_describe_default_oid(testrepo): - assert '2be5719' == testrepo.describe(show_commit_oid_as_fallback=True) + assert testrepo.describe(show_commit_oid_as_fallback=True) == '2be5719' def test_describe_strategies(testrepo): - assert 'heads/master' == testrepo.describe(describe_strategy=DescribeStrategy.ALL) + assert testrepo.describe(describe_strategy=DescribeStrategy.ALL) == 'heads/master' testrepo.create_reference( 'refs/tags/thetag', '4ec4389a8068641da2d6578db0419484972284c8' ) with pytest.raises(KeyError): testrepo.describe() - assert 'thetag-2-g2be5719' == testrepo.describe( - describe_strategy=DescribeStrategy.TAGS + assert ( + testrepo.describe(describe_strategy=DescribeStrategy.TAGS) + == 'thetag-2-g2be5719' ) @@ -70,20 +71,21 @@ def test_describe_pattern(testrepo): add_tag(testrepo, 'private/tag1', '5ebeeebb320790caf276b9fc8b24546d63316533') add_tag(testrepo, 'public/tag2', '4ec4389a8068641da2d6578db0419484972284c8') - assert 'public/tag2-2-g2be5719' == testrepo.describe(pattern='public/*') + assert testrepo.describe(pattern='public/*') == 'public/tag2-2-g2be5719' def test_describe_committish(testrepo): add_tag(testrepo, 'thetag', 'acecd5ea2924a4b900e7e149496e1f4b57976e51') - assert 'thetag-4-g2be5719' == testrepo.describe(committish='HEAD') - assert 'thetag-1-g5ebeeeb' == testrepo.describe(committish='HEAD^') + assert testrepo.describe(committish='HEAD') == 'thetag-4-g2be5719' + assert testrepo.describe(committish='HEAD^') == 'thetag-1-g5ebeeeb' - assert 'thetag-4-g2be5719' == testrepo.describe(committish=testrepo.head) + assert testrepo.describe(committish=testrepo.head) == 'thetag-4-g2be5719' - assert 'thetag-1-g6aaa262' == testrepo.describe( - committish='6aaa262e655dd54252e5813c8e5acd7780ed097d' + assert ( + testrepo.describe(committish='6aaa262e655dd54252e5813c8e5acd7780ed097d') + == 'thetag-1-g6aaa262' ) - assert 'thetag-1-g6aaa262' == testrepo.describe(committish='6aaa262') + assert testrepo.describe(committish='6aaa262') == 'thetag-1-g6aaa262' def test_describe_follows_first_branch_only(testrepo): @@ -94,20 +96,20 @@ def test_describe_follows_first_branch_only(testrepo): def test_describe_abbreviated_size(testrepo): add_tag(testrepo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') - assert 'thetag-2-g2be5719152d4f82c' == testrepo.describe(abbreviated_size=16) - assert 'thetag' == testrepo.describe(abbreviated_size=0) + assert testrepo.describe(abbreviated_size=16) == 'thetag-2-g2be5719152d4f82c' + assert testrepo.describe(abbreviated_size=0) == 'thetag' def test_describe_long_format(testrepo): add_tag(testrepo, 'thetag', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') - assert 'thetag-0-g2be5719' == testrepo.describe(always_use_long_format=True) + assert testrepo.describe(always_use_long_format=True) == 'thetag-0-g2be5719' def test_describe_dirty(dirtyrepo): add_tag(dirtyrepo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22') - assert 'thetag' == dirtyrepo.describe() + assert dirtyrepo.describe() == 'thetag' def test_describe_dirty_with_suffix(dirtyrepo): add_tag(dirtyrepo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22') - assert 'thetag-dirty' == dirtyrepo.describe(dirty_suffix='-dirty') + assert dirtyrepo.describe(dirty_suffix='-dirty') == 'thetag-dirty' diff --git a/test/test_diff.py b/test/test_diff.py index b2138526..869fc3ec 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -25,15 +25,14 @@ """Tests for Diff objects.""" -from itertools import chain import textwrap +from itertools import chain import pytest import pygit2 from pygit2.enums import DeltaStatus, DiffFlag, DiffOption, DiffStatsFormat, FileMode - COMMIT_SHA1_1 = '5fe808e8953c12735680c257f56600cb0de44b10' COMMIT_SHA1_2 = 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' COMMIT_SHA1_3 = '2cdae28389c059815e951d0bb9eed6533f61a46b' @@ -114,11 +113,11 @@ def test_diff_empty_index(dirtyrepo): diff = head.tree.diff_to_index(repo.index) files = [patch.delta.new_file.path for patch in diff] - assert DIFF_HEAD_TO_INDEX_EXPECTED == files + assert files == DIFF_HEAD_TO_INDEX_EXPECTED diff = repo.diff('HEAD', cached=True) files = [patch.delta.new_file.path for patch in diff] - assert DIFF_HEAD_TO_INDEX_EXPECTED == files + assert files == DIFF_HEAD_TO_INDEX_EXPECTED def test_workdir_to_tree(dirtyrepo): @@ -127,17 +126,17 @@ def test_workdir_to_tree(dirtyrepo): diff = head.tree.diff_to_workdir() files = [patch.delta.new_file.path for patch in diff] - assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files + assert files == DIFF_HEAD_TO_WORKDIR_EXPECTED diff = repo.diff('HEAD') files = [patch.delta.new_file.path for patch in diff] - assert DIFF_HEAD_TO_WORKDIR_EXPECTED == files + assert files == DIFF_HEAD_TO_WORKDIR_EXPECTED def test_index_to_workdir(dirtyrepo): diff = dirtyrepo.diff() files = [patch.delta.new_file.path for patch in diff] - assert DIFF_INDEX_TO_WORK_EXPECTED == files + assert files == DIFF_INDEX_TO_WORK_EXPECTED def test_diff_invalid(barerepo): @@ -172,7 +171,7 @@ def test_diff_tree(barerepo): def _test(diff): assert diff is not None - assert 2 == sum(map(lambda x: len(x.hunks), diff)) + assert sum((len(x.hunks) for x in diff)) == 2 patch = diff[0] hunk = patch.hunks[0] @@ -204,18 +203,18 @@ def test_diff_empty_tree(barerepo): diff = commit_a.tree.diff_to_tree() def get_context_for_lines(diff): - hunks = chain.from_iterable(map(lambda x: x.hunks, diff)) - lines = chain.from_iterable(map(lambda x: x.lines, hunks)) - return map(lambda x: x.origin, lines) + hunks = chain.from_iterable((x.hunks for x in diff)) + lines = chain.from_iterable((x.lines for x in hunks)) + return (x.origin for x in lines) entries = [p.delta.new_file.path for p in diff] assert all(commit_a.tree[x] for x in entries) - assert all('-' == x for x in get_context_for_lines(diff)) + assert all(x == '-' for x in get_context_for_lines(diff)) diff_swaped = commit_a.tree.diff_to_tree(swap=True) entries = [p.delta.new_file.path for p in diff_swaped] assert all(commit_a.tree[x] for x in entries) - assert all('+' == x for x in get_context_for_lines(diff_swaped)) + assert all(x == '+' for x in get_context_for_lines(diff_swaped)) def test_diff_revparse(barerepo): @@ -230,11 +229,11 @@ def test_diff_tree_opts(barerepo): for flag in [DiffOption.IGNORE_WHITESPACE, DiffOption.IGNORE_WHITESPACE_EOL]: diff = commit_c.tree.diff_to_tree(commit_d.tree, flag) assert diff is not None - assert 0 == len(diff[0].hunks) + assert len(diff[0].hunks) == 0 diff = commit_c.tree.diff_to_tree(commit_d.tree) assert diff is not None - assert 1 == len(diff[0].hunks) + assert len(diff[0].hunks) == 1 def test_diff_merge(barerepo): @@ -270,7 +269,7 @@ def test_diff_patch(barerepo): diff = commit_a.tree.diff_to_tree(commit_b.tree) assert diff.patch == PATCH - assert len(diff) == len([patch for patch in diff]) + assert len(diff) == len(list(diff)) def test_diff_ids(barerepo): @@ -296,7 +295,7 @@ def test_hunk_content(barerepo): patch = commit_a.tree.diff_to_tree(commit_b.tree)[0] hunk = patch.hunks[0] lines = (f'{x.origin} {x.content}' for x in hunk.lines) - assert HUNK_EXPECTED == ''.join(lines) + assert ''.join(lines) == HUNK_EXPECTED for line in hunk.lines: assert line.content == line.raw_content.decode() @@ -321,13 +320,13 @@ def test_diff_stats(barerepo): diff = commit_a.tree.diff_to_tree(commit_b.tree) stats = diff.stats - assert 1 == stats.insertions - assert 2 == stats.deletions - assert 2 == stats.files_changed + assert stats.insertions == 1 + assert stats.deletions == 2 + assert stats.files_changed == 2 formatted = stats.format( format=DiffStatsFormat.FULL | DiffStatsFormat.INCLUDE_SUMMARY, width=80 ) - assert STATS_EXPECTED == formatted + assert formatted == STATS_EXPECTED def test_deltas(barerepo): @@ -357,12 +356,12 @@ def test_diff_parse(barerepo): diff = pygit2.Diff.parse_diff(PATCH) stats = diff.stats - assert 2 == stats.deletions - assert 1 == stats.insertions - assert 2 == stats.files_changed + assert stats.deletions == 2 + assert stats.insertions == 1 + assert stats.files_changed == 2 deltas = list(diff.deltas) - assert 2 == len(deltas) + assert len(deltas) == 2 def test_parse_diff_null(): diff --git a/test/test_diff_binary.py b/test/test_diff_binary.py index 17eceaac..b4b877db 100644 --- a/test/test_diff_binary.py +++ b/test/test_diff_binary.py @@ -56,10 +56,10 @@ def repo(tmp_path): def test_binary_diff(repo): diff = repo.diff('HEAD', 'HEAD^') - assert PATCH_BINARY == diff.patch + assert diff.patch == PATCH_BINARY diff = repo.diff('HEAD', 'HEAD^', flags=DiffOption.SHOW_BINARY) - assert PATCH_BINARY_SHOW == diff.patch + assert diff.patch == PATCH_BINARY_SHOW diff = repo.diff(b'HEAD', b'HEAD^') - assert PATCH_BINARY == diff.patch + assert diff.patch == PATCH_BINARY diff = repo.diff(b'HEAD', b'HEAD^', flags=DiffOption.SHOW_BINARY) - assert PATCH_BINARY_SHOW == diff.patch + assert diff.patch == PATCH_BINARY_SHOW diff --git a/test/test_filter.py b/test/test_filter.py index f37f9e1c..43ce09c7 100644 --- a/test/test_filter.py +++ b/test/test_filter.py @@ -1,5 +1,6 @@ -from io import BytesIO import codecs +from io import BytesIO + import pytest import pygit2 @@ -75,44 +76,44 @@ def test_filter(testrepo, rot13_filter): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD - assert b'olr jbeyq\n' == blob.data + assert blob.data == b'olr jbeyq\n' with pygit2.BlobIO(blob) as reader: - assert b'olr jbeyq\n' == reader.read() + assert reader.read() == b'olr jbeyq\n' with pygit2.BlobIO(blob, as_path='bye.txt', flags=flags) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' def test_filter_buffered(testrepo, buffered_filter): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD - assert b'olr jbeyq\n' == blob.data + assert blob.data == b'olr jbeyq\n' with pygit2.BlobIO(blob) as reader: - assert b'olr jbeyq\n' == reader.read() + assert reader.read() == b'olr jbeyq\n' with pygit2.BlobIO(blob, 'bye.txt', flags=flags) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' def test_filter_passthrough(testrepo, passthrough_filter): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD - assert b'bye world\n' == blob.data + assert blob.data == b'bye world\n' with pygit2.BlobIO(blob) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' with pygit2.BlobIO(blob, 'bye.txt', flags=flags) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' def test_filter_unmatched(testrepo, unmatched_filter): blob_oid = testrepo.create_blob_fromworkdir('bye.txt') blob = testrepo[blob_oid] flags = BlobFilter.CHECK_FOR_BINARY | BlobFilter.ATTRIBUTES_FROM_HEAD - assert b'bye world\n' == blob.data + assert blob.data == b'bye world\n' with pygit2.BlobIO(blob) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' with pygit2.BlobIO(blob, as_path='bye.txt', flags=flags) as reader: - assert b'bye world\n' == reader.read() + assert reader.read() == b'bye world\n' def test_filter_cleanup(dirtyrepo, rot13_filter): diff --git a/test/test_index.py b/test/test_index.py index cf10eefd..beaf9c0f 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -30,8 +30,9 @@ import pytest import pygit2 -from pygit2 import Repository, Index, Oid +from pygit2 import Index, Oid, Repository from pygit2.enums import FileMode + from . import utils @@ -164,7 +165,7 @@ def test_iter(testrepo): # Compare SHAs, not IndexEntry object identity entries = [index[x].id for x in range(n)] - assert list(x.id for x in index) == entries + assert [x.id for x in index] == entries def test_mode(testrepo): @@ -224,9 +225,9 @@ def test_change_attributes(testrepo): entry.path = 'foo.txt' entry.id = ign_entry.id entry.mode = FileMode.BLOB_EXECUTABLE - assert 'foo.txt' == entry.path + assert entry.path == 'foo.txt' assert ign_entry.id == entry.id - assert FileMode.BLOB_EXECUTABLE == entry.mode + assert entry.mode == FileMode.BLOB_EXECUTABLE def test_write_tree_to(testrepo, tmp_path): @@ -242,7 +243,7 @@ def test_create_entry(testrepo): hello_entry = index['hello.txt'] entry = pygit2.IndexEntry('README.md', hello_entry.id, hello_entry.mode) index.add(entry) - assert '60e769e57ae1d6a2ab75d8d253139e6260e1f912' == index.write_tree() + assert index.write_tree() == '60e769e57ae1d6a2ab75d8d253139e6260e1f912' def test_create_entry_aspath(testrepo): diff --git a/test/test_mailmap.py b/test/test_mailmap.py index 0adeaa63..96765ed2 100644 --- a/test/test_mailmap.py +++ b/test/test_mailmap.py @@ -27,7 +27,6 @@ from pygit2 import Mailmap - TEST_MAILMAP = """\ # Simple Comment line diff --git a/test/test_merge.py b/test/test_merge.py index cadc1657..cd68b1bd 100644 --- a/test/test_merge.py +++ b/test/test_merge.py @@ -30,7 +30,7 @@ import pytest import pygit2 -from pygit2.enums import FileStatus, MergeAnalysis, MergeFavor, MergeFlag, MergeFileFlag +from pygit2.enums import FileStatus, MergeAnalysis, MergeFavor, MergeFileFlag, MergeFlag @pytest.mark.parametrize('id', [None, 42]) @@ -46,12 +46,12 @@ def test_merge_analysis_uptodate(mergerepo): analysis, preference = mergerepo.merge_analysis(branch_id) assert analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD - assert {} == mergerepo.status() + assert mergerepo.status() == {} analysis, preference = mergerepo.merge_analysis(branch_id, 'refs/heads/ff-branch') assert analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD - assert {} == mergerepo.status() + assert mergerepo.status() == {} def test_merge_analysis_fastforward(mergerepo): @@ -61,12 +61,12 @@ def test_merge_analysis_fastforward(mergerepo): analysis, preference = mergerepo.merge_analysis(branch_id) assert not analysis & MergeAnalysis.UP_TO_DATE assert analysis & MergeAnalysis.FASTFORWARD - assert {} == mergerepo.status() + assert mergerepo.status() == {} analysis, preference = mergerepo.merge_analysis(branch_id, 'refs/heads/master') assert not analysis & MergeAnalysis.UP_TO_DATE assert analysis & MergeAnalysis.FASTFORWARD - assert {} == mergerepo.status() + assert mergerepo.status() == {} def test_merge_no_fastforward_no_conflicts(mergerepo): @@ -76,8 +76,8 @@ def test_merge_no_fastforward_no_conflicts(mergerepo): assert not analysis & MergeAnalysis.UP_TO_DATE assert not analysis & MergeAnalysis.FASTFORWARD # Asking twice to assure the reference counting is correct - assert {} == mergerepo.status() - assert {} == mergerepo.status() + assert mergerepo.status() == {} + assert mergerepo.status() == {} def test_merge_invalid_hex(mergerepo): @@ -113,22 +113,22 @@ def test_merge_no_fastforward_conflicts(mergerepo): status = FileStatus.CONFLICTED # Asking twice to assure the reference counting is correct - assert {'.gitignore': status} == mergerepo.status() - assert {'.gitignore': status} == mergerepo.status() + assert mergerepo.status() == {'.gitignore': status} + assert mergerepo.status() == {'.gitignore': status} ancestor, ours, theirs = mergerepo.index.conflicts['.gitignore'] assert ancestor is None assert ours is not None assert theirs is not None - assert '.gitignore' == ours.path - assert '.gitignore' == theirs.path - assert 1 == len(list(mergerepo.index.conflicts)) + assert ours.path == '.gitignore' + assert theirs.path == '.gitignore' + assert len(list(mergerepo.index.conflicts)) == 1 # Checking the index works as expected mergerepo.index.add('.gitignore') mergerepo.index.write() assert mergerepo.index.conflicts is None - assert {'.gitignore': FileStatus.INDEX_MODIFIED} == mergerepo.status() + assert mergerepo.status() == {'.gitignore': FileStatus.INDEX_MODIFIED} def test_merge_remove_conflicts(mergerepo): diff --git a/test/test_note.py b/test/test_note.py index dbd85ec9..7a761d95 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -25,9 +25,9 @@ """Tests for note objects.""" -from pygit2 import Signature import pytest +from pygit2 import Signature NOTE = ('6c8980ba963cad8b25a9bcaf68d4023ee57370d8', 'note message') diff --git a/test/test_object.py b/test/test_object.py index 40c01d7a..c42dd873 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -27,10 +27,9 @@ import pytest -from pygit2 import Tree, Tag +from pygit2 import Tag, Tree from pygit2.enums import ObjectType - BLOB_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_CONTENT = """hello world hola mundo @@ -48,7 +47,7 @@ def test_equality(testrepo): assert commit_a is not commit_b assert commit_a == commit_b - assert not (commit_a != commit_b) + assert commit_a == commit_b def test_hashing(testrepo): diff --git a/test/test_odb.py b/test/test_odb.py index fe91c957..8724248d 100644 --- a/test/test_odb.py +++ b/test/test_odb.py @@ -34,8 +34,8 @@ # pygit2 from pygit2 import Odb, Oid from pygit2.enums import ObjectType -from . import utils +from . import utils BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) @@ -74,14 +74,14 @@ def test_read(odb): ab = odb.read(BLOB_OID) a = odb.read(BLOB_HEX) assert ab == a - assert (ObjectType.BLOB, b'a contents\n') == a + assert a == (ObjectType.BLOB, b'a contents\n') a2 = odb.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') - assert (ObjectType.BLOB, b'a contents 2\n') == a2 + assert a2 == (ObjectType.BLOB, b'a contents 2\n') a_hex_prefix = BLOB_HEX[:4] a3 = odb.read(a_hex_prefix) - assert (ObjectType.BLOB, b'a contents\n') == a3 + assert a3 == (ObjectType.BLOB, b'a contents\n') def test_write(odb): diff --git a/test/test_odb_backend.py b/test/test_odb_backend.py index c99d47f4..f91d6f04 100644 --- a/test/test_odb_backend.py +++ b/test/test_odb_backend.py @@ -34,8 +34,8 @@ # pygit2 import pygit2 from pygit2.enums import ObjectType -from . import utils +from . import utils BLOB_HEX = 'af431f20fc541ed6d5afede3e2dc7160f6f01f16' BLOB_RAW = binascii.unhexlify(BLOB_HEX.encode('ascii')) @@ -106,7 +106,7 @@ def proxy(barerepo): def test_iterable(proxy): - assert BLOB_HEX in [o for o in proxy] + assert BLOB_HEX in list(proxy) def test_read(proxy): @@ -117,13 +117,13 @@ def test_read(proxy): ab = proxy.read(BLOB_OID) a = proxy.read(BLOB_HEX) assert ab == a - assert (ObjectType.BLOB, b'a contents\n') == a + assert a == (ObjectType.BLOB, b'a contents\n') def test_read_prefix(proxy): a_hex_prefix = BLOB_HEX[:4] a3 = proxy.read_prefix(a_hex_prefix) - assert (ObjectType.BLOB, b'a contents\n', BLOB_OID) == a3 + assert a3 == (ObjectType.BLOB, b'a contents\n', BLOB_OID) def test_exists(proxy): @@ -136,7 +136,7 @@ def test_exists(proxy): def test_exists_prefix(proxy): a_hex_prefix = BLOB_HEX[:4] - assert BLOB_HEX == proxy.exists_prefix(a_hex_prefix) + assert proxy.exists_prefix(a_hex_prefix) == BLOB_HEX # diff --git a/test/test_oid.py b/test/test_oid.py index c16d688f..e2070ec7 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -28,9 +28,9 @@ # Standard Library from binascii import unhexlify -from pygit2 import Oid import pytest +from pygit2 import Oid HEX = '15b648aec6ed045b5ca6f57f8b7831a8b4757298' RAW = unhexlify(HEX.encode('ascii')) @@ -85,7 +85,7 @@ def test_cmp(): # Other assert oid1 < oid2 assert oid1 <= oid2 - assert not oid1 == oid2 + assert oid1 != oid2 assert not oid1 > oid2 assert not oid1 >= oid2 diff --git a/test/test_packbuilder.py b/test/test_packbuilder.py index f61d475f..235b79b8 100644 --- a/test/test_packbuilder.py +++ b/test/test_packbuilder.py @@ -29,6 +29,7 @@ import pygit2 from pygit2 import PackBuilder + from . import utils @@ -41,7 +42,7 @@ def test_create_packbuilder(testrepo): def test_add(testrepo): # Add a few objects and confirm that the count is correct packbuilder = PackBuilder(testrepo) - objects_to_add = [obj for obj in testrepo] + objects_to_add = list(testrepo) packbuilder.add(objects_to_add[0]) assert len(packbuilder) == 1 packbuilder.add(objects_to_add[1]) @@ -98,14 +99,14 @@ def confirm_same_repo_after_packing(testrepo, tmp_path, pack_delegate): # assert that the number of written objects is the same as the number of objects in the repo written_objects = testrepo.pack(pack_path, pack_delegate=pack_delegate) - assert written_objects == len([obj for obj in testrepo]) + assert written_objects == len(list(testrepo)) # assert that the number of objects in the pack repo is the same as the original repo - orig_objects = [obj for obj in testrepo.odb] - packed_objects = [obj for obj in pack_repo.odb] + orig_objects = list(testrepo.odb) + packed_objects = list(pack_repo.odb) assert len(packed_objects) == len(orig_objects) # assert that the objects in the packed repo are the same objects as the original repo - for i, obj in enumerate(orig_objects): + for _i, obj in enumerate(orig_objects): assert pack_repo[obj].type == testrepo[obj].type assert pack_repo[obj].read_raw() == testrepo[obj].read_raw() diff --git a/test/test_patch.py b/test/test_patch.py index d15a2e53..6c4496ae 100644 --- a/test/test_patch.py +++ b/test/test_patch.py @@ -23,9 +23,9 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -import pygit2 import pytest +import pygit2 BLOB_OLD_SHA = 'a520c24d85fbfc815d385957eed41406ca5a860b' BLOB_NEW_SHA = '3b18e512dba79e4c8300dd08aeb37f8e728b8dad' diff --git a/test/test_patch_encoding.py b/test/test_patch_encoding.py index 12f8b514..bd2b2a8d 100644 --- a/test/test_patch_encoding.py +++ b/test/test_patch_encoding.py @@ -25,7 +25,6 @@ import pygit2 - expected_diff = b"""diff --git a/iso-8859-1.txt b/iso-8859-1.txt index e84e339..201e0c9 100644 --- a/iso-8859-1.txt diff --git a/test/test_refdb_backend.py b/test/test_refdb_backend.py index 19a944b1..c4913e91 100644 --- a/test/test_refdb_backend.py +++ b/test/test_refdb_backend.py @@ -27,9 +27,10 @@ from pathlib import Path -import pygit2 import pytest +import pygit2 + # Note: the refdb abstraction from libgit2 is meant to provide information # which libgit2 transforms into something more useful, and in general YMMV by diff --git a/test/test_refs.py b/test/test_refs.py index a6caa103..3f8aaf1e 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -29,11 +29,17 @@ import pytest -from pygit2 import Commit, Signature, Tree, reference_is_valid_name -from pygit2 import AlreadyExistsError, GitError, InvalidSpecError +from pygit2 import ( + AlreadyExistsError, + Commit, + GitError, + InvalidSpecError, + Signature, + Tree, + reference_is_valid_name, +) from pygit2.enums import ReferenceType - LAST_COMMIT = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' @@ -60,8 +66,8 @@ def test_refs_list(testrepo): def test_head(testrepo): head = testrepo.head - assert LAST_COMMIT == testrepo[head.target].id - assert LAST_COMMIT == testrepo[head.raw_target].id + assert testrepo[head.target].id == LAST_COMMIT + assert testrepo[head.raw_target].id == LAST_COMMIT def test_refs_getitem(testrepo): @@ -150,11 +156,11 @@ def test_refs_delete(testrepo): # Access the deleted reference with pytest.raises(GitError): - getattr(reference, 'name') + reference.name with pytest.raises(GitError): - getattr(reference, 'type') + reference.type with pytest.raises(GitError): - getattr(reference, 'target') + reference.target with pytest.raises(GitError): reference.delete() with pytest.raises(GitError): @@ -261,10 +267,10 @@ def test_refs_equality(testrepo): assert ref1 is not ref2 assert ref1 == ref2 - assert not ref1 != ref2 + assert ref1 == ref2 assert ref1 != ref3 - assert not ref1 == ref3 + assert ref1 != ref3 def test_refs_compress(testrepo): @@ -583,11 +589,11 @@ def test_delete(testrepo): # Access the deleted reference with pytest.raises(GitError): - getattr(reference, 'name') + reference.name with pytest.raises(GitError): - getattr(reference, 'type') + reference.type with pytest.raises(GitError): - getattr(reference, 'target') + reference.target with pytest.raises(GitError): reference.delete() with pytest.raises(GitError): diff --git a/test/test_remote.py b/test/test_remote.py index 0688cbb0..6f5fc578 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -25,16 +25,16 @@ """Tests for Remote objects.""" -from unittest.mock import patch import sys +from unittest.mock import patch import pytest import pygit2 from pygit2 import Oid from pygit2.ffi import ffi -from . import utils +from . import utils REMOTE_NAME = 'origin' REMOTE_URL = 'https://github.com/libgit2/pygit2.git' @@ -84,8 +84,8 @@ def test_remote_create_anonymous(testrepo): assert remote.name is None assert url == remote.url assert remote.push_url is None - assert [] == remote.fetch_refspecs - assert [] == remote.push_refspecs + assert remote.fetch_refspecs == [] + assert remote.push_refspecs == [] def test_remote_delete(testrepo): @@ -93,21 +93,21 @@ def test_remote_delete(testrepo): url = 'https://github.com/libgit2/pygit2.git' testrepo.remotes.create(name, url) - assert 2 == len(testrepo.remotes) + assert len(testrepo.remotes) == 2 remote = testrepo.remotes[1] assert name == remote.name testrepo.remotes.delete(remote.name) - assert 1 == len(testrepo.remotes) + assert len(testrepo.remotes) == 1 def test_remote_rename(testrepo): remote = testrepo.remotes[0] - assert REMOTE_NAME == remote.name + assert remote.name == REMOTE_NAME problems = testrepo.remotes.rename(remote.name, 'new') - assert [] == problems - assert 'new' != remote.name + assert problems == [] + assert remote.name != 'new' with pytest.raises(ValueError): testrepo.remotes.rename('', '') @@ -117,7 +117,7 @@ def test_remote_rename(testrepo): def test_remote_set_url(testrepo): remote = testrepo.remotes['origin'] - assert REMOTE_URL == remote.url + assert remote.url == REMOTE_URL new_url = 'https://github.com/cholin/pygit2.git' testrepo.remotes.set_url('origin', new_url) @@ -142,34 +142,34 @@ def test_refspec(testrepo): assert refspec.src == REMOTE_FETCHSPEC_SRC assert refspec.dst == REMOTE_FETCHSPEC_DST assert refspec.force is True - assert ORIGIN_REFSPEC == refspec.string + assert refspec.string == ORIGIN_REFSPEC assert list == type(remote.fetch_refspecs) - assert 1 == len(remote.fetch_refspecs) - assert ORIGIN_REFSPEC == remote.fetch_refspecs[0] + assert len(remote.fetch_refspecs) == 1 + assert remote.fetch_refspecs[0] == ORIGIN_REFSPEC assert refspec.src_matches('refs/heads/master') assert refspec.dst_matches('refs/remotes/origin/master') - assert 'refs/remotes/origin/master' == refspec.transform('refs/heads/master') - assert 'refs/heads/master' == refspec.rtransform('refs/remotes/origin/master') + assert refspec.transform('refs/heads/master') == 'refs/remotes/origin/master' + assert refspec.rtransform('refs/remotes/origin/master') == 'refs/heads/master' assert list == type(remote.push_refspecs) - assert 0 == len(remote.push_refspecs) + assert len(remote.push_refspecs) == 0 push_specs = remote.push_refspecs assert list == type(push_specs) - assert 0 == len(push_specs) + assert len(push_specs) == 0 testrepo.remotes.add_fetch('origin', '+refs/test/*:refs/test/remotes/*') remote = testrepo.remotes['origin'] fetch_specs = remote.fetch_refspecs assert list == type(fetch_specs) - assert 2 == len(fetch_specs) - assert [ + assert len(fetch_specs) == 2 + assert fetch_specs == [ '+refs/heads/*:refs/remotes/origin/*', '+refs/test/*:refs/test/remotes/*', - ] == fetch_specs + ] testrepo.remotes.add_push('origin', '+refs/test/*:refs/test/remotes/*') @@ -177,14 +177,14 @@ def test_refspec(testrepo): testrepo.remotes.add_fetch(['+refs/*:refs/*', 5]) remote = testrepo.remotes['origin'] - assert ['+refs/test/*:refs/test/remotes/*'] == remote.push_refspecs + assert remote.push_refspecs == ['+refs/test/*:refs/test/remotes/*'] def test_remote_list(testrepo): - assert 1 == len(testrepo.remotes) + assert len(testrepo.remotes) == 1 remote = testrepo.remotes[0] - assert REMOTE_NAME == remote.name - assert REMOTE_URL == remote.url + assert remote.name == REMOTE_NAME + assert remote.url == REMOTE_URL name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' @@ -195,7 +195,7 @@ def test_remote_list(testrepo): @utils.requires_network def test_ls_remotes(testrepo): - assert 1 == len(testrepo.remotes) + assert len(testrepo.remotes) == 1 remote = testrepo.remotes[0] refs = remote.ls_remotes() @@ -207,8 +207,8 @@ def test_ls_remotes(testrepo): def test_remote_collection(testrepo): remote = testrepo.remotes['origin'] - assert REMOTE_NAME == remote.name - assert REMOTE_URL == remote.url + assert remote.name == REMOTE_NAME + assert remote.url == REMOTE_URL with pytest.raises(KeyError): testrepo.remotes['upstream'] diff --git a/test/test_remote_utf8.py b/test/test_remote_utf8.py index bd410218..5e4b5372 100644 --- a/test/test_remote_utf8.py +++ b/test/test_remote_utf8.py @@ -23,8 +23,10 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -import pygit2 import pytest + +import pygit2 + from . import utils diff --git a/test/test_repository.py b/test/test_repository.py index fb240323..4fa76861 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -23,16 +23,15 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -from pathlib import Path import shutil import tempfile +from pathlib import Path import pytest # pygit2 import pygit2 -from pygit2 import init_repository, clone_repository, discover_repository -from pygit2 import Oid +from pygit2 import Oid, clone_repository, discover_repository, init_repository from pygit2.enums import ( CheckoutNotify, CheckoutStrategy, @@ -43,6 +42,7 @@ ResetMode, StashApplyProgress, ) + from . import utils @@ -151,7 +151,7 @@ def checkout_notify(self, why, path, baseline, target, workdir): head = testrepo.head head = testrepo[head.target] assert 'new' not in head.tree - assert b'bye world\n' == read_bye_txt() + assert read_bye_txt() == b'bye world\n' callbacks = MyCheckoutCallbacks() # checkout i18n with GIT_CHECKOUT_FORCE - callbacks should prevent checkout from completing @@ -162,7 +162,7 @@ def checkout_notify(self, why, path, baseline, target, workdir): assert callbacks.invoked_times == 2 assert 'new' not in head.tree - assert b'bye world\n' == read_bye_txt() + assert read_bye_txt() == b'bye world\n' def test_checkout_branch(testrepo): @@ -219,7 +219,7 @@ def test_checkout_alternative_dir(testrepo): extra_dir.mkdir() assert len(list(extra_dir.iterdir())) == 0 testrepo.checkout(ref_i18n, directory=extra_dir) - assert not len(list(extra_dir.iterdir())) == 0 + assert len(list(extra_dir.iterdir())) != 0 def test_checkout_paths(testrepo): @@ -277,15 +277,15 @@ def test_ahead_behind(testrepo): '5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8', ) - assert 1 == ahead - assert 2 == behind + assert ahead == 1 + assert behind == 2 ahead, behind = testrepo.ahead_behind( '4ec4389a8068641da2d6578db0419484972284c8', '5ebeeebb320790caf276b9fc8b24546d63316533', ) - assert 2 == ahead - assert 1 == behind + assert ahead == 2 + assert behind == 1 def test_reset_hard(testrepo): @@ -360,7 +360,7 @@ def test_stash(testrepo): ) # make sure we're starting with no stashes - assert [] == testrepo.listall_stashes() + assert testrepo.listall_stashes() == [] # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: @@ -370,7 +370,7 @@ def test_stash(testrepo): assert 'hello.txt' not in testrepo.status() repo_stashes = testrepo.listall_stashes() - assert 1 == len(repo_stashes) + assert len(repo_stashes) == 1 assert repr(repo_stashes[0]) == f'' assert repo_stashes[0].commit_id == stash_hash assert repo_stashes[0].message == 'On master: ' + stash_message @@ -380,7 +380,7 @@ def test_stash(testrepo): assert repo_stashes == testrepo.listall_stashes() # still the same stashes testrepo.stash_drop() - assert [] == testrepo.listall_stashes() + assert testrepo.listall_stashes() == [] with pytest.raises(KeyError): testrepo.stash_pop() @@ -393,7 +393,7 @@ def test_stash_partial(testrepo): ) # make sure we're starting with no stashes - assert [] == testrepo.listall_stashes() + assert testrepo.listall_stashes() == [] # some changes to working dir with (Path(testrepo.workdir) / 'hello.txt').open('w') as f: @@ -411,7 +411,7 @@ def stash_pathspecs(paths): ) stash_commit = testrepo[stash_id].peel(pygit2.Commit) stash_diff = testrepo.diff(stash_commit.parents[0], stash_commit) - stash_files = set(patch.delta.new_file.path for patch in stash_diff) + stash_files = {patch.delta.new_file.path for patch in stash_diff} return stash_files == set(paths) # Stash a modified file @@ -492,7 +492,7 @@ def stash_apply_progress(self, progress: StashApplyProgress): # and since we didn't let stash_pop run to completion, the stash itself should still be here repo_stashes = testrepo.listall_stashes() - assert 1 == len(repo_stashes) + assert len(repo_stashes) == 1 assert repo_stashes[0].message == 'On master: custom stash message' @@ -579,8 +579,8 @@ def test_default_signature(testrepo): config['user.email'] = 'rjh@example.com' sig = testrepo.default_signature - assert 'Random J Hacker' == sig.name - assert 'rjh@example.com' == sig.email + assert sig.name == 'Random J Hacker' + assert sig.email == 'rjh@example.com' def test_new_repo(tmp_path): diff --git a/test/test_repository_bare.py b/test/test_repository_bare.py index d418acdf..00fb6c45 100644 --- a/test/test_repository_bare.py +++ b/test/test_repository_bare.py @@ -26,15 +26,16 @@ # Standard Library import binascii import os -from pathlib import Path import sys import tempfile +from pathlib import Path + import pytest import pygit2 from pygit2.enums import FileMode, ObjectType -from . import utils +from . import utils HEAD_SHA = '784855caf26449a1914d2cf62d12b9374d76ae78' PARENT_SHA = 'f5e5aa4e36ab0fe62ee1ccc6eb8f79b866863b87' # HEAD^ @@ -53,7 +54,7 @@ def test_is_bare(barerepo): def test_head(barerepo): head = barerepo.head - assert HEAD_SHA == head.target + assert head.target == HEAD_SHA assert type(head) == pygit2.Reference assert not barerepo.head_is_unborn assert not barerepo.head_is_detached @@ -77,14 +78,14 @@ def test_read(barerepo): ab = barerepo.read(BLOB_OID) a = barerepo.read(BLOB_HEX) assert ab == a - assert (ObjectType.BLOB, b'a contents\n') == a + assert a == (ObjectType.BLOB, b'a contents\n') a2 = barerepo.read('7f129fd57e31e935c6d60a0c794efe4e6927664b') - assert (ObjectType.BLOB, b'a contents 2\n') == a2 + assert a2 == (ObjectType.BLOB, b'a contents 2\n') a_hex_prefix = BLOB_HEX[:4] a3 = barerepo.read(a_hex_prefix) - assert (ObjectType.BLOB, b'a contents\n') == a3 + assert a3 == (ObjectType.BLOB, b'a contents\n') def test_write(barerepo): @@ -109,7 +110,7 @@ def test_contains(barerepo): def test_iterable(barerepo): oid = pygit2.Oid(hex=BLOB_HEX) - assert oid in [obj for obj in barerepo] + assert oid in list(barerepo) def test_lookup_blob(barerepo): @@ -117,23 +118,23 @@ def test_lookup_blob(barerepo): barerepo[123] assert barerepo[BLOB_OID].id == BLOB_HEX a = barerepo[BLOB_HEX] - assert b'a contents\n' == a.read_raw() - assert BLOB_HEX == a.id - assert ObjectType.BLOB == a.type + assert a.read_raw() == b'a contents\n' + assert a.id == BLOB_HEX + assert a.type == ObjectType.BLOB def test_lookup_blob_prefix(barerepo): a = barerepo[BLOB_HEX[:5]] - assert b'a contents\n' == a.read_raw() - assert BLOB_HEX == a.id - assert ObjectType.BLOB == a.type + assert a.read_raw() == b'a contents\n' + assert a.id == BLOB_HEX + assert a.type == ObjectType.BLOB def test_lookup_commit(barerepo): commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' commit = barerepo[commit_sha] assert commit_sha == commit.id - assert ObjectType.COMMIT == commit.type + assert commit.type == ObjectType.COMMIT assert commit.message == ( 'Second test data commit.\n\n' 'This commit has some additional text.\n' ) @@ -145,10 +146,10 @@ def test_lookup_commit_prefix(barerepo): too_short_prefix = commit_sha[:3] commit = barerepo[commit_sha_prefix] assert commit_sha == commit.id - assert ObjectType.COMMIT == commit.type + assert commit.type == ObjectType.COMMIT assert ( - 'Second test data commit.\n\n' - 'This commit has some additional text.\n' == commit.message + commit.message == 'Second test data commit.\n\n' + 'This commit has some additional text.\n' ) with pytest.raises(ValueError): barerepo.__getitem__(too_short_prefix) diff --git a/test/test_repository_custom.py b/test/test_repository_custom.py index 5961ef65..a1acba78 100644 --- a/test/test_repository_custom.py +++ b/test/test_repository_custom.py @@ -24,6 +24,7 @@ # Boston, MA 02110-1301, USA. from pathlib import Path + import pytest import pygit2 @@ -58,4 +59,4 @@ def test_references(repo): def test_objects(repo): a = repo.read('323fae03f4606ea9991df8befbb2fca795e648fa') - assert (ObjectType.BLOB, b'foobar\n') == a + assert a == (ObjectType.BLOB, b'foobar\n') diff --git a/test/test_revparse.py b/test/test_revparse.py index 0bc7b40f..bc076c23 100644 --- a/test/test_revparse.py +++ b/test/test_revparse.py @@ -25,9 +25,10 @@ """Tests for revision parsing.""" +from pytest import raises + from pygit2 import InvalidSpecError from pygit2.enums import RevSpecFlag -from pytest import raises HEAD_SHA = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98' PARENT_SHA = '5ebeeebb320790caf276b9fc8b24546d63316533' # HEAD^ diff --git a/test/test_revwalk.py b/test/test_revwalk.py index a4edd7c8..faa80b37 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -27,7 +27,6 @@ from pygit2.enums import SortMode - # In the order given by git log log = [ '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98', @@ -108,8 +107,8 @@ def test_simplify_first_parent(testrepo): def test_default_sorting(testrepo): walker = testrepo.walk(log[0], SortMode.NONE) - list1 = list([x.id for x in walker]) + list1 = [x.id for x in walker] walker = testrepo.walk(log[0]) - list2 = list([x.id for x in walker]) + list2 = [x.id for x in walker] assert list1 == list2 diff --git a/test/test_signature.py b/test/test_signature.py index fbbc99c6..e4466a83 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -99,7 +99,7 @@ def test_incorrect_encoding(): ) # repr() and str() may display junk, but they must not crash - assert "pygit2.Signature('(error)', '(error)', 999, 0, '(error)')" == repr( - signature + assert ( + repr(signature) == "pygit2.Signature('(error)', '(error)', 999, 0, '(error)')" ) - assert '(error) <(error)>' == str(signature) + assert str(signature) == '(error) <(error)>' diff --git a/test/test_submodule.py b/test/test_submodule.py index bf456e97..a91ab5a0 100644 --- a/test/test_submodule.py +++ b/test/test_submodule.py @@ -27,12 +27,13 @@ from pathlib import Path -import pygit2 import pytest -from . import utils -from pygit2.enums import SubmoduleIgnore as SI, SubmoduleStatus as SS +import pygit2 +from pygit2.enums import SubmoduleIgnore as SI +from pygit2.enums import SubmoduleStatus as SS +from . import utils SUBM_NAME = 'TestGitRepository' SUBM_PATH = 'TestGitRepository' @@ -108,17 +109,17 @@ class CustomRepoClass(pygit2.Repository): def test_name(repo): s = repo.submodules[SUBM_PATH] - assert SUBM_NAME == s.name + assert s.name == SUBM_NAME def test_path(repo): s = repo.submodules[SUBM_PATH] - assert SUBM_PATH == s.path + assert s.path == SUBM_PATH def test_url(repo): s = repo.submodules[SUBM_PATH] - assert SUBM_URL == s.url + assert s.url == SUBM_URL def test_missing_url(repo): @@ -239,7 +240,7 @@ def test_add_submodule(repo, depth): sm_repo = sm.open() assert sm_repo_path == sm.path - assert SUBM_URL == sm.url + assert sm.url == SUBM_URL assert not sm_repo.is_empty if depth == 0: diff --git a/test/test_tag.py b/test/test_tag.py index 73cfcf0b..d31b9dc3 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -30,7 +30,6 @@ import pygit2 from pygit2.enums import ObjectType - TAG_SHA = '3d2962987c695a29f1f80b6c3aa4ec046ef44369' @@ -39,11 +38,11 @@ def test_read_tag(barerepo): tag = repo[TAG_SHA] target = repo[tag.target] assert isinstance(tag, pygit2.Tag) - assert ObjectType.TAG == tag.type - assert ObjectType.COMMIT == target.type - assert 'root' == tag.name - assert 'Tagged root commit.\n' == tag.message - assert 'Initial test data commit.\n' == target.message + assert tag.type == ObjectType.TAG + assert target.type == ObjectType.COMMIT + assert tag.name == 'root' + assert tag.message == 'Tagged root commit.\n' + assert target.message == 'Initial test data commit.\n' assert tag.tagger == pygit2.Signature( 'Dave Borowitz', 'dborowitz@google.com', 1288724692, -420 ) @@ -63,7 +62,7 @@ def test_new_tag(barerepo): sha = barerepo.create_tag(name, target_prefix, ObjectType.BLOB, tagger, message) tag = barerepo[sha] - assert '3ee44658fd11660e828dfc96b9b5c5f38d5b49bb' == tag.id + assert tag.id == '3ee44658fd11660e828dfc96b9b5c5f38d5b49bb' assert name == tag.name assert target == tag.target assert tagger == tag.tagger diff --git a/test/test_tree.py b/test/test_tree.py index 0f9ec2d1..01c474c7 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -24,6 +24,7 @@ # Boston, MA 02110-1301, USA. import operator + import pytest import pygit2 @@ -31,7 +32,6 @@ from . import utils - TREE_SHA = '967fce8df97cc71722d3c2a5930ef3e6f1d27b12' SUBTREE_SHA = '614fd9a3094bf618ea938fffc00e7d1a54f89ad0' @@ -54,7 +54,7 @@ def test_read_tree(barerepo): utils.assertRaisesWithArg(IndexError, 3, lambda: tree[3]) utils.assertRaisesWithArg(KeyError, 'abcd', lambda: tree / 'abcd') - assert 3 == len(tree) + assert len(tree) == 3 sha = '7f129fd57e31e935c6d60a0c794efe4e6927664b' assert 'a' in tree assertTreeEntryEqual(tree[0], sha, 'a', 0o0100644) @@ -93,7 +93,7 @@ def test_equality(barerepo): def test_sorting(barerepo): tree_a = barerepo['18e2d2e9db075f9eb43bcb2daa65a2867d29a15e'] - assert list(tree_a) == sorted(reversed(list(tree_a)), key=pygit2.tree_entry_key) + assert list(tree_a) == sorted(tree_a, key=pygit2.tree_entry_key) assert list(tree_a) != reversed(list(tree_a)) @@ -111,7 +111,7 @@ def test_read_subtree(barerepo): assert subtree_entry.type_str == 'tree' subtree = barerepo[subtree_entry.id] - assert 1 == len(subtree) + assert len(subtree) == 1 sha = '297efb891a47de80be0cfe9c639e4b8c9b450989' assertTreeEntryEqual(subtree[0], sha, 'd', 0o0100644) @@ -185,7 +185,7 @@ def test_iterate_tree_nested(barerepo): tree = barerepo[TREE_SHA] for tree_entry in tree: if isinstance(tree_entry, pygit2.Tree): - for tree_entry2 in tree_entry: + for _tree_entry2 in tree_entry: pass diff --git a/test/utils.py b/test/utils.py index 1df9135f..952c775b 100644 --- a/test/utils.py +++ b/test/utils.py @@ -25,12 +25,12 @@ # Standard library import hashlib -from pathlib import Path import shutil import socket import stat import sys import zipfile +from pathlib import Path # Requirements import pytest @@ -38,7 +38,6 @@ # Pygit2 import pygit2 - requires_future_libgit2 = pytest.mark.skipif( pygit2.LIBGIT2_VER < (2, 0, 0), reason='This test may work with a future version of libgit2', From 6330d4b759709d15af1f693888f792838bb79339 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 19:52:51 +0700 Subject: [PATCH 04/44] add type checking configuration (use pyright) --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 50c037af..4f505542 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,3 +49,7 @@ lint.ignore = [ [tool.ruff.format] quote-style = "single" + +[tool.pyright] +typeCheckingMode = "strict" +pythonVersion = "3.10" From 325891bd19ad03abb961e44d99a88521747aae7e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 20:06:57 +0700 Subject: [PATCH 05/44] add _libgit2.pyi --- pygit2/_libgit2.pyi | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pygit2/_libgit2.pyi diff --git a/pygit2/_libgit2.pyi b/pygit2/_libgit2.pyi new file mode 100644 index 00000000..2fb45d89 --- /dev/null +++ b/pygit2/_libgit2.pyi @@ -0,0 +1,4 @@ +import _cffi_backend + +ffi: _cffi_backend.FFI +C: _cffi_backend.Lib From aa37e2d1270fe97e0441a914a14900d56e17d8c3 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:28:32 +0700 Subject: [PATCH 06/44] mega fix _pygit2.pyi - remove GIT_OBJ_* - add additional constants for type checking --- pygit2/_pygit2.pyi | 394 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 317 insertions(+), 77 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 5c3c879c..48bdabe2 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -1,5 +1,5 @@ from io import IOBase -from typing import Iterator, Literal, Optional, overload +from typing import Any, Iterator, Literal, Optional, TypeAlias, overload from . import Index from .enums import ( @@ -21,48 +21,284 @@ from .enums import ( SortMode, ) -GIT_OBJ_BLOB: Literal[3] -GIT_OBJ_COMMIT: Literal[1] -GIT_OBJ_TAG: Literal[4] -GIT_OBJ_TREE: Literal[2] -GIT_OID_HEXSZ: int -GIT_OID_HEX_ZERO: str -GIT_OID_MINPREFIXLEN: int -GIT_OID_RAWSZ: int +# version constants LIBGIT2_VERSION: str LIBGIT2_VER_MAJOR: int LIBGIT2_VER_MINOR: int LIBGIT2_VER_REVISION: int +# libgit2 constants +GIT_OBJECT_BLOB: Literal[3] +GIT_OBJECT_COMMIT: Literal[1] +GIT_OBJECT_TAG: Literal[4] +GIT_OBJECT_TREE: Literal[2] +GIT_OID_HEXSZ: Literal[40] +GIT_OID_HEX_ZERO: Literal['0000000000000000000000000000000000000000'] +GIT_OID_MINPREFIXLEN: Literal[4] +GIT_OID_RAWSZ: Literal[20] +GIT_APPLY_LOCATION_BOTH: Literal[2] +GIT_APPLY_LOCATION_INDEX: Literal[1] +GIT_APPLY_LOCATION_WORKDIR: Literal[0] +GIT_BLAME_FIRST_PARENT: Literal[16] +GIT_BLAME_IGNORE_WHITESPACE: Literal[64] +GIT_BLAME_NORMAL: Literal[0] +GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES: Literal[8] +GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES: Literal[4] +GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES: Literal[2] +GIT_BLAME_TRACK_COPIES_SAME_FILE: Literal[1] +GIT_BLAME_USE_MAILMAP: Literal[32] +GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT: Literal[8] +GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD: Literal[4] +GIT_BLOB_FILTER_CHECK_FOR_BINARY: Literal[1] +GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES: Literal[2] +GIT_BRANCH_ALL: Literal[3] +GIT_BRANCH_LOCAL: Literal[1] +GIT_BRANCH_REMOTE: Literal[2] +GIT_CHECKOUT_ALLOW_CONFLICTS: Literal[16] +GIT_CHECKOUT_CONFLICT_STYLE_DIFF3: Literal[2097152] +GIT_CHECKOUT_CONFLICT_STYLE_MERGE: Literal[1048576] +GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3: Literal[33554432] +GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH: Literal[8192] +GIT_CHECKOUT_DONT_OVERWRITE_IGNORED: Literal[524288] +GIT_CHECKOUT_DONT_REMOVE_EXISTING: Literal[4194304] +GIT_CHECKOUT_DONT_UPDATE_INDEX: Literal[256] +GIT_CHECKOUT_DONT_WRITE_INDEX: Literal[8388608] +GIT_CHECKOUT_DRY_RUN: Literal[16777216] +GIT_CHECKOUT_FORCE: Literal[2] +GIT_CHECKOUT_NONE: Literal[0] +GIT_CHECKOUT_NO_REFRESH: Literal[512] +GIT_CHECKOUT_RECREATE_MISSING: Literal[4] +GIT_CHECKOUT_REMOVE_IGNORED: Literal[64] +GIT_CHECKOUT_REMOVE_UNTRACKED: Literal[32] +GIT_CHECKOUT_SAFE: Literal[1] +GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES: Literal[262144] +GIT_CHECKOUT_SKIP_UNMERGED: Literal[1024] +GIT_CHECKOUT_UPDATE_ONLY: Literal[128] +GIT_CHECKOUT_USE_OURS: Literal[2048] +GIT_CHECKOUT_USE_THEIRS: Literal[4096] +GIT_CONFIG_HIGHEST_LEVEL: Literal[-1] +GIT_CONFIG_LEVEL_APP: Literal[7] +GIT_CONFIG_LEVEL_GLOBAL: Literal[4] +GIT_CONFIG_LEVEL_LOCAL: Literal[5] +GIT_CONFIG_LEVEL_PROGRAMDATA: Literal[1] +GIT_CONFIG_LEVEL_SYSTEM: Literal[2] +GIT_CONFIG_LEVEL_WORKTREE: Literal[6] +GIT_CONFIG_LEVEL_XDG: Literal[3] +GIT_DELTA_ADDED: Literal[1] +GIT_DELTA_CONFLICTED: Literal[10] +GIT_DELTA_COPIED: Literal[5] +GIT_DELTA_DELETED: Literal[2] +GIT_DELTA_IGNORED: Literal[6] +GIT_DELTA_MODIFIED: Literal[3] +GIT_DELTA_RENAMED: Literal[4] +GIT_DELTA_TYPECHANGE: Literal[8] +GIT_DELTA_UNMODIFIED: Literal[0] +GIT_DELTA_UNREADABLE: Literal[9] +GIT_DELTA_UNTRACKED: Literal[7] +GIT_DESCRIBE_ALL: Literal[2] +GIT_DESCRIBE_DEFAULT: Literal[0] +GIT_DESCRIBE_TAGS: Literal[1] +GIT_DIFF_BREAK_REWRITES: Literal[32] +GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY: Literal[32768] +GIT_DIFF_DISABLE_PATHSPEC_MATCH: Literal[4096] +GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS: Literal[16384] +GIT_DIFF_FIND_ALL: Literal[255] +GIT_DIFF_FIND_AND_BREAK_REWRITES: Literal[48] +GIT_DIFF_FIND_BY_CONFIG: Literal[0] +GIT_DIFF_FIND_COPIES: Literal[4] +GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED: Literal[8] +GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE: Literal[8192] +GIT_DIFF_FIND_EXACT_MATCH_ONLY: Literal[16384] +GIT_DIFF_FIND_FOR_UNTRACKED: Literal[64] +GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE: Literal[0] +GIT_DIFF_FIND_IGNORE_WHITESPACE: Literal[4096] +GIT_DIFF_FIND_REMOVE_UNMODIFIED: Literal[65536] +GIT_DIFF_FIND_RENAMES: Literal[1] +GIT_DIFF_FIND_RENAMES_FROM_REWRITES: Literal[2] +GIT_DIFF_FIND_REWRITES: Literal[16] +GIT_DIFF_FLAG_BINARY: Literal[1] +GIT_DIFF_FLAG_EXISTS: Literal[8] +GIT_DIFF_FLAG_NOT_BINARY: Literal[2] +GIT_DIFF_FLAG_VALID_ID: Literal[4] +GIT_DIFF_FLAG_VALID_SIZE: Literal[16] +GIT_DIFF_FORCE_BINARY: Literal[2097152] +GIT_DIFF_FORCE_TEXT: Literal[1048576] +GIT_DIFF_IGNORE_BLANK_LINES: Literal[524288] +GIT_DIFF_IGNORE_CASE: Literal[1024] +GIT_DIFF_IGNORE_FILEMODE: Literal[256] +GIT_DIFF_IGNORE_SUBMODULES: Literal[512] +GIT_DIFF_IGNORE_WHITESPACE: Literal[4194304] +GIT_DIFF_IGNORE_WHITESPACE_CHANGE: Literal[8388608] +GIT_DIFF_IGNORE_WHITESPACE_EOL: Literal[16777216] +GIT_DIFF_INCLUDE_CASECHANGE: Literal[2048] +GIT_DIFF_INCLUDE_IGNORED: Literal[2] +GIT_DIFF_INCLUDE_TYPECHANGE: Literal[64] +GIT_DIFF_INCLUDE_TYPECHANGE_TREES: Literal[128] +GIT_DIFF_INCLUDE_UNMODIFIED: Literal[32] +GIT_DIFF_INCLUDE_UNREADABLE: Literal[65536] +GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED: Literal[131072] +GIT_DIFF_INCLUDE_UNTRACKED: Literal[8] +GIT_DIFF_INDENT_HEURISTIC: Literal[262144] +GIT_DIFF_MINIMAL: Literal[536870912] +GIT_DIFF_NORMAL: Literal[0] +GIT_DIFF_PATIENCE: Literal[268435456] +GIT_DIFF_RECURSE_IGNORED_DIRS: Literal[4] +GIT_DIFF_RECURSE_UNTRACKED_DIRS: Literal[16] +GIT_DIFF_REVERSE: Literal[1] +GIT_DIFF_SHOW_BINARY: Literal[1073741824] +GIT_DIFF_SHOW_UNMODIFIED: Literal[67108864] +GIT_DIFF_SHOW_UNTRACKED_CONTENT: Literal[33554432] +GIT_DIFF_SKIP_BINARY_CHECK: Literal[8192] +GIT_DIFF_STATS_FULL: Literal[1] +GIT_DIFF_STATS_INCLUDE_SUMMARY: Literal[8] +GIT_DIFF_STATS_NONE: Literal[0] +GIT_DIFF_STATS_NUMBER: Literal[4] +GIT_DIFF_STATS_SHORT: Literal[2] +GIT_DIFF_UPDATE_INDEX: Literal[32768] +GIT_FILEMODE_BLOB: Literal[33188] +GIT_FILEMODE_BLOB_EXECUTABLE: Literal[33261] +GIT_FILEMODE_COMMIT: Literal[57344] +GIT_FILEMODE_LINK: Literal[40960] +GIT_FILEMODE_TREE: Literal[16384] +GIT_FILEMODE_UNREADABLE: Literal[0] +GIT_FILTER_ALLOW_UNSAFE: Literal[1] +GIT_FILTER_ATTRIBUTES_FROM_COMMIT: Literal[8] +GIT_FILTER_ATTRIBUTES_FROM_HEAD: Literal[4] +GIT_FILTER_CLEAN: Literal[1] +GIT_FILTER_DEFAULT: Literal[0] +GIT_FILTER_DRIVER_PRIORITY: Literal[200] +GIT_FILTER_NO_SYSTEM_ATTRIBUTES: Literal[2] +GIT_FILTER_SMUDGE: Literal[0] +GIT_FILTER_TO_ODB: Literal[1] +GIT_FILTER_TO_WORKTREE: Literal[0] +GIT_MERGE_ANALYSIS_FASTFORWARD: Literal[4] +GIT_MERGE_ANALYSIS_NONE: Literal[0] +GIT_MERGE_ANALYSIS_NORMAL: Literal[1] +GIT_MERGE_ANALYSIS_UNBORN: Literal[8] +GIT_MERGE_ANALYSIS_UP_TO_DATE: Literal[2] +GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY: Literal[2] +GIT_MERGE_PREFERENCE_NONE: Literal[0] +GIT_MERGE_PREFERENCE_NO_FASTFORWARD: Literal[1] +GIT_OBJECT_ANY: Literal[-2] +GIT_OBJECT_INVALID: Literal[-1] +GIT_OBJECT_OFS_DELTA: Literal[6] +GIT_OBJECT_REF_DELTA: Literal[7] +GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS: Literal[27] +GIT_OPT_ENABLE_CACHING: Literal[8] +GIT_OPT_ENABLE_FSYNC_GITDIR: Literal[19] +GIT_OPT_ENABLE_OFS_DELTA: Literal[18] +GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION: Literal[22] +GIT_OPT_ENABLE_STRICT_OBJECT_CREATION: Literal[14] +GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION: Literal[15] +GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY: Literal[24] +GIT_OPT_GET_CACHED_MEMORY: Literal[9] +GIT_OPT_GET_MWINDOW_FILE_LIMIT: Literal[29] +GIT_OPT_GET_MWINDOW_MAPPED_LIMIT: Literal[2] +GIT_OPT_GET_MWINDOW_SIZE: Literal[0] +GIT_OPT_GET_OWNER_VALIDATION: Literal[35] +GIT_OPT_GET_PACK_MAX_OBJECTS: Literal[25] +GIT_OPT_GET_SEARCH_PATH: Literal[4] +GIT_OPT_GET_TEMPLATE_PATH: Literal[10] +GIT_OPT_GET_USER_AGENT: Literal[17] +GIT_OPT_GET_WINDOWS_SHAREMODE: Literal[20] +GIT_OPT_SET_ALLOCATOR: Literal[23] +GIT_OPT_SET_CACHE_MAX_SIZE: Literal[7] +GIT_OPT_SET_CACHE_OBJECT_LIMIT: Literal[6] +GIT_OPT_SET_MWINDOW_FILE_LIMIT: Literal[30] +GIT_OPT_SET_MWINDOW_MAPPED_LIMIT: Literal[3] +GIT_OPT_SET_MWINDOW_SIZE: Literal[1] +GIT_OPT_SET_OWNER_VALIDATION: Literal[36] +GIT_OPT_SET_PACK_MAX_OBJECTS: Literal[26] +GIT_OPT_SET_SEARCH_PATH: Literal[5] +GIT_OPT_SET_SSL_CERT_LOCATIONS: Literal[12] +GIT_OPT_SET_SSL_CIPHERS: Literal[16] +GIT_OPT_SET_TEMPLATE_PATH: Literal[11] +GIT_OPT_SET_USER_AGENT: Literal[13] +GIT_OPT_SET_WINDOWS_SHAREMODE: Literal[21] +GIT_REFERENCES_ALL: Literal[0] +GIT_REFERENCES_BRANCHES: Literal[1] +GIT_REFERENCES_TAGS: Literal[2] +GIT_RESET_HARD: Literal[3] +GIT_RESET_MIXED: Literal[2] +GIT_RESET_SOFT: Literal[1] +GIT_REVSPEC_MERGE_BASE: Literal[4] +GIT_REVSPEC_RANGE: Literal[2] +GIT_REVSPEC_SINGLE: Literal[1] +GIT_SORT_NONE: Literal[0] +GIT_SORT_REVERSE: Literal[4] +GIT_SORT_TIME: Literal[2] +GIT_SORT_TOPOLOGICAL: Literal[1] +GIT_STASH_APPLY_DEFAULT: Literal[0] +GIT_STASH_APPLY_REINSTATE_INDEX: Literal[1] +GIT_STASH_DEFAULT: Literal[0] +GIT_STASH_INCLUDE_IGNORED: Literal[4] +GIT_STASH_INCLUDE_UNTRACKED: Literal[2] +GIT_STASH_KEEP_ALL: Literal[8] +GIT_STASH_KEEP_INDEX: Literal[1] +GIT_STATUS_CONFLICTED: Literal[32768] +GIT_STATUS_CURRENT: Literal[0] +GIT_STATUS_IGNORED: Literal[16384] +GIT_STATUS_INDEX_DELETED: Literal[4] +GIT_STATUS_INDEX_MODIFIED: Literal[2] +GIT_STATUS_INDEX_NEW: Literal[1] +GIT_STATUS_INDEX_RENAMED: Literal[8] +GIT_STATUS_INDEX_TYPECHANGE: Literal[16] +GIT_STATUS_WT_DELETED: Literal[512] +GIT_STATUS_WT_MODIFIED: Literal[256] +GIT_STATUS_WT_NEW: Literal[128] +GIT_STATUS_WT_RENAMED: Literal[2048] +GIT_STATUS_WT_TYPECHANGE: Literal[1024] +GIT_STATUS_WT_UNREADABLE: Literal[4096] +GIT_SUBMODULE_IGNORE_ALL: Literal[4] +GIT_SUBMODULE_IGNORE_DIRTY: Literal[3] +GIT_SUBMODULE_IGNORE_NONE: Literal[1] +GIT_SUBMODULE_IGNORE_UNSPECIFIED: Literal[-1] +GIT_SUBMODULE_IGNORE_UNTRACKED: Literal[2] +GIT_SUBMODULE_STATUS_INDEX_ADDED: Literal[16] +GIT_SUBMODULE_STATUS_INDEX_DELETED: Literal[32] +GIT_SUBMODULE_STATUS_INDEX_MODIFIED: Literal[64] +GIT_SUBMODULE_STATUS_IN_CONFIG: Literal[4] +GIT_SUBMODULE_STATUS_IN_HEAD: Literal[1] +GIT_SUBMODULE_STATUS_IN_INDEX: Literal[2] +GIT_SUBMODULE_STATUS_IN_WD: Literal[8] +GIT_SUBMODULE_STATUS_WD_ADDED: Literal[256] +GIT_SUBMODULE_STATUS_WD_DELETED: Literal[512] +GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED: Literal[2048] +GIT_SUBMODULE_STATUS_WD_MODIFIED: Literal[1024] +GIT_SUBMODULE_STATUS_WD_UNINITIALIZED: Literal[128] +GIT_SUBMODULE_STATUS_WD_UNTRACKED: Literal[8192] +GIT_SUBMODULE_STATUS_WD_WD_MODIFIED: Literal[4096] + +_OidArg: TypeAlias = 'str | Oid' + class Object: _pointer: bytes filemode: FileMode - hex: str id: Oid name: str | None - oid: Oid raw_name: bytes | None short_id: str - type: 'Literal[GIT_OBJ_COMMIT] | Literal[GIT_OBJ_TREE] | Literal[GIT_OBJ_TAG] | Literal[GIT_OBJ_BLOB]' + # GIT_OBJECT_COMMIT | GIT_OBJECT_TREE | GIT_OBJECT_TAG | GIT_OBJECT_BLOB + type: Literal[1, 2, 4, 3] type_str: "Literal['commit'] | Literal['tree'] | Literal['tag'] | Literal['blob']" @overload - def peel(self, target_type: 'Literal[GIT_OBJ_COMMIT]') -> 'Commit': ... + def peel(self, target_type: Literal[1] | type[Commit]) -> Commit: ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_TREE]') -> 'Tree': ... + def peel(self, target_type: Literal[2] | type[Tree]) -> Tree: ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_TAG]') -> 'Tag': ... + def peel(self, target_type: Literal[4] | type[Tag]) -> Tag: ... @overload - def peel(self, target_type: 'Literal[GIT_OBJ_BLOB]') -> 'Blob': ... + def peel(self, target_type: Literal[3] | type[Blob]) -> Blob: ... @overload - def peel(self, target_type: 'None') -> 'Commit|Tree|Blob': ... + def peel(self, target_type: None) -> Commit | Tree | Blob: ... def read_raw(self) -> bytes: ... - def __eq__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... def __hash__(self) -> int: ... - def __le__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __ne__(self, other) -> bool: ... + def __le__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... class Reference: name: str @@ -72,28 +308,32 @@ class Reference: shorthand: str target: Oid | str type: ReferenceType - def __init__(self, *args) -> None: ... + + @overload + def __init__(self, name: str, target: str) -> None: ... + @overload + def __init__(self, name: str, oid: Oid, peel: Oid) -> None: ... def delete(self) -> None: ... def log(self) -> Iterator[RefLogEntry]: ... @overload - def peel(self, type: 'Literal[GIT_OBJ_COMMIT]') -> 'Commit': ... + def peel(self, type: Literal[1] | type[Commit]) -> Commit: ... @overload - def peel(self, type: 'Literal[GIT_OBJ_TREE]') -> 'Tree': ... + def peel(self, type: Literal[2] | type[Tree]) -> Tree: ... @overload - def peel(self, type: 'Literal[GIT_OBJ_TAG]') -> 'Tag': ... + def peel(self, type: Literal[4] | type[Tag]) -> Tag: ... @overload - def peel(self, type: 'Literal[GIT_OBJ_BLOB]') -> 'Blob': ... + def peel(self, type: Literal[3] | type[Blob]) -> Blob: ... @overload - def peel(self, type: 'None') -> 'Commit|Tree|Blob': ... + def peel(self, type: None) -> Commit | Tree | Blob: ... def rename(self, new_name: str) -> None: ... def resolve(self) -> Reference: ... def set_target(self, target: _OidArg, message: str = ...) -> None: ... - def __eq__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... - def __le__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __ne__(self, other) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... + def __le__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... class AlreadyExistsError(ValueError): ... @@ -125,7 +365,7 @@ class Branch(Reference): def delete(self) -> None: ... def is_checked_out(self) -> bool: ... def is_head(self) -> bool: ... - def rename(self, name: str, force: bool = False) -> None: ... + def rename(self, name: str, force: bool = False) -> None: ... # type: ignore class Commit(Object): author: Signature @@ -158,7 +398,7 @@ class Diff: ) -> None: ... def merge(self, diff: Diff) -> None: ... @staticmethod - def from_c(diff, repo) -> Diff: ... + def from_c(diff: Any, repo: Any) -> Diff: ... @staticmethod def parse_diff(git_diff: str | bytes) -> Diff: ... def __getitem__(self, index: int) -> Patch: ... # Diff_getitem @@ -183,7 +423,7 @@ class DiffFile: raw_path: bytes size: int @staticmethod - def from_c(bytes) -> DiffFile: ... + def from_c(bytes: bytes) -> DiffFile: ... class DiffHunk: header: str @@ -212,7 +452,7 @@ class GitError(Exception): ... class InvalidSpecError(ValueError): ... class Mailmap: - def __init__(self, *args) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def add_entry( self, real_name: str = ..., @@ -237,7 +477,7 @@ class Note: class Odb: backends: Iterator[OdbBackend] - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def add_backend(self, backend: OdbBackend, priority: int) -> None: ... def add_disk_alternate(self, path: str) -> None: ... def exists(self, oid: _OidArg) -> bool: ... @@ -247,7 +487,7 @@ class Odb: def __iter__(self) -> Iterator[Oid]: ... # Odb_as_iter class OdbBackend: - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def exists(self, oid: _OidArg) -> bool: ... def exists_prefix(self, partial_id: _OidArg) -> Oid: ... def read(self, oid: _OidArg) -> tuple[int, bytes]: ... @@ -257,22 +497,22 @@ class OdbBackend: def __iter__(self) -> Iterator[Oid]: ... # OdbBackend_as_iter class OdbBackendLoose(OdbBackend): - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... class OdbBackendPack(OdbBackend): - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... class Oid: - hex: str raw: bytes def __init__(self, raw: bytes = ..., hex: str = ...) -> None: ... - def __eq__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... def __hash__(self) -> int: ... - def __le__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __ne__(self, other) -> bool: ... + def __le__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... + def __str__(self) -> str: ... class Patch: data: bytes @@ -297,10 +537,10 @@ class RefLogEntry: message: str oid_new: Oid oid_old: Oid - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... class Refdb: - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def compress(self) -> None: ... @staticmethod def new(repo: Repository) -> Refdb: ... @@ -309,7 +549,7 @@ class Refdb: def set_backend(self, backend: RefdbBackend) -> None: ... class RefdbBackend: - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def compress(self) -> None: ... def delete(self, ref_name: str, old_id: _OidArg, old_target: str) -> None: ... def ensure_log(self, ref_name: str) -> bool: ... @@ -330,7 +570,7 @@ class RefdbBackend: ) -> None: ... class RefdbFsBackend(RefdbBackend): - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... class Repository: _pointer: bytes @@ -345,10 +585,10 @@ class Repository: path: str refdb: Refdb workdir: str - def __init__(self, *args, **kwargs) -> None: ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... def TreeBuilder(self, src: Tree | _OidArg = ...) -> TreeBuilder: ... - def _disown(self, *args, **kwargs) -> None: ... - def _from_c(self, *args, **kwargs) -> None: ... + def _disown(self, *args: Any, **kwargs: Any) -> None: ... + def _from_c(self, *args: Any, **kwargs: Any) -> None: ... def add_worktree(self, name: str, path: str, ref: Reference = ...) -> Worktree: ... def applies( self, @@ -365,7 +605,9 @@ class Repository: def create_blob_fromdisk(self, path: str) -> Oid: ... def create_blob_fromiobase(self, iobase: IOBase) -> Oid: ... def create_blob_fromworkdir(self, path: str) -> Oid: ... - def create_branch(self, name: str, commit: Commit, force=False) -> Branch: ... + def create_branch( + self, name: str, commit: Commit, force: bool = False + ) -> Branch: ... def create_commit( self, reference_name: Optional[str], @@ -439,7 +681,7 @@ class Repository: def references_iterator_init(self) -> Iterator[Reference]: ... def references_iterator_next( self, - iter: Iterator, + iter: Iterator[Any], references_return_type: ReferenceFilter = ReferenceFilter.ALL, ) -> Reference: ... def reset(self, oid: _OidArg, reset_type: ResetMode) -> None: ... @@ -478,29 +720,29 @@ class Signature: offset: int = 0, encoding: Optional[str] = None, ) -> None: ... - def __eq__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... - def __le__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __ne__(self, other) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... + def __le__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... class Stash: commit_id: Oid message: str raw_message: bytes - def __eq__(self, other) -> bool: ... - def __ge__(self, other) -> bool: ... - def __gt__(self, other) -> bool: ... - def __le__(self, other) -> bool: ... - def __lt__(self, other) -> bool: ... - def __ne__(self, other) -> bool: ... + def __eq__(self, other: object) -> bool: ... + def __ge__(self, other: object) -> bool: ... + def __gt__(self, other: object) -> bool: ... + def __le__(self, other: object) -> bool: ... + def __lt__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... class Tag(Object): message: str - name: str + name: str | None raw_message: bytes - raw_name: bytes + raw_name: bytes | None tagger: Signature target: Oid def get_object(self) -> Object: ... @@ -555,7 +797,7 @@ class Worktree: is_prunable: bool name: str path: str - def prune(self, force=False) -> None: ... + def prune(self, force: bool = False) -> None: ... def discover_repository( path: str, across_fs: bool = False, ceiling_dirs: str = ... @@ -563,8 +805,6 @@ 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: ... +def option(opt: Option, *args: Any) -> None: ... def reference_is_valid_name(refname: str) -> bool: ... def tree_entry_cmp(a: Object, b: Object) -> int: ... - -_OidArg = str | Oid From b53b9a775e52127b07564ec3cd519b616e1a424d Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:34:05 +0700 Subject: [PATCH 07/44] fix stubs --- pygit2/_libgit2.pyi | 2 +- pygit2/ffi.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pygit2/_libgit2.pyi b/pygit2/_libgit2.pyi index 2fb45d89..fdef4d9d 100644 --- a/pygit2/_libgit2.pyi +++ b/pygit2/_libgit2.pyi @@ -1,4 +1,4 @@ import _cffi_backend ffi: _cffi_backend.FFI -C: _cffi_backend.Lib +lib: _cffi_backend.Lib diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 25814e0e..7d712195 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -26,3 +26,5 @@ # Import from pygit2 from ._libgit2 import ffi from ._libgit2 import lib as C + +__all__ = ["ffi", "C"] From c70241d6018bd720b865d30000c58ab839934e03 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:35:20 +0700 Subject: [PATCH 08/44] change lib to Any --- pygit2/_libgit2.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygit2/_libgit2.pyi b/pygit2/_libgit2.pyi index fdef4d9d..4e0c929a 100644 --- a/pygit2/_libgit2.pyi +++ b/pygit2/_libgit2.pyi @@ -1,4 +1,6 @@ +from typing import Any + import _cffi_backend ffi: _cffi_backend.FFI -lib: _cffi_backend.Lib +lib: Any From 42b5129ba9d8a05a5ceb704affd880e0148606af Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:39:23 +0700 Subject: [PATCH 09/44] fix pyright config --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 4f505542..e8f77358 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,3 +53,4 @@ quote-style = "single" [tool.pyright] typeCheckingMode = "strict" pythonVersion = "3.10" +reportPrivateUsage = "none" From 5f6657e7dd001e08b70e0f0c5a49959b3b816af7 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:44:17 +0700 Subject: [PATCH 10/44] fix _pygit2.pyi again --- pygit2/_pygit2.pyi | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 48bdabe2..dead6b66 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -282,15 +282,19 @@ class Object: type: Literal[1, 2, 4, 3] type_str: "Literal['commit'] | Literal['tree'] | Literal['tag'] | Literal['blob']" @overload - def peel(self, target_type: Literal[1] | type[Commit]) -> Commit: ... + def peel( + self, target_type: Literal[1, ObjectType.COMMIT] | type[Commit] + ) -> Commit: ... @overload - def peel(self, target_type: Literal[2] | type[Tree]) -> Tree: ... + def peel(self, target_type: Literal[2, ObjectType.TREE] | type[Tree]) -> Tree: ... @overload - def peel(self, target_type: Literal[4] | type[Tag]) -> Tag: ... + def peel(self, target_type: Literal[4, ObjectType.TAG] | type[Tag]) -> Tag: ... @overload - def peel(self, target_type: Literal[3] | type[Blob]) -> Blob: ... + def peel(self, target_type: Literal[3, ObjectType.BLOB] | type[Blob]) -> Blob: ... @overload - def peel(self, target_type: None) -> Commit | Tree | Blob: ... + def peel( + self, target_type: Literal[None, ObjectType.ANY] + ) -> Commit | Tree | Blob: ... def read_raw(self) -> bytes: ... def __eq__(self, other: object) -> bool: ... def __ge__(self, other: object) -> bool: ... @@ -318,13 +322,13 @@ class Reference: @overload def peel(self, type: Literal[1] | type[Commit]) -> Commit: ... @overload - def peel(self, type: Literal[2] | type[Tree]) -> Tree: ... + def peel(self, type: Literal[2, ObjectType.TREE] | type[Tree]) -> Tree: ... @overload - def peel(self, type: Literal[4] | type[Tag]) -> Tag: ... + def peel(self, type: Literal[4, ObjectType.TAG] | type[Tag]) -> Tag: ... @overload - def peel(self, type: Literal[3] | type[Blob]) -> Blob: ... + def peel(self, type: Literal[3, ObjectType.BLOB] | type[Blob]) -> Blob: ... @overload - def peel(self, type: None) -> Commit | Tree | Blob: ... + def peel(self, type: Literal[None, ObjectType.ANY]) -> Commit | Tree | Blob: ... def rename(self, new_name: str) -> None: ... def resolve(self) -> Reference: ... def set_target(self, target: _OidArg, message: str = ...) -> None: ... From 4b3878b6a2bc204752367951ef02e21d60619cc7 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:45:03 +0700 Subject: [PATCH 11/44] fix enums.py --- pygit2/enums.py | 173 ++++++++++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/pygit2/enums.py b/pygit2/enums.py index e5bd54d9..f32d35d0 100644 --- a/pygit2/enums.py +++ b/pygit2/enums.py @@ -24,6 +24,7 @@ # Boston, MA 02110-1301, USA. from enum import IntEnum, IntFlag +from typing import cast from . import _pygit2 from .ffi import C @@ -52,12 +53,12 @@ class ApplyLocation(IntEnum): class AttrCheck(IntFlag): - FILE_THEN_INDEX = C.GIT_ATTR_CHECK_FILE_THEN_INDEX - INDEX_THEN_FILE = C.GIT_ATTR_CHECK_INDEX_THEN_FILE - INDEX_ONLY = C.GIT_ATTR_CHECK_INDEX_ONLY - NO_SYSTEM = C.GIT_ATTR_CHECK_NO_SYSTEM - INCLUDE_HEAD = C.GIT_ATTR_CHECK_INCLUDE_HEAD - INCLUDE_COMMIT = C.GIT_ATTR_CHECK_INCLUDE_COMMIT + FILE_THEN_INDEX = cast(int, C.GIT_ATTR_CHECK_FILE_THEN_INDEX) + INDEX_THEN_FILE = cast(int, C.GIT_ATTR_CHECK_INDEX_THEN_FILE) + INDEX_ONLY = cast(int, C.GIT_ATTR_CHECK_INDEX_ONLY) + NO_SYSTEM = cast(int, C.GIT_ATTR_CHECK_NO_SYSTEM) + INCLUDE_HEAD = cast(int, C.GIT_ATTR_CHECK_INCLUDE_HEAD) + INCLUDE_COMMIT = cast(int, C.GIT_ATTR_CHECK_INCLUDE_COMMIT) class BlameFlag(IntFlag): @@ -119,28 +120,28 @@ class CheckoutNotify(IntFlag): ones via `CheckoutCallbacks.checkout_notify_flags`. """ - NONE = C.GIT_CHECKOUT_NOTIFY_NONE + NONE = cast(int, C.GIT_CHECKOUT_NOTIFY_NONE) - CONFLICT = C.GIT_CHECKOUT_NOTIFY_CONFLICT + CONFLICT = cast(int, C.GIT_CHECKOUT_NOTIFY_CONFLICT) 'Invokes checkout on conflicting paths.' - DIRTY = C.GIT_CHECKOUT_NOTIFY_DIRTY + DIRTY = cast(int, C.GIT_CHECKOUT_NOTIFY_DIRTY) """ Notifies about "dirty" files, i.e. those that do not need an update but no longer match the baseline. Core git displays these files when checkout runs, but won't stop the checkout. """ - UPDATED = C.GIT_CHECKOUT_NOTIFY_UPDATED + UPDATED = cast(int, C.GIT_CHECKOUT_NOTIFY_UPDATED) 'Sends notification for any file changed.' - UNTRACKED = C.GIT_CHECKOUT_NOTIFY_UNTRACKED + UNTRACKED = cast(int, C.GIT_CHECKOUT_NOTIFY_UNTRACKED) 'Notifies about untracked files.' - IGNORED = C.GIT_CHECKOUT_NOTIFY_IGNORED + IGNORED = cast(int, C.GIT_CHECKOUT_NOTIFY_IGNORED) 'Notifies about ignored files.' - ALL = C.GIT_CHECKOUT_NOTIFY_ALL + ALL = cast(int, C.GIT_CHECKOUT_NOTIFY_ALL) class CheckoutStrategy(IntFlag): @@ -268,29 +269,29 @@ class CredentialType(IntFlag): authentication methods supported by the library. """ - USERPASS_PLAINTEXT = C.GIT_CREDENTIAL_USERPASS_PLAINTEXT + USERPASS_PLAINTEXT = cast(int, C.GIT_CREDENTIAL_USERPASS_PLAINTEXT) 'A vanilla user/password request' - SSH_KEY = C.GIT_CREDENTIAL_SSH_KEY + SSH_KEY = cast(int, C.GIT_CREDENTIAL_SSH_KEY) 'An SSH key-based authentication request' - SSH_CUSTOM = C.GIT_CREDENTIAL_SSH_CUSTOM + SSH_CUSTOM = cast(int, C.GIT_CREDENTIAL_SSH_CUSTOM) 'An SSH key-based authentication request, with a custom signature' - DEFAULT = C.GIT_CREDENTIAL_DEFAULT + DEFAULT = cast(int, C.GIT_CREDENTIAL_DEFAULT) 'An NTLM/Negotiate-based authentication request.' - SSH_INTERACTIVE = C.GIT_CREDENTIAL_SSH_INTERACTIVE + SSH_INTERACTIVE = cast(int, C.GIT_CREDENTIAL_SSH_INTERACTIVE) 'An SSH interactive authentication request.' - USERNAME = C.GIT_CREDENTIAL_USERNAME + USERNAME = cast(int, C.GIT_CREDENTIAL_USERNAME) """ Username-only authentication request. Used as a pre-authentication step if the underlying transport (eg. SSH, with no username in its URL) does not know which username to use. """ - SSH_MEMORY = C.GIT_CREDENTIAL_SSH_MEMORY + SSH_MEMORY = cast(int, C.GIT_CREDENTIAL_SSH_MEMORY) """ An SSH key-based authentication request. Allows credentials to be read from memory instead of files. @@ -651,23 +652,23 @@ class Feature(IntFlag): was compiled. """ - THREADS = C.GIT_FEATURE_THREADS - HTTPS = C.GIT_FEATURE_HTTPS - SSH = C.GIT_FEATURE_SSH - NSEC = C.GIT_FEATURE_NSEC + THREADS = cast(int, C.GIT_FEATURE_THREADS) + HTTPS = cast(int, C.GIT_FEATURE_HTTPS) + SSH = cast(int, C.GIT_FEATURE_SSH) + NSEC = cast(int, C.GIT_FEATURE_NSEC) class FetchPrune(IntEnum): """Acceptable prune settings when fetching.""" - UNSPECIFIED = C.GIT_FETCH_PRUNE_UNSPECIFIED + UNSPECIFIED = cast(int, C.GIT_FETCH_PRUNE_UNSPECIFIED) 'Use the setting from the configuration' - PRUNE = C.GIT_FETCH_PRUNE + PRUNE = cast(int, C.GIT_FETCH_PRUNE) """Force pruning on: remove any remote branch in the local repository that does not exist in the remote.""" - NO_PRUNE = C.GIT_FETCH_NO_PRUNE + NO_PRUNE = cast(int, C.GIT_FETCH_NO_PRUNE) """Force pruning off: always keep the remote branches.""" @@ -783,7 +784,7 @@ class MergeFavor(IntEnum): merging functionality how to deal with conflicting regions of the files. """ - NORMAL = C.GIT_MERGE_FILE_FAVOR_NORMAL + NORMAL = cast(int, C.GIT_MERGE_FILE_FAVOR_NORMAL) """ When a region of a file is changed in both branches, a conflict will be recorded in the index so that `checkout` can produce a merge file with @@ -792,7 +793,7 @@ class MergeFavor(IntEnum): This is the default. """ - OURS = C.GIT_MERGE_FILE_FAVOR_OURS + OURS = cast(int, C.GIT_MERGE_FILE_FAVOR_OURS) """ When a region of a file is changed in both branches, the file created in the index will contain the "ours" side of any conflicting region. @@ -800,7 +801,7 @@ class MergeFavor(IntEnum): The index will not record a conflict. """ - THEIRS = C.GIT_MERGE_FILE_FAVOR_THEIRS + THEIRS = cast(int, C.GIT_MERGE_FILE_FAVOR_THEIRS) """ When a region of a file is changed in both branches, the file created in the index will contain the "theirs" side of any conflicting region. @@ -808,7 +809,7 @@ class MergeFavor(IntEnum): The index will not record a conflict. """ - UNION = C.GIT_MERGE_FILE_FAVOR_UNION + UNION = cast(int, C.GIT_MERGE_FILE_FAVOR_UNION) """ When a region of a file is changed in both branches, the file created in the index will contain each unique line from each side, @@ -821,37 +822,37 @@ class MergeFavor(IntEnum): class MergeFileFlag(IntFlag): """File merging flags""" - DEFAULT = C.GIT_MERGE_FILE_DEFAULT + DEFAULT = cast(int, C.GIT_MERGE_FILE_DEFAULT) """ Defaults """ - STYLE_MERGE = C.GIT_MERGE_FILE_STYLE_MERGE + STYLE_MERGE = cast(int, C.GIT_MERGE_FILE_STYLE_MERGE) """ Create standard conflicted merge files """ - STYLE_DIFF3 = C.GIT_MERGE_FILE_STYLE_DIFF3 + STYLE_DIFF3 = cast(int, C.GIT_MERGE_FILE_STYLE_DIFF3) """ Create diff3-style files """ - SIMPLIFY_ALNUM = C.GIT_MERGE_FILE_SIMPLIFY_ALNUM + SIMPLIFY_ALNUM = cast(int, C.GIT_MERGE_FILE_SIMPLIFY_ALNUM) """ Condense non-alphanumeric regions for simplified diff file """ - IGNORE_WHITESPACE = C.GIT_MERGE_FILE_IGNORE_WHITESPACE + IGNORE_WHITESPACE = cast(int, C.GIT_MERGE_FILE_IGNORE_WHITESPACE) """ Ignore all whitespace """ - IGNORE_WHITESPACE_CHANGE = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE + IGNORE_WHITESPACE_CHANGE = cast(int, C.GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE) """ Ignore changes in amount of whitespace """ - IGNORE_WHITESPACE_EOL = C.GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL + IGNORE_WHITESPACE_EOL = cast(int, C.GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL) """ Ignore whitespace at end of line """ - DIFF_PATIENCE = C.GIT_MERGE_FILE_DIFF_PATIENCE + DIFF_PATIENCE = cast(int, C.GIT_MERGE_FILE_DIFF_PATIENCE) """ Use the "patience diff" algorithm """ - DIFF_MINIMAL = C.GIT_MERGE_FILE_DIFF_MINIMAL + DIFF_MINIMAL = cast(int, C.GIT_MERGE_FILE_DIFF_MINIMAL) """ Take extra time to find minimal diff """ - STYLE_ZDIFF3 = C.GIT_MERGE_FILE_STYLE_ZDIFF3 + STYLE_ZDIFF3 = cast(int, C.GIT_MERGE_FILE_STYLE_ZDIFF3) """ Create zdiff3 ("zealous diff3")-style files """ - ACCEPT_CONFLICTS = C.GIT_MERGE_FILE_ACCEPT_CONFLICTS + ACCEPT_CONFLICTS = cast(int, C.GIT_MERGE_FILE_ACCEPT_CONFLICTS) """ Do not produce file conflicts when common regions have changed; keep the conflict markers in the file and accept that as the merge result. @@ -864,26 +865,26 @@ class MergeFlag(IntFlag): A combination of these flags can be passed in via the `flags` value. """ - FIND_RENAMES = C.GIT_MERGE_FIND_RENAMES + FIND_RENAMES = cast(int, C.GIT_MERGE_FIND_RENAMES) """ Detect renames that occur between the common ancestor and the "ours" side or the common ancestor and the "theirs" side. This will enable the ability to merge between a modified and renamed file. """ - FAIL_ON_CONFLICT = C.GIT_MERGE_FAIL_ON_CONFLICT + FAIL_ON_CONFLICT = cast(int, C.GIT_MERGE_FAIL_ON_CONFLICT) """ If a conflict occurs, exit immediately instead of attempting to continue resolving conflicts. The merge operation will raise GitError (GIT_EMERGECONFLICT) and no index will be returned. """ - SKIP_REUC = C.GIT_MERGE_SKIP_REUC + SKIP_REUC = cast(int, C.GIT_MERGE_SKIP_REUC) """ Do not write the REUC extension on the generated index. """ - NO_RECURSIVE = C.GIT_MERGE_NO_RECURSIVE + NO_RECURSIVE = cast(int, C.GIT_MERGE_NO_RECURSIVE) """ If the commits being merged have multiple merge bases, do not build a recursive merge base (by merging the multiple merge bases), @@ -891,7 +892,7 @@ class MergeFlag(IntFlag): merge base to `git-merge-resolve`. """ - VIRTUAL_BASE = C.GIT_MERGE_VIRTUAL_BASE + VIRTUAL_BASE = cast(int, C.GIT_MERGE_VIRTUAL_BASE) """ Treat this merge as if it is to produce the virtual base of a recursive merge. This will ensure that there are no conflicts, any conflicting @@ -1006,16 +1007,16 @@ class ReferenceFilter(IntEnum): class ReferenceType(IntFlag): """Basic type of any Git reference.""" - INVALID = C.GIT_REFERENCE_INVALID + INVALID = cast(int, C.GIT_REFERENCE_INVALID) 'Invalid reference' - DIRECT = C.GIT_REFERENCE_DIRECT + DIRECT = cast(int, C.GIT_REFERENCE_DIRECT) 'A reference that points at an object id' - SYMBOLIC = C.GIT_REFERENCE_SYMBOLIC + SYMBOLIC = cast(int, C.GIT_REFERENCE_SYMBOLIC) 'A reference that points at another reference' - ALL = C.GIT_REFERENCE_ALL + ALL = cast(int, C.GIT_REFERENCE_ALL) 'Bitwise OR of (DIRECT | SYMBOLIC)' @@ -1024,33 +1025,33 @@ class RepositoryInitFlag(IntFlag): Option flags for pygit2.init_repository(). """ - BARE = C.GIT_REPOSITORY_INIT_BARE + BARE = cast(int, C.GIT_REPOSITORY_INIT_BARE) 'Create a bare repository with no working directory.' - NO_REINIT = C.GIT_REPOSITORY_INIT_NO_REINIT + NO_REINIT = cast(int, C.GIT_REPOSITORY_INIT_NO_REINIT) 'Raise GitError if the path appears to already be a git repository.' - NO_DOTGIT_DIR = C.GIT_REPOSITORY_INIT_NO_DOTGIT_DIR + NO_DOTGIT_DIR = cast(int, C.GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) """Normally a "/.git/" will be appended to the repo path for non-bare repos (if it is not already there), but passing this flag prevents that behavior.""" - MKDIR = C.GIT_REPOSITORY_INIT_MKDIR + MKDIR = cast(int, C.GIT_REPOSITORY_INIT_MKDIR) """Make the repo_path (and workdir_path) as needed. Init is always willing to create the ".git" directory even without this flag. This flag tells init to create the trailing component of the repo and workdir paths as needed.""" - MKPATH = C.GIT_REPOSITORY_INIT_MKPATH + MKPATH = cast(int, C.GIT_REPOSITORY_INIT_MKPATH) 'Recursively make all components of the repo and workdir paths as necessary.' - EXTERNAL_TEMPLATE = C.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE + EXTERNAL_TEMPLATE = cast(int, C.GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) """libgit2 normally uses internal templates to initialize a new repo. This flags enables external templates, looking at the "template_path" from the options if set, or the `init.templatedir` global config if not, or falling back on "/usr/share/git-core/templates" if it exists.""" - RELATIVE_GITLINK = C.GIT_REPOSITORY_INIT_RELATIVE_GITLINK + RELATIVE_GITLINK = cast(int, C.GIT_REPOSITORY_INIT_RELATIVE_GITLINK) """If an alternate workdir is specified, use relative paths for the gitdir and core.worktree.""" @@ -1060,16 +1061,16 @@ class RepositoryInitMode(IntEnum): Mode options for pygit2.init_repository(). """ - SHARED_UMASK = C.GIT_REPOSITORY_INIT_SHARED_UMASK + SHARED_UMASK = cast(int, C.GIT_REPOSITORY_INIT_SHARED_UMASK) 'Use permissions configured by umask - the default.' - SHARED_GROUP = C.GIT_REPOSITORY_INIT_SHARED_GROUP + SHARED_GROUP = cast(int, C.GIT_REPOSITORY_INIT_SHARED_GROUP) """ Use '--shared=group' behavior, chmod'ing the new repo to be group writable and "g+sx" for sticky group assignment. """ - SHARED_ALL = C.GIT_REPOSITORY_INIT_SHARED_ALL + SHARED_ALL = cast(int, C.GIT_REPOSITORY_INIT_SHARED_ALL) "Use '--shared=all' behavior, adding world readability." @@ -1081,14 +1082,14 @@ class RepositoryOpenFlag(IntFlag): DEFAULT = 0 'Default flags.' - NO_SEARCH = C.GIT_REPOSITORY_OPEN_NO_SEARCH + NO_SEARCH = cast(int, C.GIT_REPOSITORY_OPEN_NO_SEARCH) """ Only open the repository if it can be immediately found in the start_path. Do not walk up from the start_path looking at parent directories. """ - CROSS_FS = C.GIT_REPOSITORY_OPEN_CROSS_FS + CROSS_FS = cast(int, C.GIT_REPOSITORY_OPEN_CROSS_FS) """ Unless this flag is set, open will not continue searching across filesystem boundaries (i.e. when `st_dev` changes from the `stat` @@ -1097,21 +1098,21 @@ class RepositoryOpenFlag(IntFlag): "/" is a different filesystem than "/home". """ - BARE = C.GIT_REPOSITORY_OPEN_BARE + BARE = cast(int, C.GIT_REPOSITORY_OPEN_BARE) """ Open repository as a bare repo regardless of core.bare config, and defer loading config file for faster setup. Unlike `git_repository_open_bare`, this can follow gitlinks. """ - NO_DOTGIT = C.GIT_REPOSITORY_OPEN_NO_DOTGIT + NO_DOTGIT = cast(int, C.GIT_REPOSITORY_OPEN_NO_DOTGIT) """ Do not check for a repository by appending /.git to the start_path; only open the repository if start_path itself points to the git directory. """ - FROM_ENV = C.GIT_REPOSITORY_OPEN_FROM_ENV + FROM_ENV = cast(int, C.GIT_REPOSITORY_OPEN_FROM_ENV) """ Find and open a git repository, respecting the environment variables used by the git command-line tools. @@ -1135,18 +1136,18 @@ class RepositoryState(IntEnum): to be in, based on the current operation which is ongoing. """ - NONE = C.GIT_REPOSITORY_STATE_NONE - MERGE = C.GIT_REPOSITORY_STATE_MERGE - REVERT = C.GIT_REPOSITORY_STATE_REVERT - REVERT_SEQUENCE = C.GIT_REPOSITORY_STATE_REVERT_SEQUENCE - CHERRYPICK = C.GIT_REPOSITORY_STATE_CHERRYPICK - CHERRYPICK_SEQUENCE = C.GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE - BISECT = C.GIT_REPOSITORY_STATE_BISECT - REBASE = C.GIT_REPOSITORY_STATE_REBASE - REBASE_INTERACTIVE = C.GIT_REPOSITORY_STATE_REBASE_INTERACTIVE - REBASE_MERGE = C.GIT_REPOSITORY_STATE_REBASE_MERGE - APPLY_MAILBOX = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX - APPLY_MAILBOX_OR_REBASE = C.GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE + NONE = cast(int, C.GIT_REPOSITORY_STATE_NONE) + MERGE = cast(int, C.GIT_REPOSITORY_STATE_MERGE) + REVERT = cast(int, C.GIT_REPOSITORY_STATE_REVERT) + REVERT_SEQUENCE = cast(int, C.GIT_REPOSITORY_STATE_REVERT_SEQUENCE) + CHERRYPICK = cast(int, C.GIT_REPOSITORY_STATE_CHERRYPICK) + CHERRYPICK_SEQUENCE = cast(int, C.GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE) + BISECT = cast(int, C.GIT_REPOSITORY_STATE_BISECT) + REBASE = cast(int, C.GIT_REPOSITORY_STATE_REBASE) + REBASE_INTERACTIVE = cast(int, C.GIT_REPOSITORY_STATE_REBASE_INTERACTIVE) + REBASE_MERGE = cast(int, C.GIT_REPOSITORY_STATE_REBASE_MERGE) + APPLY_MAILBOX = cast(int, C.GIT_REPOSITORY_STATE_APPLY_MAILBOX) + APPLY_MAILBOX_OR_REBASE = cast(int, C.GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE) class ResetMode(IntEnum): @@ -1214,27 +1215,27 @@ class StashApplyProgress(IntEnum): Stash apply progression states """ - NONE = C.GIT_STASH_APPLY_PROGRESS_NONE + NONE = cast(int, C.GIT_STASH_APPLY_PROGRESS_NONE) - LOADING_STASH = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH + LOADING_STASH = cast(int, C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH) 'Loading the stashed data from the object database.' - ANALYZE_INDEX = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX + ANALYZE_INDEX = cast(int, C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX) 'The stored index is being analyzed.' - ANALYZE_MODIFIED = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED + ANALYZE_MODIFIED = cast(int, C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED) 'The modified files are being analyzed.' - ANALYZE_UNTRACKED = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED + ANALYZE_UNTRACKED = cast(int, C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED) 'The untracked and ignored files are being analyzed.' - CHECKOUT_UNTRACKED = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED + CHECKOUT_UNTRACKED = cast(int, C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED) 'The untracked files are being written to disk.' - CHECKOUT_MODIFIED = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED + CHECKOUT_MODIFIED = cast(int, C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED) 'The modified files are being written to disk.' - DONE = C.GIT_STASH_APPLY_PROGRESS_DONE + DONE = cast(int, C.GIT_STASH_APPLY_PROGRESS_DONE) 'The stash was applied successfully.' From 49bc71b2e912a832c64c342f14b0425706179905 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 22:58:30 +0700 Subject: [PATCH 12/44] add FilterSource --- pygit2/_pygit2.pyi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index dead6b66..ba4ec393 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -11,6 +11,8 @@ from .enums import ( DiffOption, DiffStatsFormat, FileMode, + FilterFlag, + FilterMode, MergeAnalysis, MergePreference, ObjectType, @@ -803,6 +805,14 @@ class Worktree: path: str def prune(self, force: bool = False) -> None: ... +class FilterSource: + filemode: int + flags: FilterFlag + mode: FilterMode + oid: Oid | None + path: str + repo: Repository + def discover_repository( path: str, across_fs: bool = False, ceiling_dirs: str = ... ) -> str | None: ... From 7433683e1fc647646a68524efcf8a8570d32f9c6 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:02:05 +0700 Subject: [PATCH 13/44] yet another fix the _pygit2.pyi --- pygit2/_pygit2.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index ba4ec393..8626cd0c 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -1,5 +1,5 @@ from io import IOBase -from typing import Any, Iterator, Literal, Optional, TypeAlias, overload +from typing import Any, Callable, Iterator, Literal, Optional, TypeAlias, overload from . import Index from .enums import ( @@ -822,3 +822,5 @@ def init_file_backend(path: str, flags: int = 0) -> object: ... def option(opt: Option, *args: Any) -> None: ... def reference_is_valid_name(refname: str) -> bool: ... def tree_entry_cmp(a: Object, b: Object) -> int: ... + +_cache_enums: Callable[..., None] From 3aabbc7c6f8b09345b8d2254fa1e67877c194f01 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:08:01 +0700 Subject: [PATCH 14/44] add typing to credentials.py --- pygit2/credentials.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/pygit2/credentials.py b/pygit2/credentials.py index f16aac4a..03c374e1 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -23,8 +23,19 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from __future__ import annotations + +from typing import Any, Protocol + from .enums import CredentialType -from .ffi import C + + +class BaseCredentials(Protocol): + @property + def credential_type(self) -> CredentialType: ... + @property + def credential_tuple(self) -> tuple[str, ...]: ... + def __call__(self, _url: Any, _username: Any, _allowed: Any) -> BaseCredentials: ... class Username: @@ -34,7 +45,7 @@ class Username: callback and for returning from said callback. """ - def __init__(self, username): + def __init__(self, username: str): self._username = username @property @@ -45,7 +56,7 @@ def credential_type(self) -> CredentialType: def credential_tuple(self): return (self._username,) - def __call__(self, _url, _username, _allowed): + def __call__(self, _url: Any, _username: Any, _allowed: Any): return self @@ -56,7 +67,7 @@ class UserPass: callback and for returning from said callback. """ - def __init__(self, username, password): + def __init__(self, username: str, password: str): self._username = username self._password = password @@ -68,7 +79,7 @@ def credential_type(self) -> CredentialType: def credential_tuple(self): return (self._username, self._password) - def __call__(self, _url, _username, _allowed): + def __call__(self, _url: Any, _username: Any, _allowed: Any): return self @@ -95,7 +106,13 @@ class Keypair: no passphrase is required. """ - def __init__(self, username, pubkey, privkey, passphrase): + def __init__( + self, + username: str, + pubkey: str | None, + privkey: str | None, + passphrase: str | None, + ): self._username = username self._pubkey = pubkey self._privkey = privkey @@ -109,12 +126,12 @@ def credential_type(self) -> CredentialType: def credential_tuple(self): return (self._username, self._pubkey, self._privkey, self._passphrase) - def __call__(self, _url, _username, _allowed): + def __call__(self, _url: Any, _username: Any, _allowed: Any): return self class KeypairFromAgent(Keypair): - def __init__(self, username): + def __init__(self, username: str): super().__init__(username, None, None, None) From 9a1affc9fbc1b09aba2722d86603c0f69b044a06 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:10:49 +0700 Subject: [PATCH 15/44] add typing to errors.py --- pygit2/errors.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pygit2/errors.py b/pygit2/errors.py index 53108072..c4aff076 100644 --- a/pygit2/errors.py +++ b/pygit2/errors.py @@ -30,7 +30,7 @@ value_errors = {C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EAMBIGUOUS} -def check_error(err, io=False): +def check_error(err: int, io: bool = False) -> None: if err >= 0: return @@ -41,7 +41,9 @@ def check_error(err, io=False): # Error message giterr = C.git_error_last() if giterr != ffi.NULL: - message = ffi.string(giterr.message).decode('utf8') + message = ffi.string(giterr.message) + if isinstance(message, bytes): + message = message.decode('utf8') else: message = f'err {err} (no message provided)' @@ -66,6 +68,6 @@ def check_error(err, io=False): # Indicate that we want libgit2 to pretend a function was not set -class Passthrough(Exception): +class Passthrough(NotImplementedError): def __init__(self): super().__init__('The function asked for pass-through') From 735ba5a6283b388034c402d9b11d87d56fa4de62 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:12:35 +0700 Subject: [PATCH 16/44] fix pyproject again --- pyproject.toml | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e8f77358..3e07c3b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ skip = "pp3* *musllinux_aarch64 *musllinux_ppc64le" archs = ["auto"] build-frontend = "default" dependency-versions = "pinned" -environment = {LIBGIT2_VERSION="1.8.4", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSION="3.2.3", LIBGIT2="/project/ci"} +environment = { LIBGIT2_VERSION = "1.8.4", LIBSSH2_VERSION = "1.11.1", OPENSSL_VERSION = "3.2.3", LIBGIT2 = "/project/ci" } before-all = "sh build.sh" @@ -21,19 +21,27 @@ repair-wheel-command = "LD_LIBRARY_PATH=/project/ci/lib auditwheel repair -w {de [tool.cibuildwheel.macos] archs = ["universal2"] -environment = {LIBGIT2_VERSION="1.8.4", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSION="3.2.3", LIBGIT2="/Users/runner/work/pygit2/pygit2/ci"} +environment = { LIBGIT2_VERSION = "1.8.4", LIBSSH2_VERSION = "1.11.1", OPENSSL_VERSION = "3.2.3", LIBGIT2 = "/Users/runner/work/pygit2/pygit2/ci" } repair-wheel-command = "DYLD_LIBRARY_PATH=/Users/runner/work/pygit2/pygit2/ci/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}" [tool.ruff] -target-version = "py310" # oldest supported Python version +target-version = "py310" # oldest supported Python version fix = true -extend-exclude = [ - ".cache", - ".coverage", - "build", - "venv*", +extend-exclude = [".cache", ".coverage", "build", "venv*"] +lint.select = [ + "C", + "E", + "W", + "F", + "I", + "B", + "C4", + "ARG", + "SIM", + "PTH", + "PL", + "TID", ] -lint.select = ["C", "E", "W", "F", "I", "B", "C4", "ARG", "SIM", "PTH", "PL", "TID"] lint.ignore = [ "W291", # Trailing whitespace "E501", # Line too long @@ -44,7 +52,8 @@ lint.ignore = [ "PLW0603", # Global statement "PLR0913", # Too many arguments "B010", # setattr - "F401" # unused imports + "F401", # unused imports + "ARG002", # unused arguments ] [tool.ruff.format] From 5fc3b70fe0315a9b08d3527eec50bf0bb755d82f Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:15:35 +0700 Subject: [PATCH 17/44] add filter_* functions to _pygit2.pyi --- pygit2/_pygit2.pyi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 8626cd0c..f745f289 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -1,6 +1,8 @@ from io import IOBase from typing import Any, Callable, Iterator, Literal, Optional, TypeAlias, overload +from pygit2.filter import Filter + from . import Index from .enums import ( ApplyLocation, @@ -822,5 +824,9 @@ def init_file_backend(path: str, flags: int = 0) -> object: ... def option(opt: Option, *args: Any) -> None: ... def reference_is_valid_name(refname: str) -> bool: ... def tree_entry_cmp(a: Object, b: Object) -> int: ... +def filter_register( + name: str, filter_cls: type[Filter], priority: int = GIT_FILTER_DRIVER_PRIORITY +) -> None: ... +def filter_unregister(name: str) -> None: ... _cache_enums: Callable[..., None] From 95b003f0ddf8b5cee73c63922ad3acb21144a438 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:28:08 +0700 Subject: [PATCH 18/44] add typing to utils.py --- pygit2/utils.py | 60 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/pygit2/utils.py b/pygit2/utils.py index bf6d7ead..fa220467 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -23,21 +23,33 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from __future__ import annotations + import contextlib import os +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast # Import from pygit2 from .ffi import C, ffi +if TYPE_CHECKING: + from _typeshed import SupportsLenAndGetItem + +_T = TypeVar('_T') + -def maybe_string(ptr): +def maybe_string(ptr: Any) -> str | None: if not ptr: return None - return ffi.string(ptr).decode('utf8') + out = ffi.string(ptr) + if isinstance(out, bytes): + out = out.decode('utf8') + return out -def to_bytes(s, encoding='utf-8', errors='strict'): +def to_bytes(s: Any, encoding: str = 'utf-8', errors: str = 'strict') -> bytes | Any: if s == ffi.NULL or s is None: return ffi.NULL @@ -50,27 +62,27 @@ def to_bytes(s, encoding='utf-8', errors='strict'): return s.encode(encoding, errors) -def to_str(s): +def to_str(s: Any) -> str: if hasattr(s, '__fspath__'): s = os.fspath(s) - if type(s) is str: + if isinstance(s, str): return s - if type(s) is bytes: + if isinstance(s, bytes): return s.decode() raise TypeError(f'unexpected type "{repr(s)}"') -def ptr_to_bytes(ptr_cdata): +def ptr_to_bytes(ptr_cdata: Any) -> bytes: """ Convert a pointer coming from C code () to a byte buffer containing the address that the pointer refers to. """ pp = ffi.new('void **', ptr_cdata) - return bytes(ffi.buffer(pp)[:]) + return bytes(ffi.buffer(pp)[:]) # type: ignore @contextlib.contextmanager @@ -80,7 +92,7 @@ def new_git_strarray(): C.git_strarray_dispose(strarray) -def strarray_to_strings(arr): +def strarray_to_strings(arr: Any) -> list[str]: """ Return a list of strings from a git_strarray pointer. @@ -88,7 +100,7 @@ def strarray_to_strings(arr): calling this function. """ try: - return [ffi.string(arr.strings[i]).decode('utf-8') for i in range(arr.count)] + return [ffi.string(arr.strings[i]).decode('utf-8') for i in range(arr.count)] # type: ignore finally: C.git_strarray_dispose(arr) @@ -113,18 +125,20 @@ class StrArray: contents of 'struct' only remain valid within the StrArray context. """ - def __init__(self, l): + def __init__(self, listarg: Any): # Allow passing in None as lg2 typically considers them the same as empty - if l is None: + if listarg is None: self.__array = ffi.NULL return - if not isinstance(l, (list, tuple)): + if not isinstance(listarg, (list, tuple)): raise TypeError('Value must be a list') - strings = [None] * len(l) - for i in range(len(l)): - li = l[i] + listarg = cast(list[Any], listarg) + + strings: list[Any] = [None] * len(listarg) + for i in range(len(listarg)): + li = listarg[i] if not isinstance(li, str) and not hasattr(li, '__fspath__'): raise TypeError('Value must be a string or PathLike object') @@ -137,14 +151,16 @@ def __init__(self, l): def __enter__(self): return self - def __exit__(self, type, value, traceback): + def __exit__( + self, type: type[BaseException], value: BaseException, traceback: TracebackType + ) -> None: pass @property def ptr(self): return self.__array - def assign_to(self, git_strarray): + def assign_to(self, git_strarray: Any): if self.__array == ffi.NULL: git_strarray.strings = ffi.NULL git_strarray.count = 0 @@ -153,22 +169,22 @@ def assign_to(self, git_strarray): git_strarray.count = len(self.__strings) -class GenericIterator: +class GenericIterator(Generic[_T]): """Helper to easily implement an iterator. The constructor gets a container which must implement __len__ and __getitem__ """ - def __init__(self, container): + def __init__(self, container: SupportsLenAndGetItem[_T]): self.container = container self.length = len(container) self.idx = 0 - def next(self): + def next(self) -> _T: return self.__next__() - def __next__(self): + def __next__(self) -> _T: idx = self.idx if idx >= self.length: raise StopIteration From e71d03ba175818184ffe926a95c29c2f582933d0 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:29:31 +0700 Subject: [PATCH 19/44] fix ffi.py --- pygit2/ffi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 7d712195..1fb809fb 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -27,4 +27,4 @@ from ._libgit2 import ffi from ._libgit2 import lib as C -__all__ = ["ffi", "C"] +__all__ = ['ffi', 'C'] From 9b68da868e6577b7c2b1c13928acbbdc2b808c08 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sat, 21 Dec 2024 23:29:42 +0700 Subject: [PATCH 20/44] initial commit of pygit2 __init__ --- pygit2/__init__.py | 758 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 755 insertions(+), 3 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index e245184c..e1d7655e 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -34,7 +34,297 @@ # Low level API from ._pygit2 import * -from ._pygit2 import _cache_enums +from ._pygit2 import ( + GIT_APPLY_LOCATION_BOTH, + GIT_APPLY_LOCATION_INDEX, + GIT_APPLY_LOCATION_WORKDIR, + GIT_BLAME_FIRST_PARENT, + GIT_BLAME_IGNORE_WHITESPACE, + GIT_BLAME_NORMAL, + GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES, + GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES, + GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES, + GIT_BLAME_TRACK_COPIES_SAME_FILE, + GIT_BLAME_USE_MAILMAP, + GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT, + GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD, + GIT_BLOB_FILTER_CHECK_FOR_BINARY, + GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES, + GIT_BRANCH_ALL, + GIT_BRANCH_LOCAL, + GIT_BRANCH_REMOTE, + GIT_CHECKOUT_ALLOW_CONFLICTS, + GIT_CHECKOUT_CONFLICT_STYLE_DIFF3, + GIT_CHECKOUT_CONFLICT_STYLE_MERGE, + GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3, + GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH, + GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, + GIT_CHECKOUT_DONT_REMOVE_EXISTING, + GIT_CHECKOUT_DONT_UPDATE_INDEX, + GIT_CHECKOUT_DONT_WRITE_INDEX, + GIT_CHECKOUT_DRY_RUN, + GIT_CHECKOUT_FORCE, + GIT_CHECKOUT_NO_REFRESH, + GIT_CHECKOUT_NONE, + GIT_CHECKOUT_RECREATE_MISSING, + GIT_CHECKOUT_REMOVE_IGNORED, + GIT_CHECKOUT_REMOVE_UNTRACKED, + GIT_CHECKOUT_SAFE, + GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES, + GIT_CHECKOUT_SKIP_UNMERGED, + GIT_CHECKOUT_UPDATE_ONLY, + GIT_CHECKOUT_USE_OURS, + GIT_CHECKOUT_USE_THEIRS, + GIT_CONFIG_HIGHEST_LEVEL, + GIT_CONFIG_LEVEL_APP, + GIT_CONFIG_LEVEL_GLOBAL, + GIT_CONFIG_LEVEL_LOCAL, + GIT_CONFIG_LEVEL_PROGRAMDATA, + GIT_CONFIG_LEVEL_SYSTEM, + GIT_CONFIG_LEVEL_WORKTREE, + GIT_CONFIG_LEVEL_XDG, + GIT_DELTA_ADDED, + GIT_DELTA_CONFLICTED, + GIT_DELTA_COPIED, + GIT_DELTA_DELETED, + GIT_DELTA_IGNORED, + GIT_DELTA_MODIFIED, + GIT_DELTA_RENAMED, + GIT_DELTA_TYPECHANGE, + GIT_DELTA_UNMODIFIED, + GIT_DELTA_UNREADABLE, + GIT_DELTA_UNTRACKED, + GIT_DESCRIBE_ALL, + GIT_DESCRIBE_DEFAULT, + GIT_DESCRIBE_TAGS, + GIT_DIFF_BREAK_REWRITES, + GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, + GIT_DIFF_DISABLE_PATHSPEC_MATCH, + GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, + GIT_DIFF_FIND_ALL, + GIT_DIFF_FIND_AND_BREAK_REWRITES, + GIT_DIFF_FIND_BY_CONFIG, + GIT_DIFF_FIND_COPIES, + GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, + GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, + GIT_DIFF_FIND_EXACT_MATCH_ONLY, + GIT_DIFF_FIND_FOR_UNTRACKED, + GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, + GIT_DIFF_FIND_IGNORE_WHITESPACE, + GIT_DIFF_FIND_REMOVE_UNMODIFIED, + GIT_DIFF_FIND_RENAMES, + GIT_DIFF_FIND_RENAMES_FROM_REWRITES, + GIT_DIFF_FIND_REWRITES, + GIT_DIFF_FLAG_BINARY, + GIT_DIFF_FLAG_EXISTS, + GIT_DIFF_FLAG_NOT_BINARY, + GIT_DIFF_FLAG_VALID_ID, + GIT_DIFF_FLAG_VALID_SIZE, + GIT_DIFF_FORCE_BINARY, + GIT_DIFF_FORCE_TEXT, + GIT_DIFF_IGNORE_BLANK_LINES, + GIT_DIFF_IGNORE_CASE, + GIT_DIFF_IGNORE_FILEMODE, + GIT_DIFF_IGNORE_SUBMODULES, + GIT_DIFF_IGNORE_WHITESPACE, + GIT_DIFF_IGNORE_WHITESPACE_CHANGE, + GIT_DIFF_IGNORE_WHITESPACE_EOL, + GIT_DIFF_INCLUDE_CASECHANGE, + GIT_DIFF_INCLUDE_IGNORED, + GIT_DIFF_INCLUDE_TYPECHANGE, + GIT_DIFF_INCLUDE_TYPECHANGE_TREES, + GIT_DIFF_INCLUDE_UNMODIFIED, + GIT_DIFF_INCLUDE_UNREADABLE, + GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED, + GIT_DIFF_INCLUDE_UNTRACKED, + GIT_DIFF_INDENT_HEURISTIC, + GIT_DIFF_MINIMAL, + GIT_DIFF_NORMAL, + GIT_DIFF_PATIENCE, + GIT_DIFF_RECURSE_IGNORED_DIRS, + GIT_DIFF_RECURSE_UNTRACKED_DIRS, + GIT_DIFF_REVERSE, + GIT_DIFF_SHOW_BINARY, + GIT_DIFF_SHOW_UNMODIFIED, + GIT_DIFF_SHOW_UNTRACKED_CONTENT, + GIT_DIFF_SKIP_BINARY_CHECK, + GIT_DIFF_STATS_FULL, + GIT_DIFF_STATS_INCLUDE_SUMMARY, + GIT_DIFF_STATS_NONE, + GIT_DIFF_STATS_NUMBER, + GIT_DIFF_STATS_SHORT, + GIT_DIFF_UPDATE_INDEX, + GIT_FILEMODE_BLOB, + GIT_FILEMODE_BLOB_EXECUTABLE, + GIT_FILEMODE_COMMIT, + GIT_FILEMODE_LINK, + GIT_FILEMODE_TREE, + GIT_FILEMODE_UNREADABLE, + GIT_FILTER_ALLOW_UNSAFE, + GIT_FILTER_ATTRIBUTES_FROM_COMMIT, + GIT_FILTER_ATTRIBUTES_FROM_HEAD, + GIT_FILTER_CLEAN, + GIT_FILTER_DEFAULT, + GIT_FILTER_DRIVER_PRIORITY, + GIT_FILTER_NO_SYSTEM_ATTRIBUTES, + GIT_FILTER_SMUDGE, + GIT_FILTER_TO_ODB, + GIT_FILTER_TO_WORKTREE, + GIT_MERGE_ANALYSIS_FASTFORWARD, + GIT_MERGE_ANALYSIS_NONE, + GIT_MERGE_ANALYSIS_NORMAL, + GIT_MERGE_ANALYSIS_UNBORN, + GIT_MERGE_ANALYSIS_UP_TO_DATE, + GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, + GIT_MERGE_PREFERENCE_NO_FASTFORWARD, + GIT_MERGE_PREFERENCE_NONE, + GIT_OBJECT_ANY, + GIT_OBJECT_BLOB, + GIT_OBJECT_COMMIT, + GIT_OBJECT_INVALID, + GIT_OBJECT_OFS_DELTA, + GIT_OBJECT_REF_DELTA, + GIT_OBJECT_TAG, + GIT_OBJECT_TREE, + GIT_OID_HEX_ZERO, + GIT_OID_HEXSZ, + GIT_OID_MINPREFIXLEN, + GIT_OID_RAWSZ, + GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, + GIT_OPT_ENABLE_CACHING, + GIT_OPT_ENABLE_FSYNC_GITDIR, + GIT_OPT_ENABLE_OFS_DELTA, + GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, + GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, + GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, + GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, + GIT_OPT_GET_CACHED_MEMORY, + GIT_OPT_GET_MWINDOW_FILE_LIMIT, + GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, + GIT_OPT_GET_MWINDOW_SIZE, + GIT_OPT_GET_OWNER_VALIDATION, + GIT_OPT_GET_PACK_MAX_OBJECTS, + GIT_OPT_GET_SEARCH_PATH, + GIT_OPT_GET_TEMPLATE_PATH, + GIT_OPT_GET_USER_AGENT, + GIT_OPT_GET_WINDOWS_SHAREMODE, + GIT_OPT_SET_ALLOCATOR, + GIT_OPT_SET_CACHE_MAX_SIZE, + GIT_OPT_SET_CACHE_OBJECT_LIMIT, + GIT_OPT_SET_MWINDOW_FILE_LIMIT, + GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, + GIT_OPT_SET_MWINDOW_SIZE, + GIT_OPT_SET_OWNER_VALIDATION, + GIT_OPT_SET_PACK_MAX_OBJECTS, + GIT_OPT_SET_SEARCH_PATH, + GIT_OPT_SET_SSL_CERT_LOCATIONS, + GIT_OPT_SET_SSL_CIPHERS, + GIT_OPT_SET_TEMPLATE_PATH, + GIT_OPT_SET_USER_AGENT, + GIT_OPT_SET_WINDOWS_SHAREMODE, + GIT_REFERENCES_ALL, + GIT_REFERENCES_BRANCHES, + GIT_REFERENCES_TAGS, + GIT_RESET_HARD, + GIT_RESET_MIXED, + GIT_RESET_SOFT, + GIT_REVSPEC_MERGE_BASE, + GIT_REVSPEC_RANGE, + GIT_REVSPEC_SINGLE, + GIT_SORT_NONE, + GIT_SORT_REVERSE, + GIT_SORT_TIME, + GIT_SORT_TOPOLOGICAL, + GIT_STASH_APPLY_DEFAULT, + GIT_STASH_APPLY_REINSTATE_INDEX, + GIT_STASH_DEFAULT, + GIT_STASH_INCLUDE_IGNORED, + GIT_STASH_INCLUDE_UNTRACKED, + GIT_STASH_KEEP_ALL, + GIT_STASH_KEEP_INDEX, + GIT_STATUS_CONFLICTED, + GIT_STATUS_CURRENT, + GIT_STATUS_IGNORED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_RENAMED, + GIT_STATUS_INDEX_TYPECHANGE, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_RENAMED, + GIT_STATUS_WT_TYPECHANGE, + GIT_STATUS_WT_UNREADABLE, + GIT_SUBMODULE_IGNORE_ALL, + GIT_SUBMODULE_IGNORE_DIRTY, + GIT_SUBMODULE_IGNORE_NONE, + GIT_SUBMODULE_IGNORE_UNSPECIFIED, + GIT_SUBMODULE_IGNORE_UNTRACKED, + GIT_SUBMODULE_STATUS_IN_CONFIG, + GIT_SUBMODULE_STATUS_IN_HEAD, + GIT_SUBMODULE_STATUS_IN_INDEX, + GIT_SUBMODULE_STATUS_IN_WD, + GIT_SUBMODULE_STATUS_INDEX_ADDED, + GIT_SUBMODULE_STATUS_INDEX_DELETED, + GIT_SUBMODULE_STATUS_INDEX_MODIFIED, + GIT_SUBMODULE_STATUS_WD_ADDED, + GIT_SUBMODULE_STATUS_WD_DELETED, + GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, + GIT_SUBMODULE_STATUS_WD_MODIFIED, + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, + GIT_SUBMODULE_STATUS_WD_UNTRACKED, + GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, + LIBGIT2_VER_MAJOR, + LIBGIT2_VER_MINOR, + LIBGIT2_VER_REVISION, + LIBGIT2_VERSION, + AlreadyExistsError, + Blob, + Branch, + Commit, + Diff, + DiffDelta, + DiffFile, + DiffHunk, + DiffLine, + DiffStats, + FilterSource, + GitError, + InvalidSpecError, + Mailmap, + Note, + Object, + Odb, + OdbBackend, + OdbBackendLoose, + OdbBackendPack, + Oid, + Patch, + Refdb, + RefdbBackend, + RefdbFsBackend, + Reference, + RefLogEntry, + RevSpec, + Signature, + Stash, + Tag, + Tree, + TreeBuilder, + Walker, + Worktree, + _cache_enums, + discover_repository, + filter_register, + filter_unregister, + hash, + hashfile, + init_file_backend, + option, + reference_is_valid_name, + tree_entry_cmp, +) from .blame import Blame, BlameHunk from .blob import BlobIO from .callbacks import ( @@ -47,12 +337,81 @@ git_fetch_options, ) from .config import Config -from .credentials import * +from .credentials import ( + Keypair, + KeypairFromAgent, + KeypairFromMemory, + Username, + UserPass, +) from .errors import Passthrough, check_error from .ffi import C, ffi from .filter import Filter from .index import Index, IndexEntry -from .legacyenums import * +from .legacyenums import ( + GIT_ATTR_CHECK_FILE_THEN_INDEX, + GIT_ATTR_CHECK_INCLUDE_COMMIT, + GIT_ATTR_CHECK_INCLUDE_HEAD, + GIT_ATTR_CHECK_INDEX_ONLY, + GIT_ATTR_CHECK_INDEX_THEN_FILE, + GIT_ATTR_CHECK_NO_SYSTEM, + GIT_CHECKOUT_NOTIFY_ALL, + GIT_CHECKOUT_NOTIFY_CONFLICT, + GIT_CHECKOUT_NOTIFY_DIRTY, + GIT_CHECKOUT_NOTIFY_IGNORED, + GIT_CHECKOUT_NOTIFY_NONE, + GIT_CHECKOUT_NOTIFY_UNTRACKED, + GIT_CHECKOUT_NOTIFY_UPDATED, + GIT_CREDENTIAL_DEFAULT, + GIT_CREDENTIAL_SSH_CUSTOM, + GIT_CREDENTIAL_SSH_INTERACTIVE, + GIT_CREDENTIAL_SSH_KEY, + GIT_CREDENTIAL_SSH_MEMORY, + GIT_CREDENTIAL_USERNAME, + GIT_CREDENTIAL_USERPASS_PLAINTEXT, + GIT_FEATURE_HTTPS, + GIT_FEATURE_NSEC, + GIT_FEATURE_SSH, + GIT_FEATURE_THREADS, + GIT_FETCH_NO_PRUNE, + GIT_FETCH_PRUNE, + GIT_FETCH_PRUNE_UNSPECIFIED, + GIT_REPOSITORY_INIT_BARE, + GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, + GIT_REPOSITORY_INIT_MKDIR, + GIT_REPOSITORY_INIT_MKPATH, + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, + GIT_REPOSITORY_INIT_NO_REINIT, + GIT_REPOSITORY_INIT_RELATIVE_GITLINK, + GIT_REPOSITORY_INIT_SHARED_ALL, + GIT_REPOSITORY_INIT_SHARED_GROUP, + GIT_REPOSITORY_INIT_SHARED_UMASK, + GIT_REPOSITORY_OPEN_BARE, + GIT_REPOSITORY_OPEN_CROSS_FS, + GIT_REPOSITORY_OPEN_FROM_ENV, + GIT_REPOSITORY_OPEN_NO_DOTGIT, + GIT_REPOSITORY_OPEN_NO_SEARCH, + GIT_REPOSITORY_STATE_APPLY_MAILBOX, + GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE, + GIT_REPOSITORY_STATE_BISECT, + GIT_REPOSITORY_STATE_CHERRYPICK, + GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE, + GIT_REPOSITORY_STATE_MERGE, + GIT_REPOSITORY_STATE_NONE, + GIT_REPOSITORY_STATE_REBASE, + GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, + GIT_REPOSITORY_STATE_REBASE_MERGE, + GIT_REPOSITORY_STATE_REVERT, + GIT_REPOSITORY_STATE_REVERT_SEQUENCE, + GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX, + GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED, + GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED, + GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED, + GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED, + GIT_STASH_APPLY_PROGRESS_DONE, + GIT_STASH_APPLY_PROGRESS_LOADING_STASH, + GIT_STASH_APPLY_PROGRESS_NONE, +) from .packbuilder import PackBuilder from .remotes import Remote from .repository import Repository @@ -60,6 +419,399 @@ from .submodules import Submodule from .utils import to_bytes, to_str +__all__ = [ + 'AlreadyExistsError', + 'Blame', + 'BlameHunk', + 'Blob', + 'BlobIO', + 'Branch', + 'C', + 'CheckoutCallbacks', + 'Commit', + 'Config', + # 'CredentialType', + 'Diff', + 'DiffDelta', + 'DiffFile', + 'DiffHunk', + 'DiffLine', + 'DiffStats', + 'Filter', + 'FilterSource', + 'GIT_APPLY_LOCATION_BOTH', + 'GIT_APPLY_LOCATION_INDEX', + 'GIT_APPLY_LOCATION_WORKDIR', + 'GIT_ATTR_CHECK_FILE_THEN_INDEX', + 'GIT_ATTR_CHECK_INCLUDE_COMMIT', + 'GIT_ATTR_CHECK_INCLUDE_HEAD', + 'GIT_ATTR_CHECK_INDEX_ONLY', + 'GIT_ATTR_CHECK_INDEX_THEN_FILE', + 'GIT_ATTR_CHECK_NO_SYSTEM', + 'GIT_BLAME_FIRST_PARENT', + 'GIT_BLAME_IGNORE_WHITESPACE', + 'GIT_BLAME_NORMAL', + 'GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES', + 'GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES', + 'GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES', + 'GIT_BLAME_TRACK_COPIES_SAME_FILE', + 'GIT_BLAME_USE_MAILMAP', + 'GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT', + 'GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD', + 'GIT_BLOB_FILTER_CHECK_FOR_BINARY', + 'GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES', + 'GIT_BRANCH_ALL', + 'GIT_BRANCH_LOCAL', + 'GIT_BRANCH_REMOTE', + 'GIT_CHECKOUT_ALLOW_CONFLICTS', + 'GIT_CHECKOUT_CONFLICT_STYLE_DIFF3', + 'GIT_CHECKOUT_CONFLICT_STYLE_MERGE', + 'GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3', + 'GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH', + 'GIT_CHECKOUT_DONT_OVERWRITE_IGNORED', + 'GIT_CHECKOUT_DONT_REMOVE_EXISTING', + 'GIT_CHECKOUT_DONT_UPDATE_INDEX', + 'GIT_CHECKOUT_DONT_WRITE_INDEX', + 'GIT_CHECKOUT_DRY_RUN', + 'GIT_CHECKOUT_FORCE', + 'GIT_CHECKOUT_NONE', + 'GIT_CHECKOUT_NOTIFY_ALL', + 'GIT_CHECKOUT_NOTIFY_CONFLICT', + 'GIT_CHECKOUT_NOTIFY_DIRTY', + 'GIT_CHECKOUT_NOTIFY_IGNORED', + 'GIT_CHECKOUT_NOTIFY_NONE', + 'GIT_CHECKOUT_NOTIFY_UNTRACKED', + 'GIT_CHECKOUT_NOTIFY_UPDATED', + 'GIT_CHECKOUT_NO_REFRESH', + 'GIT_CHECKOUT_RECREATE_MISSING', + 'GIT_CHECKOUT_REMOVE_IGNORED', + 'GIT_CHECKOUT_REMOVE_UNTRACKED', + 'GIT_CHECKOUT_SAFE', + 'GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES', + 'GIT_CHECKOUT_SKIP_UNMERGED', + 'GIT_CHECKOUT_UPDATE_ONLY', + 'GIT_CHECKOUT_USE_OURS', + 'GIT_CHECKOUT_USE_THEIRS', + 'GIT_CONFIG_HIGHEST_LEVEL', + 'GIT_CONFIG_LEVEL_APP', + 'GIT_CONFIG_LEVEL_GLOBAL', + 'GIT_CONFIG_LEVEL_LOCAL', + 'GIT_CONFIG_LEVEL_PROGRAMDATA', + 'GIT_CONFIG_LEVEL_SYSTEM', + 'GIT_CONFIG_LEVEL_WORKTREE', + 'GIT_CONFIG_LEVEL_XDG', + 'GIT_CREDENTIAL_DEFAULT', + 'GIT_CREDENTIAL_SSH_CUSTOM', + 'GIT_CREDENTIAL_SSH_INTERACTIVE', + 'GIT_CREDENTIAL_SSH_KEY', + 'GIT_CREDENTIAL_SSH_MEMORY', + 'GIT_CREDENTIAL_USERNAME', + 'GIT_CREDENTIAL_USERPASS_PLAINTEXT', + 'GIT_DELTA_ADDED', + 'GIT_DELTA_CONFLICTED', + 'GIT_DELTA_COPIED', + 'GIT_DELTA_DELETED', + 'GIT_DELTA_IGNORED', + 'GIT_DELTA_MODIFIED', + 'GIT_DELTA_RENAMED', + 'GIT_DELTA_TYPECHANGE', + 'GIT_DELTA_UNMODIFIED', + 'GIT_DELTA_UNREADABLE', + 'GIT_DELTA_UNTRACKED', + 'GIT_DESCRIBE_ALL', + 'GIT_DESCRIBE_DEFAULT', + 'GIT_DESCRIBE_TAGS', + 'GIT_DIFF_BREAK_REWRITES', + 'GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY', + 'GIT_DIFF_DISABLE_PATHSPEC_MATCH', + 'GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS', + 'GIT_DIFF_FIND_ALL', + 'GIT_DIFF_FIND_AND_BREAK_REWRITES', + 'GIT_DIFF_FIND_BY_CONFIG', + 'GIT_DIFF_FIND_COPIES', + 'GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED', + 'GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE', + 'GIT_DIFF_FIND_EXACT_MATCH_ONLY', + 'GIT_DIFF_FIND_FOR_UNTRACKED', + 'GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE', + 'GIT_DIFF_FIND_IGNORE_WHITESPACE', + 'GIT_DIFF_FIND_REMOVE_UNMODIFIED', + 'GIT_DIFF_FIND_RENAMES', + 'GIT_DIFF_FIND_RENAMES_FROM_REWRITES', + 'GIT_DIFF_FIND_REWRITES', + 'GIT_DIFF_FLAG_BINARY', + 'GIT_DIFF_FLAG_EXISTS', + 'GIT_DIFF_FLAG_NOT_BINARY', + 'GIT_DIFF_FLAG_VALID_ID', + 'GIT_DIFF_FLAG_VALID_SIZE', + 'GIT_DIFF_FORCE_BINARY', + 'GIT_DIFF_FORCE_TEXT', + 'GIT_DIFF_IGNORE_BLANK_LINES', + 'GIT_DIFF_IGNORE_CASE', + 'GIT_DIFF_IGNORE_FILEMODE', + 'GIT_DIFF_IGNORE_SUBMODULES', + 'GIT_DIFF_IGNORE_WHITESPACE', + 'GIT_DIFF_IGNORE_WHITESPACE_CHANGE', + 'GIT_DIFF_IGNORE_WHITESPACE_EOL', + 'GIT_DIFF_INCLUDE_CASECHANGE', + 'GIT_DIFF_INCLUDE_IGNORED', + 'GIT_DIFF_INCLUDE_TYPECHANGE', + 'GIT_DIFF_INCLUDE_TYPECHANGE_TREES', + 'GIT_DIFF_INCLUDE_UNMODIFIED', + 'GIT_DIFF_INCLUDE_UNREADABLE', + 'GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED', + 'GIT_DIFF_INCLUDE_UNTRACKED', + 'GIT_DIFF_INDENT_HEURISTIC', + 'GIT_DIFF_MINIMAL', + 'GIT_DIFF_NORMAL', + 'GIT_DIFF_PATIENCE', + 'GIT_DIFF_RECURSE_IGNORED_DIRS', + 'GIT_DIFF_RECURSE_UNTRACKED_DIRS', + 'GIT_DIFF_REVERSE', + 'GIT_DIFF_SHOW_BINARY', + 'GIT_DIFF_SHOW_UNMODIFIED', + 'GIT_DIFF_SHOW_UNTRACKED_CONTENT', + 'GIT_DIFF_SKIP_BINARY_CHECK', + 'GIT_DIFF_STATS_FULL', + 'GIT_DIFF_STATS_INCLUDE_SUMMARY', + 'GIT_DIFF_STATS_NONE', + 'GIT_DIFF_STATS_NUMBER', + 'GIT_DIFF_STATS_SHORT', + 'GIT_DIFF_UPDATE_INDEX', + 'GIT_FEATURE_HTTPS', + 'GIT_FEATURE_NSEC', + 'GIT_FEATURE_SSH', + 'GIT_FEATURE_THREADS', + 'GIT_FETCH_NO_PRUNE', + 'GIT_FETCH_PRUNE', + 'GIT_FETCH_PRUNE_UNSPECIFIED', + 'GIT_FILEMODE_BLOB', + 'GIT_FILEMODE_BLOB_EXECUTABLE', + 'GIT_FILEMODE_COMMIT', + 'GIT_FILEMODE_LINK', + 'GIT_FILEMODE_TREE', + 'GIT_FILEMODE_UNREADABLE', + 'GIT_FILTER_ALLOW_UNSAFE', + 'GIT_FILTER_ATTRIBUTES_FROM_COMMIT', + 'GIT_FILTER_ATTRIBUTES_FROM_HEAD', + 'GIT_FILTER_CLEAN', + 'GIT_FILTER_DEFAULT', + 'GIT_FILTER_DRIVER_PRIORITY', + 'GIT_FILTER_NO_SYSTEM_ATTRIBUTES', + 'GIT_FILTER_SMUDGE', + 'GIT_FILTER_TO_ODB', + 'GIT_FILTER_TO_WORKTREE', + 'GIT_MERGE_ANALYSIS_FASTFORWARD', + 'GIT_MERGE_ANALYSIS_NONE', + 'GIT_MERGE_ANALYSIS_NORMAL', + 'GIT_MERGE_ANALYSIS_UNBORN', + 'GIT_MERGE_ANALYSIS_UP_TO_DATE', + 'GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY', + 'GIT_MERGE_PREFERENCE_NONE', + 'GIT_MERGE_PREFERENCE_NO_FASTFORWARD', + 'GIT_OBJECT_ANY', + 'GIT_OBJECT_BLOB', + 'GIT_OBJECT_COMMIT', + 'GIT_OBJECT_INVALID', + 'GIT_OBJECT_OFS_DELTA', + 'GIT_OBJECT_REF_DELTA', + 'GIT_OBJECT_TAG', + 'GIT_OBJECT_TREE', + 'GIT_OID_HEXSZ', + 'GIT_OID_HEX_ZERO', + 'GIT_OID_MINPREFIXLEN', + 'GIT_OID_RAWSZ', + 'GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS', + 'GIT_OPT_ENABLE_CACHING', + 'GIT_OPT_ENABLE_FSYNC_GITDIR', + 'GIT_OPT_ENABLE_OFS_DELTA', + 'GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION', + 'GIT_OPT_ENABLE_STRICT_OBJECT_CREATION', + 'GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION', + 'GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY', + 'GIT_OPT_GET_CACHED_MEMORY', + 'GIT_OPT_GET_MWINDOW_FILE_LIMIT', + 'GIT_OPT_GET_MWINDOW_MAPPED_LIMIT', + 'GIT_OPT_GET_MWINDOW_SIZE', + 'GIT_OPT_GET_OWNER_VALIDATION', + 'GIT_OPT_GET_PACK_MAX_OBJECTS', + 'GIT_OPT_GET_SEARCH_PATH', + 'GIT_OPT_GET_TEMPLATE_PATH', + 'GIT_OPT_GET_USER_AGENT', + 'GIT_OPT_GET_WINDOWS_SHAREMODE', + 'GIT_OPT_SET_ALLOCATOR', + 'GIT_OPT_SET_CACHE_MAX_SIZE', + 'GIT_OPT_SET_CACHE_OBJECT_LIMIT', + 'GIT_OPT_SET_MWINDOW_FILE_LIMIT', + 'GIT_OPT_SET_MWINDOW_MAPPED_LIMIT', + 'GIT_OPT_SET_MWINDOW_SIZE', + 'GIT_OPT_SET_OWNER_VALIDATION', + 'GIT_OPT_SET_PACK_MAX_OBJECTS', + 'GIT_OPT_SET_SEARCH_PATH', + 'GIT_OPT_SET_SSL_CERT_LOCATIONS', + 'GIT_OPT_SET_SSL_CIPHERS', + 'GIT_OPT_SET_TEMPLATE_PATH', + 'GIT_OPT_SET_USER_AGENT', + 'GIT_OPT_SET_WINDOWS_SHAREMODE', + 'GIT_REFERENCES_ALL', + 'GIT_REFERENCES_BRANCHES', + 'GIT_REFERENCES_TAGS', + 'GIT_REPOSITORY_INIT_BARE', + 'GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE', + 'GIT_REPOSITORY_INIT_MKDIR', + 'GIT_REPOSITORY_INIT_MKPATH', + 'GIT_REPOSITORY_INIT_NO_DOTGIT_DIR', + 'GIT_REPOSITORY_INIT_NO_REINIT', + 'GIT_REPOSITORY_INIT_RELATIVE_GITLINK', + 'GIT_REPOSITORY_INIT_SHARED_ALL', + 'GIT_REPOSITORY_INIT_SHARED_GROUP', + 'GIT_REPOSITORY_INIT_SHARED_UMASK', + 'GIT_REPOSITORY_OPEN_BARE', + 'GIT_REPOSITORY_OPEN_CROSS_FS', + 'GIT_REPOSITORY_OPEN_FROM_ENV', + 'GIT_REPOSITORY_OPEN_NO_DOTGIT', + 'GIT_REPOSITORY_OPEN_NO_SEARCH', + 'GIT_REPOSITORY_STATE_APPLY_MAILBOX', + 'GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE', + 'GIT_REPOSITORY_STATE_BISECT', + 'GIT_REPOSITORY_STATE_CHERRYPICK', + 'GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE', + 'GIT_REPOSITORY_STATE_MERGE', + 'GIT_REPOSITORY_STATE_NONE', + 'GIT_REPOSITORY_STATE_REBASE', + 'GIT_REPOSITORY_STATE_REBASE_INTERACTIVE', + 'GIT_REPOSITORY_STATE_REBASE_MERGE', + 'GIT_REPOSITORY_STATE_REVERT', + 'GIT_REPOSITORY_STATE_REVERT_SEQUENCE', + 'GIT_RESET_HARD', + 'GIT_RESET_MIXED', + 'GIT_RESET_SOFT', + 'GIT_REVSPEC_MERGE_BASE', + 'GIT_REVSPEC_RANGE', + 'GIT_REVSPEC_SINGLE', + 'GIT_SORT_NONE', + 'GIT_SORT_REVERSE', + 'GIT_SORT_TIME', + 'GIT_SORT_TOPOLOGICAL', + 'GIT_STASH_APPLY_DEFAULT', + 'GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX', + 'GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED', + 'GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED', + 'GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED', + 'GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED', + 'GIT_STASH_APPLY_PROGRESS_DONE', + 'GIT_STASH_APPLY_PROGRESS_LOADING_STASH', + 'GIT_STASH_APPLY_PROGRESS_NONE', + 'GIT_STASH_APPLY_REINSTATE_INDEX', + 'GIT_STASH_DEFAULT', + 'GIT_STASH_INCLUDE_IGNORED', + 'GIT_STASH_INCLUDE_UNTRACKED', + 'GIT_STASH_KEEP_ALL', + 'GIT_STASH_KEEP_INDEX', + 'GIT_STATUS_CONFLICTED', + 'GIT_STATUS_CURRENT', + 'GIT_STATUS_IGNORED', + 'GIT_STATUS_INDEX_DELETED', + 'GIT_STATUS_INDEX_MODIFIED', + 'GIT_STATUS_INDEX_NEW', + 'GIT_STATUS_INDEX_RENAMED', + 'GIT_STATUS_INDEX_TYPECHANGE', + 'GIT_STATUS_WT_DELETED', + 'GIT_STATUS_WT_MODIFIED', + 'GIT_STATUS_WT_NEW', + 'GIT_STATUS_WT_RENAMED', + 'GIT_STATUS_WT_TYPECHANGE', + 'GIT_STATUS_WT_UNREADABLE', + 'GIT_SUBMODULE_IGNORE_ALL', + 'GIT_SUBMODULE_IGNORE_DIRTY', + 'GIT_SUBMODULE_IGNORE_NONE', + 'GIT_SUBMODULE_IGNORE_UNSPECIFIED', + 'GIT_SUBMODULE_IGNORE_UNTRACKED', + 'GIT_SUBMODULE_STATUS_INDEX_ADDED', + 'GIT_SUBMODULE_STATUS_INDEX_DELETED', + 'GIT_SUBMODULE_STATUS_INDEX_MODIFIED', + 'GIT_SUBMODULE_STATUS_IN_CONFIG', + 'GIT_SUBMODULE_STATUS_IN_HEAD', + 'GIT_SUBMODULE_STATUS_IN_INDEX', + 'GIT_SUBMODULE_STATUS_IN_WD', + 'GIT_SUBMODULE_STATUS_WD_ADDED', + 'GIT_SUBMODULE_STATUS_WD_DELETED', + 'GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED', + 'GIT_SUBMODULE_STATUS_WD_MODIFIED', + 'GIT_SUBMODULE_STATUS_WD_UNINITIALIZED', + 'GIT_SUBMODULE_STATUS_WD_UNTRACKED', + 'GIT_SUBMODULE_STATUS_WD_WD_MODIFIED', + 'GitError', + 'Index', + 'IndexEntry', + 'InvalidSpecError', + 'Keypair', + 'KeypairFromAgent', + 'KeypairFromMemory', + 'LIBGIT2_VER', + 'LIBGIT2_VERSION', + 'LIBGIT2_VER_MAJOR', + 'LIBGIT2_VER_MINOR', + 'LIBGIT2_VER_REVISION', + 'Mailmap', + 'Note', + 'Object', + 'Odb', + 'OdbBackend', + 'OdbBackendLoose', + 'OdbBackendPack', + 'Oid', + 'PackBuilder', + 'Passthrough', + 'Patch', + 'PathLike', + 'Payload', + 'RefLogEntry', + 'Refdb', + 'RefdbBackend', + 'RefdbFsBackend', + 'Reference', + 'Remote', + 'RemoteCallbacks', + 'Repository', + 'RevSpec', + 'Settings', + 'Signature', + 'Stash', + 'StashApplyCallbacks', + 'Submodule', + 'Tag', + 'Tree', + 'TreeBuilder', + 'UserPass', + 'Username', + 'Walker', + 'Worktree', + '__version__', + 'check_error', + 'clone_repository', + 'discover_repository', + 'features', + 'ffi', + 'filter', + 'filter_register', + 'filter_unregister', + 'functools', + 'get_credentials', + 'git_clone_options', + 'git_fetch_options', + 'hash', + 'hashfile', + 'init_file_backend', + 'init_repository', + 'option', + 'reference_is_valid_name', + 'settings', + 'to_bytes', + 'to_str', + 'tree_entry_key', +] + # Features features = enums.Feature(C.git_libgit2_features()) From 7afd6949b621973cb5194c0d89bd9cf56b97fb8e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 10:39:12 +0700 Subject: [PATCH 21/44] remove pygit2 star imports --- pygit2/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index e1d7655e..6d00ead4 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -33,7 +33,6 @@ from ._build import __version__ # Low level API -from ._pygit2 import * from ._pygit2 import ( GIT_APPLY_LOCATION_BOTH, GIT_APPLY_LOCATION_INDEX, From b2680c3a2122f5cd162acee60b7fbb10c74fbd9f Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 14:36:51 +0700 Subject: [PATCH 22/44] fix pyproject again --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 3e07c3b4..2336a934 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,3 +63,5 @@ quote-style = "single" typeCheckingMode = "strict" pythonVersion = "3.10" reportPrivateUsage = "none" +reportAttributeAccessIssue = "none" +reportUnknownMemberType = "none" From 4970a94bbbe375826ee6ffe4e28db42012e943f7 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 14:48:40 +0700 Subject: [PATCH 23/44] fix pyproject --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 2336a934..5304bca9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ lint.ignore = [ "B010", # setattr "F401", # unused imports "ARG002", # unused arguments + "SIM105", # try-except-pass ] [tool.ruff.format] From 40f44c12c54ffe623fc414434d3488f3f97fa286 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 18:44:52 +0700 Subject: [PATCH 24/44] fix pyproject again --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 5304bca9..975516b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,3 +66,4 @@ pythonVersion = "3.10" reportPrivateUsage = "none" reportAttributeAccessIssue = "none" reportUnknownMemberType = "none" +reportUnusedFunction = "none" From e3ed43f96f1f5e2a4fdec31add8e869fffbefe1f Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 18:48:17 +0700 Subject: [PATCH 25/44] initial commit of pygit2 callbacks.py --- pygit2/callbacks.py | 75 ++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/pygit2/callbacks.py b/pygit2/callbacks.py index 5ebe475c..3ba46252 100644 --- a/pygit2/callbacks.py +++ b/pygit2/callbacks.py @@ -62,10 +62,12 @@ API. """ +from __future__ import annotations + # Standard Library from contextlib import contextmanager from functools import wraps -from typing import Optional, Union +from typing import TYPE_CHECKING, Any, Callable, Optional, Union # pygit2 from ._pygit2 import DiffFile, Oid @@ -74,6 +76,10 @@ from .ffi import C, ffi from .utils import StrArray, maybe_string, ptr_to_bytes, to_bytes +if TYPE_CHECKING: + from .remotes import Remote, TransferProgress + from .repository import Repository + # # The payload is the way to pass information from the pygit2 API, through # libgit2, to the Python callbacks. And back. @@ -81,12 +87,12 @@ class Payload: - def __init__(self, **kw): + def __init__(self, **kw: Any): for key, value in kw.items(): setattr(self, key, value) - self._stored_exception = None + self._stored_exception: BaseException | None = None - def check_error(self, error_code): + def check_error(self, error_code: int): if error_code == C.GIT_EUSER: assert self._stored_exception is not None raise self._stored_exception @@ -112,6 +118,10 @@ class RemoteCallbacks(Payload): RemoteCallbacks(certificate=certificate). """ + if TYPE_CHECKING: + repository: Callable[[str, bool], Repository] + remote: Callable[[Repository, str, str], Remote] + def __init__(self, credentials=None, certificate_check=None): super().__init__() if credentials is not None: @@ -119,7 +129,7 @@ def __init__(self, credentials=None, certificate_check=None): if certificate_check is not None: self.certificate_check = certificate_check - def sideband_progress(self, string): + def sideband_progress(self, string: str): """ Progress output callback. Override this function with your own progress reporting function @@ -158,7 +168,7 @@ def credentials( """ raise Passthrough - def certificate_check(self, certificate, valid, host): + def certificate_check(self, certificate: None, valid: bool, host: str): """ Certificate callback. Override with your own function to determine whether to accept the server's certificate. @@ -180,7 +190,7 @@ def certificate_check(self, certificate, valid, host): raise Passthrough - def transfer_progress(self, stats): + def transfer_progress(self, stats: TransferProgress): """ Transfer progress callback. Override with your own function to report transfer progress. @@ -191,7 +201,7 @@ def transfer_progress(self, stats): The progress up to now. """ - def update_tips(self, refname, old, new): + def update_tips(self, refname: str, old: Oid, new: Oid): """ Update tips callback. Override with your own function to report reference updates. @@ -208,7 +218,7 @@ def update_tips(self, refname, old, new): The reference's new value. """ - def push_update_reference(self, refname, message): + def push_update_reference(self, refname: str, message: str | None): """ Push update reference callback. Override with your own function to report the remote's acceptance or rejection of reference updates. @@ -415,9 +425,9 @@ def git_remote_callbacks(payload): # -def libgit2_callback(f): +def libgit2_callback(f: Callable[..., int]): @wraps(f) - def wrapper(*args): + def wrapper(*args: Any) -> int: data = ffi.from_handle(args[-1]) args = args[:-1] + (data,) try: @@ -436,10 +446,10 @@ def wrapper(*args): return ffi.def_extern()(wrapper) -def libgit2_callback_void(f): +def libgit2_callback_void(f: Callable[..., object]): @wraps(f) - def wrapper(*args): - data = ffi.from_handle(args[-1]) + def wrapper(*args: Any): + data: Payload = ffi.from_handle(args[-1]) args = args[:-1] + (data,) try: f(*args) @@ -457,7 +467,7 @@ def wrapper(*args): @libgit2_callback -def _certificate_check_cb(cert_i, valid, host, data): +def _certificate_check_cb(cert_i, valid: int, host, data: RemoteCallbacks): # We want to simulate what should happen if libgit2 supported pass-through # for this callback. For SSH, 'valid' is always False, because it doesn't # look at known_hosts, but we do want to let it through in order to do what @@ -479,7 +489,7 @@ def _certificate_check_cb(cert_i, valid, host, data): @libgit2_callback -def _credentials_cb(cred_out, url, username, allowed, data): +def _credentials_cb(cred_out, url, username, allowed: int, data: RemoteCallbacks): credentials = getattr(data, 'credentials', None) if not credentials: return 0 @@ -493,7 +503,7 @@ def _credentials_cb(cred_out, url, username, allowed, data): @libgit2_callback -def _push_update_reference_cb(ref, msg, data): +def _push_update_reference_cb(ref, msg, data: RemoteCallbacks): push_update_reference = getattr(data, 'push_update_reference', None) if not push_update_reference: return 0 @@ -519,7 +529,7 @@ def _remote_create_cb(remote_out, repo, name, url, data): @libgit2_callback -def _repository_create_cb(repo_out, path, bare, data): +def _repository_create_cb(repo_out, path, bare: int, data: RemoteCallbacks): repository = data.repository(ffi.string(path), bare != 0) # we no longer own the C object repository._disown() @@ -529,7 +539,7 @@ def _repository_create_cb(repo_out, path, bare, data): @libgit2_callback -def _sideband_progress_cb(string, length, data): +def _sideband_progress_cb(string, length: int, data: RemoteCallbacks): sideband_progress = getattr(data, 'sideband_progress', None) if not sideband_progress: return 0 @@ -540,7 +550,7 @@ def _sideband_progress_cb(string, length, data): @libgit2_callback -def _transfer_progress_cb(stats_ptr, data): +def _transfer_progress_cb(stats_ptr, data: RemoteCallbacks): from .remotes import TransferProgress transfer_progress = getattr(data, 'transfer_progress', None) @@ -552,7 +562,7 @@ def _transfer_progress_cb(stats_ptr, data): @libgit2_callback -def _update_tips_cb(refname, a, b, data): +def _update_tips_cb(refname, a, b, data: RemoteCallbacks): update_tips = getattr(data, 'update_tips', None) if not update_tips: return 0 @@ -569,7 +579,7 @@ def _update_tips_cb(refname, a, b, data): # -def get_credentials(fn, url, username, allowed): +def get_credentials(fn, url, username, allowed: CredentialType): """Call fn and return the credentials object.""" url_str = maybe_string(url) username_str = maybe_string(username) @@ -633,7 +643,7 @@ def get_credentials(fn, url, username, allowed): @libgit2_callback def _checkout_notify_cb( - why, path_cstr, baseline, target, workdir, data: CheckoutCallbacks + why: int, path_cstr, baseline, target, workdir, data: CheckoutCallbacks ): pypath = maybe_string(path_cstr) pybaseline = DiffFile.from_c(ptr_to_bytes(baseline)) @@ -660,8 +670,8 @@ def _checkout_progress_cb(path, completed_steps, total_steps, data: CheckoutCall def _git_checkout_options( - callbacks=None, - strategy=None, + callbacks: CheckoutCallbacks | None = None, + strategy: CheckoutStrategy | None = None, directory=None, paths=None, c_checkout_options_ptr=None, @@ -693,7 +703,7 @@ def _git_checkout_options( if paths: strarray = StrArray(paths) - refs.append(strarray) + refs.append(strarray) # type: ignore opts.paths = strarray.ptr[0] # If we want to receive any notifications, set up notify_cb in the options @@ -717,7 +727,12 @@ def _git_checkout_options( @contextmanager -def git_checkout_options(callbacks=None, strategy=None, directory=None, paths=None): +def git_checkout_options( + callbacks: CheckoutCallbacks | None = None, + strategy=None, + directory=None, + paths=None, +): yield _git_checkout_options( callbacks=callbacks, strategy=strategy, directory=directory, paths=paths ) @@ -746,7 +761,11 @@ def _stash_apply_progress_cb(progress: StashApplyProgress, data: StashApplyCallb @contextmanager def git_stash_apply_options( - callbacks=None, reinstate_index=False, strategy=None, directory=None, paths=None + callbacks: StashApplyCallbacks | None = None, + reinstate_index: bool = False, + strategy=None, + directory=None, + paths=None, ): if callbacks is None: callbacks = StashApplyCallbacks() From 35fb5e1e2a300558de7860bfa7cbf990afdec7c8 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 22 Dec 2024 18:54:59 +0700 Subject: [PATCH 26/44] typing fix for __init__.py --- pygit2/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 6d00ead4..778ba6cb 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -823,7 +823,7 @@ def init_repository( - path: typing.Union[str, bytes, PathLike, None], + path: typing.Union[str, bytes, PathLike[str], PathLike[bytes], None], bare: bool = False, flags: enums.RepositoryInitFlag = enums.RepositoryInitFlag.MKPATH, mode: typing.Union[ @@ -901,15 +901,15 @@ def init_repository( def clone_repository( - url, - path, - bare=False, - repository=None, - remote=None, - checkout_branch=None, - callbacks=None, - depth=0, -): + url: str, + path: str, + bare: bool = False, + repository: typing.Callable[[str, bool], Repository] | None = None, + remote: typing.Callable[[Repository, str, str], Remote] | None = None, + checkout_branch: str | None = None, + callbacks: RemoteCallbacks | None = None, + depth: int = 0, +) -> Repository: """ Clones a new Git repository from *url* in the given *path*. From c3b01f8e6e92fe1d4a93007a879cbcf1adf75f3e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Tue, 24 Dec 2024 16:57:14 +0700 Subject: [PATCH 27/44] fix typing for _build.py and _run.py --- pygit2/_build.py | 2 +- pygit2/_run.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pygit2/_build.py b/pygit2/_build.py index 1e7272a9..65716021 100644 --- a/pygit2/_build.py +++ b/pygit2/_build.py @@ -48,7 +48,7 @@ def _get_libgit2_path(): # Default if os.name == 'nt': - return Path(r'%s\libgit2' % os.getenv('ProgramFiles')) + return Path(r'%s\libgit2' % os.getenv('ProgramFiles')) # noqa: SIM112 return Path('/usr/local') diff --git a/pygit2/_run.py b/pygit2/_run.py index 94f99426..cd798214 100644 --- a/pygit2/_run.py +++ b/pygit2/_run.py @@ -45,7 +45,7 @@ # C_HEADER_SRC if getattr(sys, 'frozen', False): if hasattr(sys, '_MEIPASS'): - dir_path = Path(sys._MEIPASS) + dir_path = Path(sys._MEIPASS) # type: ignore else: dir_path = Path(sys.executable).parent else: @@ -83,10 +83,10 @@ 'submodule.h', 'callbacks.h', # Bridge from libgit2 to Python ] -h_source = [] +h_source: list[str] = [] for h_file in h_files: - h_file = dir_path / 'decl' / h_file - with codecs.open(h_file, 'r', 'utf-8') as f: + h_file = dir_path / 'decl' / h_file # noqa: PLW2901 + with codecs.open(str(h_file), 'r', 'utf-8') as f: h_source.append(f.read()) C_HEADER_SRC = '\n'.join(h_source) @@ -99,7 +99,7 @@ # ffi _, libgit2_kw = get_libgit2_paths() ffi = FFI() -ffi.set_source('pygit2._libgit2', C_PREAMBLE, **libgit2_kw) +ffi.set_source('pygit2._libgit2', C_PREAMBLE, **libgit2_kw) # type: ignore ffi.cdef(C_HEADER_SRC) From 0fe1aacdf93a2d63187d8ab06cee345e3535f989 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 17:49:30 +0700 Subject: [PATCH 28/44] fix pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 975516b1..49e097e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,6 @@ target-version = "py310" # oldest supported Python version fix = true extend-exclude = [".cache", ".coverage", "build", "venv*"] lint.select = [ - "C", "E", "W", "F", @@ -67,3 +66,4 @@ reportPrivateUsage = "none" reportAttributeAccessIssue = "none" reportUnknownMemberType = "none" reportUnusedFunction = "none" +reportUnnecessaryIsInstance = "none" From 58b88c2d46cc4ae011cd3cb22ede1c9b2a93992c Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 18:00:17 +0700 Subject: [PATCH 29/44] add typing for packbuilder.py --- pygit2/packbuilder.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pygit2/packbuilder.py b/pygit2/packbuilder.py index 8fefcd89..47686d80 100644 --- a/pygit2/packbuilder.py +++ b/pygit2/packbuilder.py @@ -25,24 +25,29 @@ # Import from pygit2 + +from typing import Any + +from ._pygit2 import Oid from .errors import check_error from .ffi import C, ffi -from .utils import to_bytes +from .repository import BaseRepository +from .utils import StrOrBytesOrPathLike, buffer_to_bytes, to_bytes class PackBuilder: - def __init__(self, repo): + def __init__(self, repo: BaseRepository): cpackbuilder = ffi.new('git_packbuilder **') err = C.git_packbuilder_new(cpackbuilder, repo._repo) check_error(err) self._repo = repo - self._packbuilder = cpackbuilder[0] + self._packbuilder: Any = cpackbuilder[0] self._cpackbuilder = cpackbuilder @property def _pointer(self): - return bytes(ffi.buffer(self._packbuilder)[:]) + return bytes(buffer_to_bytes(self._packbuilder)) def __del__(self): C.git_packbuilder_free(self._packbuilder) @@ -51,29 +56,31 @@ def __len__(self): return C.git_packbuilder_object_count(self._packbuilder) @staticmethod - def __convert_object_to_oid(oid): + def __convert_object_to_oid(oid: Oid): git_oid = ffi.new('git_oid *') ffi.buffer(git_oid)[:] = oid.raw[:] return git_oid - def add(self, oid): + def add(self, oid: Oid): 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): 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): 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: StrOrBytesOrPathLike | None = None): + resolved_path = ffi.NULL if path is None else to_bytes(path) + err = C.git_packbuilder_write( + self._packbuilder, resolved_path, 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) From d8755fb005d1b95e22ebdd593a1daeb2e4ddb8df Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 18:00:49 +0700 Subject: [PATCH 30/44] fix typing for utils --- pygit2/utils.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/pygit2/utils.py b/pygit2/utils.py index fa220467..90890644 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -28,18 +28,20 @@ import contextlib import os from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast +from typing import TYPE_CHECKING, Any, Generic, TypeAlias, TypeVar, cast, overload # Import from pygit2 from .ffi import C, ffi if TYPE_CHECKING: + from _cffi_backend import _CDataBase as CData from _typeshed import SupportsLenAndGetItem _T = TypeVar('_T') +StrOrBytesOrPathLike: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] -def maybe_string(ptr: Any) -> str | None: +def maybe_string(ptr: CData | Any) -> str | None: if not ptr: return None @@ -49,20 +51,33 @@ def maybe_string(ptr: Any) -> str | None: return out -def to_bytes(s: Any, encoding: str = 'utf-8', errors: str = 'strict') -> bytes | Any: +# Cannot describe ffi.NULL, so using CData + + +@overload +def to_bytes(s: CData | None) -> CData: ... +@overload +def to_bytes(s: StrOrBytesOrPathLike) -> bytes: ... + + +def to_bytes( + s: StrOrBytesOrPathLike | CData | None, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> bytes | CData: if s == ffi.NULL or s is None: return ffi.NULL - if hasattr(s, '__fspath__'): + if isinstance(s, os.PathLike): s = os.fspath(s) if isinstance(s, bytes): return s - return s.encode(encoding, errors) + return cast(str, s).encode(encoding, errors) -def to_str(s: Any) -> str: +def to_str(s: StrOrBytesOrPathLike) -> str: if hasattr(s, '__fspath__'): s = os.fspath(s) @@ -75,14 +90,18 @@ def to_str(s: Any) -> str: raise TypeError(f'unexpected type "{repr(s)}"') +def buffer_to_bytes(cdata: CData) -> bytes: + buf = ffi.buffer(cdata) + return cast(bytes, buf[:]) + + def ptr_to_bytes(ptr_cdata: Any) -> bytes: """ Convert a pointer coming from C code () to a byte buffer containing the address that the pointer refers to. """ - pp = ffi.new('void **', ptr_cdata) - return bytes(ffi.buffer(pp)[:]) # type: ignore + return buffer_to_bytes(ffi.new('void **', ptr_cdata)) @contextlib.contextmanager From 8114f2b285887b55d5f76010f92bdad82ca009cf Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 18:02:02 +0700 Subject: [PATCH 31/44] hotfix for packbuilder.py --- pygit2/packbuilder.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pygit2/packbuilder.py b/pygit2/packbuilder.py index 47686d80..81e2dbbe 100644 --- a/pygit2/packbuilder.py +++ b/pygit2/packbuilder.py @@ -23,17 +23,18 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from __future__ import annotations -# Import from pygit2 - -from typing import Any +from typing import TYPE_CHECKING, Any +# Import from pygit2 from ._pygit2 import Oid from .errors import check_error from .ffi import C, ffi -from .repository import BaseRepository from .utils import StrOrBytesOrPathLike, buffer_to_bytes, to_bytes +if TYPE_CHECKING: + from .repository import BaseRepository class PackBuilder: def __init__(self, repo: BaseRepository): From 071df9d588200153c4dd8119835785c494d9a5a4 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 18:03:16 +0700 Subject: [PATCH 32/44] fix _pygit2.pyi --- pygit2/_pygit2.pyi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index f745f289..c0257ca2 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -298,7 +298,7 @@ class Object: @overload def peel( self, target_type: Literal[None, ObjectType.ANY] - ) -> Commit | Tree | Blob: ... + ) -> Commit | Tree | Blob | Tag: ... def read_raw(self) -> bytes: ... def __eq__(self, other: object) -> bool: ... def __ge__(self, other: object) -> bool: ... @@ -693,9 +693,9 @@ class Repository: references_return_type: ReferenceFilter = ReferenceFilter.ALL, ) -> Reference: ... def reset(self, oid: _OidArg, reset_type: ResetMode) -> None: ... - def revparse(self, revspec: str) -> RevSpec: ... - def revparse_ext(self, revision: str) -> tuple[Object, Reference]: ... - def revparse_single(self, revision: str) -> Object: ... + def revparse(self, revspec: str | bytes) -> RevSpec: ... + def revparse_ext(self, revision: str | bytes) -> tuple[Object, Reference]: ... + def revparse_single(self, revision: str | bytes) -> Object: ... def set_odb(self, odb: Odb) -> None: ... def set_refdb(self, refdb: Refdb) -> None: ... def status( From 9a10a41cd9feb2549c2397e0af04b92d4d134d1d Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Wed, 25 Dec 2024 18:03:40 +0700 Subject: [PATCH 33/44] initial stub for repository.py --- pygit2/repository.py | 301 +++++++++++++++++++++++++++---------------- 1 file changed, 188 insertions(+), 113 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index fb2d7038..cc3799f4 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -23,6 +23,8 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from __future__ import annotations + import tarfile import typing from io import BytesIO @@ -36,6 +38,7 @@ Blob, Commit, InvalidSpecError, + Object, Oid, Reference, Signature, @@ -47,7 +50,12 @@ from ._pygit2 import Repository as _Repository from .blame import Blame from .branches import Branches -from .callbacks import git_checkout_options, git_stash_apply_options +from .callbacks import ( + CheckoutCallbacks, + StashApplyCallbacks, + git_checkout_options, + git_stash_apply_options, +) from .config import Config from .enums import ( AttrCheck, @@ -73,9 +81,11 @@ from .submodules import SubmoduleCollection from .utils import StrArray, to_bytes +T = typing.TypeVar('T') + class BaseRepository(_Repository): - def __init__(self, *args, **kwargs): + def __init__(self, *args: typing.Any, **kwargs: typing.Any): super().__init__(*args, **kwargs) self._common_init() @@ -92,22 +102,28 @@ def _common_init(self): self._repo = repo_cptr[0] # Backwards compatible ODB access - def read(self, *args, **kwargs): + # def read(self, *args, **kwargs): + def read(self, oid: Oid | str) -> tuple[int, int, bytes]: """read(oid) -> type, data, size Read raw object data from the repository. """ - return self.odb.read(*args, **kwargs) + return self.odb.read(oid) - def write(self, *args, **kwargs): + def write(self, type: int, data: bytes) -> Oid: """write(type, data) -> Oid Write raw object data into the repository. First arg is the object type, the second one a buffer with data. Return the Oid of the created object.""" - return self.odb.write(*args, **kwargs) + return self.odb.write(type, data) - def pack(self, path=None, pack_delegate=None, n_threads=None): + def pack( + self, + path: str | bytes | PathLike[str] | PathLike[bytes] | None = None, + pack_delegate: typing.Callable[[PackBuilder], object] | None = None, + n_threads: int | None = None, + ) -> int: """Pack the objects in the odb chosen by the pack_delegate function and write `.pack` and `.idx` files for them. @@ -125,7 +141,7 @@ def pack(self, path=None, pack_delegate=None, n_threads=None): The number of threads the `PackBuilder` will spawn. If set to 0, libgit2 will autodetect the number of CPUs. """ - def pack_all_objects(pack_builder): + def pack_all_objects(pack_builder: PackBuilder) -> None: for obj in self.odb: pack_builder.add(obj) @@ -141,10 +157,10 @@ def pack_all_objects(pack_builder): def hashfile( self, - path: str, + path: str | bytes | PathLike[str] | PathLike[bytes], object_type: ObjectType = ObjectType.BLOB, - as_path: typing.Optional[str] = None, - ): + as_path: typing.Optional[str | bytes | PathLike[str] | PathLike[bytes]] = None, + ) -> Oid: """Calculate the hash of a file using repository filtering rules. If you simply want to calculate the hash of a file on disk with no filters, @@ -185,8 +201,7 @@ def hashfile( ) check_error(err) - oid = Oid(raw=bytes(ffi.buffer(c_oid.id)[:])) - return oid + return Oid(raw=bytes(ffi.buffer(c_oid.id)[:])) def __iter__(self): return iter(self.odb) @@ -194,27 +209,30 @@ def __iter__(self): # # Mapping interface # - def get(self, key, default=None): + def get(self, key: Oid | str, default: T = None) -> Object | T: value = self.git_object_lookup_prefix(key) return value if (value is not None) else default - def __getitem__(self, key): + def __getitem__(self, key: Object | Oid | str) -> Object: + # Maybe either returns itself or lookup???? + if isinstance(key, Object): + key = key.id value = self.git_object_lookup_prefix(key) if value is None: raise KeyError(key) return value - def __contains__(self, key): + def __contains__(self, key: Oid | str) -> bool: return self.git_object_lookup_prefix(key) is not None - def __repr__(self): + def __repr__(self) -> str: return f'pygit2.Repository({repr(self.path)})' # # Configuration # @property - def config(self): + def config(self) -> Config: """The configuration file for this repository. If a the configuration hasn't been set yet, the default config for @@ -228,7 +246,7 @@ def config(self): return Config.from_c(self, cconfig[0]) @property - def config_snapshot(self): + def config_snapshot(self) -> Config: """A snapshot for this repositiory's configuration This allows reads over multiple values to use the same version @@ -243,7 +261,13 @@ def config_snapshot(self): # # References # - def create_reference(self, name, target, force=False, message=None): + def create_reference( + self, + name: str, + target: Oid | str, + force: bool = False, + message: str | None = None, + ) -> Reference: """Create a new reference "name" which points to an object or to another reference. @@ -265,7 +289,7 @@ def create_reference(self, name, target, force=False, message=None): repo.create_reference('refs/tags/foo', 'refs/heads/master') repo.create_reference('refs/tags/foo', 'bbb78a9cec580') """ - direct = type(target) is Oid or ( + direct = isinstance(target, Oid) or ( all(c in hexdigits for c in target) and GIT_OID_MINPREFIXLEN <= len(target) <= GIT_OID_HEXSZ ) @@ -275,15 +299,15 @@ def create_reference(self, name, target, force=False, message=None): return self.create_reference_symbolic(name, target, force, message=message) - def listall_references(self) -> typing.List[str]: + def listall_references(self) -> list[str]: """Return a list with all the references in the repository.""" return [x.name for x in self.references.iterator()] - def listall_reference_objects(self) -> typing.List[Reference]: + def listall_reference_objects(self) -> list[Reference]: """Return a list with all the reference objects in the repository.""" return list(self.references.iterator()) - def resolve_refish(self, refish): + def resolve_refish(self, refish: str) -> tuple[Commit, Reference | None]: """Convert a reference-like short name "ref-ish" to a valid (commit, reference) pair. @@ -301,7 +325,7 @@ def resolve_refish(self, refish): reference = self.lookup_reference_dwim(refish) except (KeyError, InvalidSpecError): reference = None - commit = self.revparse_single(refish) + commit: Commit = self.revparse_single(refish) else: commit = reference.peel(Commit) @@ -311,21 +335,36 @@ def resolve_refish(self, refish): # Checkout # - def checkout_head(self, **kwargs): + def checkout_head( + self, + strategy: CheckoutStrategy = CheckoutStrategy.SAFE + | CheckoutStrategy.RECREATE_MISSING, + directory: str | None = None, + paths: list[str] | None = None, + callbacks: CheckoutCallbacks | None = None, + ): """Checkout HEAD For arguments, see Repository.checkout(). """ - with git_checkout_options(**kwargs) as payload: + with git_checkout_options(callbacks, strategy, directory, paths) as payload: err = C.git_checkout_head(self._repo, payload.checkout_options) payload.check_error(err) - def checkout_index(self, index=None, **kwargs): + def checkout_index( + self, + index: Index | None = None, + strategy: CheckoutStrategy = CheckoutStrategy.SAFE + | CheckoutStrategy.RECREATE_MISSING, + directory: str | None = None, + paths: list[str] | None = None, + callbacks: CheckoutCallbacks | None = None, + ) -> None: """Checkout the given index or the repository's index For arguments, see Repository.checkout(). """ - with git_checkout_options(**kwargs) as payload: + with git_checkout_options(callbacks, strategy, directory, paths) as payload: err = C.git_checkout_index( self._repo, index._index if index else ffi.NULL, @@ -333,18 +372,34 @@ def checkout_index(self, index=None, **kwargs): ) payload.check_error(err) - def checkout_tree(self, treeish, **kwargs): + def checkout_tree( + self, + treeish: typing.Any, + strategy: CheckoutStrategy = CheckoutStrategy.SAFE + | CheckoutStrategy.RECREATE_MISSING, + directory: str | None = None, + paths: list[str] | None = None, + callbacks: CheckoutCallbacks | None = None, + ) -> None: """Checkout the given treeish For arguments, see Repository.checkout(). """ - with git_checkout_options(**kwargs) as payload: + with git_checkout_options(callbacks, strategy, directory, paths) as payload: cptr = ffi.new('git_object **') ffi.buffer(cptr)[:] = treeish._pointer[:] err = C.git_checkout_tree(self._repo, cptr[0], payload.checkout_options) payload.check_error(err) - def checkout(self, refname=None, **kwargs): + def checkout( + self, + refname: str | Reference | None = None, + strategy: CheckoutStrategy = CheckoutStrategy.SAFE + | CheckoutStrategy.RECREATE_MISSING, + directory: str | None = None, + paths: list[str] | None = None, + callbacks: CheckoutCallbacks | None = None, + ): """ Checkout the given reference using the given strategy, and update the HEAD. @@ -389,11 +444,15 @@ def checkout(self, refname=None, **kwargs): # Case 1: Checkout index if refname is None: - return self.checkout_index(**kwargs) + return self.checkout_index( + strategy=strategy, directory=directory, paths=paths, callbacks=callbacks + ) # Case 2: Checkout head if refname == 'HEAD': - return self.checkout_head(**kwargs) + return self.checkout_head( + strategy=strategy, directory=directory, paths=paths, callbacks=callbacks + ) # Case 3: Reference if isinstance(refname, Reference): @@ -404,15 +463,21 @@ def checkout(self, refname=None, **kwargs): oid = reference.resolve().target treeish = self[oid] - self.checkout_tree(treeish, **kwargs) + self.checkout_tree( + treeish, + strategy=strategy, + directory=directory, + paths=paths, + callbacks=callbacks, + ) - if 'paths' not in kwargs: + if paths is None: self.set_head(refname) # # Setting HEAD # - def set_head(self, target): + def set_head(self, target: str | Oid) -> None: """ Set HEAD to point to the given target. @@ -436,34 +501,34 @@ def set_head(self, target): # # Diff # - def __whatever_to_tree_or_blob(self, obj): + def __whatever_to_tree_or_blob(self, obj: str | bytes | Oid | None): if obj is None: return None # If it's a string, then it has to be valid revspec - if isinstance(obj, (str, bytes)): - obj = self.revparse_single(obj) - elif isinstance(obj, Oid): - obj = self[obj] + if isinstance(obj, (str, bytes)): # noqa: SIM108 + robj = self.revparse_single(obj) + else: + robj = self[obj] # First we try to get to a blob try: - obj = obj.peel(Blob) + robj = robj.peel(Blob) except Exception: # And if that failed, try to get a tree, raising a type # error if that still doesn't work try: - obj = obj.peel(Tree) + robj = robj.peel(Tree) except Exception: - raise TypeError(f'unexpected "{type(obj)}"') + raise TypeError(f'unexpected "{type(obj)}"') from None - return obj + return robj def diff( self, - a=None, - b=None, - cached=False, + a: str | Reference | None = None, + b: str | Reference | None = None, + cached: bool = False, flags: DiffOption = DiffOption.NORMAL, context_lines: int = 3, interhunk_lines: int = 0, @@ -525,30 +590,34 @@ def diff( API (Tree.diff_to_tree()) directly. """ - a = self.__whatever_to_tree_or_blob(a) - b = self.__whatever_to_tree_or_blob(b) - - opt_keys = ['flags', 'context_lines', 'interhunk_lines'] - opt_values = [int(flags), context_lines, interhunk_lines] + resolved_a = self.__whatever_to_tree_or_blob(a) + resolved_b = self.__whatever_to_tree_or_blob(b) # Case 1: Diff tree to tree - if isinstance(a, Tree) and isinstance(b, Tree): - return a.diff_to_tree(b, **dict(zip(opt_keys, opt_values, strict=False))) + if isinstance(resolved_a, Tree) and isinstance(resolved_b, Tree): + return resolved_a.diff_to_tree( + resolved_b, + flags=flags, + context_lines=context_lines, + interhunk_lines=interhunk_lines, + ) # Case 2: Index to workdir - elif a is None and b is None: - return self.index.diff_to_workdir(*opt_values) + elif resolved_a is None and resolved_b is None: + return self.index.diff_to_workdir(flags, context_lines, interhunk_lines) # Case 3: Diff tree to index or workdir - elif isinstance(a, Tree) and b is None: + elif isinstance(resolved_a, Tree) and resolved_b is None: if cached: - return a.diff_to_index(self.index, *opt_values) + return resolved_a.diff_to_index( + self.index, flags, context_lines, interhunk_lines + ) else: - return a.diff_to_workdir(*opt_values) + return resolved_a.diff_to_workdir(flags, context_lines, interhunk_lines) # Case 4: Diff blob to blob - if isinstance(a, Blob) and isinstance(b, Blob): - return a.diff(b) + if isinstance(resolved_a, Blob) and isinstance(resolved_b, Blob): + return resolved_a.diff(resolved_b) raise ValueError('Only blobs and treeish can be diffed') @@ -563,7 +632,7 @@ def state(self) -> RepositoryState: return RepositoryState(cstate) except ValueError: # Some value not in the IntEnum - newer libgit2 version? - return cstate + raise def state_cleanup(self): """Remove all the metadata associated with an ongoing command like @@ -577,13 +646,13 @@ def state_cleanup(self): # def blame( self, - path, + path: str, flags: BlameFlag = BlameFlag.NORMAL, - min_match_characters=None, - newest_commit=None, - oldest_commit=None, - min_line=None, - max_line=None, + min_match_characters: int | None = None, + newest_commit: Oid | str | None = None, + oldest_commit: Oid | str | None = None, + min_line: int | None = None, + max_line: int | None = None, ): """ Return a Blame object for a single file. @@ -700,13 +769,11 @@ def merge_file_from_index( """ cmergeresult = ffi.new('git_merge_file_result *') - cancestor, ancestor_str_ref = ( + cancestor, _ = ( ancestor._to_c() if ancestor is not None else (ffi.NULL, ffi.NULL) ) - cours, ours_str_ref = ours._to_c() if ours is not None else (ffi.NULL, ffi.NULL) - ctheirs, theirs_str_ref = ( - theirs._to_c() if theirs is not None else (ffi.NULL, ffi.NULL) - ) + cours, _ = ours._to_c() if ours is not None else (ffi.NULL, ffi.NULL) + ctheirs, _ = theirs._to_c() if theirs is not None else (ffi.NULL, ffi.NULL) err = C.git_merge_file_from_index( cmergeresult, self._repo, cancestor, cours, ctheirs, ffi.NULL @@ -722,9 +789,9 @@ def merge_commits( self, ours: typing.Union[str, Oid, Commit], theirs: typing.Union[str, Oid, Commit], - favor=MergeFavor.NORMAL, - flags=MergeFlag.FIND_RENAMES, - file_flags=MergeFileFlag.DEFAULT, + favor: MergeFavor = MergeFavor.NORMAL, + flags: MergeFlag = MergeFlag.FIND_RENAMES, + file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, ) -> Index: """ Merge two arbitrary commits. @@ -756,18 +823,20 @@ def merge_commits( theirs_ptr = ffi.new('git_commit **') cindex = ffi.new('git_index **') + object_ours, object_theirs = ours, theirs + if isinstance(ours, (str, Oid)): - ours = self[ours] + object_ours = self[ours] if isinstance(theirs, (str, Oid)): - theirs = self[theirs] + object_theirs = self[theirs] - ours = ours.peel(Commit) - theirs = theirs.peel(Commit) + cours = object_ours.peel(Commit) # type: ignore + ctheirs = object_theirs.peel(Commit) # type: ignore opts = self._merge_options(favor, flags, file_flags) - ffi.buffer(ours_ptr)[:] = ours._pointer[:] - ffi.buffer(theirs_ptr)[:] = theirs._pointer[:] + ffi.buffer(ours_ptr)[:] = cours._pointer[:] + ffi.buffer(theirs_ptr)[:] = ctheirs._pointer[:] err = C.git_merge_commits(cindex, self._repo, ours_ptr[0], theirs_ptr[0], opts) check_error(err) @@ -779,10 +848,10 @@ def merge_trees( ancestor: typing.Union[str, Oid, Tree], ours: typing.Union[str, Oid, Tree], theirs: typing.Union[str, Oid, Tree], - favor=MergeFavor.NORMAL, - flags=MergeFlag.FIND_RENAMES, - file_flags=MergeFileFlag.DEFAULT, - ): + favor: MergeFavor = MergeFavor.NORMAL, + flags: MergeFlag = MergeFlag.FIND_RENAMES, + file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, + ) -> Index: """ Merge two trees. @@ -815,15 +884,15 @@ def merge_trees( cindex = ffi.new('git_index **') if isinstance(ancestor, (str, Oid)): - ancestor = self[ancestor] + object_ancestor = self[ancestor] if isinstance(ours, (str, Oid)): - ours = self[ours] + object_ours = self[ours] if isinstance(theirs, (str, Oid)): - theirs = self[theirs] + object_theirs = self[theirs] - ancestor = ancestor.peel(Tree) - ours = ours.peel(Tree) - theirs = theirs.peel(Tree) + ancestor = object_ancestor.peel(Tree) # type: ignore + ours = object_ours.peel(Tree) # type: ignore + theirs = object_theirs.peel(Tree) # type: ignore opts = self._merge_options(favor, flags, file_flags) @@ -841,10 +910,10 @@ def merge_trees( def merge( self, id: typing.Union[Oid, str], - favor=MergeFavor.NORMAL, - flags=MergeFlag.FIND_RENAMES, - file_flags=MergeFileFlag.DEFAULT, - ): + favor: MergeFavor = MergeFavor.NORMAL, + flags: MergeFlag = MergeFlag.FIND_RENAMES, + file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, + ) -> None: """ Merges the given id into HEAD. @@ -906,7 +975,7 @@ def raw_message(self) -> bytes: if err == C.GIT_ENOTFOUND: return b'' check_error(err) - return ffi.string(buf.ptr) + return ffi.string(buf.ptr) # type: ignore finally: C.git_buf_dispose(buf) @@ -941,16 +1010,16 @@ def remove_message(self): # def describe( self, - committish=None, - max_candidates_tags=None, + committish: str | Reference | Commit | None = None, + max_candidates_tags: int | None = None, describe_strategy: DescribeStrategy = DescribeStrategy.DEFAULT, - pattern=None, - only_follow_first_parent=None, - show_commit_oid_as_fallback=None, - abbreviated_size=None, - always_use_long_format=None, - dirty_suffix=None, - ): + pattern: str | None = None, + only_follow_first_parent: bool | None = None, + show_commit_oid_as_fallback: bool | None = None, + abbreviated_size: int | None = None, + always_use_long_format: bool | None = None, + dirty_suffix: str | None = None, + ) -> str: """ Describe a commit-ish or the current working tree. @@ -1076,7 +1145,7 @@ def stash( include_ignored: bool = False, keep_all: bool = False, paths: typing.Optional[typing.List[str]] = None, - ): + ) -> Oid: """ Save changes to the working directory to the stash. @@ -1140,7 +1209,13 @@ def stash( return Oid(raw=bytes(ffi.buffer(coid)[:])) - def stash_apply(self, index=0, **kwargs): + def stash_apply( + self, + index: int = 0, + strategy: CheckoutStrategy | None = None, + reinstate_index: bool = False, + callbacks: StashApplyCallbacks | None = None, + ) -> None: """ Apply a stashed state in the stash list to the working directory. @@ -1174,11 +1249,11 @@ def stash_apply(self, index=0, **kwargs): >>> repo.stash(repo.default_signature(), 'WIP: stashing') >>> repo.stash_apply(strategy=CheckoutStrategy.ALLOW_CONFLICTS) """ - with git_stash_apply_options(**kwargs) as payload: + with git_stash_apply_options(callbacks, reinstate_index, strategy) as payload: err = C.git_stash_apply(self._repo, index, payload.stash_apply_options) payload.check_error(err) - def stash_drop(self, index=0): + def stash_drop(self, index: int = 0) -> None: """ Remove a stashed state from the stash list. @@ -1616,7 +1691,7 @@ def __init__( super().__init__() @classmethod - def _from_c(cls, ptr, owned): + def _from_c(cls, ptr, owned: bool) -> Repository: cptr = ffi.new('git_repository **') cptr[0] = ptr repo = cls.__new__(cls) From 5f2d9aebc2b542a0e695974de00aeca96d2dfa90 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Sun, 29 Dec 2024 12:45:43 +0700 Subject: [PATCH 34/44] add typing for branches.py --- pygit2/branches.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pygit2/branches.py b/pygit2/branches.py index 21d887ad..4d637da5 100644 --- a/pygit2/branches.py +++ b/pygit2/branches.py @@ -27,7 +27,7 @@ from typing import TYPE_CHECKING -from ._pygit2 import Commit, Oid +from ._pygit2 import Branch, Commit, Oid, Reference from .enums import BranchType, ReferenceType # Need BaseRepository for type hints, but don't let it cause a circular dependency @@ -37,7 +37,10 @@ class Branches: def __init__( - self, repository: BaseRepository, flag: BranchType = BranchType.ALL, commit=None + self, + repository: BaseRepository, + flag: BranchType = BranchType.ALL, + commit: Commit | Oid | str | None = None, ): self._repository = repository self._flag = flag @@ -76,13 +79,13 @@ def __iter__(self): if self._commit is None or self.get(branch_name) is not None: yield branch_name - def create(self, name: str, commit, force=False): + def create(self, name: str, commit: Commit, force: bool = False): return self._repository.create_branch(name, commit, force) def delete(self, name: str): self[name].delete() - def _valid(self, branch): + def _valid(self, branch: Branch | Reference): if branch.type == ReferenceType.SYMBOLIC: branch = branch.resolve() @@ -92,9 +95,9 @@ def _valid(self, branch): or self._repository.descendant_of(branch.target, self._commit) ) - def with_commit(self, commit): + def with_commit(self, commit: Commit | Oid | str | None): assert self._commit is None return Branches(self._repository, self._flag, commit) - def __contains__(self, name): + def __contains__(self, name: str): return self.get(name) is not None From 720881ec94318aa993a1ba52fd5d332d98ce0bdf Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 11:42:19 +0700 Subject: [PATCH 35/44] add typing to blob.py and fix _pygit2.pyi --- pygit2/_pygit2.pyi | 15 ++++++++++++++- pygit2/blob.py | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index c0257ca2..50b84982 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -1,4 +1,6 @@ -from io import IOBase +import queue +import threading +from io import DEFAULT_BUFFER_SIZE, IOBase from typing import Any, Callable, Iterator, Literal, Optional, TypeAlias, overload from pygit2.filter import Filter @@ -6,6 +8,7 @@ from pygit2.filter import Filter from . import Index from .enums import ( ApplyLocation, + BlobFilter, BranchType, DeltaStatus, DiffFind, @@ -363,6 +366,16 @@ class Blob(Object): old_as_path: str = ..., buffer_as_path: str = ..., ) -> Patch: ... + def _write_to_queue( + self, + queue: queue.Queue[bytes | None], + closed: threading.Event, + chunk_size: int = DEFAULT_BUFFER_SIZE, + *, + as_path: str | None = None, + flags: BlobFilter = BlobFilter.CHECK_FOR_BINARY, + commit_id: Oid | None = None, + ) -> None: ... class Branch(Reference): branch_name: str diff --git a/pygit2/blob.py b/pygit2/blob.py index 19e3f3c4..144f2b79 100644 --- a/pygit2/blob.py +++ b/pygit2/blob.py @@ -1,13 +1,19 @@ +from __future__ import annotations + import io import threading import time from contextlib import AbstractContextManager from queue import Queue -from typing import Optional +from types import TracebackType +from typing import TYPE_CHECKING, Any, Optional, overload from ._pygit2 import Blob, Oid from .enums import BlobFilter +if TYPE_CHECKING: + from _typeshed import WriteableBuffer + class _BlobIO(io.RawIOBase): """Low-level wrapper for streaming blob content. @@ -26,7 +32,7 @@ def __init__( ): super().__init__() self._blob = blob - self._queue = Queue(maxsize=1) + self._queue: Queue[bytes | None] | None = Queue(maxsize=1) self._ready = threading.Event() self._writer_closed = threading.Event() self._chunk: Optional[bytes] = None @@ -42,10 +48,15 @@ def __init__( ) self._thread.start() - def __exit__(self, exc_type, exc_value, traceback): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: self.close() - def isatty(): + def isatty(self): return False def readable(self): @@ -57,7 +68,14 @@ def writable(self): def seekable(self): return False - def readinto(self, b, /): + # workaround for type checking + if TYPE_CHECKING: + @overload + def readinto(self, b: WriteableBuffer, /) -> int: ... # type: ignore + + def readinto(self, b: Any, /) -> int: + if not self._queue: + raise RuntimeError('Blob I/O is closed') try: while self._chunk is None: self._ready.wait() @@ -96,7 +114,7 @@ def close(self): self._queue = None -class BlobIO(io.BufferedReader, AbstractContextManager): +class BlobIO(io.BufferedReader, AbstractContextManager['BlobIO']): """Read-only wrapper for streaming blob content. Supports reading both raw and filtered blob content. @@ -147,7 +165,12 @@ def __init__( raw = _BlobIO(blob, as_path=as_path, flags=flags, commit_id=commit_id) super().__init__(raw) - def __exit__(self, exc_type, exc_value, traceback): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: self.close() From 0e55fb33f11a9217f024ef1b7174d6b26c1789e2 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 20:24:32 +0700 Subject: [PATCH 36/44] add typing for blame.py and add _ctyping --- pygit2/_ctyping.py | 1 + pygit2/_ctyping.pyi | 27 +++++++++++++++++++++++++++ pygit2/blame.py | 44 +++++++++++++++++++++++++------------------- 3 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 pygit2/_ctyping.py create mode 100644 pygit2/_ctyping.pyi diff --git a/pygit2/_ctyping.py b/pygit2/_ctyping.py new file mode 100644 index 00000000..c5ca357e --- /dev/null +++ b/pygit2/_ctyping.py @@ -0,0 +1 @@ +# placeholder for pyright diff --git a/pygit2/_ctyping.pyi b/pygit2/_ctyping.pyi new file mode 100644 index 00000000..f33e2044 --- /dev/null +++ b/pygit2/_ctyping.pyi @@ -0,0 +1,27 @@ +from _cffi_backend import _CDataBase + +class CData(_CDataBase): ... # type: ignore + +class _CSignatureTime(CData): + time: int + offset: int + +class _CSignature(CData): + name: CData + email: CData + when: _CSignatureTime + +class _COid(CData): + id: CData + +class _CHunk(CData): + boundary: CData + final_commit_id: _COid + final_signature: _CSignature + final_start_line_number: int + lines_in_hunk: int + orig_commit_id: _COid + orig_path: str + orig_signature: _CSignature + orig_start_line_number: int + diff --git a/pygit2/blame.py b/pygit2/blame.py index 8a156362..4ae36e98 100644 --- a/pygit2/blame.py +++ b/pygit2/blame.py @@ -23,19 +23,29 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. +from __future__ import annotations + +from typing import TYPE_CHECKING, cast + # Import from pygit2 from ._pygit2 import Oid, Signature from .ffi import C, ffi -from .utils import GenericIterator +from .utils import GenericIterator, buffer_to_bytes, maybe_string + +if TYPE_CHECKING: + from _cffi_backend import _CDataBase as CData + from ._ctyping import _CHunk, _CSignature + from .repository import BaseRepository -def wrap_signature(csig): + +def wrap_signature(csig: _CSignature): if not csig: return None return Signature( - ffi.string(csig.name).decode('utf-8'), - ffi.string(csig.email).decode('utf-8'), + cast(str, maybe_string(csig.name)), + cast(str, maybe_string(csig.email)), csig.when.time, csig.when.offset, 'utf-8', @@ -43,8 +53,12 @@ def wrap_signature(csig): class BlameHunk: + if TYPE_CHECKING: + _blame: Blame + _hunk: _CHunk + @classmethod - def _from_c(cls, blame, ptr): + def _from_c(cls, blame: Blame, ptr: _CHunk): hunk = cls.__new__(cls) hunk._blame = blame hunk._hunk = ptr @@ -73,9 +87,7 @@ def final_committer(self): @property def final_commit_id(self): - return Oid( - raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'final_commit_id'))[:]) - ) + return Oid(raw=buffer_to_bytes(ffi.addressof(self._hunk, 'final_commit_id'))) @property def orig_start_line_number(self): @@ -89,23 +101,17 @@ def orig_committer(self): @property def orig_commit_id(self): - return Oid( - raw=bytes(ffi.buffer(ffi.addressof(self._hunk, 'orig_commit_id'))[:]) - ) + return Oid(raw=buffer_to_bytes(ffi.addressof(self._hunk, 'orig_commit_id'))) @property def orig_path(self): """Original path""" - path = self._hunk.orig_path - if not path: - return None - - return ffi.string(path).decode('utf-8') + return maybe_string(self._hunk.orig_path) class Blame: @classmethod - def _from_c(cls, repo, ptr): + def _from_c(cls, repo: BaseRepository, ptr: CData): blame = cls.__new__(cls) blame._repo = repo blame._blame = ptr @@ -117,14 +123,14 @@ def __del__(self): def __len__(self): return C.git_blame_get_hunk_count(self._blame) - def __getitem__(self, index): + def __getitem__(self, index: int): chunk = C.git_blame_get_hunk_byindex(self._blame, index) if not chunk: raise IndexError return BlameHunk._from_c(self, chunk) - def for_line(self, line_no): + def for_line(self, line_no: int) -> BlameHunk: """ Returns the object for a given line given its number in the current Blame. From 5dca60a00ec6690f278922f37afec82af9f3434c Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 20:38:38 +0700 Subject: [PATCH 37/44] change pathtype name to StrOrBytesPath --- pygit2/packbuilder.py | 4 ++-- pygit2/utils.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pygit2/packbuilder.py b/pygit2/packbuilder.py index 81e2dbbe..a4bae3a0 100644 --- a/pygit2/packbuilder.py +++ b/pygit2/packbuilder.py @@ -31,7 +31,7 @@ from ._pygit2 import Oid from .errors import check_error from .ffi import C, ffi -from .utils import StrOrBytesOrPathLike, buffer_to_bytes, to_bytes +from .utils import StrOrBytesPath, buffer_to_bytes, to_bytes if TYPE_CHECKING: from .repository import BaseRepository @@ -75,7 +75,7 @@ def add_recur(self, oid: Oid): def set_threads(self, n_threads: int): return C.git_packbuilder_set_threads(self._packbuilder, n_threads) - def write(self, path: StrOrBytesOrPathLike | None = None): + def write(self, path: StrOrBytesPath | None = None): resolved_path = ffi.NULL if path is None else to_bytes(path) err = C.git_packbuilder_write( self._packbuilder, resolved_path, 0, ffi.NULL, ffi.NULL diff --git a/pygit2/utils.py b/pygit2/utils.py index d2f13e55..61dcd549 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -38,7 +38,7 @@ from _typeshed import SupportsLenAndGetItem _T = TypeVar('_T') -StrOrBytesOrPathLike: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] +StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] def maybe_string(ptr: CData | Any) -> str | None: @@ -57,11 +57,11 @@ def maybe_string(ptr: CData | Any) -> str | None: @overload def to_bytes(s: CData | None) -> CData: ... @overload -def to_bytes(s: StrOrBytesOrPathLike) -> bytes: ... +def to_bytes(s: StrOrBytesPath) -> bytes: ... def to_bytes( - s: StrOrBytesOrPathLike | CData | None, + s: StrOrBytesPath | CData | None, encoding: str = 'utf-8', errors: str = 'strict', ) -> bytes | CData: @@ -77,7 +77,7 @@ def to_bytes( return cast(str, s).encode(encoding, errors) -def to_str(s: StrOrBytesOrPathLike) -> str: +def to_str(s: StrOrBytesPath) -> str: if hasattr(s, '__fspath__'): s = os.fspath(s) From 599f2b7ca718024461e1907f2562da17f79dceb1 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 20:53:52 +0700 Subject: [PATCH 38/44] fix _ctyping --- pygit2/_ctyping.pyi | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pygit2/_ctyping.pyi b/pygit2/_ctyping.pyi index f33e2044..9697ae4e 100644 --- a/pygit2/_ctyping.pyi +++ b/pygit2/_ctyping.pyi @@ -21,7 +21,15 @@ class _CHunk(CData): final_start_line_number: int lines_in_hunk: int orig_commit_id: _COid - orig_path: str + orig_path: _CDataBase orig_signature: _CSignature orig_start_line_number: int +class _CConfigEntry(CData): + backend_type: _CDataBase + free: _CDataBase + include_depth: int + level: int + name: _CDataBase + origin_path: _CDataBase + value: _CDataBase From d3be17af49c2f9bbb2c07728389676cfdd620e8b Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 20:55:02 +0700 Subject: [PATCH 39/44] fix _ctyping again --- pygit2/_ctyping.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygit2/_ctyping.pyi b/pygit2/_ctyping.pyi index 9697ae4e..2be1411f 100644 --- a/pygit2/_ctyping.pyi +++ b/pygit2/_ctyping.pyi @@ -7,12 +7,12 @@ class _CSignatureTime(CData): offset: int class _CSignature(CData): - name: CData - email: CData + name: _CDataBase + email: _CDataBase when: _CSignatureTime class _COid(CData): - id: CData + id: _CDataBase class _CHunk(CData): boundary: CData From dddd720f5706b72e75223d4225a6cf75d115bc9f Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 30 Dec 2024 21:16:57 +0700 Subject: [PATCH 40/44] add maybe_bytes --- pygit2/utils.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pygit2/utils.py b/pygit2/utils.py index 61dcd549..fb261bcf 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -41,11 +41,16 @@ StrOrBytesPath: TypeAlias = str | bytes | os.PathLike[str] | os.PathLike[bytes] -def maybe_string(ptr: CData | Any) -> str | None: +def maybe_bytes(ptr: CData | Any) -> bytes | None: if not ptr: return None out = ffi.string(ptr) + return cast(bytes, out) + + +def maybe_string(ptr: CData | Any) -> str | None: + out = maybe_bytes(ptr) if isinstance(out, bytes): out = out.decode('utf8') return out From f0194204fed200245e5b4dd43f752be7e9b9074e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 13 Jan 2025 18:45:12 +0700 Subject: [PATCH 41/44] add typing to config.py --- pygit2/config.py | 116 +++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/pygit2/config.py b/pygit2/config.py index f000b54b..c75c0954 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -23,20 +23,25 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -try: - from functools import cached_property -except ImportError: - from cached_property import cached_property +from __future__ import annotations -# Import from pygit2 import contextlib +from functools import cached_property +from typing import TYPE_CHECKING, Callable, cast +# Import from pygit2 from .errors import check_error from .ffi import C, ffi -from .utils import to_bytes +from .utils import StrOrBytesPath, maybe_bytes, to_bytes + +if TYPE_CHECKING: + from _cffi_backend import _CDataBase as CData + + from ._ctyping import _CConfigEntry + from .repository import BaseRepository -def str_to_bytes(value, name): +def str_to_bytes(value: StrOrBytesPath, name: str): if not isinstance(value, str): raise TypeError(f'{name} must be a string') @@ -44,7 +49,7 @@ def str_to_bytes(value, name): class ConfigIterator: - def __init__(self, config, ptr): + def __init__(self, config: Config, ptr: CData): self._iter = ptr self._config = config @@ -62,11 +67,11 @@ def _next_entry(self): err = C.git_config_next(centry, self._iter) check_error(err) - return ConfigEntry._from_c(centry[0], self) + return ConfigEntry._from_c(cast(_CConfigEntry, centry[0]), self) class ConfigMultivarIterator(ConfigIterator): - def __next__(self): + def __next__(self): # type: ignore entry = self._next_entry() return entry.value @@ -74,7 +79,11 @@ def __next__(self): class Config: """Git configuration management.""" - def __init__(self, path=None): + if TYPE_CHECKING: + _repo: BaseRepository + _config: CData + + def __init__(self, path: StrOrBytesPath | None = None): cconfig = ffi.new('git_config **') if not path: @@ -87,7 +96,7 @@ def __init__(self, path=None): self._config = cconfig[0] @classmethod - def from_c(cls, repo, ptr): + def from_c(cls, repo: BaseRepository, ptr: CData): config = cls.__new__(cls) config._repo = repo config._config = ptr @@ -98,15 +107,15 @@ def __del__(self): with contextlib.suppress(AttributeError): C.git_config_free(self._config) - def _get(self, key): - key = str_to_bytes(key, 'key') + def _get(self, key: str | bytes): + rkey = str_to_bytes(key, 'key') entry = ffi.new('git_config_entry **') - err = C.git_config_get_entry(entry, self._config, key) + err = C.git_config_get_entry(entry, self._config, rkey) - return err, ConfigEntry._from_c(entry[0]) + return err, ConfigEntry._from_c(cast(_CConfigEntry, entry[0])) - def _get_entry(self, key): + def _get_entry(self, key: str | bytes): err, entry = self._get(key) if err == C.GIT_ENOTFOUND: @@ -115,8 +124,8 @@ def _get_entry(self, key): check_error(err) return entry - def __contains__(self, key): - err, cstr = self._get(key) + def __contains__(self, key: str): + err, _ = self._get(key) if err == C.GIT_ENOTFOUND: return False @@ -125,7 +134,7 @@ def __contains__(self, key): return True - def __getitem__(self, key): + def __getitem__(self, key: str): """ When using the mapping interface, the value is returned as a string. In order to apply the git-config parsing rules, you can use @@ -135,23 +144,23 @@ def __getitem__(self, key): return entry.value - def __setitem__(self, key, value): - key = str_to_bytes(key, 'key') + def __setitem__(self, key: str, value: int | bool | str): + rkey = str_to_bytes(key, 'key') err = 0 if isinstance(value, bool): - err = C.git_config_set_bool(self._config, key, value) + err = C.git_config_set_bool(self._config, rkey, value) elif isinstance(value, int): - err = C.git_config_set_int64(self._config, key, value) + err = C.git_config_set_int64(self._config, rkey, value) else: - err = C.git_config_set_string(self._config, key, to_bytes(value)) + err = C.git_config_set_string(self._config, rkey, to_bytes(value)) check_error(err) - def __delitem__(self, key): - key = str_to_bytes(key, 'key') + def __delitem__(self, key: str): + rkey = str_to_bytes(key, 'key') - err = C.git_config_delete_entry(self._config, key) + err = C.git_config_delete_entry(self._config, rkey) check_error(err) def __iter__(self): @@ -165,24 +174,27 @@ def __iter__(self): err = C.git_config_iterator_new(citer, self._config) check_error(err) - return ConfigIterator(self, citer[0]) + ptr = cast(CData, citer[0]) - def get_multivar(self, name, regex=None): + return ConfigIterator(self, ptr) + + def get_multivar(self, name: str | bytes, regex: CData | str | bytes | None = None): """Get each value of a multivar ''name'' as a list of strings. The optional ''regex'' parameter is expected to be a regular expression to filter the variables we're interested in. """ name = str_to_bytes(name, 'name') - regex = to_bytes(regex or None) + regex = to_bytes(regex) citer = ffi.new('git_config_iterator **') err = C.git_config_multivar_iterator_new(citer, self._config, name, regex) check_error(err) + cit = cast(CData, citer[0]) - return ConfigMultivarIterator(self, citer[0]) + return ConfigMultivarIterator(self, cit) - def set_multivar(self, name, regex, value): + def set_multivar(self, name: str | bytes, regex: str | bytes, value: str | bytes): """Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression to indicate which values to replace. """ @@ -193,7 +205,7 @@ def set_multivar(self, name, regex, value): err = C.git_config_set_multivar(self._config, name, regex, value) check_error(err) - def delete_multivar(self, name, regex): + def delete_multivar(self, name: str | bytes, regex: str | bytes): """Delete a multivar ''name''. ''regexp'' is a regular expression to indicate which values to delete. """ @@ -203,7 +215,7 @@ def delete_multivar(self, name, regex): err = C.git_config_delete_multivar(self._config, name, regex) check_error(err) - def get_bool(self, key): + def get_bool(self, key: str | bytes): """Look up *key* and parse its value as a boolean as per the git-config rules. Return a boolean value (True or False). @@ -216,9 +228,9 @@ def get_bool(self, key): err = C.git_config_parse_bool(res, entry.c_value) check_error(err) - return res[0] != 0 + return cast(int, res[0]) != 0 - def get_int(self, key): + def get_int(self, key: str | bytes): """Look up *key* and parse its value as an integer as per the git-config rules. Return an integer. @@ -231,9 +243,9 @@ def get_int(self, key): err = C.git_config_parse_int64(res, entry.c_value) check_error(err) - return res[0] + return cast(int, res[0]) - def add_file(self, path, level=0, force=0): + def add_file(self, path: StrOrBytesPath, level: int = 0, force: int = 0): """Add a config file instance to an existing config.""" err = C.git_config_add_file_ondisk( @@ -251,41 +263,42 @@ def snapshot(self): err = C.git_config_snapshot(ccfg, self._config) check_error(err) - return Config.from_c(self._repo, ccfg[0]) + return Config.from_c(self._repo, cast(CData, ccfg[0])) # # Methods to parse a string according to the git-config rules # @staticmethod - def parse_bool(text): + def parse_bool(text: str): res = ffi.new('int *') err = C.git_config_parse_bool(res, to_bytes(text)) check_error(err) - return res[0] != 0 + return cast(int, res[0]) != 0 @staticmethod - def parse_int(text): + def parse_int(text: str): res = ffi.new('int64_t *') err = C.git_config_parse_int64(res, to_bytes(text)) check_error(err) - return res[0] + return cast(int, res[0]) # # Static methods to get specialized version of the config # @staticmethod - def _from_found_config(fn): + def _from_found_config(fn: Callable[[CData], int]): buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = fn(buf) check_error(err, io=True) - cpath = ffi.string(buf.ptr).decode('utf-8') + cpath = maybe_bytes(cast(CData, buf.ptr)) + assert cpath C.git_buf_dispose(buf) - return Config(cpath) + return Config(cpath.decode('utf-8')) @staticmethod def get_system_config(): @@ -306,8 +319,11 @@ def get_xdg_config(): class ConfigEntry: """An entry in a configuation object.""" + if TYPE_CHECKING: + _entry: _CConfigEntry + @classmethod - def _from_c(cls, ptr, iterator=None): + def _from_c(cls, ptr: _CConfigEntry, iterator: ConfigIterator | None = None): """Builds the entry from a ``git_config_entry`` pointer. ``iterator`` must be a ``ConfigIterator`` instance if the entry was @@ -341,11 +357,11 @@ def c_value(self): @cached_property def raw_name(self): - return ffi.string(self._entry.name) + return cast(bytes, maybe_bytes(self._entry.name)) @cached_property def raw_value(self): - return ffi.string(self.c_value) + return cast(bytes, maybe_bytes(self.c_value)) @cached_property def level(self): From b0da06bd13a68d9063af3708e2b60c943165a172 Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 13 Jan 2025 19:02:54 +0700 Subject: [PATCH 42/44] fix typing --- pygit2/config.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pygit2/config.py b/pygit2/config.py index 8a2bccda..408eaaf7 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -67,7 +67,7 @@ def _next_entry(self): err = C.git_config_next(centry, self._iter) check_error(err) - return ConfigEntry._from_c(cast(_CConfigEntry, centry[0]), self) + return ConfigEntry._from_c(cast("_CConfigEntry", centry[0]), self) class ConfigMultivarIterator(ConfigIterator): @@ -113,7 +113,7 @@ def _get(self, key: str | bytes): entry = ffi.new('git_config_entry **') err = C.git_config_get_entry(entry, self._config, rkey) - return err, ConfigEntry._from_c(cast(_CConfigEntry, entry[0])) + return err, ConfigEntry._from_c(cast("_CConfigEntry", entry[0])) def _get_entry(self, key: str | bytes): err, entry = self._get(key) @@ -174,7 +174,7 @@ def __iter__(self): err = C.git_config_iterator_new(citer, self._config) check_error(err) - ptr = cast(CData, citer[0]) + ptr = cast("CData", citer[0]) return ConfigIterator(self, ptr) @@ -190,7 +190,7 @@ def get_multivar(self, name: str | bytes, regex: CData | str | bytes | None = No citer = ffi.new('git_config_iterator **') err = C.git_config_multivar_iterator_new(citer, self._config, name, regex) check_error(err) - cit = cast(CData, citer[0]) + cit = cast("CData", citer[0]) return ConfigMultivarIterator(self, cit) @@ -263,7 +263,7 @@ def snapshot(self): err = C.git_config_snapshot(ccfg, self._config) check_error(err) - return Config.from_c(self._repo, cast(CData, ccfg[0])) + return Config.from_c(self._repo, cast("CData", ccfg[0])) # # Methods to parse a string according to the git-config rules @@ -294,7 +294,7 @@ def _from_found_config(fn: Callable[[CData], int]): buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = fn(buf) check_error(err, io=True) - cpath = maybe_bytes(cast(CData, buf.ptr)) + cpath = maybe_bytes(cast("CData", buf.ptr)) assert cpath C.git_buf_dispose(buf) From 0fab374180d252da811752e3b12f38d92bd6658e Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 17 Feb 2025 18:38:47 +0700 Subject: [PATCH 43/44] Solve confilcts --- .github/workflows/codespell.yml | 23 +++ .github/workflows/tests.yml | 21 +-- .github/workflows/wheels.yml | 45 ++++- .mailmap | 2 + CHANGELOG.md | 44 ++++- build.sh | 1 + docs/conf.py | 3 +- docs/development.rst | 2 +- docs/install.rst | 8 +- docs/merge.rst | 2 +- docs/objects.rst | 6 +- docs/oid.rst | 11 +- pygit2/__init__.py | 295 +------------------------------- pygit2/_build.py | 2 +- pygit2/_pygit2.pyi | 1 + pygit2/callbacks.py | 17 +- pygit2/config.py | 73 +++++++- pygit2/credentials.py | 5 + pygit2/decl/callbacks.h | 6 + pygit2/decl/commit.h | 5 + pygit2/decl/repository.h | 2 +- pygit2/refspec.py | 2 +- pygit2/remotes.py | 6 +- pygit2/repository.py | 77 +++++++-- pygit2/utils.py | 11 +- pyproject.toml | 19 +- src/odb_backend.c | 2 +- src/oid.c | 50 +++++- src/pygit2.c | 2 +- src/reference.c | 11 +- src/repository.c | 4 +- src/signature.c | 2 +- test/test_commit.py | 4 +- test/test_diff.py | 2 +- test/test_merge.py | 91 ++++++---- test/test_nonunicode.py | 48 ++++++ test/test_object.py | 4 +- test/test_odb.py | 2 +- test/test_oid.py | 7 + test/test_remote.py | 129 ++++++++++++-- test/test_repository.py | 13 +- test/test_repository_bare.py | 14 +- test/test_tree.py | 2 +- test/utils.py | 12 +- 44 files changed, 632 insertions(+), 456 deletions(-) create mode 100644 .github/workflows/codespell.yml create mode 100644 test/test_nonunicode.py diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 00000000..9b5e5a6b --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,23 @@ +# Codespell configuration is within pyproject.toml +--- +name: Codespell + +on: + pull_request: + push: + +permissions: + contents: read + +jobs: + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Annotate locations with typos + uses: codespell-project/codespell-problem-matcher@v1 + - name: Codespell + uses: codespell-project/actions-codespell@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2e0cc78f..0eab65c2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,7 @@ on: - '**.rst' jobs: - linux-x86_64: + linux: runs-on: ${{ matrix.os }} strategy: matrix: @@ -18,6 +18,8 @@ jobs: python-version: '3.13' - os: ubuntu-24.04 python-version: 'pypy3.10' + - os: ubuntu-24.04-arm + python-version: '3.13' steps: - name: Checkout pygit2 @@ -33,23 +35,6 @@ jobs: sudo apt install tinyproxy LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.0 /bin/sh build.sh test - linux-arm64: - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Build & test - uses: uraimo/run-on-arch-action@v2 - with: - arch: aarch64 - distro: ubuntu22.04 - install: | - apt-get update -q -y - apt-get install -q -y cmake libssl-dev python3-dev python3-venv wget - run: | - LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.0 /bin/sh build.sh test - linux-s390x: runs-on: ubuntu-24.04 if: github.ref == 'refs/heads/master' diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 1b55ce6e..46435270 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -4,21 +4,51 @@ on: push: branches: - master + - wheels-* tags: - 'v*' jobs: build_wheels: - name: Build wheels on ${{ matrix.os }} + name: Wheels for ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: matrix: include: - - name: linux + - name: linux-amd os: ubuntu-24.04 + - name: linux-arm + os: ubuntu-24.04-arm - name: macos os: macos-13 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install cibuildwheel + run: python -m pip install cibuildwheel==2.22.0 + + - name: Build wheels + run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_MANYLINUX_AARCH64_IMAGE: "manylinux_2_28" + CIBW_MANYLINUX_X86_64_IMAGE: "manylinux_2_28" + CIBW_MANYLINUX_PYPY_X86_64_IMAGE: "manylinux_2_28" + CIBW_MANYLINUX_PYPY_AARCH64_IMAGE: "manylinux_2_28" + + - uses: actions/upload-artifact@v4 + with: + name: wheels-${{ matrix.name }} + path: ./wheelhouse/*.whl + + build_wheels_ppc: + name: Wheels for linux-ppc + runs-on: ubuntu-24.04 + steps: - uses: actions/checkout@v4 @@ -27,24 +57,27 @@ jobs: python-version: '3.11' - uses: docker/setup-qemu-action@v3 - if: runner.os == 'Linux' with: - platforms: all + platforms: linux/ppc64le - name: Install cibuildwheel run: python -m pip install cibuildwheel==2.22.0 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_ARCHS: ppc64le + CIBW_ENVIRONMENT: LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.0 LIBGIT2=/project/ci + CIBW_MANYLINUX_PPC64LE_IMAGE: "manylinux_2_28" - uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.name }} + name: wheels-linux-ppc path: ./wheelhouse/*.whl pypi: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - needs: [build_wheels] + needs: [build_wheels, build_wheels_ppc] runs-on: ubuntu-24.04 steps: diff --git a/.mailmap b/.mailmap index 6445697e..36dc04ce 100644 --- a/.mailmap +++ b/.mailmap @@ -12,6 +12,7 @@ J. David Ibáñez Jeremy Westwood Jose Plana Kaarel Kitsemets +Karl Malmros <44969574+ktpa@users.noreply.github.com> Lukas Fleischer Martin Lenders Matthew Duggan @@ -32,6 +33,7 @@ Tamir Bahar Victor Garcia Victor Florea Vlad Temian +William Schueller Wim Jeantine-Glenn Xavier Delannoy Xu Tao diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ad27b3..076b1133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +# 1.17.1 (UNRELEASED) + +- Upgrade Linux Glibc wheels to `manylinux_2_28` + +- Add `RemoteCallbacks.push_transfer_progress(...)` callback + [#1345](https://github.com/libgit2/pygit2/pull/1345) + +- New `bool(oid)` and some doc/typing fixes + [#1347](https://github.com/libgit2/pygit2/pull/1347) + +- Now `Repository.merge(...)` accepts a commit or reference object + [#1348](https://github.com/libgit2/pygit2/pull/1348) + +- Fix when a reference name has non UTF-8 chars + [#1329](https://github.com/libgit2/pygit2/pull/1329) + +- Fix condition check in `Repository.remotes.rename(...)` + [#1342](https://github.com/libgit2/pygit2/pull/1342) + +- Add codespell workflow, fix a number of typos + [#1344](https://github.com/libgit2/pygit2/pull/1344) + +- More typing + [#1343](https://github.com/libgit2/pygit2/pull/1343) + +- CI: Use ARM runner for tests and wheels + [#1346](https://github.com/libgit2/pygit2/pull/1346) + +Deprecations: + +- Passing str to `Repository.merge(...)` is deprecated, + instead pass an oid object (or a commit, or a reference) + + # 1.17.0 (2025-01-08) - Upgrade to libgit2 1.9 @@ -259,7 +293,7 @@ Deprecations: - New `keep_all` and `paths` optional arguments for `Repository.stash(...)` [#1202](https://github.com/libgit2/pygit2/pull/1202) -- New `Respository.state()` +- New `Repository.state()` [#1204](https://github.com/libgit2/pygit2/pull/1204) - Improve `Repository.write_archive(...)` performance [#1183](https://github.com/libgit2/pygit2/pull/1183) @@ -439,7 +473,7 @@ Breaking changes: Breaking changes: -- Remove deprecated `GIT_CREDTYPE_XXX` contants, use +- Remove deprecated `GIT_CREDTYPE_XXX` constants, use `GIT_CREDENTIAL_XXX` instead. - Remove deprecated `Patch.patch` getter, use `Patch.text` instead. @@ -536,7 +570,7 @@ Deprecations: - Deprecate `Repository.create_remote(...)`, use instead `Repository.remotes.create(...)` -- Deprecate `GIT_CREDTYPE_XXX` contants, use `GIT_CREDENTIAL_XXX` +- Deprecate `GIT_CREDTYPE_XXX` constants, use `GIT_CREDENTIAL_XXX` instead. # 1.2.0 (2020-04-05) @@ -657,7 +691,7 @@ Breaking changes: Breaking changes: -- Now the Repository has a new attribue `odb` for object database: +- Now the Repository has a new attribute `odb` for object database: # Before repository.read(...) @@ -862,7 +896,7 @@ Other changes: [#610](https://github.com/libgit2/pygit2/issues/610) - Fix tests failing in some cases [#795](https://github.com/libgit2/pygit2/issues/795) -- Automatize wheels upload to pypi +- Automate wheels upload to pypi [#563](https://github.com/libgit2/pygit2/issues/563) # 0.27.0 (2018-03-30) diff --git a/build.sh b/build.sh index 646e46a1..1a415a23 100644 --- a/build.sh +++ b/build.sh @@ -69,6 +69,7 @@ if [ "$CIBUILDWHEEL" = "1" ]; then yum install openssl-devel -y else yum install perl-IPC-Cmd -y + yum install perl-Pod-Html -y fi elif [ -f /sbin/apk ]; then apk add wget diff --git a/docs/conf.py b/docs/conf.py index b3c6c1c2..ea8b1ed4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,7 +4,8 @@ # list see the documentation: # http://www.sphinx-doc.org/en/master/config -import os, sys +import os +import sys # -- Path setup -------------------------------------------------------------- diff --git a/docs/development.rst b/docs/development.rst index 771a7080..b9422bf3 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -82,7 +82,7 @@ Step 3. Build pygit2 with debug symbols:: Step 4. Install requirements:: $ $PYTHONBIN/python3 setup.py install - $ pip insall pytest + $ pip install pytest Step 4. Run valgrind:: diff --git a/docs/install.rst b/docs/install.rst index bdfbbd86..eec1cdbd 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -60,7 +60,7 @@ Python requirements (these are specified in ``setup.py``): Libgit2 **v1.9.x**; binary wheels already include libgit2, so you only need to worry about this if you install the source package. -Optional libgit2 dependecies to support ssh and https: +Optional libgit2 dependencies to support ssh and https: - https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL. - ssh: libssh2 1.9.0 or later, pkg-config @@ -214,7 +214,7 @@ libgit2 within a virtual environment This is how to install both libgit2 and pygit2 within a virtual environment. -This is useful if you don't have root acces to install libgit2 system wide. +This is useful if you don't have root access to install libgit2 system wide. Or if you wish to have different versions of libgit2/pygit2 installed in different virtual environments, isolated from each other. @@ -265,7 +265,7 @@ So you need to either set ``LD_LIBRARY_PATH`` before using pygit2, like: Or, like we have done in the instructions above, use the `rpath `_, it hard-codes extra search paths within the pygit2 extension modules, so you don't need to set ``LD_LIBRARY_PATH`` -everytime. Verify yourself if curious: +every time. Verify yourself if curious: .. code-block:: sh @@ -317,7 +317,7 @@ source package. The easiest way is to first install libgit2 with the `Homebrew `_ package manager and then use pip3 for pygit2. The following example assumes that -XCode and Hombrew are already installed. +XCode and Homebrew are already installed. .. code-block:: sh diff --git a/docs/merge.rst b/docs/merge.rst index 43c99d48..dcc8c528 100644 --- a/docs/merge.rst +++ b/docs/merge.rst @@ -66,7 +66,7 @@ The following methods perform the calculation for a base to an n-way merge. .. automethod:: pygit2.Repository.merge_base_many .. automethod:: pygit2.Repository.merge_base_octopus -With this base at hand one can do repeated invokations of +With this base at hand one can do repeated invocations of :py:meth:`.Repository.merge_commits` and :py:meth:`.Repository.merge_trees` to perform the actual merge into one tree (and deal with conflicts along the way). \ No newline at end of file diff --git a/docs/objects.rst b/docs/objects.rst index 97ef286a..7d323a3a 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -16,7 +16,7 @@ Object lookup In the previous chapter we learnt about Object IDs. With an Oid we can ask the repository to get the associated object. To do that the ``Repository`` class -implementes a subset of the mapping interface. +implements a subset of the mapping interface. .. autoclass:: pygit2.Repository :noindex: @@ -90,7 +90,7 @@ Blobs ================= A blob is just a raw byte string. They are the Git equivalent to files in -a filesytem. +a filesystem. This is their API: @@ -221,7 +221,7 @@ Creating trees Commits ================= -A commit is a snapshot of the working dir with meta informations like author, +A commit is a snapshot of the working dir with meta information like author, committer and others. .. autoclass:: pygit2.Commit diff --git a/docs/oid.rst b/docs/oid.rst index 2ad8ed4e..97de2688 100644 --- a/docs/oid.rst +++ b/docs/oid.rst @@ -56,9 +56,9 @@ The Oid type >>> raw = unhexlify("cff3ceaefc955f0dbe1957017db181bc49913781") >>> oid2 = Oid(raw=raw) -And the other way around, from an Oid object we can get the hexadecimal and raw -forms. You can use the built-in `str()` (or `unicode()` in python 2) to get the -hexadecimal representation of the Oid. +And the other way around, from an Oid object we can get the raw form via +`oid.raw`. You can use `str(oid)` to get the hexadecimal representation of the +Oid. .. method:: Oid.__str__() .. autoattribute:: pygit2.Oid.raw @@ -68,8 +68,11 @@ The Oid type supports: - rich comparisons, not just for equality, also: lesser-than, lesser-or-equal, etc. -- hashing, so Oid objects can be used as keys in a dictionary. +- `hash(oid)`, so Oid objects can be used as keys in a dictionary. +- `bool(oid)`, returning False if the Oid is a null SHA-1 (all zeros). + +- `str(oid)`, returning the hexadecimal representation of the Oid. Constants ========= diff --git a/pygit2/__init__.py b/pygit2/__init__.py index c44b7e4f..825c7318 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -33,297 +33,8 @@ from ._build import __version__ # Low level API -from ._pygit2 import ( - GIT_APPLY_LOCATION_BOTH, - GIT_APPLY_LOCATION_INDEX, - GIT_APPLY_LOCATION_WORKDIR, - GIT_BLAME_FIRST_PARENT, - GIT_BLAME_IGNORE_WHITESPACE, - GIT_BLAME_NORMAL, - GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES, - GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES, - GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES, - GIT_BLAME_TRACK_COPIES_SAME_FILE, - GIT_BLAME_USE_MAILMAP, - GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT, - GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD, - GIT_BLOB_FILTER_CHECK_FOR_BINARY, - GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES, - GIT_BRANCH_ALL, - GIT_BRANCH_LOCAL, - GIT_BRANCH_REMOTE, - GIT_CHECKOUT_ALLOW_CONFLICTS, - GIT_CHECKOUT_CONFLICT_STYLE_DIFF3, - GIT_CHECKOUT_CONFLICT_STYLE_MERGE, - GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3, - GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH, - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, - GIT_CHECKOUT_DONT_REMOVE_EXISTING, - GIT_CHECKOUT_DONT_UPDATE_INDEX, - GIT_CHECKOUT_DONT_WRITE_INDEX, - GIT_CHECKOUT_DRY_RUN, - GIT_CHECKOUT_FORCE, - GIT_CHECKOUT_NO_REFRESH, - GIT_CHECKOUT_NONE, - GIT_CHECKOUT_RECREATE_MISSING, - GIT_CHECKOUT_REMOVE_IGNORED, - GIT_CHECKOUT_REMOVE_UNTRACKED, - GIT_CHECKOUT_SAFE, - GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES, - GIT_CHECKOUT_SKIP_UNMERGED, - GIT_CHECKOUT_UPDATE_ONLY, - GIT_CHECKOUT_USE_OURS, - GIT_CHECKOUT_USE_THEIRS, - GIT_CONFIG_HIGHEST_LEVEL, - GIT_CONFIG_LEVEL_APP, - GIT_CONFIG_LEVEL_GLOBAL, - GIT_CONFIG_LEVEL_LOCAL, - GIT_CONFIG_LEVEL_PROGRAMDATA, - GIT_CONFIG_LEVEL_SYSTEM, - GIT_CONFIG_LEVEL_WORKTREE, - GIT_CONFIG_LEVEL_XDG, - GIT_DELTA_ADDED, - GIT_DELTA_CONFLICTED, - GIT_DELTA_COPIED, - GIT_DELTA_DELETED, - GIT_DELTA_IGNORED, - GIT_DELTA_MODIFIED, - GIT_DELTA_RENAMED, - GIT_DELTA_TYPECHANGE, - GIT_DELTA_UNMODIFIED, - GIT_DELTA_UNREADABLE, - GIT_DELTA_UNTRACKED, - GIT_DESCRIBE_ALL, - GIT_DESCRIBE_DEFAULT, - GIT_DESCRIBE_TAGS, - GIT_DIFF_BREAK_REWRITES, - GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, - GIT_DIFF_DISABLE_PATHSPEC_MATCH, - GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, - GIT_DIFF_FIND_ALL, - GIT_DIFF_FIND_AND_BREAK_REWRITES, - GIT_DIFF_FIND_BY_CONFIG, - GIT_DIFF_FIND_COPIES, - GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, - GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, - GIT_DIFF_FIND_EXACT_MATCH_ONLY, - GIT_DIFF_FIND_FOR_UNTRACKED, - GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, - GIT_DIFF_FIND_IGNORE_WHITESPACE, - GIT_DIFF_FIND_REMOVE_UNMODIFIED, - GIT_DIFF_FIND_RENAMES, - GIT_DIFF_FIND_RENAMES_FROM_REWRITES, - GIT_DIFF_FIND_REWRITES, - GIT_DIFF_FLAG_BINARY, - GIT_DIFF_FLAG_EXISTS, - GIT_DIFF_FLAG_NOT_BINARY, - GIT_DIFF_FLAG_VALID_ID, - GIT_DIFF_FLAG_VALID_SIZE, - GIT_DIFF_FORCE_BINARY, - GIT_DIFF_FORCE_TEXT, - GIT_DIFF_IGNORE_BLANK_LINES, - GIT_DIFF_IGNORE_CASE, - GIT_DIFF_IGNORE_FILEMODE, - GIT_DIFF_IGNORE_SUBMODULES, - GIT_DIFF_IGNORE_WHITESPACE, - GIT_DIFF_IGNORE_WHITESPACE_CHANGE, - GIT_DIFF_IGNORE_WHITESPACE_EOL, - GIT_DIFF_INCLUDE_CASECHANGE, - GIT_DIFF_INCLUDE_IGNORED, - GIT_DIFF_INCLUDE_TYPECHANGE, - GIT_DIFF_INCLUDE_TYPECHANGE_TREES, - GIT_DIFF_INCLUDE_UNMODIFIED, - GIT_DIFF_INCLUDE_UNREADABLE, - GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED, - GIT_DIFF_INCLUDE_UNTRACKED, - GIT_DIFF_INDENT_HEURISTIC, - GIT_DIFF_MINIMAL, - GIT_DIFF_NORMAL, - GIT_DIFF_PATIENCE, - GIT_DIFF_RECURSE_IGNORED_DIRS, - GIT_DIFF_RECURSE_UNTRACKED_DIRS, - GIT_DIFF_REVERSE, - GIT_DIFF_SHOW_BINARY, - GIT_DIFF_SHOW_UNMODIFIED, - GIT_DIFF_SHOW_UNTRACKED_CONTENT, - GIT_DIFF_SKIP_BINARY_CHECK, - GIT_DIFF_STATS_FULL, - GIT_DIFF_STATS_INCLUDE_SUMMARY, - GIT_DIFF_STATS_NONE, - GIT_DIFF_STATS_NUMBER, - GIT_DIFF_STATS_SHORT, - GIT_DIFF_UPDATE_INDEX, - GIT_FILEMODE_BLOB, - GIT_FILEMODE_BLOB_EXECUTABLE, - GIT_FILEMODE_COMMIT, - GIT_FILEMODE_LINK, - GIT_FILEMODE_TREE, - GIT_FILEMODE_UNREADABLE, - GIT_FILTER_ALLOW_UNSAFE, - GIT_FILTER_ATTRIBUTES_FROM_COMMIT, - GIT_FILTER_ATTRIBUTES_FROM_HEAD, - GIT_FILTER_CLEAN, - GIT_FILTER_DEFAULT, - GIT_FILTER_DRIVER_PRIORITY, - GIT_FILTER_NO_SYSTEM_ATTRIBUTES, - GIT_FILTER_SMUDGE, - GIT_FILTER_TO_ODB, - GIT_FILTER_TO_WORKTREE, - GIT_MERGE_ANALYSIS_FASTFORWARD, - GIT_MERGE_ANALYSIS_NONE, - GIT_MERGE_ANALYSIS_NORMAL, - GIT_MERGE_ANALYSIS_UNBORN, - GIT_MERGE_ANALYSIS_UP_TO_DATE, - GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, - GIT_MERGE_PREFERENCE_NO_FASTFORWARD, - GIT_MERGE_PREFERENCE_NONE, - GIT_OBJECT_ANY, - GIT_OBJECT_BLOB, - GIT_OBJECT_COMMIT, - GIT_OBJECT_INVALID, - GIT_OBJECT_OFS_DELTA, - GIT_OBJECT_REF_DELTA, - GIT_OBJECT_TAG, - GIT_OBJECT_TREE, - GIT_OID_HEX_ZERO, - GIT_OID_HEXSZ, - GIT_OID_MINPREFIXLEN, - GIT_OID_RAWSZ, - GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, - GIT_OPT_ENABLE_CACHING, - GIT_OPT_ENABLE_FSYNC_GITDIR, - GIT_OPT_ENABLE_OFS_DELTA, - GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, - GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, - GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, - GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, - GIT_OPT_GET_CACHED_MEMORY, - GIT_OPT_GET_MWINDOW_FILE_LIMIT, - GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, - GIT_OPT_GET_MWINDOW_SIZE, - GIT_OPT_GET_OWNER_VALIDATION, - GIT_OPT_GET_PACK_MAX_OBJECTS, - GIT_OPT_GET_SEARCH_PATH, - GIT_OPT_GET_TEMPLATE_PATH, - GIT_OPT_GET_USER_AGENT, - GIT_OPT_GET_WINDOWS_SHAREMODE, - GIT_OPT_SET_ALLOCATOR, - GIT_OPT_SET_CACHE_MAX_SIZE, - GIT_OPT_SET_CACHE_OBJECT_LIMIT, - GIT_OPT_SET_MWINDOW_FILE_LIMIT, - GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, - GIT_OPT_SET_MWINDOW_SIZE, - GIT_OPT_SET_OWNER_VALIDATION, - GIT_OPT_SET_PACK_MAX_OBJECTS, - GIT_OPT_SET_SEARCH_PATH, - GIT_OPT_SET_SSL_CERT_LOCATIONS, - GIT_OPT_SET_SSL_CIPHERS, - GIT_OPT_SET_TEMPLATE_PATH, - GIT_OPT_SET_USER_AGENT, - GIT_OPT_SET_WINDOWS_SHAREMODE, - GIT_REFERENCES_ALL, - GIT_REFERENCES_BRANCHES, - GIT_REFERENCES_TAGS, - GIT_RESET_HARD, - GIT_RESET_MIXED, - GIT_RESET_SOFT, - GIT_REVSPEC_MERGE_BASE, - GIT_REVSPEC_RANGE, - GIT_REVSPEC_SINGLE, - GIT_SORT_NONE, - GIT_SORT_REVERSE, - GIT_SORT_TIME, - GIT_SORT_TOPOLOGICAL, - GIT_STASH_APPLY_DEFAULT, - GIT_STASH_APPLY_REINSTATE_INDEX, - GIT_STASH_DEFAULT, - GIT_STASH_INCLUDE_IGNORED, - GIT_STASH_INCLUDE_UNTRACKED, - GIT_STASH_KEEP_ALL, - GIT_STASH_KEEP_INDEX, - GIT_STATUS_CONFLICTED, - GIT_STATUS_CURRENT, - GIT_STATUS_IGNORED, - GIT_STATUS_INDEX_DELETED, - GIT_STATUS_INDEX_MODIFIED, - GIT_STATUS_INDEX_NEW, - GIT_STATUS_INDEX_RENAMED, - GIT_STATUS_INDEX_TYPECHANGE, - GIT_STATUS_WT_DELETED, - GIT_STATUS_WT_MODIFIED, - GIT_STATUS_WT_NEW, - GIT_STATUS_WT_RENAMED, - GIT_STATUS_WT_TYPECHANGE, - GIT_STATUS_WT_UNREADABLE, - GIT_SUBMODULE_IGNORE_ALL, - GIT_SUBMODULE_IGNORE_DIRTY, - GIT_SUBMODULE_IGNORE_NONE, - GIT_SUBMODULE_IGNORE_UNSPECIFIED, - GIT_SUBMODULE_IGNORE_UNTRACKED, - GIT_SUBMODULE_STATUS_IN_CONFIG, - GIT_SUBMODULE_STATUS_IN_HEAD, - GIT_SUBMODULE_STATUS_IN_INDEX, - GIT_SUBMODULE_STATUS_IN_WD, - GIT_SUBMODULE_STATUS_INDEX_ADDED, - GIT_SUBMODULE_STATUS_INDEX_DELETED, - GIT_SUBMODULE_STATUS_INDEX_MODIFIED, - GIT_SUBMODULE_STATUS_WD_ADDED, - GIT_SUBMODULE_STATUS_WD_DELETED, - GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED, - GIT_SUBMODULE_STATUS_WD_MODIFIED, - GIT_SUBMODULE_STATUS_WD_UNINITIALIZED, - GIT_SUBMODULE_STATUS_WD_UNTRACKED, - GIT_SUBMODULE_STATUS_WD_WD_MODIFIED, - LIBGIT2_VER_MAJOR, - LIBGIT2_VER_MINOR, - LIBGIT2_VER_REVISION, - LIBGIT2_VERSION, - AlreadyExistsError, - Blob, - Branch, - Commit, - Diff, - DiffDelta, - DiffFile, - DiffHunk, - DiffLine, - DiffStats, - FilterSource, - GitError, - InvalidSpecError, - Mailmap, - Note, - Object, - Odb, - OdbBackend, - OdbBackendLoose, - OdbBackendPack, - Oid, - Patch, - Refdb, - RefdbBackend, - RefdbFsBackend, - Reference, - RefLogEntry, - RevSpec, - Signature, - Stash, - Tag, - Tree, - TreeBuilder, - Walker, - Worktree, - _cache_enums, - discover_repository, - filter_register, - filter_unregister, - hash, - hashfile, - init_file_backend, - option, - reference_is_valid_name, - tree_entry_cmp, -) +from ._pygit2 import * +from ._pygit2 import _cache_enums from .blame import Blame, BlameHunk from .blob import BlobIO from .callbacks import ( @@ -843,7 +554,7 @@ def init_repository( The *flags* may be a combination of enums.RepositoryInitFlag constants: - - BARE (overriden by the *bare* parameter) + - BARE (overridden by the *bare* parameter) - NO_REINIT - NO_DOTGIT_DIR - MKDIR diff --git a/pygit2/_build.py b/pygit2/_build.py index c291a6b4..78a8aad4 100644 --- a/pygit2/_build.py +++ b/pygit2/_build.py @@ -38,7 +38,7 @@ # -# Utility functions to get the paths required for bulding extensions +# Utility functions to get the paths required for building extensions # def _get_libgit2_path(): # LIBGIT2 environment variable takes precedence diff --git a/pygit2/_pygit2.pyi b/pygit2/_pygit2.pyi index 50b84982..03cf45dc 100644 --- a/pygit2/_pygit2.pyi +++ b/pygit2/_pygit2.pyi @@ -534,6 +534,7 @@ class Oid: def __lt__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... def __str__(self) -> str: ... + def __bool__(self) -> bool: ... class Patch: data: bytes diff --git a/pygit2/callbacks.py b/pygit2/callbacks.py index ca418cdc..06b6f05b 100644 --- a/pygit2/callbacks.py +++ b/pygit2/callbacks.py @@ -192,8 +192,10 @@ def certificate_check(self, certificate: None, valid: bool, host: str): def transfer_progress(self, stats: TransferProgress): """ - Transfer progress callback. Override with your own function to report - transfer progress. + During the download of new data, this will be regularly called with + the indexer's progress. + + Override with your own function to report transfer progress. Parameters: @@ -379,6 +381,13 @@ def git_push_options(payload, opts=None): opts.callbacks.credentials = C._credentials_cb opts.callbacks.certificate_check = C._certificate_check_cb opts.callbacks.push_update_reference = C._push_update_reference_cb + # Per libgit2 sources, push_transfer_progress may incur a performance hit. + # So, set it only if the user has overridden the no-op stub. + if ( + type(payload).push_transfer_progress + is not RemoteCallbacks.push_transfer_progress + ): + opts.callbacks.push_transfer_progress = C._push_transfer_progress_cb # Payload handle = ffi.new_handle(payload) opts.callbacks.payload = handle @@ -414,10 +423,10 @@ def git_remote_callbacks(payload): # # C callbacks # -# These functions are called by libgit2. They cannot raise execptions, since +# These functions are called by libgit2. They cannot raise exceptions, since # they return to libgit2, they can only send back error codes. # -# They cannot be overriden, but sometimes the only thing these functions do is +# They cannot be overridden, but sometimes the only thing these functions do is # to proxy the call to a user defined function. If user defined functions # raises an exception, the callback must store it somewhere and return # GIT_EUSER to libgit2, then the outer Python code will be able to reraise the diff --git a/pygit2/config.py b/pygit2/config.py index 408eaaf7..06526363 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -25,6 +25,11 @@ from __future__ import annotations +import contextlib +from functools import cached_property +from typing import TYPE_CHECKING, Callable, cast +from __future__ import annotations + import contextlib from functools import cached_property from typing import TYPE_CHECKING, Callable, cast @@ -34,6 +39,13 @@ from .ffi import C, ffi from .utils import StrOrBytesPath, maybe_bytes, to_bytes +if TYPE_CHECKING: + from _cffi_backend import _CDataBase as CData + + from ._ctyping import _CConfigEntry + from .repository import BaseRepository +from .utils import StrOrBytesPath, maybe_bytes, to_bytes + if TYPE_CHECKING: from _cffi_backend import _CDataBase as CData @@ -41,6 +53,7 @@ from .repository import BaseRepository +def str_to_bytes(value: StrOrBytesPath, name: str): def str_to_bytes(value: StrOrBytesPath, name: str): if not isinstance(value, str): raise TypeError(f'{name} must be a string') @@ -49,6 +62,7 @@ def str_to_bytes(value: StrOrBytesPath, name: str): class ConfigIterator: + def __init__(self, config: Config, ptr: CData): def __init__(self, config: Config, ptr: CData): self._iter = ptr self._config = config @@ -67,7 +81,7 @@ def _next_entry(self): err = C.git_config_next(centry, self._iter) check_error(err) - return ConfigEntry._from_c(cast("_CConfigEntry", centry[0]), self) + return ConfigEntry._from_c(cast(_CConfigEntry, centry[0]), self) class ConfigMultivarIterator(ConfigIterator): @@ -79,6 +93,11 @@ def __next__(self): # type: ignore class Config: """Git configuration management.""" + if TYPE_CHECKING: + _repo: BaseRepository + _config: CData + + def __init__(self, path: StrOrBytesPath | None = None): if TYPE_CHECKING: _repo: BaseRepository _config: CData @@ -96,6 +115,7 @@ def __init__(self, path: StrOrBytesPath | None = None): self._config = cconfig[0] @classmethod + def from_c(cls, repo: BaseRepository, ptr: CData): def from_c(cls, repo: BaseRepository, ptr: CData): config = cls.__new__(cls) config._repo = repo @@ -107,14 +127,18 @@ def __del__(self): with contextlib.suppress(AttributeError): C.git_config_free(self._config) + def _get(self, key: str | bytes): + rkey = str_to_bytes(key, 'key') def _get(self, key: str | bytes): rkey = str_to_bytes(key, 'key') entry = ffi.new('git_config_entry **') err = C.git_config_get_entry(entry, self._config, rkey) + err = C.git_config_get_entry(entry, self._config, rkey) - return err, ConfigEntry._from_c(cast("_CConfigEntry", entry[0])) + return err, ConfigEntry._from_c(cast(_CConfigEntry, entry[0])) + def _get_entry(self, key: str | bytes): def _get_entry(self, key: str | bytes): err, entry = self._get(key) @@ -124,6 +148,8 @@ def _get_entry(self, key: str | bytes): check_error(err) return entry + def __contains__(self, key: str): + err, _ = self._get(key) def __contains__(self, key: str): err, _ = self._get(key) @@ -134,6 +160,7 @@ def __contains__(self, key: str): return True + def __getitem__(self, key: str): def __getitem__(self, key: str): """ When using the mapping interface, the value is returned as a string. In @@ -144,22 +171,30 @@ def __getitem__(self, key: str): return entry.value + def __setitem__(self, key: str, value: int | bool | str): + rkey = str_to_bytes(key, 'key') def __setitem__(self, key: str, value: int | bool | str): rkey = str_to_bytes(key, 'key') err = 0 if isinstance(value, bool): err = C.git_config_set_bool(self._config, rkey, value) + err = C.git_config_set_bool(self._config, rkey, value) elif isinstance(value, int): err = C.git_config_set_int64(self._config, rkey, value) + err = C.git_config_set_int64(self._config, rkey, value) else: err = C.git_config_set_string(self._config, rkey, to_bytes(value)) + err = C.git_config_set_string(self._config, rkey, to_bytes(value)) check_error(err) + def __delitem__(self, key: str): + rkey = str_to_bytes(key, 'key') def __delitem__(self, key: str): rkey = str_to_bytes(key, 'key') + err = C.git_config_delete_entry(self._config, rkey) err = C.git_config_delete_entry(self._config, rkey) check_error(err) @@ -174,10 +209,11 @@ def __iter__(self): err = C.git_config_iterator_new(citer, self._config) check_error(err) - ptr = cast("CData", citer[0]) + ptr = cast(CData, citer[0]) return ConfigIterator(self, ptr) + def get_multivar(self, name: str | bytes, regex: CData | str | bytes | None = None): def get_multivar(self, name: str | bytes, regex: CData | str | bytes | None = None): """Get each value of a multivar ''name'' as a list of strings. @@ -186,14 +222,17 @@ def get_multivar(self, name: str | bytes, regex: CData | str | bytes | None = No """ name = str_to_bytes(name, 'name') regex = to_bytes(regex) + regex = to_bytes(regex) citer = ffi.new('git_config_iterator **') err = C.git_config_multivar_iterator_new(citer, self._config, name, regex) check_error(err) - cit = cast("CData", citer[0]) + cit = cast(CData, citer[0]) return ConfigMultivarIterator(self, cit) + return ConfigMultivarIterator(self, cit) + def set_multivar(self, name: str | bytes, regex: str | bytes, value: str | bytes): def set_multivar(self, name: str | bytes, regex: str | bytes, value: str | bytes): """Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression to indicate which values to replace. @@ -205,6 +244,7 @@ def set_multivar(self, name: str | bytes, regex: str | bytes, value: str | bytes err = C.git_config_set_multivar(self._config, name, regex, value) check_error(err) + def delete_multivar(self, name: str | bytes, regex: str | bytes): def delete_multivar(self, name: str | bytes, regex: str | bytes): """Delete a multivar ''name''. ''regexp'' is a regular expression to indicate which values to delete. @@ -215,6 +255,7 @@ def delete_multivar(self, name: str | bytes, regex: str | bytes): err = C.git_config_delete_multivar(self._config, name, regex) check_error(err) + def get_bool(self, key: str | bytes): def get_bool(self, key: str | bytes): """Look up *key* and parse its value as a boolean as per the git-config rules. Return a boolean value (True or False). @@ -229,7 +270,9 @@ def get_bool(self, key: str | bytes): check_error(err) return cast(int, res[0]) != 0 + return cast(int, res[0]) != 0 + def get_int(self, key: str | bytes): def get_int(self, key: str | bytes): """Look up *key* and parse its value as an integer as per the git-config rules. Return an integer. @@ -244,7 +287,9 @@ def get_int(self, key: str | bytes): check_error(err) return cast(int, res[0]) + return cast(int, res[0]) + def add_file(self, path: StrOrBytesPath, level: int = 0, force: int = 0): def add_file(self, path: StrOrBytesPath, level: int = 0, force: int = 0): """Add a config file instance to an existing config.""" @@ -263,42 +308,48 @@ def snapshot(self): err = C.git_config_snapshot(ccfg, self._config) check_error(err) - return Config.from_c(self._repo, cast("CData", ccfg[0])) + return Config.from_c(self._repo, cast(CData, ccfg[0])) # # Methods to parse a string according to the git-config rules # @staticmethod + def parse_bool(text: str): def parse_bool(text: str): res = ffi.new('int *') err = C.git_config_parse_bool(res, to_bytes(text)) check_error(err) return cast(int, res[0]) != 0 + return cast(int, res[0]) != 0 @staticmethod + def parse_int(text: str): def parse_int(text: str): res = ffi.new('int64_t *') err = C.git_config_parse_int64(res, to_bytes(text)) check_error(err) return cast(int, res[0]) + return cast(int, res[0]) # # Static methods to get specialized version of the config # @staticmethod + def _from_found_config(fn: Callable[[CData], int]): def _from_found_config(fn: Callable[[CData], int]): buf = ffi.new('git_buf *', (ffi.NULL, 0)) err = fn(buf) check_error(err, io=True) - cpath = maybe_bytes(cast("CData", buf.ptr)) + cpath = maybe_bytes(cast(CData, buf.ptr)) assert cpath C.git_buf_dispose(buf) return Config(cpath.decode('utf-8')) + return Config(cpath.decode('utf-8')) @staticmethod def get_system_config(): @@ -317,12 +368,16 @@ def get_xdg_config(): class ConfigEntry: - """An entry in a configuation object.""" + """An entry in a configuration object.""" + + if TYPE_CHECKING: + _entry: _CConfigEntry if TYPE_CHECKING: _entry: _CConfigEntry @classmethod + def _from_c(cls, ptr: _CConfigEntry, iterator: ConfigIterator | None = None): def _from_c(cls, ptr: _CConfigEntry, iterator: ConfigIterator | None = None): """Builds the entry from a ``git_config_entry`` pointer. @@ -337,7 +392,7 @@ def _from_c(cls, ptr: _CConfigEntry, iterator: ConfigIterator | None = None): # git_config_iterator_free when we've deleted all ConfigEntry objects. # But it's not, to reproduce the error comment the lines below and run # the script in https://github.com/libgit2/pygit2/issues/970 - # So instead we load the Python object immmediately. Ideally we should + # So instead we load the Python object immediately. Ideally we should # investigate libgit2 source code. if iterator is not None: entry.raw_name = entry.raw_name @@ -358,10 +413,12 @@ def c_value(self): @cached_property def raw_name(self): return cast(bytes, maybe_bytes(self._entry.name)) + return cast(bytes, maybe_bytes(self._entry.name)) @cached_property def raw_value(self): return cast(bytes, maybe_bytes(self.c_value)) + return cast(bytes, maybe_bytes(self.c_value)) @cached_property def level(self): diff --git a/pygit2/credentials.py b/pygit2/credentials.py index bbfdd47d..eb891eb5 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -27,6 +27,8 @@ from typing import Any, Protocol +from typing import Any, Protocol + from .enums import CredentialType @@ -45,6 +47,7 @@ class Username: callback and for returning from said callback. """ + def __init__(self, username: str): def __init__(self, username: str): self._username = username @@ -67,6 +70,7 @@ class UserPass: callback and for returning from said callback. """ + def __init__(self, username: str, password: str): def __init__(self, username: str, password: str): self._username = username self._password = password @@ -131,6 +135,7 @@ def __call__(self, _url: Any, _username: Any, _allowed: Any): class KeypairFromAgent(Keypair): + def __init__(self, username: str): def __init__(self, username: str): super().__init__(username, None, None, None) diff --git a/pygit2/decl/callbacks.h b/pygit2/decl/callbacks.h index f6991a5b..9d5409de 100644 --- a/pygit2/decl/callbacks.h +++ b/pygit2/decl/callbacks.h @@ -38,6 +38,12 @@ extern "Python" int _transfer_progress_cb( const git_indexer_progress *stats, void *payload); +extern "Python" int _push_transfer_progress_cb( + unsigned int objects_pushed, + unsigned int total_objects, + size_t bytes_pushed, + void *payload); + extern "Python" int _update_tips_cb( const char *refname, const git_oid *a, diff --git a/pygit2/decl/commit.h b/pygit2/decl/commit.h index bc1dd6b1..fc83c6b1 100644 --- a/pygit2/decl/commit.h +++ b/pygit2/decl/commit.h @@ -13,4 +13,9 @@ int git_annotated_commit_lookup( git_repository *repo, const git_oid *id); +int git_annotated_commit_from_ref( + git_annotated_commit **out, + git_repository *repo, + const struct git_reference *ref); + void git_annotated_commit_free(git_annotated_commit *commit); diff --git a/pygit2/decl/repository.h b/pygit2/decl/repository.h index cf646816..297d8710 100644 --- a/pygit2/decl/repository.h +++ b/pygit2/decl/repository.h @@ -81,7 +81,7 @@ int git_repository_set_head( int git_repository_set_head_detached( git_repository* repo, - const git_oid* commitish); + const git_oid* committish); int git_repository_hashfile(git_oid *out, git_repository *repo, const char *path, git_object_t type, const char *as_path); int git_repository_ident(const char **name, const char **email, const git_repository *repo); diff --git a/pygit2/refspec.py b/pygit2/refspec.py index 8c8e9e53..70b0f499 100644 --- a/pygit2/refspec.py +++ b/pygit2/refspec.py @@ -43,7 +43,7 @@ def src(self): @property def dst(self): - """Destinaton or rhs of the refspec""" + """Destination or rhs of the refspec""" return ffi.string(C.git_refspec_dst(self._refspec)).decode('utf-8') @property diff --git a/pygit2/remotes.py b/pygit2/remotes.py index eb46bf5b..ebc2a747 100644 --- a/pygit2/remotes.py +++ b/pygit2/remotes.py @@ -245,7 +245,7 @@ def push(self, specs, callbacks=None, proxy=None, push_options=None): function will return successfully. Thus it is strongly recommended to install a callback, that implements :py:meth:`RemoteCallbacks.push_update_reference` and check the passed - parameters for successfull operations. + parameters for successful operations. Parameters: @@ -334,7 +334,7 @@ def names(self): for name in self._ffi_names(): yield maybe_string(name) - def create(self, name, url, fetch=None): + def create(self, name, url, fetch=None) -> Remote: """Create a new remote with the given name and url. Returns a object. @@ -375,7 +375,7 @@ def rename(self, name, new_name): the standard format and thus could not be remapped. """ - if not new_name: + if not name: raise ValueError('Current remote name must be a non-empty string') if not new_name: diff --git a/pygit2/repository.py b/pygit2/repository.py index 7236a8bf..83f24e8e 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -25,6 +25,8 @@ from __future__ import annotations +from __future__ import annotations + import tarfile import typing from io import BytesIO @@ -46,6 +48,23 @@ init_file_backend, ) +# Import from pygit2 +from ._pygit2 import Repository as _Repository + +from ._pygit2 import ( + GIT_OID_HEXSZ, + GIT_OID_MINPREFIXLEN, + Blob, + Commit, + InvalidSpecError, + Object, + Oid, + Reference, + Signature, + Tree, + init_file_backend, +) + # Import from pygit2 from ._pygit2 import Repository as _Repository from .blame import Blame @@ -60,7 +79,6 @@ from .enums import ( AttrCheck, BlameFlag, - BranchType, CheckoutStrategy, DescribeStrategy, DiffOption, @@ -80,6 +98,9 @@ from .remotes import RemoteCollection from .submodules import SubmoduleCollection from .utils import StrArray, to_bytes +from .utils import StrArray, to_bytes + +T = typing.TypeVar('T') T = typing.TypeVar('T') @@ -909,23 +930,28 @@ def merge_trees( def merge( self, - id: typing.Union[Oid, str], + source: typing.Union[Reference, Commit, Oid, str], favor: MergeFavor = MergeFavor.NORMAL, flags: MergeFlag = MergeFlag.FIND_RENAMES, file_flags: MergeFileFlag = MergeFileFlag.DEFAULT, ) -> None: """ - Merges the given id into HEAD. + Merges the given Reference or Commit into HEAD. - Merges the given commit(s) into HEAD, writing the results into the working directory. + Merges the given commit into HEAD, writing the results into the working directory. Any changes are staged for commit and any conflicts are written to the index. Callers should inspect the repository's index after this completes, resolve any conflicts and prepare a commit. Parameters: - id - The id to merge into HEAD + source + The Reference, Commit, or commit Oid to merge into HEAD. + It is preferable to pass in a Reference, because this enriches the + merge with additional information (for example, Repository.message will + specify the name of the branch being merged). + Previous versions of pygit2 allowed passing in a partial commit + hash as a string; this is deprecated. favor An enums.MergeFavor constant specifying how to deal with file-level conflicts. @@ -937,12 +963,35 @@ def merge( file_flags A combination of enums.MergeFileFlag constants. """ - if not isinstance(id, (str, Oid)): - raise TypeError(f'expected oid (string or ) got {type(id)}') - id = self[id].id - c_id = ffi.new('git_oid *') - ffi.buffer(c_id)[:] = id.raw[:] + if isinstance(source, Reference): + # Annotated commit from ref + cptr = ffi.new('struct git_reference **') + ffi.buffer(cptr)[:] = source._pointer[:] + commit_ptr = ffi.new('git_annotated_commit **') + err = C.git_annotated_commit_from_ref(commit_ptr, self._repo, cptr[0]) + check_error(err) + else: + # Annotated commit from commit id + if isinstance(source, str): + # For backwards compatibility, parse a string as a partial commit hash + warnings.warn( + 'Passing str to Repository.merge is deprecated. ' + 'Pass Commit, Oid, or a Reference (such as a Branch) instead.', + DeprecationWarning, + ) + oid = self[source].peel(Commit).id + elif isinstance(source, Commit): + oid = source.id + elif isinstance(source, Oid): + oid = source + else: + raise TypeError('expected Reference, Commit, or Oid') + c_id = ffi.new('git_oid *') + ffi.buffer(c_id)[:] = oid.raw[:] + commit_ptr = ffi.new('git_annotated_commit **') + err = C.git_annotated_commit_lookup(commit_ptr, self._repo, c_id) + check_error(err) merge_opts = self._merge_options(favor, flags, file_flags) @@ -952,10 +1001,6 @@ def merge( CheckoutStrategy.SAFE | CheckoutStrategy.RECREATE_MISSING ) - commit_ptr = ffi.new('git_annotated_commit **') - err = C.git_annotated_commit_lookup(commit_ptr, self._repo, c_id) - check_error(err) - err = C.git_merge(self._repo, commit_ptr, 1, merge_opts, checkout_opts) C.git_annotated_commit_free(commit_ptr[0]) check_error(err) @@ -1062,7 +1107,7 @@ def describe( always_use_long_format : bool Always output the long format (the nearest tag, the number of - commits, and the abbrevated commit name) even when the committish + commits, and the abbreviated commit name) even when the committish matches a tag. dirty_suffix : str diff --git a/pygit2/utils.py b/pygit2/utils.py index 3a97ba5b..78573b4d 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -45,6 +45,13 @@ def maybe_bytes(ptr: CData | Any) -> bytes | None: if not ptr: return None + out = ffi.string(ptr) + if isinstance(out, bytes): + out = out.decode('utf8') + return out + + +def to_bytes(s, encoding='utf-8', errors='strict'): out = ffi.string(ptr) return cast(bytes, out) @@ -205,8 +212,8 @@ def __init__(self, container: SupportsLenAndGetItem[_T]): self.length = len(container) self.idx = 0 - def __iter__(self): - return self + def next(self) -> _T: + return self.__next__() def __next__(self) -> _T: idx = self.idx diff --git a/pyproject.toml b/pyproject.toml index 66ee54a8..1175535b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ requires = ["setuptools", "wheel"] enable = ["pypy"] skip = "*musllinux_aarch64 *musllinux_ppc64le" -archs = ["auto"] +archs = ["native"] build-frontend = "default" dependency-versions = "pinned" environment = {LIBGIT2_VERSION="1.9.0", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSION="3.2.3", LIBGIT2="/project/ci"} @@ -14,7 +14,6 @@ before-all = "sh build.sh" [tool.cibuildwheel.linux] repair-wheel-command = "LD_LIBRARY_PATH=/project/ci/lib64 auditwheel repair -w {dest_dir} {wheel}" -archs = ["x86_64", "aarch64", "ppc64le"] [[tool.cibuildwheel.overrides]] select = "*-musllinux*" @@ -26,10 +25,12 @@ environment = {LIBGIT2_VERSION="1.9.0", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSIO repair-wheel-command = "DYLD_LIBRARY_PATH=/Users/runner/work/pygit2/pygit2/ci/lib delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}" [tool.ruff] -target-version = "py310" # oldest supported Python version +extend-exclude = [ ".cache", ".coverage", "build", "site-packages", "venv*"] +target-version = "py310" # oldest supported Python version fix = true extend-exclude = [".cache", ".coverage", "build", "venv*"] lint.select = [ + "C", "E", "W", "F", @@ -54,7 +55,6 @@ lint.ignore = [ "B010", # setattr "F401", # unused imports "ARG002", # unused arguments - "SIM105", # try-except-pass ] [tool.ruff.format] @@ -68,3 +68,14 @@ reportAttributeAccessIssue = "none" reportUnknownMemberType = "none" reportUnusedFunction = "none" reportUnnecessaryIsInstance = "none" + +[tool.codespell] +# Ref: https://github.com/codespell-project/codespell#using-a-config-file +skip = '.git*' +check-hidden = true +# ignore-regex = '' +ignore-words-list = 'devault,claus' + +[tool.pyright] +typeCheckingMode = "strict" +pythonVersion = "3.10" diff --git a/src/odb_backend.c b/src/odb_backend.c index fa6901e9..a189aec5 100644 --- a/src/odb_backend.c +++ b/src/odb_backend.c @@ -103,7 +103,7 @@ pgit_odb_backend_read_prefix(git_oid *oid_out, void **ptr, size_t *sz, git_objec if (result == NULL) return git_error_for_exc(); - // Parse output from calback + // Parse output from callback PyObject *py_oid_out; Py_ssize_t type_value; const char *bytes; diff --git a/src/oid.c b/src/oid.c index f8a393e1..ffce36a2 100644 --- a/src/oid.c +++ b/src/oid.c @@ -34,6 +34,8 @@ PyTypeObject OidType; +static const git_oid oid_zero = GIT_OID_SHA1_ZERO; + PyObject * git_oid_to_python(const git_oid *oid) @@ -255,6 +257,13 @@ Oid__str__(Oid *self) return git_oid_to_py_str(&self->oid); } +int +Oid__bool(PyObject *self) +{ + git_oid *oid = &((Oid*)self)->oid; + return !git_oid_equal(oid, &oid_zero); +} + PyDoc_STRVAR(Oid_raw__doc__, "Raw oid, a 20 bytes string."); PyObject * @@ -269,6 +278,45 @@ PyGetSetDef Oid_getseters[] = { {NULL}, }; +PyNumberMethods Oid_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + Oid__bool, /* nb_bool */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + 0, /* nb_or */ + 0, /* nb_int */ + 0, /* nb_reserved */ + 0, /* nb_float */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ + 0, /* nb_index */ + 0, /* nb_matrix_multiply */ + 0, /* nb_inplace_matrix_multiply */ +}; + PyDoc_STRVAR(Oid__doc__, "Object id."); PyTypeObject OidType = { @@ -282,7 +330,7 @@ PyTypeObject OidType = { 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)Oid__str__, /* tp_repr */ - 0, /* tp_as_number */ + &Oid_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)Oid_hash, /* tp_hash */ diff --git a/src/pygit2.c b/src/pygit2.c index c2381aa8..ca473968 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -290,7 +290,7 @@ PyDoc_STRVAR(filter_register__doc__, "\n" "`priority` defaults to GIT_FILTER_DRIVER_PRIORITY which imitates a core\n" "Git filter driver that will be run last on checkout (smudge) and first \n" - "on checkin (clean).\n" + "on check-in (clean).\n" "\n" "Note that the filter registry is not thread safe. Any registering or\n" "deregistering of filters should be done outside of any possible usage\n" diff --git a/src/reference.c b/src/reference.c index 1e05d085..b535cbe4 100644 --- a/src/reference.c +++ b/src/reference.c @@ -230,7 +230,7 @@ Reference_rename(Reference *self, PyObject *py_name) if (err) return Error_set(err); - // Upadate reference + // Update reference git_reference_free(self->reference); self->reference = new_reference; @@ -440,6 +440,14 @@ Reference_type__get__(Reference *self) return pygit2_enum(ReferenceTypeEnum, c_type); } +PyDoc_STRVAR(Reference__pointer__doc__, "Get the reference's pointer. For internal use only."); + +PyObject * +Reference__pointer__get__(Reference *self) +{ + /* Bytes means a raw buffer */ + return PyBytes_FromStringAndSize((char *) &self->reference, sizeof(git_reference *)); +} PyDoc_STRVAR(Reference_log__doc__, "log() -> RefLogIter\n" @@ -668,6 +676,7 @@ PyGetSetDef Reference_getseters[] = { GETTER(Reference, target), GETTER(Reference, raw_target), GETTER(Reference, type), + GETTER(Reference, _pointer), {NULL} }; diff --git a/src/repository.c b/src/repository.c index 116ac442..4be614bf 100644 --- a/src/repository.c +++ b/src/repository.c @@ -69,7 +69,7 @@ extern PyObject *FileStatusEnum; extern PyObject *MergeAnalysisEnum; extern PyObject *MergePreferenceEnum; -/* forward-declaration for Repsository._from_c() */ +/* forward-declaration for Repository._from_c() */ PyTypeObject RepositoryType; PyObject * @@ -2078,7 +2078,7 @@ Repository_free(Repository *self) PyDoc_STRVAR(Repository_expand_id__doc__, "expand_id(hex: str) -> Oid\n" "\n" - "Expand a string into a full Oid according to the objects in this repsitory.\n"); + "Expand a string into a full Oid according to the objects in this repository.\n"); PyObject * Repository_expand_id(Repository *self, PyObject *py_hex) diff --git a/src/signature.c b/src/signature.c index 126cdea9..f384bd7d 100644 --- a/src/signature.c +++ b/src/signature.c @@ -81,7 +81,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) void Signature_dealloc(Signature *self) { - /* self->obj is the owner of the git_signature C structure, so we musn't free it */ + /* self->obj is the owner of the git_signature C structure, so we mustn't free it */ if (self->obj) { Py_CLEAR(self->obj); } else { diff --git a/test/test_commit.py b/test/test_commit.py index cf53eb1c..02e81488 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -40,7 +40,7 @@ ) -@utils.refcount +@utils.requires_refcount def test_commit_refcount(barerepo): commit = barerepo[COMMIT_SHA] start = sys.getrefcount(commit) @@ -58,7 +58,7 @@ def test_read_commit(barerepo): assert parents[0].id == 'c2792cfa289ae6321ecf2cd5806c2194b0fd070c' assert commit.message_encoding is None assert commit.message == ( - 'Second test data commit.\n\n' 'This commit has some additional text.\n' + 'Second test data commit.\n\nThis commit has some additional text.\n' ) commit_time = 1288481576 assert commit_time == commit.commit_time diff --git a/test/test_diff.py b/test/test_diff.py index 84aec15b..3ba2d00a 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -219,7 +219,7 @@ def get_context_for_lines(diff): def test_diff_revparse(barerepo): diff = barerepo.diff('HEAD', 'HEAD~6') - assert type(diff) == pygit2.Diff + assert type(diff) is pygit2.Diff def test_diff_tree_opts(barerepo): diff --git a/test/test_merge.py b/test/test_merge.py index bcd62ca2..5233786a 100644 --- a/test/test_merge.py +++ b/test/test_merge.py @@ -39,6 +39,16 @@ def test_merge_invalid_type(mergerepo, id): mergerepo.merge(id) +# TODO: Once Repository.merge drops support for str arguments, +# add an extra parameter to test_merge_invalid_type above +# to make sure we cover legacy code. +def test_merge_string_argument_deprecated(mergerepo): + branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' + + with pytest.warns(DeprecationWarning, match=r'Pass Commit.+instead'): + mergerepo.merge(branch_head_hex) + + def test_merge_analysis_uptodate(mergerepo): branch_head_hex = '5ebeeebb320790caf276b9fc8b24546d63316533' branch_id = mergerepo.get(branch_head_hex).id @@ -82,7 +92,10 @@ def test_merge_no_fastforward_no_conflicts(mergerepo): def test_merge_invalid_hex(mergerepo): branch_head_hex = '12345678' - with pytest.raises(KeyError): + with ( + pytest.raises(KeyError), + pytest.warns(DeprecationWarning, match=r'Pass Commit.+instead'), + ): mergerepo.merge(branch_head_hex) @@ -132,7 +145,7 @@ def test_merge_no_fastforward_conflicts(mergerepo): def test_merge_remove_conflicts(mergerepo): - other_branch_tip = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + other_branch_tip = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') mergerepo.merge(other_branch_tip) idx = mergerepo.index conflicts = idx.conflicts @@ -158,30 +171,29 @@ def test_merge_remove_conflicts(mergerepo): ], ) def test_merge_favor(mergerepo, favor): - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - mergerepo.merge(branch_head_hex, favor=favor) + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') + mergerepo.merge(branch_head, favor=favor) assert mergerepo.index.conflicts is None def test_merge_fail_on_conflict(mergerepo): - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') - with pytest.raises(pygit2.GitError): + with pytest.raises(pygit2.GitError, match=r'merge conflicts exist'): mergerepo.merge( - branch_head_hex, flags=MergeFlag.FIND_RENAMES | MergeFlag.FAIL_ON_CONFLICT + branch_head, flags=MergeFlag.FIND_RENAMES | MergeFlag.FAIL_ON_CONFLICT ) def test_merge_commits(mergerepo): - branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' - branch_id = mergerepo.get(branch_head_hex).id + branch_head = pygit2.Oid(hex='03490f16b15a09913edb3a067a3dc67fbb8d41f1') - merge_index = mergerepo.merge_commits(mergerepo.head.target, branch_head_hex) + merge_index = mergerepo.merge_commits(mergerepo.head.target, branch_head) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) - mergerepo.merge(branch_id) + mergerepo.merge(branch_head) index = mergerepo.index assert index.conflicts is None merge_tree = index.write_tree() @@ -190,26 +202,23 @@ def test_merge_commits(mergerepo): def test_merge_commits_favor(mergerepo): - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') merge_index = mergerepo.merge_commits( - mergerepo.head.target, branch_head_hex, favor=MergeFavor.OURS + mergerepo.head.target, branch_head, favor=MergeFavor.OURS ) assert merge_index.conflicts is None # Incorrect favor value - with pytest.raises(TypeError): - mergerepo.merge_commits(mergerepo.head.target, branch_head_hex, favor='foo') + with pytest.raises(TypeError, match=r'favor argument must be MergeFavor'): + mergerepo.merge_commits(mergerepo.head.target, branch_head, favor='foo') def test_merge_trees(mergerepo): - branch_head_hex = '03490f16b15a09913edb3a067a3dc67fbb8d41f1' - branch_id = mergerepo.get(branch_head_hex).id + branch_id = pygit2.Oid(hex='03490f16b15a09913edb3a067a3dc67fbb8d41f1') ancestor_id = mergerepo.merge_base(mergerepo.head.target, branch_id) - merge_index = mergerepo.merge_trees( - ancestor_id, mergerepo.head.target, branch_head_hex - ) + merge_index = mergerepo.merge_trees(ancestor_id, mergerepo.head.target, branch_id) assert merge_index.conflicts is None merge_commits_tree = merge_index.write_tree(mergerepo) @@ -312,25 +321,25 @@ def test_merge_octopus(mergerepo): def test_merge_mergeheads(mergerepo): assert mergerepo.listall_mergeheads() == [] - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - mergerepo.merge(branch_head_hex) + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') + mergerepo.merge(branch_head) - assert mergerepo.listall_mergeheads() == [pygit2.Oid(hex=branch_head_hex)] + assert mergerepo.listall_mergeheads() == [branch_head] mergerepo.state_cleanup() - assert ( - mergerepo.listall_mergeheads() == [] - ), 'state_cleanup() should wipe the mergeheads' + assert mergerepo.listall_mergeheads() == [], ( + 'state_cleanup() should wipe the mergeheads' + ) def test_merge_message(mergerepo): assert not mergerepo.message assert not mergerepo.raw_message - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - mergerepo.merge(branch_head_hex) + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') + mergerepo.merge(branch_head) - assert mergerepo.message.startswith(f"Merge commit '{branch_head_hex}'") + assert mergerepo.message.startswith(f"Merge commit '{branch_head}'") assert mergerepo.message.encode('utf-8') == mergerepo.raw_message mergerepo.state_cleanup() @@ -338,9 +347,27 @@ def test_merge_message(mergerepo): def test_merge_remove_message(mergerepo): - branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' - mergerepo.merge(branch_head_hex) + branch_head = pygit2.Oid(hex='1b2bae55ac95a4be3f8983b86cd579226d0eb247') + mergerepo.merge(branch_head) - assert mergerepo.message.startswith(f"Merge commit '{branch_head_hex}'") + assert mergerepo.message.startswith(f"Merge commit '{branch_head}'") mergerepo.remove_message() assert not mergerepo.message + + +def test_merge_commit(mergerepo): + commit = mergerepo['1b2bae55ac95a4be3f8983b86cd579226d0eb247'] + assert isinstance(commit, pygit2.Commit) + mergerepo.merge(commit) + + assert mergerepo.message.startswith(f"Merge commit '{str(commit.id)}'") + assert mergerepo.listall_mergeheads() == [commit.id] + + +def test_merge_reference(mergerepo): + branch = mergerepo.branches.local['branch-conflicts'] + branch_head_hex = '1b2bae55ac95a4be3f8983b86cd579226d0eb247' + mergerepo.merge(branch) + + assert mergerepo.message.startswith("Merge branch 'branch-conflicts'") + assert mergerepo.listall_mergeheads() == [pygit2.Oid(hex=branch_head_hex)] diff --git a/test/test_nonunicode.py b/test/test_nonunicode.py new file mode 100644 index 00000000..c31f4202 --- /dev/null +++ b/test/test_nonunicode.py @@ -0,0 +1,48 @@ +# Copyright 2010-2024 The pygit2 contributors +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2, +# as published by the Free Software Foundation. +# +# In addition to the permissions in the GNU General Public License, +# the authors give you unlimited permission to link the compiled +# version of this file into combinations with other programs, +# and to distribute those combinations without any restriction +# coming from the use of this file. (The General Public License +# restrictions do apply in other respects; for example, they cover +# modification of the file, and distribution when not linked into +# a combined executable.) +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +"""Tests for non unicode byte strings""" + +import os +import shutil + +import pygit2 +from . import utils + + +@utils.requires_network +@utils.requires_linux +def test_nonunicode_branchname(testrepo): + folderpath = 'temp_repo_nonutf' + if os.path.exists(folderpath): + shutil.rmtree(folderpath) + newrepo = pygit2.clone_repository( + path=folderpath, url='https://github.com/pygit2/test_branch_notutf.git' + ) + bstring = b'\xc3master' + assert bstring in [ + (ref.split('/')[-1]).encode('utf8', 'surrogateescape') + for ref in newrepo.listall_references() + ] # Remote branch among references: 'refs/remotes/origin/\udcc3master' diff --git a/test/test_object.py b/test/test_object.py index 3531dc89..bd520d75 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -87,7 +87,7 @@ def test_peel_commit(testrepo): # and peel to the tree tree = commit.peel(ObjectType.TREE) - assert type(tree) == Tree + assert type(tree) is Tree assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' @@ -96,7 +96,7 @@ def test_peel_commit_type(testrepo): commit = testrepo[commit_id] tree = commit.peel(Tree) - assert type(tree) == Tree + assert type(tree) is Tree assert tree.id == 'fd937514cb799514d4b81bb24c5fcfeb6472b245' diff --git a/test/test_odb.py b/test/test_odb.py index e18e86ec..d03d9883 100644 --- a/test/test_odb.py +++ b/test/test_odb.py @@ -91,4 +91,4 @@ def test_write(odb): odb.write(ObjectType.ANY, data) oid = odb.write(ObjectType.BLOB, data) - assert type(oid) == Oid + assert type(oid) is Oid diff --git a/test/test_oid.py b/test/test_oid.py index 40fbba98..60c80fbe 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -99,3 +99,10 @@ def test_hash(): s.add(Oid(hex='0000000000000000000000000000000000000000')) s.add(Oid(hex='0000000000000000000000000000000000000001')) assert len(s) == 3 + + +def test_bool(): + assert Oid(raw=RAW) + assert Oid(hex=HEX) + assert not Oid(raw=b'') + assert not Oid(hex='0000000000000000000000000000000000000000') diff --git a/test/test_remote.py b/test/test_remote.py index 9f012744..31c4f1c9 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -54,7 +54,7 @@ def test_remote_create(testrepo): remote = testrepo.remotes.create(name, url) - assert type(remote) == pygit2.Remote + assert type(remote) is pygit2.Remote assert name == remote.name assert url == remote.url assert remote.push_url is None @@ -70,7 +70,7 @@ def test_remote_create_with_refspec(testrepo): remote = testrepo.remotes.create(name, url, fetch) - assert type(remote) == pygit2.Remote + assert type(remote) is pygit2.Remote assert name == remote.name assert url == remote.url assert [fetch] == remote.fetch_refspecs @@ -86,6 +86,8 @@ def test_remote_create_anonymous(testrepo): assert remote.push_url is None assert remote.fetch_refspecs == [] assert remote.push_refspecs == [] + assert remote.fetch_refspecs == [] + assert remote.push_refspecs == [] def test_remote_delete(testrepo): @@ -94,20 +96,25 @@ def test_remote_delete(testrepo): testrepo.remotes.create(name, url) assert len(testrepo.remotes) == 2 + assert len(testrepo.remotes) == 2 remote = testrepo.remotes[1] assert name == remote.name testrepo.remotes.delete(remote.name) assert len(testrepo.remotes) == 1 + assert len(testrepo.remotes) == 1 def test_remote_rename(testrepo): remote = testrepo.remotes[0] + assert remote.name == REMOTE_NAME assert remote.name == REMOTE_NAME problems = testrepo.remotes.rename(remote.name, 'new') assert problems == [] assert remote.name != 'new' + assert problems == [] + assert remote.name != 'new' with pytest.raises(ValueError): testrepo.remotes.rename('', '') @@ -118,6 +125,7 @@ def test_remote_rename(testrepo): def test_remote_set_url(testrepo): remote = testrepo.remotes['origin'] assert remote.url == REMOTE_URL + assert remote.url == REMOTE_URL new_url = 'https://github.com/cholin/pygit2.git' testrepo.remotes.set_url('origin', new_url) @@ -143,22 +151,32 @@ def test_refspec(testrepo): assert refspec.dst == REMOTE_FETCHSPEC_DST assert refspec.force is True assert refspec.string == ORIGIN_REFSPEC + assert refspec.string == ORIGIN_REFSPEC assert list == type(remote.fetch_refspecs) assert len(remote.fetch_refspecs) == 1 assert remote.fetch_refspecs[0] == ORIGIN_REFSPEC + assert list is type(remote.fetch_refspecs) + assert 1 == len(remote.fetch_refspecs) + assert ORIGIN_REFSPEC == remote.fetch_refspecs[0] assert refspec.src_matches('refs/heads/master') assert refspec.dst_matches('refs/remotes/origin/master') assert refspec.transform('refs/heads/master') == 'refs/remotes/origin/master' assert refspec.rtransform('refs/remotes/origin/master') == 'refs/heads/master' + assert refspec.transform('refs/heads/master') == 'refs/remotes/origin/master' + assert refspec.rtransform('refs/remotes/origin/master') == 'refs/heads/master' assert list == type(remote.push_refspecs) assert len(remote.push_refspecs) == 0 + assert list is type(remote.push_refspecs) + assert 0 == len(remote.push_refspecs) push_specs = remote.push_refspecs assert list == type(push_specs) assert len(push_specs) == 0 + assert list is type(push_specs) + assert 0 == len(push_specs) testrepo.remotes.add_fetch('origin', '+refs/test/*:refs/test/remotes/*') remote = testrepo.remotes['origin'] @@ -167,9 +185,13 @@ def test_refspec(testrepo): assert list == type(fetch_specs) assert len(fetch_specs) == 2 assert fetch_specs == [ + assert list is type(fetch_specs) + assert 2 == len(fetch_specs) + assert [ '+refs/heads/*:refs/remotes/origin/*', '+refs/test/*:refs/test/remotes/*', ] + ] testrepo.remotes.add_push('origin', '+refs/test/*:refs/test/remotes/*') @@ -178,13 +200,17 @@ def test_refspec(testrepo): remote = testrepo.remotes['origin'] assert remote.push_refspecs == ['+refs/test/*:refs/test/remotes/*'] + assert remote.push_refspecs == ['+refs/test/*:refs/test/remotes/*'] def test_remote_list(testrepo): + assert len(testrepo.remotes) == 1 assert len(testrepo.remotes) == 1 remote = testrepo.remotes[0] assert remote.name == REMOTE_NAME assert remote.url == REMOTE_URL + assert remote.name == REMOTE_NAME + assert remote.url == REMOTE_URL name = 'upstream' url = 'https://github.com/libgit2/pygit2.git' @@ -195,6 +221,7 @@ def test_remote_list(testrepo): @utils.requires_network def test_ls_remotes(testrepo): + assert len(testrepo.remotes) == 1 assert len(testrepo.remotes) == 1 remote = testrepo.remotes[0] @@ -209,6 +236,8 @@ def test_remote_collection(testrepo): remote = testrepo.remotes['origin'] assert remote.name == REMOTE_NAME assert remote.url == REMOTE_URL + assert remote.name == REMOTE_NAME + assert remote.url == REMOTE_URL with pytest.raises(KeyError): testrepo.remotes['upstream'] @@ -220,7 +249,7 @@ def test_remote_collection(testrepo): assert remote.name in [x.name for x in testrepo.remotes] -@utils.refcount +@utils.requires_refcount def test_remote_refcount(testrepo): start = sys.getrefcount(testrepo) remote = testrepo.remotes[0] @@ -256,8 +285,8 @@ def test_fetch_depth_one(testrepo): def test_transfer_progress(emptyrepo): class MyCallbacks(pygit2.RemoteCallbacks): - def transfer_progress(emptyrepo, stats): - emptyrepo.tp = stats + def transfer_progress(self, stats): + self.tp = stats callbacks = MyCallbacks() remote = emptyrepo.remotes[0] @@ -272,13 +301,13 @@ def test_update_tips(emptyrepo): tips = [ ( 'refs/remotes/origin/master', - Oid(hex='0' * 40), - Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78'), + pygit2.Oid(hex='0' * 40), + pygit2.Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78'), ), ( 'refs/tags/root', - Oid(hex='0' * 40), - Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'), + pygit2.Oid(hex='0' * 40), + pygit2.Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'), ), ] @@ -366,6 +395,59 @@ def test_push_when_up_to_date_succeeds(origin, clone, remote): assert origin_tip == clone_tip +def test_push_transfer_progress(origin, clone, remote): + tip = clone[clone.head.target] + new_tip_id = clone.create_commit( + 'refs/heads/master', + tip.author, + tip.author, + 'empty commit', + tip.tree.id, + [tip.id], + ) + + # NOTE: We're currently not testing bytes_pushed due to a bug in libgit2 + # 1.9.0: it passes a junk value for bytes_pushed when pushing to a remote + # on the local filesystem, as is the case in this unit test. (When pushing + # to a remote over the network, the value is correct.) + class MyCallbacks(pygit2.RemoteCallbacks): + def push_transfer_progress(self, objects_pushed, total_objects, bytes_pushed): + self.objects_pushed = objects_pushed + self.total_objects = total_objects + + assert origin.branches['master'].target == tip.id + + callbacks = MyCallbacks() + remote.push(['refs/heads/master'], callbacks=callbacks) + assert callbacks.objects_pushed == 1 + assert callbacks.total_objects == 1 + assert origin.branches['master'].target == new_tip_id + + +def test_push_interrupted_from_callbacks(origin, clone, remote): + tip = clone[clone.head.target] + clone.create_commit( + 'refs/heads/master', + tip.author, + tip.author, + 'empty commit', + tip.tree.id, + [tip.id], + ) + + class MyCallbacks(pygit2.RemoteCallbacks): + def push_transfer_progress(self, objects_pushed, total_objects, bytes_pushed): + raise InterruptedError('retreat! retreat!') + + assert origin.branches['master'].target == tip.id + + callbacks = MyCallbacks() + with pytest.raises(InterruptedError, match='retreat! retreat!'): + remote.push(['refs/heads/master'], callbacks=callbacks) + + assert origin.branches['master'].target == tip.id + + def test_push_non_fast_forward_commits_to_remote_fails(origin, clone, remote): tip = origin[origin.head.target] origin.create_commit( @@ -390,22 +472,31 @@ def test_push_non_fast_forward_commits_to_remote_fails(origin, clone, remote): remote.push(['refs/heads/master']) -@patch.object(pygit2.callbacks, 'RemoteCallbacks') -def test_push_options(mock_callbacks, origin, clone, remote): - remote.push(['refs/heads/master']) - remote_push_options = mock_callbacks.return_value.push_options.remote_push_options +def test_push_options(origin, clone, remote): + from pygit2 import RemoteCallbacks + + callbacks = RemoteCallbacks() + remote.push(['refs/heads/master'], callbacks) + remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 0 - remote.push(['refs/heads/master'], push_options=[]) - remote_push_options = mock_callbacks.return_value.push_options.remote_push_options + callbacks = RemoteCallbacks() + remote.push(['refs/heads/master'], callbacks, push_options=[]) + remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 0 - remote.push(['refs/heads/master'], push_options=['foo']) - remote_push_options = mock_callbacks.return_value.push_options.remote_push_options + callbacks = RemoteCallbacks() + # Local remotes don't support push_options, so pushing will raise an error. + # However, push_options should still be set in RemoteCallbacks. + with pytest.raises(pygit2.GitError, match='push-options not supported by remote'): + remote.push(['refs/heads/master'], callbacks, push_options=['foo']) + remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 1 # strings pointed to by remote_push_options.strings[] are already freed - remote.push(['refs/heads/master'], push_options=['Option A', 'Option B']) - remote_push_options = mock_callbacks.return_value.push_options.remote_push_options + callbacks = RemoteCallbacks() + with pytest.raises(pygit2.GitError, match='push-options not supported by remote'): + remote.push(['refs/heads/master'], callbacks, push_options=['Opt A', 'Opt B']) + remote_push_options = callbacks.push_options.remote_push_options assert remote_push_options.count == 2 # strings pointed to by remote_push_options.strings[] are already freed diff --git a/test/test_repository.py b/test/test_repository.py index ac16d129..17169112 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -99,6 +99,8 @@ def __init__(self): super().__init__() self.conflicting_paths = set() self.updated_paths = set() + self.completed_steps = -1 + self.total_steps = -1 def checkout_notify_flags(self) -> CheckoutNotify: return CheckoutNotify.CONFLICT | CheckoutNotify.UPDATED @@ -109,12 +111,17 @@ def checkout_notify(self, why, path, baseline, target, workdir): elif why == CheckoutNotify.UPDATED: self.updated_paths.add(path) + def checkout_progress(self, path: str, completed_steps: int, total_steps: int): + self.completed_steps = completed_steps + self.total_steps = total_steps + # checkout i18n with conflicts and default strategy should not be possible callbacks = MyCheckoutCallbacks() with pytest.raises(pygit2.GitError): testrepo.checkout(ref_i18n, callbacks=callbacks) # make sure the callbacks caught that assert {'bye.txt'} == callbacks.conflicting_paths + assert -1 == callbacks.completed_steps # shouldn't have done anything # checkout i18n with GIT_CHECKOUT_FORCE head = testrepo.head @@ -125,6 +132,8 @@ def checkout_notify(self, why, path, baseline, target, workdir): # make sure the callbacks caught the files affected by the checkout assert set() == callbacks.conflicting_paths assert {'bye.txt', 'new'} == callbacks.updated_paths + assert callbacks.completed_steps > 0 + assert callbacks.completed_steps == callbacks.total_steps def test_checkout_aborted_from_callbacks(testrepo): @@ -587,7 +596,7 @@ def test_new_repo(tmp_path): repo = init_repository(tmp_path, False) oid = repo.write(ObjectType.BLOB, 'Test') - assert type(oid) == Oid + assert type(oid) is Oid assert (tmp_path / '.git').exists() @@ -629,7 +638,6 @@ def test_discover_repo(tmp_path): assert repo.path == discover_repository(str(subdir)) -@utils.fspath def test_discover_repo_aspath(tmp_path): repo = init_repository(Path(tmp_path), False) subdir = Path(tmp_path) / 'test1' / 'test2' @@ -811,7 +819,6 @@ def _check_worktree(worktree): assert testrepo.list_worktrees() == [] -@utils.fspath def test_worktree_aspath(testrepo): worktree_name = 'foo' worktree_dir = Path(tempfile.mkdtemp()) diff --git a/test/test_repository_bare.py b/test/test_repository_bare.py index 852a72c8..f7e2760c 100644 --- a/test/test_repository_bare.py +++ b/test/test_repository_bare.py @@ -23,9 +23,9 @@ # the Free Software Foundation, 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. -# Standard Library import binascii import os +import pathlib import sys import tempfile from pathlib import Path @@ -61,7 +61,7 @@ def test_head(barerepo): def test_set_head(barerepo): - # Test setting a detatched HEAD. + # Test setting a detached HEAD. barerepo.set_head(pygit2.Oid(hex=PARENT_SHA)) assert barerepo.head.target == PARENT_SHA # And test setting a normal HEAD. @@ -95,7 +95,7 @@ def test_write(barerepo): barerepo.write(ObjectType.ANY, data) oid = barerepo.write(ObjectType.BLOB, data) - assert type(oid) == pygit2.Oid + assert type(oid) is pygit2.Oid def test_contains(barerepo): @@ -136,7 +136,7 @@ def test_lookup_commit(barerepo): assert commit_sha == commit.id assert commit.type == ObjectType.COMMIT assert commit.message == ( - 'Second test data commit.\n\n' 'This commit has some additional text.\n' + 'Second test data commit.\n\nThis commit has some additional text.\n' ) @@ -161,7 +161,7 @@ def test_expand_id(barerepo): assert commit_sha == expanded -@utils.refcount +@utils.requires_refcount def test_lookup_commit_refcount(barerepo): start = sys.getrefcount(barerepo) commit_sha = '5fe808e8953c12735680c257f56600cb0de44b10' @@ -174,7 +174,7 @@ def test_lookup_commit_refcount(barerepo): def test_get_path(barerepo_path): barerepo, path = barerepo_path - directory = Path(barerepo.path).resolve() + directory = pathlib.Path(barerepo.path).resolve() assert directory == path.resolve() @@ -200,7 +200,7 @@ def test_hashfile(barerepo): with os.fdopen(handle, 'w') as fh: fh.write(data) hashed_sha1 = pygit2.hashfile(tempfile_path) - Path(tempfile_path).unlink() + pathlib.Path(tempfile_path).unlink() written_sha1 = barerepo.create_blob(data) assert hashed_sha1 == written_sha1 diff --git a/test/test_tree.py b/test/test_tree.py index 07981867..62ff7339 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -169,7 +169,7 @@ def test_modify_tree(barerepo): def test_iterate_tree(barerepo): """ Testing that we're able to iterate of a Tree object and that the - resulting sha strings are consitent with the sha strings we could + resulting sha strings are consistent with the sha strings we could get with other Tree access methods. """ tree = barerepo[TREE_SHA] diff --git a/test/utils.py b/test/utils.py index 46ad2a82..f97a1c9a 100644 --- a/test/utils.py +++ b/test/utils.py @@ -38,7 +38,8 @@ # Pygit2 import pygit2 -requires_future_libgit2 = pytest.mark.skipif( + +requires_future_libgit2 = pytest.mark.xfail( pygit2.LIBGIT2_VER < (2, 0, 0), reason='This test may work with a future version of libgit2', ) @@ -63,12 +64,11 @@ is_pypy = '__pypy__' in sys.builtin_module_names -fspath = pytest.mark.skipif( - is_pypy, - reason="PyPy doesn't fully support fspath, see https://foss.heptapod.net/pypy/pypy/-/issues/3168", -) +requires_refcount = pytest.mark.skipif(is_pypy, reason='skip refcounts checks in pypy') -refcount = pytest.mark.skipif(is_pypy, reason='skip refcounts checks in pypy') +requires_linux = pytest.mark.xfail( + sys.platform != 'linux', reason='probably a bug in libgit2 for non-linux platforms' +) def gen_blob_sha1(data): From 48e0dcee39a18729825c277fba405e048f3982cb Mon Sep 17 00:00:00 2001 From: DinhHuy2010 Date: Mon, 17 Feb 2025 19:01:11 +0700 Subject: [PATCH 44/44] fix config --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a1847d48..3ec6d477 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ repair-wheel-command = "DYLD_LIBRARY_PATH=/Users/runner/work/pygit2/pygit2/ci/li extend-exclude = [ ".cache", ".coverage", "build", "site-packages", "venv*"] target-version = "py310" # oldest supported Python version fix = true -extend-exclude = [".cache", ".coverage", "build", "venv*"] lint.select = [ "C", "E",