diff options
-rw-r--r-- | git/cmd.py | 53 | ||||
-rw-r--r-- | git/config.py | 18 | ||||
-rw-r--r-- | git/index/fun.py | 2 | ||||
-rw-r--r-- | git/objects/base.py | 3 | ||||
-rw-r--r-- | git/objects/commit.py | 6 | ||||
-rw-r--r-- | git/objects/submodule/base.py | 13 | ||||
-rw-r--r-- | git/objects/tree.py | 4 | ||||
-rw-r--r-- | git/objects/util.py | 12 | ||||
-rw-r--r-- | git/refs/head.py | 5 | ||||
-rw-r--r-- | git/refs/log.py | 2 | ||||
-rw-r--r-- | git/refs/reference.py | 6 | ||||
-rw-r--r-- | git/refs/symbolic.py | 91 | ||||
-rw-r--r-- | git/repo/base.py | 13 | ||||
-rw-r--r-- | git/util.py | 8 | ||||
-rw-r--r-- | pyproject.toml | 2 |
15 files changed, 138 insertions, 100 deletions
@@ -39,10 +39,10 @@ from .util import ( # typing --------------------------------------------------------------------------- -from typing import (Any, AnyStr, BinaryIO, Callable, Dict, IO, List, Mapping, +from typing import (Any, AnyStr, BinaryIO, Callable, Dict, IO, Iterator, List, Mapping, Sequence, TYPE_CHECKING, TextIO, Tuple, Union, cast, overload) -from git.types import PathLike, Literal, TBD +from git.types import PathLike, Literal if TYPE_CHECKING: from git.repo.base import Repo @@ -146,11 +146,11 @@ def dashify(string: str) -> str: return string.replace('_', '-') -def slots_to_dict(self, exclude: Sequence[str] = ()) -> Dict[str, Any]: +def slots_to_dict(self: object, exclude: Sequence[str] = ()) -> Dict[str, Any]: return {s: getattr(self, s) for s in self.__slots__ if s not in exclude} -def dict_to_slots_and__excluded_are_none(self, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None: +def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], excluded: Sequence[str] = ()) -> None: for k, v in d.items(): setattr(self, k, v) for k in excluded: @@ -192,7 +192,7 @@ class Git(LazyMixin): def __getstate__(self) -> Dict[str, Any]: return slots_to_dict(self, exclude=self._excluded_) - def __setstate__(self, d) -> None: + def __setstate__(self, d: Dict[str, Any]) -> None: dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_) # CONFIGURATION @@ -434,10 +434,13 @@ class Git(LazyMixin): if self.proc is not None: status = self.proc.wait() - def read_all_from_possibly_closed_stream(stream): - try: - return stderr + force_bytes(stream.read()) - except ValueError: + def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes: + if stream: + try: + return stderr + force_bytes(stream.read()) + except ValueError: + return stderr or b'' + else: return stderr or b'' if status != 0: @@ -907,7 +910,7 @@ class Git(LazyMixin): if self.GIT_PYTHON_TRACE == 'full': cmdstr = " ".join(redacted_command) - def as_text(stdout_value): + def as_text(stdout_value: Union[bytes, str]) -> str: return not output_stream and safe_decode(stdout_value) or '<OUTPUT_STREAM>' # end @@ -932,10 +935,10 @@ class Git(LazyMixin): else: return stdout_value - def environment(self): + def environment(self) -> Dict[str, str]: return self._environment - def update_environment(self, **kwargs): + def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]: """ Set environment variables for future git invocations. Return all changed values in a format that can be passed back into this function to revert @@ -962,7 +965,7 @@ class Git(LazyMixin): return old_env @contextmanager - def custom_environment(self, **kwargs): + def custom_environment(self, **kwargs: Any) -> Iterator[None]: """ A context manager around the above ``update_environment`` method to restore the environment back to its previous state after operation. @@ -1044,6 +1047,13 @@ class Git(LazyMixin): ... # if no args given, execute called with all defaults @overload + def _call_process(self, method: str, + istream: int, + as_process: Literal[True], + *args: Any, **kwargs: Any + ) -> 'Git.AutoInterrupt': ... + + @overload def _call_process(self, method: str, *args: Any, **kwargs: Any ) -> Union[str, bytes, Tuple[int, Union[str, bytes], str], 'Git.AutoInterrupt']: ... @@ -1156,7 +1166,7 @@ class Git(LazyMixin): return refstr.encode(defenc) def _get_persistent_cmd(self, attr_name: str, cmd_name: str, *args: Any, **kwargs: Any - ) -> Union['Git.AutoInterrupt', TBD]: + ) -> 'Git.AutoInterrupt': cur_val = getattr(self, attr_name) if cur_val is not None: return cur_val @@ -1166,12 +1176,16 @@ class Git(LazyMixin): cmd = self._call_process(cmd_name, *args, **options) setattr(self, attr_name, cmd) + cmd = cast('Git.AutoInterrupt', cmd) return cmd - def __get_object_header(self, cmd, ref: AnyStr) -> Tuple[str, str, int]: - cmd.stdin.write(self._prepare_ref(ref)) - cmd.stdin.flush() - return self._parse_object_header(cmd.stdout.readline()) + def __get_object_header(self, cmd: 'Git.AutoInterrupt', ref: AnyStr) -> Tuple[str, str, int]: + if cmd.stdin and cmd.stdout: + cmd.stdin.write(self._prepare_ref(ref)) + cmd.stdin.flush() + return self._parse_object_header(cmd.stdout.readline()) + else: + raise ValueError("cmd stdin was empty") def get_object_header(self, ref: str) -> Tuple[str, str, int]: """ Use this method to quickly examine the type and size of the object behind @@ -1200,7 +1214,8 @@ class Git(LazyMixin): :note: This method is not threadsafe, you need one independent Command instance per thread to be safe !""" cmd = self._get_persistent_cmd("cat_file_all", "cat_file", batch=True) hexsha, typename, size = self.__get_object_header(cmd, ref) - return (hexsha, typename, size, self.CatFileContentStream(size, cmd.stdout)) + cmd_stdout = cmd.stdout if cmd.stdout is not None else io.BytesIO() + return (hexsha, typename, size, self.CatFileContentStream(size, cmd_stdout)) def clear_cache(self) -> 'Git': """Clear all kinds of internal caches to release resources. diff --git a/git/config.py b/git/config.py index ad02b437..011d0e0b 100644 --- a/git/config.py +++ b/git/config.py @@ -40,6 +40,7 @@ if TYPE_CHECKING: from io import BytesIO T_ConfigParser = TypeVar('T_ConfigParser', bound='GitConfigParser') +T_OMD_value = TypeVar('T_OMD_value', str, bytes, int, float, bool) if sys.version_info[:3] < (3, 7, 2): # typing.Ordereddict not added until py 3.7.2 @@ -47,7 +48,7 @@ if sys.version_info[:3] < (3, 7, 2): OrderedDict_OMD = OrderedDict # type: ignore # until 3.6 dropped else: from typing import OrderedDict # type: ignore # until 3.6 dropped - OrderedDict_OMD = OrderedDict[str, List[_T]] # type: ignore[assignment, misc] + OrderedDict_OMD = OrderedDict[str, List[T_OMD_value]] # type: ignore[assignment, misc] # ------------------------------------------------------------- @@ -97,23 +98,23 @@ class MetaParserBuilder(abc.ABCMeta): return new_type -def needs_values(func: Callable) -> Callable: +def needs_values(func: Callable[..., _T]) -> Callable[..., _T]: """Returns method assuring we read values (on demand) before we try to access them""" @wraps(func) - def assure_data_present(self, *args: Any, **kwargs: Any) -> Any: + def assure_data_present(self: 'GitConfigParser', *args: Any, **kwargs: Any) -> _T: self.read() return func(self, *args, **kwargs) # END wrapper method return assure_data_present -def set_dirty_and_flush_changes(non_const_func: Callable) -> Callable: +def set_dirty_and_flush_changes(non_const_func: Callable[..., _T]) -> Callable[..., _T]: """Return method that checks whether given non constant function may be called. If so, the instance will be set dirty. Additionally, we flush the changes right to disk""" - def flush_changes(self, *args: Any, **kwargs: Any) -> Any: + def flush_changes(self: 'GitConfigParser', *args: Any, **kwargs: Any) -> _T: rval = non_const_func(self, *args, **kwargs) self._dirty = True self.write() @@ -356,7 +357,7 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder): self._acquire_lock() return self - def __exit__(self, exception_type, exception_value, traceback) -> None: + def __exit__(self, *args: Any) -> None: self.release() def release(self) -> None: @@ -613,12 +614,15 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder): def _write(self, fp: IO) -> None: """Write an .ini-format representation of the configuration state in git compatible format""" - def write_section(name, section_dict): + def write_section(name: str, section_dict: _OMD) -> None: fp.write(("[%s]\n" % name).encode(defenc)) + + values: Sequence[Union[str, bytes, int, float, bool]] for (key, values) in section_dict.items_all(): if key == "__name__": continue + v: Union[str, bytes, int, float, bool] for v in values: fp.write(("\t%s = %s\n" % (key, self._value_to_string(v).replace('\n', '\n\t'))).encode(defenc)) # END if key is not __name__ diff --git a/git/index/fun.py b/git/index/fun.py index 49e3f2c5..16ec744e 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -251,7 +251,7 @@ def read_cache(stream: IO[bytes]) -> Tuple[int, Dict[Tuple[PathLike, int], 'Inde return (version, entries, extension_data, content_sha) -def write_tree_from_cache(entries: List[IndexEntry], odb, sl: slice, si: int = 0 +def write_tree_from_cache(entries: List[IndexEntry], odb: 'GitCmdObjectDB', sl: slice, si: int = 0 ) -> Tuple[bytes, List['TreeCacheTup']]: """Create a tree from the given sorted list of entries and put the respective trees into the given object database diff --git a/git/objects/base.py b/git/objects/base.py index 64f105ca..a3b0f230 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -25,6 +25,7 @@ if TYPE_CHECKING: from .tree import Tree from .blob import Blob from .submodule.base import Submodule + from git.refs.reference import Reference IndexObjUnion = Union['Tree', 'Blob', 'Submodule'] @@ -59,7 +60,7 @@ class Object(LazyMixin): assert len(binsha) == 20, "Require 20 byte binary sha, got %r, len = %i" % (binsha, len(binsha)) @classmethod - def new(cls, repo: 'Repo', id): # @ReservedAssignment + def new(cls, repo: 'Repo', id: Union[str, 'Reference']) -> Commit_ish: """ :return: New Object instance of a type appropriate to the object type behind id. The id of the newly created object will be a binsha even though diff --git a/git/objects/commit.py b/git/objects/commit.py index 9d709656..b689167f 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -282,7 +282,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): proc = repo.git.rev_list(rev, args_list, as_process=True, **kwargs) return cls._iter_from_process_or_stream(repo, proc) - def iter_parents(self, paths: Union[PathLike, Sequence[PathLike]] = '', **kwargs) -> Iterator['Commit']: + def iter_parents(self, paths: Union[PathLike, Sequence[PathLike]] = '', **kwargs: Any) -> Iterator['Commit']: """Iterate _all_ parents of this commit. :param paths: @@ -362,7 +362,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): def create_from_tree(cls, repo: 'Repo', tree: Union[Tree, str], message: str, parent_commits: Union[None, List['Commit']] = None, head: bool = False, author: Union[None, Actor] = None, committer: Union[None, Actor] = None, - author_date: Union[None, str] = None, commit_date: Union[None, str] = None): + author_date: Union[None, str] = None, commit_date: Union[None, str] = None) -> 'Commit': """Commit the given tree, creating a commit object. :param repo: Repo object the commit should be part of @@ -403,7 +403,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): else: for p in parent_commits: if not isinstance(p, cls): - raise ValueError("Parent commit '%r' must be of type %s" % (p, cls)) + raise ValueError(f"Parent commit '{p!r}' must be of type {cls}") # end check parent commit types # END if parent commits are unset diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 14351190..559d2585 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -57,6 +57,7 @@ from git.types import Commit_ish, Literal, PathLike, TBD if TYPE_CHECKING: from git.index import IndexFile from git.repo import Repo + from git.refs import Head # ----------------------------------------------------------------------------- @@ -265,7 +266,7 @@ class Submodule(IndexObject, TraversableIterableObj): # end @classmethod - def _clone_repo(cls, repo, url, path, name, **kwargs): + def _clone_repo(cls, repo: 'Repo', url: str, path: PathLike, name: str, **kwargs: Any) -> 'Repo': """:return: Repo instance of newly cloned repository :param repo: our parent repository :param url: url to clone from @@ -279,7 +280,7 @@ class Submodule(IndexObject, TraversableIterableObj): module_abspath_dir = osp.dirname(module_abspath) if not osp.isdir(module_abspath_dir): os.makedirs(module_abspath_dir) - module_checkout_path = osp.join(repo.working_tree_dir, path) + module_checkout_path = osp.join(str(repo.working_tree_dir), path) # end clone = git.Repo.clone_from(url, module_checkout_path, **kwargs) @@ -484,7 +485,7 @@ class Submodule(IndexObject, TraversableIterableObj): def update(self, recursive: bool = False, init: bool = True, to_latest_revision: bool = False, progress: Union['UpdateProgress', None] = None, dry_run: bool = False, force: bool = False, keep_going: bool = False, env: Union[Mapping[str, str], None] = None, - clone_multi_options: Union[Sequence[TBD], None] = None): + clone_multi_options: Union[Sequence[TBD], None] = None) -> 'Submodule': """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -712,7 +713,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def move(self, module_path, configuration=True, module=True): + def move(self, module_path: PathLike, configuration: bool = True, module: bool = True) -> 'Submodule': """Move the submodule to a another module path. This involves physically moving the repository at our current path, changing the configuration, as well as adjusting our index entry accordingly. @@ -742,7 +743,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self # END handle no change - module_checkout_abspath = join_path_native(self.repo.working_tree_dir, module_checkout_path) + module_checkout_abspath = join_path_native(str(self.repo.working_tree_dir), module_checkout_path) if osp.isfile(module_checkout_abspath): raise ValueError("Cannot move repository onto a file: %s" % module_checkout_abspath) # END handle target files @@ -1160,7 +1161,7 @@ class Submodule(IndexObject, TraversableIterableObj): # END handle object state consistency @property - def branch(self): + def branch(self) -> 'Head': """:return: The branch instance that we are to checkout :raise InvalidGitRepositoryError: if our module is not yet checked out""" return mkhead(self.module(), self._branch_path) diff --git a/git/objects/tree.py b/git/objects/tree.py index 70f36af5..0cceb59a 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -375,8 +375,8 @@ class Tree(IndexObject, git_diff.Diffable, util.Traversable, util.Serializable): # END for each item return False - def __reversed__(self): - return reversed(self._iter_convert_to_object(self._cache)) + def __reversed__(self) -> Iterator[IndexObjUnion]: + return reversed(self._iter_convert_to_object(self._cache)) # type: ignore def _serialize(self, stream: 'BytesIO') -> 'Tree': """Serialize this tree into the stream. Please note that we will assume diff --git a/git/objects/util.py b/git/objects/util.py index db7807c2..f627211e 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -144,20 +144,20 @@ class tzoffset(tzinfo): def __reduce__(self) -> Tuple[Type['tzoffset'], Tuple[float, str]]: return tzoffset, (-self._offset.total_seconds(), self._name) - def utcoffset(self, dt) -> timedelta: + def utcoffset(self, dt: Union[datetime, None]) -> timedelta: return self._offset - def tzname(self, dt) -> str: + def tzname(self, dt: Union[datetime, None]) -> str: return self._name - def dst(self, dt) -> timedelta: + def dst(self, dt: Union[datetime, None]) -> timedelta: return ZERO utc = tzoffset(0, 'UTC') -def from_timestamp(timestamp, tz_offset: float) -> datetime: +def from_timestamp(timestamp: float, tz_offset: float) -> datetime: """Converts a timestamp + tz_offset into an aware datetime instance.""" utc_dt = datetime.fromtimestamp(timestamp, utc) try: @@ -305,7 +305,7 @@ class Traversable(Protocol): @classmethod @abstractmethod - def _get_intermediate_items(cls, item) -> Sequence['Traversable']: + def _get_intermediate_items(cls, item: Any) -> Sequence['Traversable']: """ Returns: Tuple of items connected to the given item. @@ -327,7 +327,7 @@ class Traversable(Protocol): stacklevel=2) return self._list_traverse(*args, **kwargs) - def _list_traverse(self, as_edge=False, *args: Any, **kwargs: Any + def _list_traverse(self, as_edge: bool = False, *args: Any, **kwargs: Any ) -> IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']]: """ :return: IterableList with the results of the traversal as produced by diff --git a/git/refs/head.py b/git/refs/head.py index 260bf5e7..56a87182 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: __all__ = ["HEAD", "Head"] -def strip_quotes(string): +def strip_quotes(string: str) -> str: if string.startswith('"') and string.endswith('"'): return string[1:-1] return string @@ -129,14 +129,13 @@ class Head(Reference): k_config_remote_ref = "merge" # branch to merge from remote @classmethod - def delete(cls, repo: 'Repo', *heads: 'Head', **kwargs: Any): + def delete(cls, repo: 'Repo', *heads: 'Head', force: bool = False, **kwargs: Any) -> None: """Delete the given heads :param force: If True, the heads will be deleted even if they are not yet merged into the main development stream. Default False""" - force = kwargs.get("force", False) flag = "-d" if force: flag = "-D" diff --git a/git/refs/log.py b/git/refs/log.py index 643b4114..ddd78bc7 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -157,7 +157,7 @@ class RefLog(List[RefLogEntry], Serializable): self._read_from_file() # END handle filepath - def _read_from_file(self): + def _read_from_file(self) -> None: try: fmap = file_contents_ro_filepath( self._path, stream=True, allow_mmap=True) diff --git a/git/refs/reference.py b/git/refs/reference.py index bc2c6e80..a3647fb3 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -7,7 +7,7 @@ from .symbolic import SymbolicReference, T_References # typing ------------------------------------------------------------------ -from typing import Any, Callable, Iterator, List, Match, Optional, Tuple, Type, TypeVar, Union, TYPE_CHECKING # NOQA +from typing import Any, Callable, Iterator, Type, Union, TYPE_CHECKING # NOQA from git.types import Commit_ish, PathLike, TBD, Literal, _T # NOQA if TYPE_CHECKING: @@ -63,8 +63,8 @@ class Reference(SymbolicReference, LazyMixin, IterableObj): #{ Interface # @ReservedAssignment - def set_object(self, object: Union[Commit_ish, 'SymbolicReference'], logmsg: Union[str, None] = None - ) -> 'SymbolicReference': + def set_object(self, object: Union[Commit_ish, 'SymbolicReference', str], logmsg: Union[str, None] = None + ) -> 'Reference': """Special version which checks if the head-log needs an update as well :return: self""" oldbinsha = None diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index bffcfea5..b4a933aa 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -26,7 +26,11 @@ from git.types import Commit_ish, PathLike, TBD, Literal if TYPE_CHECKING: from git.repo import Repo - from git.refs import Head, TagReference, Reference + from git.refs import Head, TagReference, RemoteReference, Reference + from .log import RefLogEntry + from git.config import GitConfigParser + from git.objects.commit import Actor + T_References = TypeVar('T_References', bound='SymbolicReference') @@ -36,7 +40,7 @@ T_References = TypeVar('T_References', bound='SymbolicReference') __all__ = ["SymbolicReference"] -def _git_dir(repo: 'Repo', path: PathLike) -> PathLike: +def _git_dir(repo: 'Repo', path: Union[PathLike, None]) -> PathLike: """ Find the git dir that's appropriate for the path""" name = f"{path}" if name in ['HEAD', 'ORIG_HEAD', 'FETCH_HEAD', 'index', 'logs']: @@ -136,11 +140,12 @@ class SymbolicReference(object): # alright. @classmethod - def dereference_recursive(cls, repo: 'Repo', ref_path: PathLike) -> str: + def dereference_recursive(cls, repo: 'Repo', ref_path: Union[PathLike, None]) -> str: """ :return: hexsha stored in the reference at the given ref_path, recursively dereferencing all intermediate references as required :param repo: the repository containing the reference at ref_path""" + while True: hexsha, ref_path = cls._get_ref_info(repo, ref_path) if hexsha is not None: @@ -148,14 +153,15 @@ class SymbolicReference(object): # END recursive dereferencing @classmethod - def _get_ref_info_helper(cls, repo: 'Repo', ref_path: PathLike): + def _get_ref_info_helper(cls, repo: 'Repo', ref_path: Union[PathLike, None] + ) -> Union[Tuple[str, None], Tuple[None, str]]: """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" tokens: Union[None, List[str], Tuple[str, str]] = None repodir = _git_dir(repo, ref_path) try: - with open(os.path.join(repodir, ref_path), 'rt', encoding='UTF-8') as fp: + with open(os.path.join(repodir, str(ref_path)), 'rt', encoding='UTF-8') as fp: value = fp.read().rstrip() # Don't only split on spaces, but on whitespace, which allows to parse lines like # 60b64ef992065e2600bfef6187a97f92398a9144 branch 'master' of git-server:/path/to/repo @@ -187,13 +193,13 @@ class SymbolicReference(object): raise ValueError("Failed to parse reference information from %r" % ref_path) @classmethod - def _get_ref_info(cls, repo, ref_path): + def _get_ref_info(cls, repo: 'Repo', ref_path: Union[PathLike, None]) -> Union[Tuple[str, None], Tuple[None, str]]: """Return: (str(sha), str(target_ref_path)) if available, the sha the file at rela_path points to, or None. target_ref_path is the reference we point to, or None""" return cls._get_ref_info_helper(repo, ref_path) - def _get_object(self): + def _get_object(self) -> Commit_ish: """ :return: The object our ref currently refers to. Refs can be cached, they will @@ -202,7 +208,7 @@ class SymbolicReference(object): # Our path will be resolved to the hexsha which will be used accordingly return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path))) - def _get_commit(self): + def _get_commit(self) -> 'Commit': """ :return: Commit object we point to, works for detached and non-detached @@ -217,7 +223,8 @@ class SymbolicReference(object): # END handle type return obj - def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg=None): + def set_commit(self, commit: Union[Commit, 'SymbolicReference', str], logmsg: Union[str, None] = None + ) -> 'SymbolicReference': """As set_object, but restricts the type of object to be a Commit :raise ValueError: If commit is not a Commit object or doesn't point to @@ -246,7 +253,8 @@ class SymbolicReference(object): return self - def set_object(self, object, logmsg=None): # @ReservedAssignment + def set_object(self, object: Union[Commit_ish, 'SymbolicReference', str], logmsg: Union[str, None] = None + ) -> 'SymbolicReference': """Set the object we point to, possibly dereference our symbolic reference first. If the reference does not exist, it will be created @@ -273,10 +281,10 @@ class SymbolicReference(object): # set the commit on our reference return self._get_reference().set_object(object, logmsg) - commit = property(_get_commit, set_commit, doc="Query or set commits directly") - object = property(_get_object, set_object, doc="Return the object our ref currently refers to") + commit = property(_get_commit, set_commit, doc="Query or set commits directly") # type: ignore + object = property(_get_object, set_object, doc="Return the object our ref currently refers to") # type: ignore - def _get_reference(self): + def _get_reference(self) -> 'Reference': """:return: Reference Object we point to :raise TypeError: If this symbolic reference is detached, hence it doesn't point to a reference, but to a commit""" @@ -285,7 +293,8 @@ class SymbolicReference(object): raise TypeError("%s is a detached symbolic reference as it points to %r" % (self, sha)) return self.from_path(self.repo, target_ref_path) - def set_reference(self, ref, logmsg=None): + def set_reference(self, ref: Union[Commit_ish, 'SymbolicReference', str], + logmsg: Union[str, None] = None) -> 'SymbolicReference': """Set ourselves to the given ref. It will stay a symbol if the ref is a Reference. Otherwise an Object, given as Object instance or refspec, is assumed and if valid, will be set which effectively detaches the refererence if it was a purely @@ -326,7 +335,7 @@ class SymbolicReference(object): raise TypeError("Require commit, got %r" % obj) # END verify type - oldbinsha = None + oldbinsha: bytes = b'' if logmsg is not None: try: oldbinsha = self.commit.binsha @@ -355,10 +364,10 @@ class SymbolicReference(object): return self # aliased reference - reference = property(_get_reference, set_reference, doc="Returns the Reference we point to") - ref: Union[Commit_ish] = reference # type: ignore # Union[str, Commit_ish, SymbolicReference] + reference = property(_get_reference, set_reference, doc="Returns the Reference we point to") # type: ignore + ref: Union['Head', 'TagReference', 'RemoteReference', 'Reference'] = reference # type: ignore - def is_valid(self): + def is_valid(self) -> bool: """ :return: True if the reference is valid, hence it can be read and points to @@ -371,7 +380,7 @@ class SymbolicReference(object): return True @property - def is_detached(self): + def is_detached(self) -> bool: """ :return: True if we are a detached reference, hence we point to a specific commit @@ -382,7 +391,7 @@ class SymbolicReference(object): except TypeError: return True - def log(self): + def log(self) -> 'RefLog': """ :return: RefLog for this reference. Its last entry reflects the latest change applied to this reference @@ -391,7 +400,8 @@ class SymbolicReference(object): instead of calling this method repeatedly. It should be considered read-only.""" return RefLog.from_file(RefLog.path(self)) - def log_append(self, oldbinsha, message, newbinsha=None): + def log_append(self, oldbinsha: bytes, message: Union[str, None], + newbinsha: Union[bytes, None] = None) -> 'RefLogEntry': """Append a logentry to the logfile of this ref :param oldbinsha: binary sha this ref used to point to @@ -403,15 +413,19 @@ class SymbolicReference(object): # correct to allow overriding the committer on a per-commit level. # See https://github.com/gitpython-developers/GitPython/pull/146 try: - committer_or_reader = self.commit.committer + committer_or_reader: Union['Actor', 'GitConfigParser'] = self.commit.committer except ValueError: committer_or_reader = self.repo.config_reader() # end handle newly cloned repositories - return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, - (newbinsha is None and self.commit.binsha) or newbinsha, - message) + if newbinsha is None: + newbinsha = self.commit.binsha + + if message is None: + message = '' + + return RefLog.append_entry(committer_or_reader, RefLog.path(self), oldbinsha, newbinsha, message) - def log_entry(self, index): + def log_entry(self, index: int) -> 'RefLogEntry': """:return: RefLogEntry at the given index :param index: python list compatible positive or negative index @@ -421,7 +435,7 @@ class SymbolicReference(object): return RefLog.entry_at(RefLog.path(self), index) @classmethod - def to_full_path(cls, path) -> PathLike: + def to_full_path(cls, path: Union[PathLike, 'SymbolicReference']) -> PathLike: """ :return: string with a full repository-relative path which can be used to initialize a Reference instance, for instance by using ``Reference.from_path``""" @@ -430,12 +444,12 @@ class SymbolicReference(object): full_ref_path = path if not cls._common_path_default: return full_ref_path - if not path.startswith(cls._common_path_default + "/"): + if not str(path).startswith(cls._common_path_default + "/"): full_ref_path = '%s/%s' % (cls._common_path_default, path) return full_ref_path @classmethod - def delete(cls, repo, path): + def delete(cls, repo: 'Repo', path: PathLike) -> None: """Delete the reference at the given path :param repo: @@ -457,8 +471,8 @@ class SymbolicReference(object): new_lines = [] made_change = False dropped_last_line = False - for line in reader: - line = line.decode(defenc) + for line_bytes in reader: + line = line_bytes.decode(defenc) _, _, line_ref = line.partition(' ') line_ref = line_ref.strip() # keep line if it is a comment or if the ref to delete is not @@ -493,7 +507,9 @@ class SymbolicReference(object): # END remove reflog @classmethod - def _create(cls, repo, path, resolve, reference, force, logmsg=None): + def _create(cls: Type[T_References], repo: 'Repo', path: PathLike, resolve: bool, + reference: Union['SymbolicReference', str], force: bool, + logmsg: Union[str, None] = None) -> T_References: """internal method used to create a new symbolic reference. If resolve is False, the reference will be taken as is, creating a proper symbolic reference. Otherwise it will be resolved to the @@ -511,7 +527,7 @@ class SymbolicReference(object): if not force and os.path.isfile(abs_ref_path): target_data = str(target) if isinstance(target, SymbolicReference): - target_data = target.path + target_data = str(target.path) if not resolve: target_data = "ref: " + target_data with open(abs_ref_path, 'rb') as fd: @@ -526,8 +542,9 @@ class SymbolicReference(object): return ref @classmethod - def create(cls, repo: 'Repo', path: PathLike, reference: Union[Commit_ish, str] = 'HEAD', - logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any): + def create(cls: Type[T_References], repo: 'Repo', path: PathLike, + reference: Union['SymbolicReference', str] = 'HEAD', + logmsg: Union[str, None] = None, force: bool = False, **kwargs: Any) -> T_References: """Create a new symbolic reference, hence a reference pointing , to another reference. :param repo: @@ -689,6 +706,6 @@ class SymbolicReference(object): # END for each type to try raise ValueError("Could not find reference type suitable to handle path %r" % path) - def is_remote(self): + def is_remote(self) -> bool: """:return: True if this symbolic reference points to a remote branch""" - return self.path.startswith(self._remote_common_path_default + "/") + return str(self.path).startswith(self._remote_common_path_default + "/") diff --git a/git/repo/base.py b/git/repo/base.py index 355f9399..5581233b 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -412,13 +412,14 @@ class Repo(object): return TagReference(self, full_path) @staticmethod - def _to_full_tag_path(path): - if path.startswith(TagReference._common_path_default + '/'): - return path - if path.startswith(TagReference._common_default + '/'): - return Reference._common_path_default + '/' + path + def _to_full_tag_path(path: PathLike) -> str: + path_str = str(path) + if path_str.startswith(TagReference._common_path_default + '/'): + return path_str + if path_str.startswith(TagReference._common_default + '/'): + return Reference._common_path_default + '/' + path_str else: - return TagReference._common_path_default + '/' + path + return TagReference._common_path_default + '/' + path_str def create_head(self, path: PathLike, commit: str = 'HEAD', force: bool = False, logmsg: Optional[str] = None diff --git a/git/util.py b/git/util.py index c0c0ecb7..92d95379 100644 --- a/git/util.py +++ b/git/util.py @@ -408,7 +408,7 @@ def expand_path(p: Union[None, PathLike], expand_vars: bool = True) -> Optional[ return None -def remove_password_if_present(cmdline): +def remove_password_if_present(cmdline: Sequence[str]) -> List[str]: """ Parse any command line argument and if on of the element is an URL with a password, replace it by stars (in-place). @@ -1033,7 +1033,7 @@ class IterableList(List[T_IterableObj]): class IterableClassWatcher(type): """ Metaclass that watches """ - def __init__(cls, name, bases, clsdict): + def __init__(cls, name: str, bases: List, clsdict: Dict) -> None: for base in bases: if type(base) == IterableClassWatcher: warnings.warn(f"GitPython Iterable subclassed by {name}. " @@ -1052,7 +1052,7 @@ class Iterable(metaclass=IterableClassWatcher): _id_attribute_ = "attribute that most suitably identifies your instance" @classmethod - def list_items(cls, repo, *args, **kwargs): + def list_items(cls, repo: 'Repo', *args: Any, **kwargs: Any) -> Any: """ Deprecated, use IterableObj instead. Find all items of this type - subclasses can specify args and kwargs differently. @@ -1062,7 +1062,7 @@ class Iterable(metaclass=IterableClassWatcher): :note: Favor the iter_items method as it will :return:list(Item,...) list of item instances""" - out_list = IterableList(cls._id_attribute_) + out_list: Any = IterableList(cls._id_attribute_) out_list.extend(cls.iter_items(repo, *args, **kwargs)) return out_list diff --git a/pyproject.toml b/pyproject.toml index 6437a719..4751ffcb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ filterwarnings = 'ignore::DeprecationWarning' # filterwarnings ignore::WarningType # ignores those warnings [tool.mypy] -# disallow_untyped_defs = true +disallow_untyped_defs = true no_implicit_optional = true warn_redundant_casts = true # warn_unused_ignores = True |