From 16f0607ed29f20c09e89f2cacc0e28e982309d60 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 12:31:00 +0100 Subject: Improve typing of config_levels, add assert_never() --- git/objects/submodule/base.py | 27 +++++++++++++++------------ git/objects/submodule/util.py | 9 ++++++++- 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index c95b66f2..6824528d 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -7,6 +7,8 @@ import stat from unittest import SkipTest import uuid +from git import IndexFile + import git from git.cmd import Git from git.compat import ( @@ -49,7 +51,7 @@ from .util import ( # typing ---------------------------------------------------------------------- -from typing import Dict, TYPE_CHECKING +from typing import Callable, Dict, TYPE_CHECKING from typing import Any, Iterator, Union from git.types import Commit_ish, PathLike @@ -131,14 +133,14 @@ class Submodule(IndexObject, TraversableIterableObj): if url is not None: self._url = url if branch_path is not None: - assert isinstance(branch_path, str) + # assert isinstance(branch_path, str) self._branch_path = branch_path if name is not None: self._name = name def _set_cache_(self, attr: str) -> None: if attr in ('path', '_url', '_branch_path'): - reader = self.config_reader() + reader: SectionConstraint = self.config_reader() # default submodule values try: self.path = reader.get('path') @@ -807,7 +809,8 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def remove(self, module=True, force=False, configuration=True, dry_run=False): + def remove(self, module: bool = True, force: bool = False, + configuration: bool = True, dry_run: bool = False) -> 'Submodule': """Remove this submodule from the repository. This will remove our entry from the .gitmodules file and the entry in the .git / config file. @@ -861,7 +864,7 @@ class Submodule(IndexObject, TraversableIterableObj): # TODO: If we run into permission problems, we have a highly inconsistent # state. Delete the .git folders last, start with the submodules first mp = self.abspath - method = None + method: Union[None, Callable[[PathLike], None]] = None if osp.islink(mp): method = os.remove elif osp.isdir(mp): @@ -928,7 +931,7 @@ class Submodule(IndexObject, TraversableIterableObj): rmtree(git_dir) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: - raise SkipTest("FIXME: fails with: PermissionError\n %s", ex) from ex + raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex else: raise # end handle separate bare repository @@ -961,7 +964,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self - def set_parent_commit(self, commit: Union[Commit_ish, None], check=True): + def set_parent_commit(self, commit: Union[Commit_ish, None], check: bool = True) -> 'Submodule': """Set this instance to use the given commit whose tree is supposed to contain the .gitmodules blob. @@ -1009,7 +1012,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def config_writer(self, index=None, write=True): + def config_writer(self, index: Union[IndexFile, None] = None, write: bool = True) -> SectionConstraint: """:return: a config writer instance allowing you to read and write the data belonging to this submodule into the .gitmodules file. @@ -1030,7 +1033,7 @@ class Submodule(IndexObject, TraversableIterableObj): return writer @unbare_repo - def rename(self, new_name): + def rename(self, new_name: str) -> 'Submodule': """Rename this submodule :note: This method takes care of renaming the submodule in various places, such as @@ -1081,7 +1084,7 @@ class Submodule(IndexObject, TraversableIterableObj): #{ Query Interface @unbare_repo - def module(self): + def module(self) -> 'Repo': """:return: Repo instance initialized from the repository at our submodule path :raise InvalidGitRepositoryError: if a repository was not available. This could also mean that it was not yet initialized""" @@ -1098,7 +1101,7 @@ class Submodule(IndexObject, TraversableIterableObj): raise InvalidGitRepositoryError("Repository at %r was not yet checked out" % module_checkout_abspath) # END handle exceptions - def module_exists(self): + def module_exists(self) -> bool: """:return: True if our module exists and is a valid git repository. See module() method""" try: self.module() @@ -1107,7 +1110,7 @@ class Submodule(IndexObject, TraversableIterableObj): return False # END handle exception - def exists(self): + def exists(self) -> bool: """ :return: True if the submodule exists, False otherwise. Please note that a submodule may exist ( in the .gitmodules file) even though its module diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 5290000b..1db473df 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -5,11 +5,18 @@ from io import BytesIO import weakref +# typing ----------------------------------------------------------------------- + from typing import Any, TYPE_CHECKING, Union +from git.types import PathLike + if TYPE_CHECKING: from .base import Submodule from weakref import ReferenceType + from git.repo import Repo + from git.refs import Head + __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', 'SubmoduleConfigParser') @@ -28,7 +35,7 @@ def sm_name(section): return section[11:-1] -def mkhead(repo, path): +def mkhead(repo: 'Repo', path: PathLike) -> 'Head': """:return: New branch/head instance""" return git.Head(repo, git.Head.to_full_path(path)) -- cgit v1.2.1 From 9400246e9255cc8aef83fe950cf200724790d431 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 13:55:48 +0100 Subject: Fix IndexFile forwardref --- git/objects/submodule/base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 6824528d..25f88b37 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -7,8 +7,6 @@ import stat from unittest import SkipTest import uuid -from git import IndexFile - import git from git.cmd import Git from git.compat import ( @@ -58,6 +56,7 @@ from git.types import Commit_ish, PathLike if TYPE_CHECKING: from git.repo import Repo + from git.index import IndexFile # ----------------------------------------------------------------------------- @@ -1012,7 +1011,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def config_writer(self, index: Union[IndexFile, None] = None, write: bool = True) -> SectionConstraint: + def config_writer(self, index: Union['IndexFile', None] = None, write: bool = True) -> SectionConstraint: """:return: a config writer instance allowing you to read and write the data belonging to this submodule into the .gitmodules file. -- cgit v1.2.1 From 23b5d6b434551e1df1c954ab5d2c0166f080fba8 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 15:42:46 +0100 Subject: Add types to submodule.util.py --- git/objects/submodule/util.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 1db473df..cde18d08 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -7,7 +7,7 @@ import weakref # typing ----------------------------------------------------------------------- -from typing import Any, TYPE_CHECKING, Union +from typing import Any, Sequence, TYPE_CHECKING, Union from git.types import PathLike @@ -16,6 +16,8 @@ if TYPE_CHECKING: from weakref import ReferenceType from git.repo import Repo from git.refs import Head + from git import Remote + from git.refs import RemoteReference __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', @@ -24,12 +26,12 @@ __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', #{ Utilities -def sm_section(name): +def sm_section(name: str) -> str: """:return: section title used in .gitmodules configuration file""" - return 'submodule "%s"' % name + return f'submodule {name}' -def sm_name(section): +def sm_name(section: str) -> str: """:return: name of the submodule as parsed from the section name""" section = section.strip() return section[11:-1] @@ -40,7 +42,7 @@ def mkhead(repo: 'Repo', path: PathLike) -> 'Head': return git.Head(repo, git.Head.to_full_path(path)) -def find_first_remote_branch(remotes, branch_name): +def find_first_remote_branch(remotes: Sequence['Remote'], branch_name: str) -> 'RemoteReference': """Find the remote branch matching the name of the given branch or raise InvalidGitRepositoryError""" for remote in remotes: try: @@ -99,7 +101,7 @@ class SubmoduleConfigParser(GitConfigParser): #{ Overridden Methods def write(self) -> None: - rval = super(SubmoduleConfigParser, self).write() + rval: None = super(SubmoduleConfigParser, self).write() self.flush_to_index() return rval # END overridden methods -- cgit v1.2.1 From c2317a768f4d6b72b9c20d4fbe455af8a0d77c36 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 18:47:44 +0100 Subject: Make bytesIO forwardref --- git/objects/submodule/base.py | 2 +- git/objects/submodule/root.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 25f88b37..7cd4356e 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -392,7 +392,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[path] + sm = repo.head.commit.tree[str(path)] sm._name = name return sm except KeyError: diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index 0af48710..c6746ad8 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -10,6 +10,15 @@ import git import logging +# typing ------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from git.repo import Repo + +# ---------------------------------------------------------------------------- + __all__ = ["RootModule", "RootUpdateProgress"] log = logging.getLogger('git.objects.submodule.root') @@ -42,7 +51,7 @@ class RootModule(Submodule): k_root_name = '__ROOT__' - def __init__(self, repo): + def __init__(self, repo: 'Repo'): # repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None) super(RootModule, self).__init__( repo, @@ -55,7 +64,7 @@ class RootModule(Submodule): branch_path=git.Head.to_full_path(self.k_head_default) ) - def _clear_cache(self): + def _clear_cache(self) -> None: """May not do anything""" pass @@ -343,7 +352,7 @@ class RootModule(Submodule): return self - def module(self): + def module(self) -> 'Repo': """:return: the actual repository containing the submodules""" return self.repo #} END interface -- cgit v1.2.1 From a9351347d704db02bd3d1103e9715ff6a999e3f9 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 19:10:21 +0100 Subject: Add types to submodule.root.py --- git/objects/submodule/root.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index c6746ad8..bcac5419 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -12,10 +12,13 @@ import logging # typing ------------------------------------------------------------------- -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union + +from git.types import Commit_ish if TYPE_CHECKING: from git.repo import Repo + from git.util import IterableList # ---------------------------------------------------------------------------- @@ -70,9 +73,11 @@ class RootModule(Submodule): #{ Interface - def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, - to_latest_revision=False, progress=None, dry_run=False, force_reset=False, - keep_going=False): + def update(self, previous_commit: Union[Commit_ish, None] = None, # type: ignore[override] + recursive: bool = True, force_remove: bool = False, init: bool = True, + to_latest_revision: bool = False, progress: Union[None, 'RootUpdateProgress'] = None, + dry_run: bool = False, force_reset: bool = False, keep_going: bool = False + ) -> 'RootModule': """Update the submodules of this repository to the current HEAD commit. This method behaves smartly by determining changes of the path of a submodules repository, next to changes to the to-be-checked-out commit or the branch to be @@ -137,8 +142,8 @@ class RootModule(Submodule): previous_commit = repo.commit(previous_commit) # obtain commit object # END handle previous commit - psms = self.list_items(repo, parent_commit=previous_commit) - sms = self.list_items(repo) + psms: 'IterableList[Submodule]' = self.list_items(repo, parent_commit=previous_commit) + sms: 'IterableList[Submodule]' = self.list_items(repo) spsms = set(psms) ssms = set(sms) @@ -171,8 +176,8 @@ class RootModule(Submodule): csms = (spsms & ssms) len_csms = len(csms) for i, csm in enumerate(csms): - psm = psms[csm.name] - sm = sms[csm.name] + psm: 'Submodule' = psms[csm.name] + sm: 'Submodule' = sms[csm.name] # PATH CHANGES ############## -- cgit v1.2.1 From 3ce319f1296a5402079e9280500e96cc1d12fd04 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 14:06:59 +0100 Subject: Add types to submodule.update() --- git/objects/submodule/base.py | 58 ++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 23 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 7cd4356e..c975c0f5 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -49,10 +49,10 @@ from .util import ( # typing ---------------------------------------------------------------------- -from typing import Callable, Dict, TYPE_CHECKING +from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING from typing import Any, Iterator, Union -from git.types import Commit_ish, PathLike +from git.types import Commit_ish, PathLike, TBD if TYPE_CHECKING: from git.repo import Repo @@ -227,7 +227,7 @@ class Submodule(IndexObject, TraversableIterableObj): return SubmoduleConfigParser(fp_module, read_only=read_only) - def _clear_cache(self): + def _clear_cache(self) -> None: # clear the possibly changed values for name in self._cache_attrs: try: @@ -247,7 +247,7 @@ class Submodule(IndexObject, TraversableIterableObj): def _config_parser_constrained(self, read_only: bool) -> SectionConstraint: """:return: Config Parser constrained to our submodule in read or write mode""" try: - pc = self.parent_commit + pc: Union['Commit_ish', None] = self.parent_commit except ValueError: pc = None # end handle empty parent repository @@ -256,10 +256,12 @@ class Submodule(IndexObject, TraversableIterableObj): return SectionConstraint(parser, sm_section(self.name)) @classmethod - def _module_abspath(cls, parent_repo, path, name): + def _module_abspath(cls, parent_repo: 'Repo', path: PathLike, name: str) -> PathLike: if cls._need_gitfile_submodules(parent_repo.git): return osp.join(parent_repo.git_dir, 'modules', name) - return osp.join(parent_repo.working_tree_dir, path) + if parent_repo.working_tree_dir: + return osp.join(parent_repo.working_tree_dir, path) + raise NotADirectoryError() # end @classmethod @@ -287,7 +289,7 @@ class Submodule(IndexObject, TraversableIterableObj): return clone @classmethod - def _to_relative_path(cls, parent_repo, path): + def _to_relative_path(cls, parent_repo: 'Repo', path: PathLike) -> PathLike: """:return: a path guaranteed to be relative to the given parent - repository :raise ValueError: if path is not contained in the parent repository's working tree""" path = to_native_path_linux(path) @@ -295,7 +297,7 @@ class Submodule(IndexObject, TraversableIterableObj): path = path[:-1] # END handle trailing slash - if osp.isabs(path): + if osp.isabs(path) and parent_repo.working_tree_dir: working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir) if not path.startswith(working_tree_linux): raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'" @@ -309,7 +311,7 @@ class Submodule(IndexObject, TraversableIterableObj): return path @classmethod - def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath): + def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None: """Writes a .git file containing a(preferably) relative path to the actual git module repository. It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir :note: will overwrite existing files ! @@ -336,7 +338,8 @@ class Submodule(IndexObject, TraversableIterableObj): @classmethod def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, - branch=None, no_checkout: bool = False, depth=None, env=None, clone_multi_options=None + branch: Union[str, None] = None, no_checkout: bool = False, depth: Union[int, None] = None, + env: Mapping[str, str] = None, clone_multi_options: Union[Sequence[TBD], None] = None ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. @@ -415,7 +418,8 @@ class Submodule(IndexObject, TraversableIterableObj): # END check url # END verify urls match - mrepo = None + # mrepo: Union[Repo, None] = None + if url is None: if not has_module: raise ValueError("A URL was not given and a repository did not exist at %s" % path) @@ -428,7 +432,7 @@ class Submodule(IndexObject, TraversableIterableObj): url = urls[0] else: # clone new repo - kwargs: Dict[str, Union[bool, int]] = {'n': no_checkout} + kwargs: Dict[str, Union[bool, int, Sequence[TBD]]] = {'n': no_checkout} if not branch_is_default: kwargs['b'] = br.name # END setup checkout-branch @@ -452,6 +456,8 @@ class Submodule(IndexObject, TraversableIterableObj): # otherwise there is a '-' character in front of the submodule listing # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8) # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one + writer: Union[GitConfigParser, SectionConstraint] + with sm.repo.config_writer() as writer: writer.set_value(sm_section(name), 'url', url) @@ -473,8 +479,10 @@ class Submodule(IndexObject, TraversableIterableObj): return sm - def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False, - force=False, keep_going=False, env=None, clone_multi_options=None): + 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: Mapping[str, str] = None, + clone_multi_options: Union[Sequence[TBD], None] = None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -581,6 +589,7 @@ class Submodule(IndexObject, TraversableIterableObj): if not dry_run: # see whether we have a valid branch to checkout try: + assert isinstance(mrepo, Repo) # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) @@ -641,7 +650,7 @@ class Submodule(IndexObject, TraversableIterableObj): may_reset = True if mrepo.head.commit.binsha != self.NULL_BIN_SHA: base_commit = mrepo.merge_base(mrepo.head.commit, hexsha) - if len(base_commit) == 0 or base_commit[0].hexsha == hexsha: + if len(base_commit) == 0 or (base_commit[0] is not None and base_commit[0].hexsha == hexsha): if force: msg = "Will force checkout or reset on local branch that is possibly in the future of" msg += "the commit it will be checked out to, effectively 'forgetting' new commits" @@ -916,7 +925,7 @@ class Submodule(IndexObject, TraversableIterableObj): import gc gc.collect() try: - rmtree(wtd) + rmtree(str(wtd)) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex @@ -954,6 +963,8 @@ class Submodule(IndexObject, TraversableIterableObj): # now git config - need the config intact, otherwise we can't query # information anymore + writer: Union[GitConfigParser, SectionConstraint] + with self.repo.config_writer() as writer: writer.remove_section(sm_section(self.name)) @@ -1067,13 +1078,14 @@ class Submodule(IndexObject, TraversableIterableObj): destination_module_abspath = self._module_abspath(self.repo, self.path, new_name) source_dir = mod.git_dir # Let's be sure the submodule name is not so obviously tied to a directory - if destination_module_abspath.startswith(mod.git_dir): + if str(destination_module_abspath).startswith(str(mod.git_dir)): tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) os.renames(source_dir, tmp_dir) source_dir = tmp_dir # end handle self-containment os.renames(source_dir, destination_module_abspath) - self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) + if mod.working_tree_dir: + self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) # end move separate git repository return self @@ -1150,26 +1162,26 @@ class Submodule(IndexObject, TraversableIterableObj): return mkhead(self.module(), self._branch_path) @property - def branch_path(self): + def branch_path(self) -> PathLike: """ :return: full(relative) path as string to the branch we would checkout from the remote and track""" return self._branch_path @property - def branch_name(self): + def branch_name(self) -> str: """:return: the name of the branch, which is the shortest possible branch name""" # use an instance method, for this we create a temporary Head instance # which uses a repository that is available at least ( it makes no difference ) return git.Head(self.repo, self._branch_path).name @property - def url(self): + def url(self) -> str: """:return: The url to the repository which our module - repository refers to""" return self._url @property - def parent_commit(self): + def parent_commit(self) -> 'Commit_ish': """:return: Commit instance with the tree containing the .gitmodules file :note: will always point to the current head's commit if it was not set explicitly""" if self._parent_commit is None: @@ -1177,7 +1189,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self._parent_commit @property - def name(self): + def name(self) -> str: """:return: The name of this submodule. It is used to identify it within the .gitmodules file. :note: by default, the name is the path at which to find the submodule, but -- cgit v1.2.1 From 2a6a2e2e44b6220f4cbc7d1672e0cfb1c13926c2 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 14:45:30 +0100 Subject: Improve types of diff.py --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index c975c0f5..401ef54e 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -47,6 +47,7 @@ from .util import ( find_first_remote_branch ) +from git.repo import Repo # typing ---------------------------------------------------------------------- from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING @@ -55,7 +56,6 @@ from typing import Any, Iterator, Union from git.types import Commit_ish, PathLike, TBD if TYPE_CHECKING: - from git.repo import Repo from git.index import IndexFile -- cgit v1.2.1 From 35783557c418921641be47f95e12c80d50b20d22 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 14:55:44 +0100 Subject: Add cast(Repo, mrepo) in try block --- git/objects/submodule/base.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 401ef54e..2ace1f03 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -47,15 +47,15 @@ from .util import ( find_first_remote_branch ) -from git.repo import Repo # typing ---------------------------------------------------------------------- -from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING +from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING, cast from typing import Any, Iterator, Union from git.types import Commit_ish, PathLike, TBD if TYPE_CHECKING: + from git.repo import Repo from git.index import IndexFile @@ -589,7 +589,8 @@ class Submodule(IndexObject, TraversableIterableObj): if not dry_run: # see whether we have a valid branch to checkout try: - assert isinstance(mrepo, Repo) + # assert isinstance(mrepo, Repo) # cant do this cos of circular import + mrepo = cast('Repo', mrepo) # Try TypeGuard wirh hasattr? # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) -- cgit v1.2.1 From 278a3713a0a560cbc5b1515c86f8852cd3119e6b Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:09:36 +0100 Subject: Fix for mrepo --- git/objects/submodule/base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 2ace1f03..53e89dba 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -418,7 +418,7 @@ class Submodule(IndexObject, TraversableIterableObj): # END check url # END verify urls match - # mrepo: Union[Repo, None] = None + mrepo: Union[Repo, None] = None if url is None: if not has_module: @@ -474,7 +474,8 @@ class Submodule(IndexObject, TraversableIterableObj): sm._branch_path = br.path # we deliberately assume that our head matches our index ! - sm.binsha = mrepo.head.commit.binsha + if mrepo is not None: + sm.binsha = mrepo.head.commit.binsha index.add([sm], write=True) return sm @@ -589,8 +590,8 @@ class Submodule(IndexObject, TraversableIterableObj): if not dry_run: # see whether we have a valid branch to checkout try: - # assert isinstance(mrepo, Repo) # cant do this cos of circular import - mrepo = cast('Repo', mrepo) # Try TypeGuard wirh hasattr? + # assert isinstance(mrepo, Repo) # cant do this cos of circular import + mrepo = cast('Repo', mrepo) # Try TypeGuard wirh hasattr, or has_remotes&_head protocol? # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) -- cgit v1.2.1 From deafa6a0ab837dffb8f78177f7c22d21ac65bf62 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:40:06 +0100 Subject: Fix for mrepo2 --- git/objects/submodule/base.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 53e89dba..3df2b41a 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -474,8 +474,8 @@ class Submodule(IndexObject, TraversableIterableObj): sm._branch_path = br.path # we deliberately assume that our head matches our index ! - if mrepo is not None: - sm.binsha = mrepo.head.commit.binsha + mrepo = cast('Repo', mrepo) + sm.binsha = mrepo.head.commit.binsha index.add([sm], write=True) return sm @@ -652,7 +652,7 @@ class Submodule(IndexObject, TraversableIterableObj): may_reset = True if mrepo.head.commit.binsha != self.NULL_BIN_SHA: base_commit = mrepo.merge_base(mrepo.head.commit, hexsha) - if len(base_commit) == 0 or (base_commit[0] is not None and base_commit[0].hexsha == hexsha): + if len(base_commit) == 0 or base_commit[0].hexsha == hexsha: # type: ignore if force: msg = "Will force checkout or reset on local branch that is possibly in the future of" msg += "the commit it will be checked out to, effectively 'forgetting' new commits" @@ -927,7 +927,7 @@ class Submodule(IndexObject, TraversableIterableObj): import gc gc.collect() try: - rmtree(str(wtd)) + rmtree(wtd) # type: ignore ## str()? except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex @@ -1015,7 +1015,7 @@ class Submodule(IndexObject, TraversableIterableObj): # If check is False, we might see a parent-commit that doesn't even contain the submodule anymore. # in that case, mark our sha as being NULL try: - self.binsha = pctree[str(self.path)].binsha + self.binsha = pctree[self.path].binsha # type: ignore # str()? except KeyError: self.binsha = self.NULL_BIN_SHA # end @@ -1080,7 +1080,7 @@ class Submodule(IndexObject, TraversableIterableObj): destination_module_abspath = self._module_abspath(self.repo, self.path, new_name) source_dir = mod.git_dir # Let's be sure the submodule name is not so obviously tied to a directory - if str(destination_module_abspath).startswith(str(mod.git_dir)): + if destination_module_abspath.startswith(str(mod.git_dir)): # type: ignore # str()? tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) os.renames(source_dir, tmp_dir) source_dir = tmp_dir -- cgit v1.2.1 From e6f340cf8617ceb99f6da5f3db902a69308cdec7 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:42:02 +0100 Subject: Rmv runtime_checkable < py3.8 --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 3df2b41a..514fcfea 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -927,7 +927,7 @@ class Submodule(IndexObject, TraversableIterableObj): import gc gc.collect() try: - rmtree(wtd) # type: ignore ## str()? + rmtree(str(wtd)) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex -- cgit v1.2.1 From ed58e2f840749bb4dabd384b812ecb259dc60304 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:43:56 +0100 Subject: Rmv runtime_checkable < py3.8 pt2 --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 514fcfea..ca408338 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -142,7 +142,7 @@ class Submodule(IndexObject, TraversableIterableObj): reader: SectionConstraint = self.config_reader() # default submodule values try: - self.path = reader.get('path') + self.path: PathLike = reader.get('path') except cp.NoSectionError as e: if self.repo.working_tree_dir is not None: raise ValueError("This submodule instance does not exist anymore in '%s' file" -- cgit v1.2.1 From eecf1486cf445e2f23585b1bb65097dfebbc9545 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:50:37 +0100 Subject: Rmv root.py types --- git/objects/submodule/base.py | 4 ++-- git/objects/submodule/root.py | 34 ++++++++++------------------------ 2 files changed, 12 insertions(+), 26 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index ca408338..76769cad 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -1015,7 +1015,7 @@ class Submodule(IndexObject, TraversableIterableObj): # If check is False, we might see a parent-commit that doesn't even contain the submodule anymore. # in that case, mark our sha as being NULL try: - self.binsha = pctree[self.path].binsha # type: ignore # str()? + self.binsha = pctree[str(self.path)].binsha except KeyError: self.binsha = self.NULL_BIN_SHA # end @@ -1080,7 +1080,7 @@ class Submodule(IndexObject, TraversableIterableObj): destination_module_abspath = self._module_abspath(self.repo, self.path, new_name) source_dir = mod.git_dir # Let's be sure the submodule name is not so obviously tied to a directory - if destination_module_abspath.startswith(str(mod.git_dir)): # type: ignore # str()? + if str(destination_module_abspath).startswith(str(mod.git_dir)): tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) os.renames(source_dir, tmp_dir) source_dir = tmp_dir diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index bcac5419..0af48710 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -10,18 +10,6 @@ import git import logging -# typing ------------------------------------------------------------------- - -from typing import TYPE_CHECKING, Union - -from git.types import Commit_ish - -if TYPE_CHECKING: - from git.repo import Repo - from git.util import IterableList - -# ---------------------------------------------------------------------------- - __all__ = ["RootModule", "RootUpdateProgress"] log = logging.getLogger('git.objects.submodule.root') @@ -54,7 +42,7 @@ class RootModule(Submodule): k_root_name = '__ROOT__' - def __init__(self, repo: 'Repo'): + def __init__(self, repo): # repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None) super(RootModule, self).__init__( repo, @@ -67,17 +55,15 @@ class RootModule(Submodule): branch_path=git.Head.to_full_path(self.k_head_default) ) - def _clear_cache(self) -> None: + def _clear_cache(self): """May not do anything""" pass #{ Interface - def update(self, previous_commit: Union[Commit_ish, None] = None, # type: ignore[override] - recursive: bool = True, force_remove: bool = False, init: bool = True, - to_latest_revision: bool = False, progress: Union[None, 'RootUpdateProgress'] = None, - dry_run: bool = False, force_reset: bool = False, keep_going: bool = False - ) -> 'RootModule': + def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, + to_latest_revision=False, progress=None, dry_run=False, force_reset=False, + keep_going=False): """Update the submodules of this repository to the current HEAD commit. This method behaves smartly by determining changes of the path of a submodules repository, next to changes to the to-be-checked-out commit or the branch to be @@ -142,8 +128,8 @@ class RootModule(Submodule): previous_commit = repo.commit(previous_commit) # obtain commit object # END handle previous commit - psms: 'IterableList[Submodule]' = self.list_items(repo, parent_commit=previous_commit) - sms: 'IterableList[Submodule]' = self.list_items(repo) + psms = self.list_items(repo, parent_commit=previous_commit) + sms = self.list_items(repo) spsms = set(psms) ssms = set(sms) @@ -176,8 +162,8 @@ class RootModule(Submodule): csms = (spsms & ssms) len_csms = len(csms) for i, csm in enumerate(csms): - psm: 'Submodule' = psms[csm.name] - sm: 'Submodule' = sms[csm.name] + psm = psms[csm.name] + sm = sms[csm.name] # PATH CHANGES ############## @@ -357,7 +343,7 @@ class RootModule(Submodule): return self - def module(self) -> 'Repo': + def module(self): """:return: the actual repository containing the submodules""" return self.repo #} END interface -- cgit v1.2.1 From b78cca1854e24de3558b43880586dbf9632831ad Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 15:56:23 +0100 Subject: Rmv base.py types --- git/objects/submodule/base.py | 101 ++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 62 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 76769cad..960ff045 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -49,14 +49,13 @@ from .util import ( # typing ---------------------------------------------------------------------- -from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING, cast +from typing import Dict, TYPE_CHECKING from typing import Any, Iterator, Union -from git.types import Commit_ish, PathLike, TBD +from git.types import Commit_ish, PathLike if TYPE_CHECKING: from git.repo import Repo - from git.index import IndexFile # ----------------------------------------------------------------------------- @@ -132,17 +131,17 @@ class Submodule(IndexObject, TraversableIterableObj): if url is not None: self._url = url if branch_path is not None: - # assert isinstance(branch_path, str) + assert isinstance(branch_path, str) self._branch_path = branch_path if name is not None: self._name = name def _set_cache_(self, attr: str) -> None: if attr in ('path', '_url', '_branch_path'): - reader: SectionConstraint = self.config_reader() + reader = self.config_reader() # default submodule values try: - self.path: PathLike = reader.get('path') + self.path = reader.get('path') except cp.NoSectionError as e: if self.repo.working_tree_dir is not None: raise ValueError("This submodule instance does not exist anymore in '%s' file" @@ -227,7 +226,7 @@ class Submodule(IndexObject, TraversableIterableObj): return SubmoduleConfigParser(fp_module, read_only=read_only) - def _clear_cache(self) -> None: + def _clear_cache(self): # clear the possibly changed values for name in self._cache_attrs: try: @@ -247,7 +246,7 @@ class Submodule(IndexObject, TraversableIterableObj): def _config_parser_constrained(self, read_only: bool) -> SectionConstraint: """:return: Config Parser constrained to our submodule in read or write mode""" try: - pc: Union['Commit_ish', None] = self.parent_commit + pc = self.parent_commit except ValueError: pc = None # end handle empty parent repository @@ -256,12 +255,10 @@ class Submodule(IndexObject, TraversableIterableObj): return SectionConstraint(parser, sm_section(self.name)) @classmethod - def _module_abspath(cls, parent_repo: 'Repo', path: PathLike, name: str) -> PathLike: + def _module_abspath(cls, parent_repo, path, name): if cls._need_gitfile_submodules(parent_repo.git): return osp.join(parent_repo.git_dir, 'modules', name) - if parent_repo.working_tree_dir: - return osp.join(parent_repo.working_tree_dir, path) - raise NotADirectoryError() + return osp.join(parent_repo.working_tree_dir, path) # end @classmethod @@ -289,7 +286,7 @@ class Submodule(IndexObject, TraversableIterableObj): return clone @classmethod - def _to_relative_path(cls, parent_repo: 'Repo', path: PathLike) -> PathLike: + def _to_relative_path(cls, parent_repo, path): """:return: a path guaranteed to be relative to the given parent - repository :raise ValueError: if path is not contained in the parent repository's working tree""" path = to_native_path_linux(path) @@ -297,7 +294,7 @@ class Submodule(IndexObject, TraversableIterableObj): path = path[:-1] # END handle trailing slash - if osp.isabs(path) and parent_repo.working_tree_dir: + if osp.isabs(path): working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir) if not path.startswith(working_tree_linux): raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'" @@ -311,7 +308,7 @@ class Submodule(IndexObject, TraversableIterableObj): return path @classmethod - def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None: + def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath): """Writes a .git file containing a(preferably) relative path to the actual git module repository. It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir :note: will overwrite existing files ! @@ -338,8 +335,7 @@ class Submodule(IndexObject, TraversableIterableObj): @classmethod def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, - branch: Union[str, None] = None, no_checkout: bool = False, depth: Union[int, None] = None, - env: Mapping[str, str] = None, clone_multi_options: Union[Sequence[TBD], None] = None + branch=None, no_checkout: bool = False, depth=None, env=None ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. @@ -373,8 +369,6 @@ class Submodule(IndexObject, TraversableIterableObj): and is defined in `os.environ`, value from `os.environ` will be used. If you want to unset some variable, consider providing empty string as its value. - :param clone_multi_options: A list of Clone options. Please see ``git.repo.base.Repo.clone`` - for details. :return: The newly created submodule instance :note: works atomically, such that no change will be done if the repository update fails for instance""" @@ -387,7 +381,7 @@ class Submodule(IndexObject, TraversableIterableObj): # assure we never put backslashes into the url, as some operating systems # like it ... if url is not None: - url = to_native_path_linux(url) + url = to_native_path_linux(url) # to_native_path_linux does nothing?? # END assure url correctness # INSTANTIATE INTERMEDIATE SM @@ -395,7 +389,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[str(path)] + sm = repo.head.commit.tree[path] # type: ignore sm._name = name return sm except KeyError: @@ -418,8 +412,7 @@ class Submodule(IndexObject, TraversableIterableObj): # END check url # END verify urls match - mrepo: Union[Repo, None] = None - + mrepo = None if url is None: if not has_module: raise ValueError("A URL was not given and a repository did not exist at %s" % path) @@ -432,7 +425,7 @@ class Submodule(IndexObject, TraversableIterableObj): url = urls[0] else: # clone new repo - kwargs: Dict[str, Union[bool, int, Sequence[TBD]]] = {'n': no_checkout} + kwargs: Dict[str, Union[bool, int]] = {'n': no_checkout} if not branch_is_default: kwargs['b'] = br.name # END setup checkout-branch @@ -442,8 +435,6 @@ class Submodule(IndexObject, TraversableIterableObj): kwargs['depth'] = depth else: raise ValueError("depth should be an integer") - if clone_multi_options: - kwargs['multi_options'] = clone_multi_options # _clone_repo(cls, repo, url, path, name, **kwargs): mrepo = cls._clone_repo(repo, url, path, name, env=env, **kwargs) @@ -456,8 +447,6 @@ class Submodule(IndexObject, TraversableIterableObj): # otherwise there is a '-' character in front of the submodule listing # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8) # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one - writer: Union[GitConfigParser, SectionConstraint] - with sm.repo.config_writer() as writer: writer.set_value(sm_section(name), 'url', url) @@ -474,16 +463,13 @@ class Submodule(IndexObject, TraversableIterableObj): sm._branch_path = br.path # we deliberately assume that our head matches our index ! - mrepo = cast('Repo', mrepo) sm.binsha = mrepo.head.commit.binsha index.add([sm], write=True) return sm - 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: Mapping[str, str] = None, - clone_multi_options: Union[Sequence[TBD], None] = None): + def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False, + force=False, keep_going=False, env=None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -514,8 +500,6 @@ class Submodule(IndexObject, TraversableIterableObj): and is defined in `os.environ`, value from `os.environ` will be used. If you want to unset some variable, consider providing empty string as its value. - :param clone_multi_options: list of Clone options. Please see ``git.repo.base.Repo.clone`` - for details. Only take effect with `init` option. :note: does nothing in bare repositories :note: method is definitely not atomic if recurisve is True :return: self""" @@ -582,16 +566,13 @@ class Submodule(IndexObject, TraversableIterableObj): progress.update(BEGIN | CLONE, 0, 1, prefix + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name)) if not dry_run: - mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True, env=env, - multi_options=clone_multi_options) + mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True, env=env) # END handle dry-run progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath) if not dry_run: # see whether we have a valid branch to checkout try: - # assert isinstance(mrepo, Repo) # cant do this cos of circular import - mrepo = cast('Repo', mrepo) # Try TypeGuard wirh hasattr, or has_remotes&_head protocol? # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) @@ -652,7 +633,7 @@ class Submodule(IndexObject, TraversableIterableObj): may_reset = True if mrepo.head.commit.binsha != self.NULL_BIN_SHA: base_commit = mrepo.merge_base(mrepo.head.commit, hexsha) - if len(base_commit) == 0 or base_commit[0].hexsha == hexsha: # type: ignore + if len(base_commit) == 0 or base_commit[0].hexsha == hexsha: if force: msg = "Will force checkout or reset on local branch that is possibly in the future of" msg += "the commit it will be checked out to, effectively 'forgetting' new commits" @@ -819,8 +800,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def remove(self, module: bool = True, force: bool = False, - configuration: bool = True, dry_run: bool = False) -> 'Submodule': + def remove(self, module=True, force=False, configuration=True, dry_run=False): """Remove this submodule from the repository. This will remove our entry from the .gitmodules file and the entry in the .git / config file. @@ -874,7 +854,7 @@ class Submodule(IndexObject, TraversableIterableObj): # TODO: If we run into permission problems, we have a highly inconsistent # state. Delete the .git folders last, start with the submodules first mp = self.abspath - method: Union[None, Callable[[PathLike], None]] = None + method = None if osp.islink(mp): method = os.remove elif osp.isdir(mp): @@ -927,7 +907,7 @@ class Submodule(IndexObject, TraversableIterableObj): import gc gc.collect() try: - rmtree(str(wtd)) + rmtree(wtd) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex @@ -941,7 +921,7 @@ class Submodule(IndexObject, TraversableIterableObj): rmtree(git_dir) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: - raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex + raise SkipTest("FIXME: fails with: PermissionError\n %s", ex) from ex else: raise # end handle separate bare repository @@ -965,8 +945,6 @@ class Submodule(IndexObject, TraversableIterableObj): # now git config - need the config intact, otherwise we can't query # information anymore - writer: Union[GitConfigParser, SectionConstraint] - with self.repo.config_writer() as writer: writer.remove_section(sm_section(self.name)) @@ -976,7 +954,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self - def set_parent_commit(self, commit: Union[Commit_ish, None], check: bool = True) -> 'Submodule': + def set_parent_commit(self, commit: Union[Commit_ish, None], check=True): """Set this instance to use the given commit whose tree is supposed to contain the .gitmodules blob. @@ -1015,7 +993,7 @@ class Submodule(IndexObject, TraversableIterableObj): # If check is False, we might see a parent-commit that doesn't even contain the submodule anymore. # in that case, mark our sha as being NULL try: - self.binsha = pctree[str(self.path)].binsha + self.binsha = pctree[self.path].binsha # type: ignore except KeyError: self.binsha = self.NULL_BIN_SHA # end @@ -1024,7 +1002,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def config_writer(self, index: Union['IndexFile', None] = None, write: bool = True) -> SectionConstraint: + def config_writer(self, index=None, write=True): """:return: a config writer instance allowing you to read and write the data belonging to this submodule into the .gitmodules file. @@ -1045,7 +1023,7 @@ class Submodule(IndexObject, TraversableIterableObj): return writer @unbare_repo - def rename(self, new_name: str) -> 'Submodule': + def rename(self, new_name): """Rename this submodule :note: This method takes care of renaming the submodule in various places, such as @@ -1080,14 +1058,13 @@ class Submodule(IndexObject, TraversableIterableObj): destination_module_abspath = self._module_abspath(self.repo, self.path, new_name) source_dir = mod.git_dir # Let's be sure the submodule name is not so obviously tied to a directory - if str(destination_module_abspath).startswith(str(mod.git_dir)): + if destination_module_abspath.startswith(mod.git_dir): tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) os.renames(source_dir, tmp_dir) source_dir = tmp_dir # end handle self-containment os.renames(source_dir, destination_module_abspath) - if mod.working_tree_dir: - self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) + self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) # end move separate git repository return self @@ -1097,7 +1074,7 @@ class Submodule(IndexObject, TraversableIterableObj): #{ Query Interface @unbare_repo - def module(self) -> 'Repo': + def module(self): """:return: Repo instance initialized from the repository at our submodule path :raise InvalidGitRepositoryError: if a repository was not available. This could also mean that it was not yet initialized""" @@ -1114,7 +1091,7 @@ class Submodule(IndexObject, TraversableIterableObj): raise InvalidGitRepositoryError("Repository at %r was not yet checked out" % module_checkout_abspath) # END handle exceptions - def module_exists(self) -> bool: + def module_exists(self): """:return: True if our module exists and is a valid git repository. See module() method""" try: self.module() @@ -1123,7 +1100,7 @@ class Submodule(IndexObject, TraversableIterableObj): return False # END handle exception - def exists(self) -> bool: + def exists(self): """ :return: True if the submodule exists, False otherwise. Please note that a submodule may exist ( in the .gitmodules file) even though its module @@ -1164,26 +1141,26 @@ class Submodule(IndexObject, TraversableIterableObj): return mkhead(self.module(), self._branch_path) @property - def branch_path(self) -> PathLike: + def branch_path(self): """ :return: full(relative) path as string to the branch we would checkout from the remote and track""" return self._branch_path @property - def branch_name(self) -> str: + def branch_name(self): """:return: the name of the branch, which is the shortest possible branch name""" # use an instance method, for this we create a temporary Head instance # which uses a repository that is available at least ( it makes no difference ) return git.Head(self.repo, self._branch_path).name @property - def url(self) -> str: + def url(self): """:return: The url to the repository which our module - repository refers to""" return self._url @property - def parent_commit(self) -> 'Commit_ish': + def parent_commit(self): """:return: Commit instance with the tree containing the .gitmodules file :note: will always point to the current head's commit if it was not set explicitly""" if self._parent_commit is None: @@ -1191,7 +1168,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self._parent_commit @property - def name(self) -> str: + def name(self): """:return: The name of this submodule. It is used to identify it within the .gitmodules file. :note: by default, the name is the path at which to find the submodule, but -- cgit v1.2.1 From 6aebb73bb818d91c275b94b6052d8ce4ddc113c6 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:01:18 +0100 Subject: Rmv submodule types --- git/objects/submodule/base.py | 19 +++++++++++++------ git/objects/submodule/util.py | 23 +++++++---------------- 2 files changed, 20 insertions(+), 22 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 960ff045..c95b66f2 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -335,7 +335,7 @@ class Submodule(IndexObject, TraversableIterableObj): @classmethod def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, - branch=None, no_checkout: bool = False, depth=None, env=None + branch=None, no_checkout: bool = False, depth=None, env=None, clone_multi_options=None ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. @@ -369,6 +369,8 @@ class Submodule(IndexObject, TraversableIterableObj): and is defined in `os.environ`, value from `os.environ` will be used. If you want to unset some variable, consider providing empty string as its value. + :param clone_multi_options: A list of Clone options. Please see ``git.repo.base.Repo.clone`` + for details. :return: The newly created submodule instance :note: works atomically, such that no change will be done if the repository update fails for instance""" @@ -381,7 +383,7 @@ class Submodule(IndexObject, TraversableIterableObj): # assure we never put backslashes into the url, as some operating systems # like it ... if url is not None: - url = to_native_path_linux(url) # to_native_path_linux does nothing?? + url = to_native_path_linux(url) # END assure url correctness # INSTANTIATE INTERMEDIATE SM @@ -389,7 +391,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[path] # type: ignore + sm = repo.head.commit.tree[path] sm._name = name return sm except KeyError: @@ -435,6 +437,8 @@ class Submodule(IndexObject, TraversableIterableObj): kwargs['depth'] = depth else: raise ValueError("depth should be an integer") + if clone_multi_options: + kwargs['multi_options'] = clone_multi_options # _clone_repo(cls, repo, url, path, name, **kwargs): mrepo = cls._clone_repo(repo, url, path, name, env=env, **kwargs) @@ -469,7 +473,7 @@ class Submodule(IndexObject, TraversableIterableObj): return sm def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False, - force=False, keep_going=False, env=None): + force=False, keep_going=False, env=None, clone_multi_options=None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -500,6 +504,8 @@ class Submodule(IndexObject, TraversableIterableObj): and is defined in `os.environ`, value from `os.environ` will be used. If you want to unset some variable, consider providing empty string as its value. + :param clone_multi_options: list of Clone options. Please see ``git.repo.base.Repo.clone`` + for details. Only take effect with `init` option. :note: does nothing in bare repositories :note: method is definitely not atomic if recurisve is True :return: self""" @@ -566,7 +572,8 @@ class Submodule(IndexObject, TraversableIterableObj): progress.update(BEGIN | CLONE, 0, 1, prefix + "Cloning url '%s' to '%s' in submodule %r" % (self.url, checkout_module_abspath, self.name)) if not dry_run: - mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True, env=env) + mrepo = self._clone_repo(self.repo, self.url, self.path, self.name, n=True, env=env, + multi_options=clone_multi_options) # END handle dry-run progress.update(END | CLONE, 0, 1, prefix + "Done cloning to %s" % checkout_module_abspath) @@ -993,7 +1000,7 @@ class Submodule(IndexObject, TraversableIterableObj): # If check is False, we might see a parent-commit that doesn't even contain the submodule anymore. # in that case, mark our sha as being NULL try: - self.binsha = pctree[self.path].binsha # type: ignore + self.binsha = pctree[str(self.path)].binsha except KeyError: self.binsha = self.NULL_BIN_SHA # end diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index cde18d08..5290000b 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -5,20 +5,11 @@ from io import BytesIO import weakref -# typing ----------------------------------------------------------------------- - -from typing import Any, Sequence, TYPE_CHECKING, Union - -from git.types import PathLike +from typing import Any, TYPE_CHECKING, Union if TYPE_CHECKING: from .base import Submodule from weakref import ReferenceType - from git.repo import Repo - from git.refs import Head - from git import Remote - from git.refs import RemoteReference - __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', 'SubmoduleConfigParser') @@ -26,23 +17,23 @@ __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', #{ Utilities -def sm_section(name: str) -> str: +def sm_section(name): """:return: section title used in .gitmodules configuration file""" - return f'submodule {name}' + return 'submodule "%s"' % name -def sm_name(section: str) -> str: +def sm_name(section): """:return: name of the submodule as parsed from the section name""" section = section.strip() return section[11:-1] -def mkhead(repo: 'Repo', path: PathLike) -> 'Head': +def mkhead(repo, path): """:return: New branch/head instance""" return git.Head(repo, git.Head.to_full_path(path)) -def find_first_remote_branch(remotes: Sequence['Remote'], branch_name: str) -> 'RemoteReference': +def find_first_remote_branch(remotes, branch_name): """Find the remote branch matching the name of the given branch or raise InvalidGitRepositoryError""" for remote in remotes: try: @@ -101,7 +92,7 @@ class SubmoduleConfigParser(GitConfigParser): #{ Overridden Methods def write(self) -> None: - rval: None = super(SubmoduleConfigParser, self).write() + rval = super(SubmoduleConfigParser, self).write() self.flush_to_index() return rval # END overridden methods -- cgit v1.2.1 From c0ab23e5d0afce4a85a8af7ec2a360bf6c71c4ac Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:04:06 +0100 Subject: Rmv submodule types2 --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index c95b66f2..499b2b30 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -391,7 +391,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[path] + sm = repo.head.commit.tree[path] # type: ignore sm._name = name return sm except KeyError: -- cgit v1.2.1 From 8d2a7703259967f0438a18b5cbc80ee060e15866 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:16:35 +0100 Subject: Rmv diff typeguard --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 499b2b30..60ff1155 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -391,7 +391,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[path] # type: ignore + sm = repo.head.commit.tree[path] # type: ignore sm._name = name return sm except KeyError: -- cgit v1.2.1 From 1fd9e8c43cadb6459438a9405cd993c005689f53 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:20:52 +0100 Subject: Re-add submodule.util.py types --- git/objects/submodule/util.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 5290000b..cde18d08 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -5,11 +5,20 @@ from io import BytesIO import weakref -from typing import Any, TYPE_CHECKING, Union +# typing ----------------------------------------------------------------------- + +from typing import Any, Sequence, TYPE_CHECKING, Union + +from git.types import PathLike if TYPE_CHECKING: from .base import Submodule from weakref import ReferenceType + from git.repo import Repo + from git.refs import Head + from git import Remote + from git.refs import RemoteReference + __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', 'SubmoduleConfigParser') @@ -17,23 +26,23 @@ __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', #{ Utilities -def sm_section(name): +def sm_section(name: str) -> str: """:return: section title used in .gitmodules configuration file""" - return 'submodule "%s"' % name + return f'submodule {name}' -def sm_name(section): +def sm_name(section: str) -> str: """:return: name of the submodule as parsed from the section name""" section = section.strip() return section[11:-1] -def mkhead(repo, path): +def mkhead(repo: 'Repo', path: PathLike) -> 'Head': """:return: New branch/head instance""" return git.Head(repo, git.Head.to_full_path(path)) -def find_first_remote_branch(remotes, branch_name): +def find_first_remote_branch(remotes: Sequence['Remote'], branch_name: str) -> 'RemoteReference': """Find the remote branch matching the name of the given branch or raise InvalidGitRepositoryError""" for remote in remotes: try: @@ -92,7 +101,7 @@ class SubmoduleConfigParser(GitConfigParser): #{ Overridden Methods def write(self) -> None: - rval = super(SubmoduleConfigParser, self).write() + rval: None = super(SubmoduleConfigParser, self).write() self.flush_to_index() return rval # END overridden methods -- cgit v1.2.1 From 1eceb8938ec98fad3a3aa2b8ffae5be8b7653976 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:29:02 +0100 Subject: Fix submodule.util.py types --- git/objects/submodule/root.py | 34 ++++++++++++++++++++++++---------- git/objects/submodule/util.py | 2 +- 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index 0af48710..bcac5419 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -10,6 +10,18 @@ import git import logging +# typing ------------------------------------------------------------------- + +from typing import TYPE_CHECKING, Union + +from git.types import Commit_ish + +if TYPE_CHECKING: + from git.repo import Repo + from git.util import IterableList + +# ---------------------------------------------------------------------------- + __all__ = ["RootModule", "RootUpdateProgress"] log = logging.getLogger('git.objects.submodule.root') @@ -42,7 +54,7 @@ class RootModule(Submodule): k_root_name = '__ROOT__' - def __init__(self, repo): + def __init__(self, repo: 'Repo'): # repo, binsha, mode=None, path=None, name = None, parent_commit=None, url=None, ref=None) super(RootModule, self).__init__( repo, @@ -55,15 +67,17 @@ class RootModule(Submodule): branch_path=git.Head.to_full_path(self.k_head_default) ) - def _clear_cache(self): + def _clear_cache(self) -> None: """May not do anything""" pass #{ Interface - def update(self, previous_commit=None, recursive=True, force_remove=False, init=True, - to_latest_revision=False, progress=None, dry_run=False, force_reset=False, - keep_going=False): + def update(self, previous_commit: Union[Commit_ish, None] = None, # type: ignore[override] + recursive: bool = True, force_remove: bool = False, init: bool = True, + to_latest_revision: bool = False, progress: Union[None, 'RootUpdateProgress'] = None, + dry_run: bool = False, force_reset: bool = False, keep_going: bool = False + ) -> 'RootModule': """Update the submodules of this repository to the current HEAD commit. This method behaves smartly by determining changes of the path of a submodules repository, next to changes to the to-be-checked-out commit or the branch to be @@ -128,8 +142,8 @@ class RootModule(Submodule): previous_commit = repo.commit(previous_commit) # obtain commit object # END handle previous commit - psms = self.list_items(repo, parent_commit=previous_commit) - sms = self.list_items(repo) + psms: 'IterableList[Submodule]' = self.list_items(repo, parent_commit=previous_commit) + sms: 'IterableList[Submodule]' = self.list_items(repo) spsms = set(psms) ssms = set(sms) @@ -162,8 +176,8 @@ class RootModule(Submodule): csms = (spsms & ssms) len_csms = len(csms) for i, csm in enumerate(csms): - psm = psms[csm.name] - sm = sms[csm.name] + psm: 'Submodule' = psms[csm.name] + sm: 'Submodule' = sms[csm.name] # PATH CHANGES ############## @@ -343,7 +357,7 @@ class RootModule(Submodule): return self - def module(self): + def module(self) -> 'Repo': """:return: the actual repository containing the submodules""" return self.repo #} END interface diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index cde18d08..a776af88 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -28,7 +28,7 @@ __all__ = ('sm_section', 'sm_name', 'mkhead', 'find_first_remote_branch', def sm_section(name: str) -> str: """:return: section title used in .gitmodules configuration file""" - return f'submodule {name}' + return f'submodule "{name}"' def sm_name(section: str) -> str: -- cgit v1.2.1 From 94c2ae405ba635e801ff7a1ea00300e51f3a70db Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:41:14 +0100 Subject: Readd submodule.base.py types --- git/objects/submodule/base.py | 88 +++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 37 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 60ff1155..87a86749 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -47,15 +47,16 @@ from .util import ( find_first_remote_branch ) +from git.repo import Repo # typing ---------------------------------------------------------------------- -from typing import Dict, TYPE_CHECKING +from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING from typing import Any, Iterator, Union -from git.types import Commit_ish, PathLike +from git.types import Commit_ish, PathLike, TBD if TYPE_CHECKING: - from git.repo import Repo + from git.index import IndexFile # ----------------------------------------------------------------------------- @@ -131,14 +132,14 @@ class Submodule(IndexObject, TraversableIterableObj): if url is not None: self._url = url if branch_path is not None: - assert isinstance(branch_path, str) + # assert isinstance(branch_path, str) self._branch_path = branch_path if name is not None: self._name = name def _set_cache_(self, attr: str) -> None: if attr in ('path', '_url', '_branch_path'): - reader = self.config_reader() + reader: SectionConstraint = self.config_reader() # default submodule values try: self.path = reader.get('path') @@ -226,7 +227,7 @@ class Submodule(IndexObject, TraversableIterableObj): return SubmoduleConfigParser(fp_module, read_only=read_only) - def _clear_cache(self): + def _clear_cache(self) -> None: # clear the possibly changed values for name in self._cache_attrs: try: @@ -246,7 +247,7 @@ class Submodule(IndexObject, TraversableIterableObj): def _config_parser_constrained(self, read_only: bool) -> SectionConstraint: """:return: Config Parser constrained to our submodule in read or write mode""" try: - pc = self.parent_commit + pc: Union['Commit_ish', None] = self.parent_commit except ValueError: pc = None # end handle empty parent repository @@ -255,10 +256,12 @@ class Submodule(IndexObject, TraversableIterableObj): return SectionConstraint(parser, sm_section(self.name)) @classmethod - def _module_abspath(cls, parent_repo, path, name): + def _module_abspath(cls, parent_repo: 'Repo', path: PathLike, name: str) -> PathLike: if cls._need_gitfile_submodules(parent_repo.git): return osp.join(parent_repo.git_dir, 'modules', name) - return osp.join(parent_repo.working_tree_dir, path) + if parent_repo.working_tree_dir: + return osp.join(parent_repo.working_tree_dir, path) + raise NotADirectoryError() # end @classmethod @@ -286,7 +289,7 @@ class Submodule(IndexObject, TraversableIterableObj): return clone @classmethod - def _to_relative_path(cls, parent_repo, path): + def _to_relative_path(cls, parent_repo: 'Repo', path: PathLike) -> PathLike: """:return: a path guaranteed to be relative to the given parent - repository :raise ValueError: if path is not contained in the parent repository's working tree""" path = to_native_path_linux(path) @@ -294,7 +297,7 @@ class Submodule(IndexObject, TraversableIterableObj): path = path[:-1] # END handle trailing slash - if osp.isabs(path): + if osp.isabs(path) and parent_repo.working_tree_dir: working_tree_linux = to_native_path_linux(parent_repo.working_tree_dir) if not path.startswith(working_tree_linux): raise ValueError("Submodule checkout path '%s' needs to be within the parents repository at '%s'" @@ -308,7 +311,7 @@ class Submodule(IndexObject, TraversableIterableObj): return path @classmethod - def _write_git_file_and_module_config(cls, working_tree_dir, module_abspath): + def _write_git_file_and_module_config(cls, working_tree_dir: PathLike, module_abspath: PathLike) -> None: """Writes a .git file containing a(preferably) relative path to the actual git module repository. It is an error if the module_abspath cannot be made into a relative path, relative to the working_tree_dir :note: will overwrite existing files ! @@ -335,7 +338,8 @@ class Submodule(IndexObject, TraversableIterableObj): @classmethod def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, - branch=None, no_checkout: bool = False, depth=None, env=None, clone_multi_options=None + branch: Union[str, None] = None, no_checkout: bool = False, depth: Union[int, None] = None, + env: Mapping[str, str] = None, clone_multi_options: Union[Sequence[TBD], None] = None ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. @@ -391,7 +395,7 @@ class Submodule(IndexObject, TraversableIterableObj): if sm.exists(): # reretrieve submodule from tree try: - sm = repo.head.commit.tree[path] # type: ignore + sm = repo.head.commit.tree[str(path)] sm._name = name return sm except KeyError: @@ -414,7 +418,8 @@ class Submodule(IndexObject, TraversableIterableObj): # END check url # END verify urls match - mrepo = None + mrepo: Union[Repo, None] = None + if url is None: if not has_module: raise ValueError("A URL was not given and a repository did not exist at %s" % path) @@ -427,7 +432,7 @@ class Submodule(IndexObject, TraversableIterableObj): url = urls[0] else: # clone new repo - kwargs: Dict[str, Union[bool, int]] = {'n': no_checkout} + kwargs: Dict[str, Union[bool, int, Sequence[TBD]]] = {'n': no_checkout} if not branch_is_default: kwargs['b'] = br.name # END setup checkout-branch @@ -451,6 +456,8 @@ class Submodule(IndexObject, TraversableIterableObj): # otherwise there is a '-' character in front of the submodule listing # a38efa84daef914e4de58d1905a500d8d14aaf45 mymodule (v0.9.0-1-ga38efa8) # -a38efa84daef914e4de58d1905a500d8d14aaf45 submodules/intermediate/one + writer: Union[GitConfigParser, SectionConstraint] + with sm.repo.config_writer() as writer: writer.set_value(sm_section(name), 'url', url) @@ -467,13 +474,15 @@ class Submodule(IndexObject, TraversableIterableObj): sm._branch_path = br.path # we deliberately assume that our head matches our index ! - sm.binsha = mrepo.head.commit.binsha + sm.binsha = mrepo.head.commit.binsha # type: ignore index.add([sm], write=True) return sm - def update(self, recursive=False, init=True, to_latest_revision=False, progress=None, dry_run=False, - force=False, keep_going=False, env=None, clone_multi_options=None): + 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: Mapping[str, str] = None, + clone_multi_options: Union[Sequence[TBD], None] = None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. @@ -580,6 +589,7 @@ class Submodule(IndexObject, TraversableIterableObj): if not dry_run: # see whether we have a valid branch to checkout try: + assert isinstance(mrepo, Repo) # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) @@ -640,7 +650,7 @@ class Submodule(IndexObject, TraversableIterableObj): may_reset = True if mrepo.head.commit.binsha != self.NULL_BIN_SHA: base_commit = mrepo.merge_base(mrepo.head.commit, hexsha) - if len(base_commit) == 0 or base_commit[0].hexsha == hexsha: + if len(base_commit) == 0 or (base_commit[0] is not None and base_commit[0].hexsha == hexsha): if force: msg = "Will force checkout or reset on local branch that is possibly in the future of" msg += "the commit it will be checked out to, effectively 'forgetting' new commits" @@ -807,7 +817,8 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def remove(self, module=True, force=False, configuration=True, dry_run=False): + def remove(self, module: bool = True, force: bool = False, + configuration: bool = True, dry_run: bool = False) -> 'Submodule': """Remove this submodule from the repository. This will remove our entry from the .gitmodules file and the entry in the .git / config file. @@ -861,7 +872,7 @@ class Submodule(IndexObject, TraversableIterableObj): # TODO: If we run into permission problems, we have a highly inconsistent # state. Delete the .git folders last, start with the submodules first mp = self.abspath - method = None + method: Union[None, Callable[[PathLike], None]] = None if osp.islink(mp): method = os.remove elif osp.isdir(mp): @@ -914,7 +925,7 @@ class Submodule(IndexObject, TraversableIterableObj): import gc gc.collect() try: - rmtree(wtd) + rmtree(str(wtd)) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: raise SkipTest("FIXME: fails with: PermissionError\n {}".format(ex)) from ex @@ -928,7 +939,7 @@ class Submodule(IndexObject, TraversableIterableObj): rmtree(git_dir) except Exception as ex: if HIDE_WINDOWS_KNOWN_ERRORS: - raise SkipTest("FIXME: fails with: PermissionError\n %s", ex) from ex + raise SkipTest(f"FIXME: fails with: PermissionError\n {ex}") from ex else: raise # end handle separate bare repository @@ -952,6 +963,8 @@ class Submodule(IndexObject, TraversableIterableObj): # now git config - need the config intact, otherwise we can't query # information anymore + writer: Union[GitConfigParser, SectionConstraint] + with self.repo.config_writer() as writer: writer.remove_section(sm_section(self.name)) @@ -961,7 +974,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self - def set_parent_commit(self, commit: Union[Commit_ish, None], check=True): + def set_parent_commit(self, commit: Union[Commit_ish, None], check: bool = True) -> 'Submodule': """Set this instance to use the given commit whose tree is supposed to contain the .gitmodules blob. @@ -1009,7 +1022,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self @unbare_repo - def config_writer(self, index=None, write=True): + def config_writer(self, index: Union['IndexFile', None] = None, write: bool = True) -> SectionConstraint: """:return: a config writer instance allowing you to read and write the data belonging to this submodule into the .gitmodules file. @@ -1030,7 +1043,7 @@ class Submodule(IndexObject, TraversableIterableObj): return writer @unbare_repo - def rename(self, new_name): + def rename(self, new_name: str) -> 'Submodule': """Rename this submodule :note: This method takes care of renaming the submodule in various places, such as @@ -1065,13 +1078,14 @@ class Submodule(IndexObject, TraversableIterableObj): destination_module_abspath = self._module_abspath(self.repo, self.path, new_name) source_dir = mod.git_dir # Let's be sure the submodule name is not so obviously tied to a directory - if destination_module_abspath.startswith(mod.git_dir): + if str(destination_module_abspath).startswith(str(mod.git_dir)): tmp_dir = self._module_abspath(self.repo, self.path, str(uuid.uuid4())) os.renames(source_dir, tmp_dir) source_dir = tmp_dir # end handle self-containment os.renames(source_dir, destination_module_abspath) - self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) + if mod.working_tree_dir: + self._write_git_file_and_module_config(mod.working_tree_dir, destination_module_abspath) # end move separate git repository return self @@ -1081,7 +1095,7 @@ class Submodule(IndexObject, TraversableIterableObj): #{ Query Interface @unbare_repo - def module(self): + def module(self) -> 'Repo': """:return: Repo instance initialized from the repository at our submodule path :raise InvalidGitRepositoryError: if a repository was not available. This could also mean that it was not yet initialized""" @@ -1098,7 +1112,7 @@ class Submodule(IndexObject, TraversableIterableObj): raise InvalidGitRepositoryError("Repository at %r was not yet checked out" % module_checkout_abspath) # END handle exceptions - def module_exists(self): + def module_exists(self) -> bool: """:return: True if our module exists and is a valid git repository. See module() method""" try: self.module() @@ -1107,7 +1121,7 @@ class Submodule(IndexObject, TraversableIterableObj): return False # END handle exception - def exists(self): + def exists(self) -> bool: """ :return: True if the submodule exists, False otherwise. Please note that a submodule may exist ( in the .gitmodules file) even though its module @@ -1148,26 +1162,26 @@ class Submodule(IndexObject, TraversableIterableObj): return mkhead(self.module(), self._branch_path) @property - def branch_path(self): + def branch_path(self) -> PathLike: """ :return: full(relative) path as string to the branch we would checkout from the remote and track""" return self._branch_path @property - def branch_name(self): + def branch_name(self) -> str: """:return: the name of the branch, which is the shortest possible branch name""" # use an instance method, for this we create a temporary Head instance # which uses a repository that is available at least ( it makes no difference ) return git.Head(self.repo, self._branch_path).name @property - def url(self): + def url(self) -> str: """:return: The url to the repository which our module - repository refers to""" return self._url @property - def parent_commit(self): + def parent_commit(self) -> 'Commit_ish': """:return: Commit instance with the tree containing the .gitmodules file :note: will always point to the current head's commit if it was not set explicitly""" if self._parent_commit is None: @@ -1175,7 +1189,7 @@ class Submodule(IndexObject, TraversableIterableObj): return self._parent_commit @property - def name(self): + def name(self) -> str: """:return: The name of this submodule. It is used to identify it within the .gitmodules file. :note: by default, the name is the path at which to find the submodule, but -- cgit v1.2.1 From af7cee514632a4a3825dbb5655a208d2ebd1f67f Mon Sep 17 00:00:00 2001 From: Yobmod Date: Tue, 6 Jul 2021 16:57:12 +0100 Subject: Make Repo a forward ref in Submodule.base --- git/objects/submodule/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 87a86749..847b4325 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -47,16 +47,16 @@ from .util import ( find_first_remote_branch ) -from git.repo import Repo # typing ---------------------------------------------------------------------- -from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING +from typing import Callable, Dict, Mapping, Sequence, TYPE_CHECKING, cast from typing import Any, Iterator, Union from git.types import Commit_ish, PathLike, TBD if TYPE_CHECKING: from git.index import IndexFile + from git.repo import Repo # ----------------------------------------------------------------------------- @@ -589,7 +589,7 @@ class Submodule(IndexObject, TraversableIterableObj): if not dry_run: # see whether we have a valid branch to checkout try: - assert isinstance(mrepo, Repo) + mrepo = cast('Repo', mrepo) # find a remote which has our branch - we try to be flexible remote_branch = find_first_remote_branch(mrepo.remotes, self.branch_name) local_branch = mkhead(mrepo, self.branch_path) -- cgit v1.2.1 From 2e2fe186d09272c3cb6c96467fff362deb90994f Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 11:30:16 +0100 Subject: Increase mypy strictness (no_implicit_optional & warn_redundant_casts) and fix errors --- git/objects/commit.py | 2 +- git/objects/submodule/base.py | 6 +++--- git/objects/tree.py | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'git/objects') diff --git a/git/objects/commit.py b/git/objects/commit.py index 81978ae8..65a87591 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -80,7 +80,7 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): "message", "parents", "encoding", "gpgsig") _id_attribute_ = "hexsha" - def __init__(self, repo: 'Repo', binsha: bytes, tree: 'Tree' = None, + def __init__(self, repo: 'Repo', binsha: bytes, tree: Union['Tree', None] = None, author: Union[Actor, None] = None, authored_date: Union[int, None] = None, author_tz_offset: Union[None, float] = None, diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 847b4325..5539069c 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -115,7 +115,7 @@ class Submodule(IndexObject, TraversableIterableObj): path: Union[PathLike, None] = None, name: Union[str, None] = None, parent_commit: Union[Commit_ish, None] = None, - url: str = None, + url: Union[str, None] = None, branch_path: Union[PathLike, None] = None ) -> None: """Initialize this instance with its attributes. We only document the ones @@ -339,7 +339,7 @@ class Submodule(IndexObject, TraversableIterableObj): @classmethod def add(cls, repo: 'Repo', name: str, path: PathLike, url: Union[str, None] = None, branch: Union[str, None] = None, no_checkout: bool = False, depth: Union[int, None] = None, - env: Mapping[str, str] = None, clone_multi_options: Union[Sequence[TBD], None] = None + env: Union[Mapping[str, str], None] = None, clone_multi_options: Union[Sequence[TBD], None] = None ) -> 'Submodule': """Add a new submodule to the given repository. This will alter the index as well as the .gitmodules file, but will not create a new commit. @@ -481,7 +481,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: Mapping[str, str] = None, + force: bool = False, keep_going: bool = False, env: Union[Mapping[str, str], None] = None, clone_multi_options: Union[Sequence[TBD], None] = None): """Update the repository of this submodule to point to the checkout we point at with the binsha of this instance. diff --git a/git/objects/tree.py b/git/objects/tree.py index 2e8d8a79..34fb93dc 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -216,7 +216,6 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): def _get_intermediate_items(cls, index_object: 'Tree', ) -> Union[Tuple['Tree', ...], Tuple[()]]: if index_object.type == "tree": - index_object = cast('Tree', index_object) return tuple(index_object._iter_convert_to_object(index_object._cache)) return () -- cgit v1.2.1 From 5d3818ed3d51d400517a352b5b62e966164af8cf Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 21:42:30 +0100 Subject: Finish initial typing of index folder --- git/objects/fun.py | 68 ++++++++++++++++++++++++++++++++++++----------------- git/objects/tree.py | 20 ++++++++-------- 2 files changed, 57 insertions(+), 31 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 339a53b8..89b02ad4 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -1,6 +1,8 @@ """Module with functions which are supposed to be as fast as possible""" from stat import S_ISDIR +from git import GitCmdObjectDB + from git.compat import ( safe_decode, defenc @@ -8,7 +10,12 @@ from git.compat import ( # typing ---------------------------------------------- -from typing import List, Tuple +from typing import Callable, List, Sequence, Tuple, TYPE_CHECKING, Union, overload + +if TYPE_CHECKING: + from _typeshed import ReadableBuffer + +EntryTup = Tuple[bytes, int, str] # same as TreeCacheTup in tree.py # --------------------------------------------------- @@ -18,7 +25,7 @@ __all__ = ('tree_to_stream', 'tree_entries_from_data', 'traverse_trees_recursive 'traverse_tree_recursive') -def tree_to_stream(entries, write): +def tree_to_stream(entries: Sequence[EntryTup], write: Callable[['ReadableBuffer'], Union[int, None]]) -> None: """Write the give list of entries into a stream using its write method :param entries: **sorted** list of tuples with (binsha, mode, name) :param write: write method which takes a data string""" @@ -42,12 +49,14 @@ def tree_to_stream(entries, write): # According to my tests, this is exactly what git does, that is it just # takes the input literally, which appears to be utf8 on linux. if isinstance(name, str): - name = name.encode(defenc) - write(b''.join((mode_str, b' ', name, b'\0', binsha))) + name_bytes = name.encode(defenc) + else: + name_bytes = name + write(b''.join((mode_str, b' ', name_bytes, b'\0', binsha))) # END for each item -def tree_entries_from_data(data: bytes) -> List[Tuple[bytes, int, str]]: +def tree_entries_from_data(data: bytes) -> List[EntryTup]: """Reads the binary representation of a tree and returns tuples of Tree items :param data: data block with tree data (as bytes) :return: list(tuple(binsha, mode, tree_relative_path), ...)""" @@ -93,36 +102,49 @@ def tree_entries_from_data(data: bytes) -> List[Tuple[bytes, int, str]]: return out -def _find_by_name(tree_data, name, is_dir, start_at): +def _find_by_name(tree_data: Sequence[Union[EntryTup, None]], name: str, is_dir: bool, start_at: int + ) -> Union[EntryTup, None]: """return data entry matching the given name and tree mode or None. Before the item is returned, the respective data item is set None in the tree_data list to mark it done""" + tree_data_list: List[Union[EntryTup, None]] = list(tree_data) try: - item = tree_data[start_at] + item = tree_data_list[start_at] if item and item[2] == name and S_ISDIR(item[1]) == is_dir: - tree_data[start_at] = None + tree_data_list[start_at] = None return item except IndexError: pass # END exception handling - for index, item in enumerate(tree_data): + for index, item in enumerate(tree_data_list): if item and item[2] == name and S_ISDIR(item[1]) == is_dir: - tree_data[index] = None + tree_data_list[index] = None return item # END if item matches # END for each item return None -def _to_full_path(item, path_prefix): +@ overload +def _to_full_path(item: None, path_prefix: str) -> None: + ... + + +@ overload +def _to_full_path(item: EntryTup, path_prefix: str) -> EntryTup: + ... + + +def _to_full_path(item: Union[EntryTup, None], path_prefix: str) -> Union[EntryTup, None]: """Rebuild entry with given path prefix""" if not item: return item return (item[0], item[1], path_prefix + item[2]) -def traverse_trees_recursive(odb, tree_shas, path_prefix): +def traverse_trees_recursive(odb: GitCmdObjectDB, tree_shas: Sequence[Union[bytes, None]], + path_prefix: str) -> List[Union[EntryTup, None]]: """ :return: list with entries according to the given binary tree-shas. The result is encoded in a list @@ -137,28 +159,29 @@ def traverse_trees_recursive(odb, tree_shas, path_prefix): :param path_prefix: a prefix to be added to the returned paths on this level, set it '' for the first iteration :note: The ordering of the returned items will be partially lost""" - trees_data = [] + trees_data: List[List[Union[EntryTup, None]]] = [] nt = len(tree_shas) for tree_sha in tree_shas: if tree_sha is None: - data = [] + data: List[Union[EntryTup, None]] = [] else: - data = tree_entries_from_data(odb.stream(tree_sha).read()) + data = list(tree_entries_from_data(odb.stream(tree_sha).read())) # make new list for typing as invariant # END handle muted trees trees_data.append(data) # END for each sha to get data for out = [] - out_append = out.append # find all matching entries and recursively process them together if the match # is a tree. If the match is a non-tree item, put it into the result. # Processed items will be set None for ti, tree_data in enumerate(trees_data): + for ii, item in enumerate(tree_data): if not item: continue # END skip already done items + entries: List[Union[EntryTup, None]] entries = [None for _ in range(nt)] entries[ti] = item _sha, mode, name = item @@ -169,17 +192,20 @@ def traverse_trees_recursive(odb, tree_shas, path_prefix): # ti+nt, not ti+1+nt for tio in range(ti + 1, ti + nt): tio = tio % nt - entries[tio] = _find_by_name(trees_data[tio], name, is_dir, ii) - # END for each other item data + td = trees_data[tio] + entries[tio] = _find_by_name(td, name, is_dir, ii) + # END for each other item data +#Revealed type is "builtins.list[Union[Tuple[builtins.bytes, builtins.int, builtins.str], None]]"## # +#Revealed type is "builtins.list[Union[Tuple[builtins.bytes, builtins.int, builtins.str], None]]" # if we are a directory, enter recursion if is_dir: out.extend(traverse_trees_recursive( odb, [((ei and ei[0]) or None) for ei in entries], path_prefix + name + '/')) else: - out_append(tuple(_to_full_path(e, path_prefix) for e in entries)) - # END handle recursion + out.extend([_to_full_path(e, path_prefix) for e in entries]) + # END handle recursion # finally mark it done tree_data[ii] = None # END for each item @@ -190,7 +216,7 @@ def traverse_trees_recursive(odb, tree_shas, path_prefix): return out -def traverse_tree_recursive(odb, tree_sha, path_prefix): +def traverse_tree_recursive(odb: GitCmdObjectDB, tree_sha: bytes, path_prefix: str) -> List[Tuple[bytes, int, str]]: """ :return: list of entries of the tree pointed to by the binary tree_sha. An entry has the following format: diff --git a/git/objects/tree.py b/git/objects/tree.py index 34fb93dc..528cf5ca 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -21,8 +21,8 @@ from .fun import ( # typing ------------------------------------------------- -from typing import (Callable, Dict, Generic, Iterable, Iterator, List, - Tuple, Type, TypeVar, Union, cast, TYPE_CHECKING) +from typing import (Callable, Dict, Iterable, Iterator, List, + Tuple, Type, Union, cast, TYPE_CHECKING) from git.types import PathLike, TypeGuard @@ -30,7 +30,7 @@ if TYPE_CHECKING: from git.repo import Repo from io import BytesIO -T_Tree_cache = TypeVar('T_Tree_cache', bound=Tuple[bytes, int, str]) +TreeCacheTup = Tuple[bytes, int, str] TraversedTreeTup = Union[Tuple[Union['Tree', None], IndexObjUnion, Tuple['Submodule', 'Submodule']]] @@ -42,7 +42,7 @@ cmp: Callable[[str, str], int] = lambda a, b: (a > b) - (a < b) __all__ = ("TreeModifier", "Tree") -def git_cmp(t1: T_Tree_cache, t2: T_Tree_cache) -> int: +def git_cmp(t1: TreeCacheTup, t2: TreeCacheTup) -> int: a, b = t1[2], t2[2] assert isinstance(a, str) and isinstance(b, str) # Need as mypy 9.0 cannot unpack TypeVar properly len_a, len_b = len(a), len(b) @@ -55,8 +55,8 @@ def git_cmp(t1: T_Tree_cache, t2: T_Tree_cache) -> int: return len_a - len_b -def merge_sort(a: List[T_Tree_cache], - cmp: Callable[[T_Tree_cache, T_Tree_cache], int]) -> None: +def merge_sort(a: List[TreeCacheTup], + cmp: Callable[[TreeCacheTup, TreeCacheTup], int]) -> None: if len(a) < 2: return None @@ -91,7 +91,7 @@ def merge_sort(a: List[T_Tree_cache], k = k + 1 -class TreeModifier(Generic[T_Tree_cache], object): +class TreeModifier(object): """A utility class providing methods to alter the underlying cache in a list-like fashion. @@ -99,7 +99,7 @@ class TreeModifier(Generic[T_Tree_cache], object): the cache of a tree, will be sorted. Assuring it will be in a serializable state""" __slots__ = '_cache' - def __init__(self, cache: List[T_Tree_cache]) -> None: + def __init__(self, cache: List[TreeCacheTup]) -> None: self._cache = cache def _index_by_name(self, name: str) -> int: @@ -141,7 +141,7 @@ class TreeModifier(Generic[T_Tree_cache], object): sha = to_bin_sha(sha) index = self._index_by_name(name) - def is_tree_cache(inp: Tuple[bytes, int, str]) -> TypeGuard[T_Tree_cache]: + def is_tree_cache(inp: Tuple[bytes, int, str]) -> TypeGuard[TreeCacheTup]: return isinstance(inp[0], bytes) and isinstance(inp[1], int) and isinstance([inp], str) item = (sha, mode, name) @@ -167,7 +167,7 @@ class TreeModifier(Generic[T_Tree_cache], object): For more information on the parameters, see ``add`` :param binsha: 20 byte binary sha""" assert isinstance(binsha, bytes) and isinstance(mode, int) and isinstance(name, str) - tree_cache = cast(T_Tree_cache, (binsha, mode, name)) + tree_cache = (binsha, mode, name) self._cache.append(tree_cache) -- cgit v1.2.1 From 9f88796704cc9f9826b1a25f322108f8dcc52ce6 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 21:45:37 +0100 Subject: Mak GitCmdObjectDB a froward ref --- git/objects/fun.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 89b02ad4..fc49e389 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -1,7 +1,6 @@ """Module with functions which are supposed to be as fast as possible""" from stat import S_ISDIR -from git import GitCmdObjectDB from git.compat import ( safe_decode, @@ -14,6 +13,7 @@ from typing import Callable, List, Sequence, Tuple, TYPE_CHECKING, Union, overlo if TYPE_CHECKING: from _typeshed import ReadableBuffer + from git import GitCmdObjectDB EntryTup = Tuple[bytes, int, str] # same as TreeCacheTup in tree.py @@ -143,7 +143,7 @@ def _to_full_path(item: Union[EntryTup, None], path_prefix: str) -> Union[EntryT return (item[0], item[1], path_prefix + item[2]) -def traverse_trees_recursive(odb: GitCmdObjectDB, tree_shas: Sequence[Union[bytes, None]], +def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[bytes, None]], path_prefix: str) -> List[Union[EntryTup, None]]: """ :return: list with entries according to the given binary tree-shas. @@ -216,7 +216,7 @@ def traverse_trees_recursive(odb: GitCmdObjectDB, tree_shas: Sequence[Union[byte return out -def traverse_tree_recursive(odb: GitCmdObjectDB, tree_sha: bytes, path_prefix: str) -> List[Tuple[bytes, int, str]]: +def traverse_tree_recursive(odb: 'GitCmdObjectDB', tree_sha: bytes, path_prefix: str) -> List[Tuple[bytes, int, str]]: """ :return: list of entries of the tree pointed to by the binary tree_sha. An entry has the following format: -- cgit v1.2.1 From 1533596b03ef07b07311821d90de3ef72abba5d6 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 22:20:59 +0100 Subject: Mak EntryTup a froward ref --- git/objects/fun.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index fc49e389..4ff56fdd 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from git import GitCmdObjectDB EntryTup = Tuple[bytes, int, str] # same as TreeCacheTup in tree.py - +EntryTupOrNone = Union[EntryTup, None] # --------------------------------------------------- -- cgit v1.2.1 From 4333dcb182da3c9f9bd2c358bdf38db278cab557 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 22:49:34 +0100 Subject: Mmmmm --- git/objects/fun.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 4ff56fdd..e6ad7892 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -102,13 +102,13 @@ def tree_entries_from_data(data: bytes) -> List[EntryTup]: return out -def _find_by_name(tree_data: Sequence[Union[EntryTup, None]], name: str, is_dir: bool, start_at: int - ) -> Union[EntryTup, None]: +def _find_by_name(tree_data: Sequence[EntryTupOrNone], name: str, is_dir: bool, start_at: int + ) -> EntryTupOrNone: """return data entry matching the given name and tree mode or None. Before the item is returned, the respective data item is set None in the tree_data list to mark it done""" - tree_data_list: List[Union[EntryTup, None]] = list(tree_data) + tree_data_list: List[EntryTupOrNone] = list(tree_data) try: item = tree_data_list[start_at] if item and item[2] == name and S_ISDIR(item[1]) == is_dir: @@ -136,7 +136,7 @@ def _to_full_path(item: EntryTup, path_prefix: str) -> EntryTup: ... -def _to_full_path(item: Union[EntryTup, None], path_prefix: str) -> Union[EntryTup, None]: +def _to_full_path(item: EntryTupOrNone, path_prefix: str) -> EntryTupOrNone: """Rebuild entry with given path prefix""" if not item: return item @@ -144,7 +144,7 @@ def _to_full_path(item: Union[EntryTup, None], path_prefix: str) -> Union[EntryT def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[bytes, None]], - path_prefix: str) -> List[Union[EntryTup, None]]: + path_prefix: str) -> List[EntryTupOrNone]: """ :return: list with entries according to the given binary tree-shas. The result is encoded in a list @@ -159,11 +159,11 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by :param path_prefix: a prefix to be added to the returned paths on this level, set it '' for the first iteration :note: The ordering of the returned items will be partially lost""" - trees_data: List[List[Union[EntryTup, None]]] = [] + trees_data: List[List[EntryTupOrNone]] = [] nt = len(tree_shas) for tree_sha in tree_shas: if tree_sha is None: - data: List[Union[EntryTup, None]] = [] + data: List[EntryTupOrNone] = [] else: data = list(tree_entries_from_data(odb.stream(tree_sha).read())) # make new list for typing as invariant # END handle muted trees @@ -181,7 +181,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by if not item: continue # END skip already done items - entries: List[Union[EntryTup, None]] + entries: List[EntryTupOrNone] entries = [None for _ in range(nt)] entries[ti] = item _sha, mode, name = item @@ -196,8 +196,6 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by entries[tio] = _find_by_name(td, name, is_dir, ii) # END for each other item data -#Revealed type is "builtins.list[Union[Tuple[builtins.bytes, builtins.int, builtins.str], None]]"## # -#Revealed type is "builtins.list[Union[Tuple[builtins.bytes, builtins.int, builtins.str], None]]" # if we are a directory, enter recursion if is_dir: out.extend(traverse_trees_recursive( -- cgit v1.2.1 From d344abf5594bebe0147feaba7e87c0079d28374f Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 23:12:42 +0100 Subject: Fix traverse_trees_recursive() --- git/objects/fun.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index e6ad7892..2abd7b09 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -144,9 +144,9 @@ def _to_full_path(item: EntryTupOrNone, path_prefix: str) -> EntryTupOrNone: def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[bytes, None]], - path_prefix: str) -> List[EntryTupOrNone]: + path_prefix: str) -> List[List[EntryTupOrNone]]: """ - :return: list with entries according to the given binary tree-shas. + :return: list of list with entries according to the given binary tree-shas. The result is encoded in a list of n tuple|None per blob/commit, (n == len(tree_shas)), where * [0] == 20 byte sha @@ -170,7 +170,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by trees_data.append(data) # END for each sha to get data for - out = [] + out: List[List[EntryTupOrNone]] = [] # find all matching entries and recursively process them together if the match # is a tree. If the match is a non-tree item, put it into the result. @@ -201,7 +201,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by out.extend(traverse_trees_recursive( odb, [((ei and ei[0]) or None) for ei in entries], path_prefix + name + '/')) else: - out.extend([_to_full_path(e, path_prefix) for e in entries]) + out.append([_to_full_path(e, path_prefix) for e in entries]) # END handle recursion # finally mark it done -- cgit v1.2.1 From dfbc0f42c7555b7145768774b861029c4283178c Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 23:20:58 +0100 Subject: Fix traverse_trees_recursive() again --- git/objects/fun.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 2abd7b09..cb323afb 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -144,7 +144,7 @@ def _to_full_path(item: EntryTupOrNone, path_prefix: str) -> EntryTupOrNone: def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[bytes, None]], - path_prefix: str) -> List[List[EntryTupOrNone]]: + path_prefix: str) -> List[tuple[EntryTupOrNone, ...]]: """ :return: list of list with entries according to the given binary tree-shas. The result is encoded in a list @@ -170,7 +170,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by trees_data.append(data) # END for each sha to get data for - out: List[List[EntryTupOrNone]] = [] + out: List[Tuple[EntryTupOrNone, ...]] = [] # find all matching entries and recursively process them together if the match # is a tree. If the match is a non-tree item, put it into the result. @@ -201,7 +201,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by out.extend(traverse_trees_recursive( odb, [((ei and ei[0]) or None) for ei in entries], path_prefix + name + '/')) else: - out.append([_to_full_path(e, path_prefix) for e in entries]) + out.append(tuple(_to_full_path(e, path_prefix) for e in entries)) # END handle recursion # finally mark it done -- cgit v1.2.1 From c27d2b078b515a8321b3f7f7abdcea363d8049df Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 23:25:18 +0100 Subject: Use Tuple not tuple --- git/objects/fun.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index cb323afb..42954fc2 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -144,7 +144,7 @@ def _to_full_path(item: EntryTupOrNone, path_prefix: str) -> EntryTupOrNone: def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[bytes, None]], - path_prefix: str) -> List[tuple[EntryTupOrNone, ...]]: + path_prefix: str) -> List[Tuple[EntryTupOrNone, ...]]: """ :return: list of list with entries according to the given binary tree-shas. The result is encoded in a list @@ -165,7 +165,8 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by if tree_sha is None: data: List[EntryTupOrNone] = [] else: - data = list(tree_entries_from_data(odb.stream(tree_sha).read())) # make new list for typing as invariant + # make new list for typing as list invariant + data = [x for x in tree_entries_from_data(odb.stream(tree_sha).read())] # END handle muted trees trees_data.append(data) # END for each sha to get data for -- cgit v1.2.1 From 4f13b4e23526616f307370dc9a869b067e90b276 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 23:49:01 +0100 Subject: fix base,ours,theirs typing --- git/objects/fun.py | 8 ++++---- git/objects/tree.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 42954fc2..57cefcf2 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -102,13 +102,13 @@ def tree_entries_from_data(data: bytes) -> List[EntryTup]: return out -def _find_by_name(tree_data: Sequence[EntryTupOrNone], name: str, is_dir: bool, start_at: int +def _find_by_name(tree_data: List[EntryTupOrNone], name: str, is_dir: bool, start_at: int ) -> EntryTupOrNone: """return data entry matching the given name and tree mode or None. Before the item is returned, the respective data item is set None in the tree_data list to mark it done""" - tree_data_list: List[EntryTupOrNone] = list(tree_data) + tree_data_list: List[EntryTupOrNone] = tree_data try: item = tree_data_list[start_at] if item and item[2] == name and S_ISDIR(item[1]) == is_dir: @@ -160,6 +160,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by set it '' for the first iteration :note: The ordering of the returned items will be partially lost""" trees_data: List[List[EntryTupOrNone]] = [] + nt = len(tree_shas) for tree_sha in tree_shas: if tree_sha is None: @@ -193,8 +194,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by # ti+nt, not ti+1+nt for tio in range(ti + 1, ti + nt): tio = tio % nt - td = trees_data[tio] - entries[tio] = _find_by_name(td, name, is_dir, ii) + entries[tio] = _find_by_name(trees_data[tio], name, is_dir, ii) # END for each other item data # if we are a directory, enter recursion diff --git a/git/objects/tree.py b/git/objects/tree.py index 528cf5ca..d3681e23 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -44,7 +44,7 @@ __all__ = ("TreeModifier", "Tree") def git_cmp(t1: TreeCacheTup, t2: TreeCacheTup) -> int: a, b = t1[2], t2[2] - assert isinstance(a, str) and isinstance(b, str) # Need as mypy 9.0 cannot unpack TypeVar properly + # assert isinstance(a, str) and isinstance(b, str) len_a, len_b = len(a), len(b) min_len = min(len_a, len_b) min_cmp = cmp(a[:min_len], b[:min_len]) -- cgit v1.2.1 From 627defff96470464884ca81899fd0271e614b3e8 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 23:55:09 +0100 Subject: Change List to MutableSequence in fun.py _find_by_name() --- git/objects/fun.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index 57cefcf2..be541eb8 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -9,7 +9,7 @@ from git.compat import ( # typing ---------------------------------------------- -from typing import Callable, List, Sequence, Tuple, TYPE_CHECKING, Union, overload +from typing import Callable, List, MutableSequence, Sequence, Tuple, TYPE_CHECKING, Union, overload if TYPE_CHECKING: from _typeshed import ReadableBuffer @@ -102,24 +102,24 @@ def tree_entries_from_data(data: bytes) -> List[EntryTup]: return out -def _find_by_name(tree_data: List[EntryTupOrNone], name: str, is_dir: bool, start_at: int +def _find_by_name(tree_data: MutableSequence[EntryTupOrNone], name: str, is_dir: bool, start_at: int ) -> EntryTupOrNone: """return data entry matching the given name and tree mode or None. Before the item is returned, the respective data item is set None in the tree_data list to mark it done""" - tree_data_list: List[EntryTupOrNone] = tree_data + try: - item = tree_data_list[start_at] + item = tree_data[start_at] if item and item[2] == name and S_ISDIR(item[1]) == is_dir: - tree_data_list[start_at] = None + tree_data[start_at] = None return item except IndexError: pass # END exception handling - for index, item in enumerate(tree_data_list): + for index, item in enumerate(tree_data): if item and item[2] == name and S_ISDIR(item[1]) == is_dir: - tree_data_list[index] = None + tree_data[index] = None return item # END if item matches # END for each item -- cgit v1.2.1 From f271c58adb36550a02607811e97cc19feae4bafb Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 00:00:19 +0100 Subject: tests TraversableIterableObj typeguard --- git/objects/util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'git/objects') diff --git a/git/objects/util.py b/git/objects/util.py index 0b449b7b..5de9c3e9 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -33,7 +33,7 @@ if TYPE_CHECKING: from .tree import Tree, TraversedTreeTup from subprocess import Popen - + T_TIobj = TypeVar('T_TIobj', bound='TraversableIterableObj') # for TraversableIterableObj.traverse() TraversedTup = Union[Tuple[Union['Traversable', None], 'Traversable'], # for commit, submodule @@ -314,9 +314,9 @@ class Traversable(object): def is_TraversableIterableObj(inp: 'Traversable') -> TypeGuard['TraversableIterableObj']: # return isinstance(self, TraversableIterableObj) - # Can it be anythin else? - return isinstance(self, Traversable) - + # Can it be anything else? Check this + return isinstance(self, TraversableIterableObj) + assert is_TraversableIterableObj(self), f"{type(self)}" out: IterableList['TraversableIterableObj'] = IterableList(self._id_attribute_) out.extend(self.traverse(*args, **kwargs)) @@ -364,7 +364,7 @@ class Traversable(object): Submodule -> Iterator[Submodule, Tuple[Submodule, Submodule]] Tree -> Iterator[Union[Blob, Tree, Submodule, Tuple[Union[Submodule, Tree], Union[Blob, Tree, Submodule]]] - + ignore_self=True is_edge=True -> Iterator[item] ignore_self=True is_edge=False --> Iterator[item] ignore_self=False is_edge=True -> Iterator[item] | Iterator[Tuple[src, item]] -- cgit v1.2.1 From 4802a36bd0fec7e6ae03d6713ceae70de8e1783a Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 00:07:42 +0100 Subject: improve TraversableIterableObj typeguard --- git/objects/util.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'git/objects') diff --git a/git/objects/util.py b/git/objects/util.py index 5de9c3e9..5fb4c58a 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -317,10 +317,12 @@ class Traversable(object): # Can it be anything else? Check this return isinstance(self, TraversableIterableObj) - assert is_TraversableIterableObj(self), f"{type(self)}" - out: IterableList['TraversableIterableObj'] = IterableList(self._id_attribute_) - out.extend(self.traverse(*args, **kwargs)) - return out + if is_TraversableIterableObj(self): + out: IterableList['TraversableIterableObj'] = IterableList(self._id_attribute_) + out.extend(self.traverse(*args, **kwargs)) + return out + else: + return IterableList("") # Its a Tree, which doesnt have _id_attribute_ def traverse(self, predicate: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: True, -- cgit v1.2.1 From 1faa25fcf828062d2c4ad35a962b4d8d3b05e855 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 00:56:20 +0100 Subject: Rmv typeguard from list_traverse(), was wrong --- git/objects/util.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'git/objects') diff --git a/git/objects/util.py b/git/objects/util.py index 5fb4c58a..7173bc7a 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -23,7 +23,7 @@ from datetime import datetime, timedelta, tzinfo from typing import (Any, Callable, Deque, Iterator, NamedTuple, overload, Sequence, TYPE_CHECKING, Tuple, Type, TypeVar, Union, cast) -from git.types import Literal, TypeGuard +from git.types import Literal if TYPE_CHECKING: from io import BytesIO, StringIO @@ -306,23 +306,19 @@ class Traversable(object): """ raise NotImplementedError("To be implemented in subclass") - def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList['TraversableIterableObj']: + def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList: """ :return: IterableList with the results of the traversal as produced by traverse() - List objects must be IterableObj and Traversable e.g. Commit, Submodule""" - - def is_TraversableIterableObj(inp: 'Traversable') -> TypeGuard['TraversableIterableObj']: - # return isinstance(self, TraversableIterableObj) - # Can it be anything else? Check this - return isinstance(self, TraversableIterableObj) + """ + if isinstance(self, TraversableIterableObj): + id = self._id_attribute_ + else: # Tree + id = "" - if is_TraversableIterableObj(self): - out: IterableList['TraversableIterableObj'] = IterableList(self._id_attribute_) - out.extend(self.traverse(*args, **kwargs)) - return out - else: - return IterableList("") # Its a Tree, which doesnt have _id_attribute_ + out: IterableList = IterableList(id) + out.extend(self.traverse(*args, **kwargs)) + return out def traverse(self, predicate: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: True, @@ -449,7 +445,7 @@ class TraversableIterableObj(Traversable, IterableObj): TIobj_tuple = Tuple[Union[T_TIobj, None], T_TIobj] - @overload # type: ignore + @ overload # type: ignore def traverse(self: T_TIobj, predicate: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], prune: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], @@ -459,7 +455,7 @@ class TraversableIterableObj(Traversable, IterableObj): ) -> Iterator[T_TIobj]: ... - @overload + @ overload def traverse(self: T_TIobj, predicate: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], prune: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], @@ -469,7 +465,7 @@ class TraversableIterableObj(Traversable, IterableObj): ) -> Iterator[Tuple[Union[T_TIobj, None], T_TIobj]]: ... - @overload + @ overload def traverse(self: T_TIobj, predicate: Callable[[Union[T_TIobj, TIobj_tuple], int], bool], prune: Callable[[Union[T_TIobj, TIobj_tuple], int], bool], -- cgit v1.2.1 From f4cb7dbc707ea83f312aa669f3bea4dc10d3a24c Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 10:08:58 +0100 Subject: Change type of list_traverse() again. --- git/objects/util.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'git/objects') diff --git a/git/objects/util.py b/git/objects/util.py index 7173bc7a..982e7ac7 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -32,6 +32,7 @@ if TYPE_CHECKING: from .tag import TagObject from .tree import Tree, TraversedTreeTup from subprocess import Popen + from .submodule.base import Submodule T_TIobj = TypeVar('T_TIobj', bound='TraversableIterableObj') # for TraversableIterableObj.traverse() @@ -306,18 +307,28 @@ class Traversable(object): """ raise NotImplementedError("To be implemented in subclass") - def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList: + def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']]: """ :return: IterableList with the results of the traversal as produced by traverse() + Commit -> IterableList['Commit'] + Submodule -> IterableList['Submodule'] + Tree -> IterableList[Union['Submodule', 'Tree', 'Blob']] """ - if isinstance(self, TraversableIterableObj): + # Commit and Submodule have id.__attribute__ as IterableObj + # Tree has id.__attribute__ inherited from IndexObject + if isinstance(self, (TraversableIterableObj, Tree)): id = self._id_attribute_ - else: # Tree - id = "" + else: + id = "" # shouldn't reach here, unless Traversable subclass created with no _id_attribute_ + # could add _id_attribute_ to Traversable, or make all Traversable also Iterable? + + out: IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']] = IterableList(id) + # overloads in subclasses (mypy does't allow typing self: subclass) + # Union[IterableList['Commit'], IterableList['Submodule'], IterableList[Union['Submodule', 'Tree', 'Blob']]] - out: IterableList = IterableList(id) - out.extend(self.traverse(*args, **kwargs)) + # NOTE: if is_edge=True, self.traverse returns a Tuple, so should be prevented or flattened? + out.extend(self.traverse(*args, **kwargs)) # type: ignore return out def traverse(self, -- cgit v1.2.1 From 030b1fded8b8e1bcf3855beaf9035b4e3e755f5c Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 10:23:14 +0100 Subject: Add list_traverse() to Tree and TraversableIterableObj. --- git/objects/tree.py | 7 +++++-- git/objects/util.py | 7 ++++++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'git/objects') diff --git a/git/objects/tree.py b/git/objects/tree.py index d3681e23..804554d8 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -4,7 +4,7 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -from git.util import join_path +from git.util import IterableList, join_path import git.diff as diff from git.util import to_bin_sha @@ -21,7 +21,7 @@ from .fun import ( # typing ------------------------------------------------- -from typing import (Callable, Dict, Iterable, Iterator, List, +from typing import (Any, Callable, Dict, Iterable, Iterator, List, Tuple, Type, Union, cast, TYPE_CHECKING) from git.types import PathLike, TypeGuard @@ -323,6 +323,9 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): super(Tree, self).traverse(predicate, prune, depth, # type: ignore branch_first, visit_once, ignore_self)) + def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[Union['Tree', 'Submodule', 'Blob']]: + return super(Tree, self).list_traverse(* args, **kwargs) + # List protocol def __getslice__(self, i: int, j: int) -> List[IndexObjUnion]: diff --git a/git/objects/util.py b/git/objects/util.py index 982e7ac7..4dce0aee 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -19,6 +19,8 @@ import time import calendar from datetime import datetime, timedelta, tzinfo +from git.objects.base import IndexObject # just for an isinstance check + # typing ------------------------------------------------------------ from typing import (Any, Callable, Deque, Iterator, NamedTuple, overload, Sequence, TYPE_CHECKING, Tuple, Type, TypeVar, Union, cast) @@ -317,7 +319,7 @@ class Traversable(object): """ # Commit and Submodule have id.__attribute__ as IterableObj # Tree has id.__attribute__ inherited from IndexObject - if isinstance(self, (TraversableIterableObj, Tree)): + if isinstance(self, (TraversableIterableObj, IndexObject)): id = self._id_attribute_ else: id = "" # shouldn't reach here, unless Traversable subclass created with no _id_attribute_ @@ -456,6 +458,9 @@ class TraversableIterableObj(Traversable, IterableObj): TIobj_tuple = Tuple[Union[T_TIobj, None], T_TIobj] + def list_traverse(self: T_TIobj, *args: Any, **kwargs: Any) -> IterableList[T_TIobj]: # type: ignore[override] + return super(TraversableIterableObj, self).list_traverse(* args, **kwargs) + @ overload # type: ignore def traverse(self: T_TIobj, predicate: Callable[[Union[T_TIobj, Tuple[Union[T_TIobj, None], T_TIobj]], int], bool], -- cgit v1.2.1 From 3710e24d6b60213454af10b0dc0ff0c49717169f Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 10:33:12 +0100 Subject: Rmv circular import, create Has_id_attribute Protocol instead --- git/objects/tree.py | 2 +- git/objects/util.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'git/objects') diff --git a/git/objects/tree.py b/git/objects/tree.py index 804554d8..e168c6c4 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -323,7 +323,7 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): super(Tree, self).traverse(predicate, prune, depth, # type: ignore branch_first, visit_once, ignore_self)) - def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[Union['Tree', 'Submodule', 'Blob']]: + def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[IndexObjUnion]: return super(Tree, self).list_traverse(* args, **kwargs) # List protocol diff --git a/git/objects/util.py b/git/objects/util.py index 4dce0aee..1c266563 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -19,13 +19,11 @@ import time import calendar from datetime import datetime, timedelta, tzinfo -from git.objects.base import IndexObject # just for an isinstance check - # typing ------------------------------------------------------------ from typing import (Any, Callable, Deque, Iterator, NamedTuple, overload, Sequence, TYPE_CHECKING, Tuple, Type, TypeVar, Union, cast) -from git.types import Literal +from git.types import Has_id_attribute, Literal if TYPE_CHECKING: from io import BytesIO, StringIO @@ -319,7 +317,7 @@ class Traversable(object): """ # Commit and Submodule have id.__attribute__ as IterableObj # Tree has id.__attribute__ inherited from IndexObject - if isinstance(self, (TraversableIterableObj, IndexObject)): + if isinstance(self, (TraversableIterableObj, Has_id_attribute)): id = self._id_attribute_ else: id = "" # shouldn't reach here, unless Traversable subclass created with no _id_attribute_ -- cgit v1.2.1 From 5eea8910b2e07d424a2e33299149d13392a80a54 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 10:37:31 +0100 Subject: Fix list_traverse() docstring for Autodoc --- git/objects/tree.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'git/objects') diff --git a/git/objects/tree.py b/git/objects/tree.py index e168c6c4..9d221652 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -324,6 +324,11 @@ class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): branch_first, visit_once, ignore_self)) def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[IndexObjUnion]: + """ + :return: IterableList with the results of the traversal as produced by + traverse() + Tree -> IterableList[Union['Submodule', 'Tree', 'Blob']] + """ return super(Tree, self).list_traverse(* args, **kwargs) # List protocol -- cgit v1.2.1 From 3c6deb002c82c852bbd044fc9af2c1ecc9611efb Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 11:56:16 +0100 Subject: Flatten list_traverse() --- git/objects/util.py | 1 + 1 file changed, 1 insertion(+) (limited to 'git/objects') diff --git a/git/objects/util.py b/git/objects/util.py index 1c266563..d3f3a622 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -328,6 +328,7 @@ class Traversable(object): # Union[IterableList['Commit'], IterableList['Submodule'], IterableList[Union['Submodule', 'Tree', 'Blob']]] # NOTE: if is_edge=True, self.traverse returns a Tuple, so should be prevented or flattened? + kwargs['as_edge'] = False out.extend(self.traverse(*args, **kwargs)) # type: ignore return out -- cgit v1.2.1 From a024bddd2a36c67967eda4e9f931c648924f0b19 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 14:27:40 +0100 Subject: Move TraverseNT to global, cos mypy complained on testing --- git/objects/submodule/base.py | 1 + git/objects/util.py | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index 5539069c..f366e44c 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -425,6 +425,7 @@ class Submodule(IndexObject, TraversableIterableObj): raise ValueError("A URL was not given and a repository did not exist at %s" % path) # END check url mrepo = sm.module() + assert isinstance(mrepo, Repo) urls = [r.url for r in mrepo.remotes] if not urls: raise ValueError("Didn't find any remote url in repository at %s" % sm.abspath) diff --git a/git/objects/util.py b/git/objects/util.py index d3f3a622..fbe3d9de 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -35,6 +35,12 @@ if TYPE_CHECKING: from .submodule.base import Submodule +class TraverseNT(NamedTuple): + depth: int + item: Union['Traversable', 'Blob'] + src: Union['Traversable', None] + + T_TIobj = TypeVar('T_TIobj', bound='TraversableIterableObj') # for TraversableIterableObj.traverse() TraversedTup = Union[Tuple[Union['Traversable', None], 'Traversable'], # for commit, submodule @@ -379,10 +385,6 @@ class Traversable(object): ignore_self=True is_edge=False --> Iterator[item] ignore_self=False is_edge=True -> Iterator[item] | Iterator[Tuple[src, item]] ignore_self=False is_edge=False -> Iterator[Tuple[src, item]]""" - class TraverseNT(NamedTuple): - depth: int - item: Union['Traversable', 'Blob'] - src: Union['Traversable', None] visited = set() stack = deque() # type: Deque[TraverseNT] -- cgit v1.2.1 From 627166094f9280a3e00b755b754a4bd6ed72bb66 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 14:33:15 +0100 Subject: Rmv submodule.base Repo assert --- git/objects/submodule/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'git/objects') diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index f366e44c..b485dbf6 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -425,7 +425,7 @@ class Submodule(IndexObject, TraversableIterableObj): raise ValueError("A URL was not given and a repository did not exist at %s" % path) # END check url mrepo = sm.module() - assert isinstance(mrepo, Repo) + # assert isinstance(mrepo, git.Repo) urls = [r.url for r in mrepo.remotes] if not urls: raise ValueError("Didn't find any remote url in repository at %s" % sm.abspath) -- cgit v1.2.1 From 7c6ae2b94cfd1593c12366b6abc0cd5bbb6e07b2 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 15:07:50 +0100 Subject: Try to distinguation git.diff module from diff.Diff.diff and diff.Daffable.diff() --- git/objects/tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git/objects') diff --git a/git/objects/tree.py b/git/objects/tree.py index 9d221652..4c2a02bf 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -5,7 +5,7 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php from git.util import IterableList, join_path -import git.diff as diff +import git.diff as git_diff from git.util import to_bin_sha from . import util @@ -180,7 +180,7 @@ class TreeModifier(object): #} END mutators -class Tree(IndexObject, diff.Diffable, util.Traversable, util.Serializable): +class Tree(IndexObject, git_diff.Diffable, util.Traversable, util.Serializable): """Tree objects represent an ordered list of Blobs and other Trees. -- cgit v1.2.1 From e6a27adb71d21c81628acbdd65bf07037604ff90 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 23:33:53 +0100 Subject: Use TreeCacheTup type alias throughout --- git/objects/fun.py | 2 +- git/objects/tree.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'git/objects') diff --git a/git/objects/fun.py b/git/objects/fun.py index be541eb8..fc2ea1e7 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -215,7 +215,7 @@ def traverse_trees_recursive(odb: 'GitCmdObjectDB', tree_shas: Sequence[Union[by return out -def traverse_tree_recursive(odb: 'GitCmdObjectDB', tree_sha: bytes, path_prefix: str) -> List[Tuple[bytes, int, str]]: +def traverse_tree_recursive(odb: 'GitCmdObjectDB', tree_sha: bytes, path_prefix: str) -> List[EntryTup]: """ :return: list of entries of the tree pointed to by the binary tree_sha. An entry has the following format: diff --git a/git/objects/tree.py b/git/objects/tree.py index 4c2a02bf..a9656c1d 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -31,9 +31,14 @@ if TYPE_CHECKING: from io import BytesIO TreeCacheTup = Tuple[bytes, int, str] + TraversedTreeTup = Union[Tuple[Union['Tree', None], IndexObjUnion, Tuple['Submodule', 'Submodule']]] + +def is_tree_cache(inp: Tuple[bytes, int, str]) -> TypeGuard[TreeCacheTup]: + return isinstance(inp[0], bytes) and isinstance(inp[1], int) and isinstance([inp], str) + #-------------------------------------------------------- @@ -141,11 +146,8 @@ class TreeModifier(object): sha = to_bin_sha(sha) index = self._index_by_name(name) - def is_tree_cache(inp: Tuple[bytes, int, str]) -> TypeGuard[TreeCacheTup]: - return isinstance(inp[0], bytes) and isinstance(inp[1], int) and isinstance([inp], str) - item = (sha, mode, name) - assert is_tree_cache(item) + # assert is_tree_cache(item) if index == -1: self._cache.append(item) @@ -223,12 +225,12 @@ class Tree(IndexObject, git_diff.Diffable, util.Traversable, util.Serializable): if attr == "_cache": # Set the data when we need it ostream = self.repo.odb.stream(self.binsha) - self._cache: List[Tuple[bytes, int, str]] = tree_entries_from_data(ostream.read()) + self._cache: List[TreeCacheTup] = tree_entries_from_data(ostream.read()) else: super(Tree, self)._set_cache_(attr) # END handle attribute - def _iter_convert_to_object(self, iterable: Iterable[Tuple[bytes, int, str]] + def _iter_convert_to_object(self, iterable: Iterable[TreeCacheTup] ) -> Iterator[IndexObjUnion]: """Iterable yields tuples of (binsha, mode, name), which will be converted to the respective object representation""" -- cgit v1.2.1