From 1e5a9449573bfe70987dcaf44e120895131e9c66 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Sun, 17 Mar 2024 19:25:34 -0400 Subject: [PATCH 01/34] Add a script to validate refactored imports This script can be removed after imports are refactored and checked to see that module contents are the same as before or otherwise non-broken. This script assumes that module contents are the same as the contents of their dictionaries, and that all modules in the project get imported as a consequence of importing the top-level module. These are both the case currently for GitPython, but they do not hold for all projects, and may not hold for GitPython at some point in the future. --- .gitignore | 4 ++++ modattrs.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100755 modattrs.py diff --git a/.gitignore b/.gitignore index 7765293d8..1be4f3201 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,7 @@ output.txt # Finder metadata .DS_Store + +# Output files for modattrs.py (these entries will be removed soon) +a +b diff --git a/modattrs.py b/modattrs.py new file mode 100755 index 000000000..245f68912 --- /dev/null +++ b/modattrs.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +"""Script to get the names and "stabilized" reprs of module attributes in GitPython. + +Run with :envvar:`PYTHONHASHSEED` set to ``0`` for fully comparable results. These are +only still meaningful for comparing if the same platform and Python version are used. + +The output of this script should probably not be committed, because within the reprs of +objects found in modules, it may contain sensitive information, such as API keys stored +in environment variables. The "sanitization" performed here is only for common forms of +whitespace that clash with the output format. +""" + +# fmt: off + +__all__ = ["git", "main"] + +import itertools +import re +import sys + +import git + + +def main(): + # This assumes `import git` causes all of them to be loaded. + gitpython_modules = sorted( + (module_name, module) + for module_name, module in sys.modules.items() + if re.match(r"git(?:\.|$)", module_name) + ) + + # We will print two blank lines between successive module reports. + separators = itertools.chain(("",), itertools.repeat("\n\n")) + + # Report each module's contents. + for (module_name, module), separator in zip(gitpython_modules, separators): + print(f"{separator}{module_name}:") + + attributes = sorted( + (name, value) + for name, value in module.__dict__.items() + if name != "__all__" # Because we are deliberately adding these. + ) + + for name, value in attributes: + sanitized_repr = re.sub(r"[\r\n\v\f]", "?", repr(value)) + normalized_repr = re.sub(r" at 0x[0-9a-fA-F]+", " at 0x...", sanitized_repr) + print(f" {name}: {normalized_repr}") + + +if __name__ == "__main__": + main() From 5b2771d23af2156fb7e12ee6406bffbabcb9e95d Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 10:59:37 -0400 Subject: [PATCH 02/34] Add regression tests of the git.util aliasing situation Although this situation is not inherently desirable, for backward compatibility it cannot change at this time. It may be possible to change it in the next major version of GitPython, but even then it should not be changed accidentally, which can easily happen while refactoring imports. This tests the highest-risk accidental change (of those that are currently known) of the kind that the temporary modattrs.py script exists to help safeguard against. That script will be removed when the immediately forthcoming import refactoring is complete, whereas these test cases can be kept. For information about the specific situation this helps ensure isn't changed accidentally, see the new test cases' docstrings, as well as the next commit (which will test modattrs.py and these test cases by performing an incomplete change that would be a bug until completed). This commit adds three test cases. The first tests the unintuitive aspect of the current situation: - test_git_util_attribute_is_git_index_util The other two test the intuitive aspects of it, i.e., they test that changes (perhaps in an attempt to preserve the aspect needed for backward compatibility) do not make `git.util` unusual in new (and themselves incompatible) ways: - test_git_index_util_attribute_is_git_index_util - test_separate_git_util_module_exists The latter tests should also clarify, for readers of the tests, the limited nature of the condition the first test asserts. --- test/test_imports.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/test_imports.py diff --git a/test/test_imports.py b/test/test_imports.py new file mode 100644 index 000000000..8e70c6689 --- /dev/null +++ b/test/test_imports.py @@ -0,0 +1,32 @@ +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ + +import sys + +import git + + +def test_git_util_attribute_is_git_index_util(): + """The top-level module's ``util`` attribute is really :mod:`git.index.util`. + + Although this situation is unintuitive and not a design goal, this has historically + been the case, and it should not be changed without considering the effect on + backward compatibility. In practice, it cannot be changed at least until the next + major version of GitPython. This test checks that it is not accidentally changed, + which could happen when refactoring imports. + """ + assert git.util is git.index.util + + +def test_git_index_util_attribute_is_git_index_util(): + """Nothing unusual is happening with git.index.util itself.""" + assert git.index.util is sys.modules["git.index.util"] + + +def test_separate_git_util_module_exists(): + """The real git.util and git.index.util modules really are separate. + + The real git.util module can be accessed to import a name ``...` by writing + ``from git.util import ...``, and the module object can be accessed in sys.modules. + """ + assert sys.modules["git.util"] is not sys.modules["git.index.util"] From fc86a238f99a6406ed949a7d690ec0f6be2f31e0 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Sat, 24 Feb 2024 10:52:49 -0500 Subject: [PATCH 03/34] Incompletely change git.index imports to test modattrs.py This also checks if the regression tests in test_imports.py work. This replaces wildcard imports in git.index with explicit imports, and adds git.index.__all__. But this temporarily omits from it the public attributes of git.index that name its Python submodules and are thus are present as an indirect effect of importing names *from* them (since doing so also imports them). This partial change, until it is completed in the next commit, represents the kind of bug that modattrs.py seeks to safeguard against, and verifies that a diff between old and current output of modattrs.py clearly shows it. This diff is temporarily included as ab.diff, which will be deleted in the next commit. The key problem that diff reveals is the changed value of the util attribute of the top-level git module. Due to the way wildcard imports have been used within GitPython for its own modules, expressions like `git.util` (where `git` is the top-level git module) and imports like `from git import util` are actually referring to git.index.util, rather than the util Python submodule of the git module (conceptually git.util), which can only be accessed via `from git.util import ...` or in `sys.modules`. Although this is not an inherently good situation, in practice it has to be maintained at least until the next major version, because reasonable code that uses GitPython would be broken if the situation were changed to be more intuitive. But the more intuitive behavior automatically happens if wildcard imports are replaced with explicit imports of the names they had originally intended to import (in this case, in the top-level git module, which has not yet been done but will be), or if __all__ is added where helpful (in this case, in git.index, which this does). Adding the names explicitly is sufficient to restore the old behavior that is needed for backward compatibility, and has the further advantage that the unintuitive behavior will be explicit once all wildcard imports are replaced and __all__ is added to each module where it would be helpful. The modattrs.py script serves to ensure incompatible changes are not made; this commit checks it. The tests in test_imports.py also cover this specific anticipated incompatibility in git.util, but not all breakages that may arise when refactoring imports and that diff-ing modattrs.py output would help catch. --- ab.diff | 30 ++++++++++++++++++++++++++++++ git/index/__init__.py | 13 +++++++++++-- git/index/base.py | 5 ++--- git/index/typ.py | 5 ++--- 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 ab.diff diff --git a/ab.diff b/ab.diff new file mode 100644 index 000000000..34abf9b16 --- /dev/null +++ b/ab.diff @@ -0,0 +1,30 @@ +diff --git a/a b/b +index 81b3f984..7b8c8ede 100644 +--- a/a ++++ b/b +@@ -83,14 +83,12 @@ git: + __path__: ['C:\\Users\\ek\\source\\repos\\GitPython\\git'] + __spec__: ModuleSpec(name='git', loader=<_frozen_importlib_external.SourceFileLoader object at 0x...>, origin='C:\\Users\\ek\\source\\repos\\GitPython\\git\\__init__.py', submodule_search_locations=['C:\\Users\\ek\\source\\repos\\GitPython\\git']) + __version__: 'git' +- base: + cmd: + compat: + config: + db: + diff: + exc: +- fun: + head: + index: + log: +@@ -106,9 +104,8 @@ git: + symbolic: + tag: + to_hex_sha: +- typ: + types: +- util: ++ util: + + + git.cmd: diff --git a/git/index/__init__.py b/git/index/__init__.py index c65722cd8..ba48110fd 100644 --- a/git/index/__init__.py +++ b/git/index/__init__.py @@ -3,5 +3,14 @@ """Initialize the index package.""" -from .base import * # noqa: F401 F403 -from .typ import * # noqa: F401 F403 +__all__ = [ + "BaseIndexEntry", + "BlobFilter", + "CheckoutError", + "IndexEntry", + "IndexFile", + "StageType", +] + +from .base import CheckoutError, IndexFile +from .typ import BaseIndexEntry, BlobFilter, IndexEntry, StageType diff --git a/git/index/base.py b/git/index/base.py index fb91e092c..38c14252b 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -6,6 +6,8 @@ """Module containing :class:`IndexFile`, an Index implementation facilitating all kinds of index manipulations such as querying and merging.""" +__all__ = ("IndexFile", "CheckoutError", "StageType") + import contextlib import datetime import glob @@ -81,9 +83,6 @@ # ------------------------------------------------------------------------------------ -__all__ = ("IndexFile", "CheckoutError", "StageType") - - @contextlib.contextmanager def _named_temporary_file_for_subprocess(directory: PathLike) -> Generator[str, None, None]: """Create a named temporary file git subprocesses can open, deleting it afterward. diff --git a/git/index/typ.py b/git/index/typ.py index ffd76dc46..eb0dd4341 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -3,13 +3,14 @@ """Additional types used by the index.""" +__all__ = ("BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType") + from binascii import b2a_hex from pathlib import Path from .util import pack, unpack from git.objects import Blob - # typing ---------------------------------------------------------------------- from typing import NamedTuple, Sequence, TYPE_CHECKING, Tuple, Union, cast @@ -23,8 +24,6 @@ # --------------------------------------------------------------------------------- -__all__ = ("BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType") - # { Invariants CE_NAMEMASK = 0x0FFF CE_STAGEMASK = 0x3000 From 4badc19f489c177a976d99da85d3c632797f7aeb Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 12:16:33 -0400 Subject: [PATCH 04/34] Fix git.index imports - Add the base, fun, typ, and util Python submodules of git.index to git.index.__all__ to restore the old behavior. - Import them explicitly for clarity, even though they are bound to the same-named attributes of git.index either way. This should help human readers know what the entries in __all__ are referring to, and may also help some automated tools. This is a preliminary decision and not necessarily the one that will ultimately be taken in this import refactoring. It *may* cause some kinds of mistakes, such as those arising from ill-advised use of wildcard imports in production code *outside* GitPython, somewhat worse in their effects. - Remove the ab.diff file that showed the problem this solves. This resolves the problem deliberately introduced in the previous incomplete commit, which modattrs.py output and test_imports test results confirm. --- ab.diff | 30 ------------------------------ git/index/__init__.py | 5 +++++ 2 files changed, 5 insertions(+), 30 deletions(-) delete mode 100644 ab.diff diff --git a/ab.diff b/ab.diff deleted file mode 100644 index 34abf9b16..000000000 --- a/ab.diff +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/a b/b -index 81b3f984..7b8c8ede 100644 ---- a/a -+++ b/b -@@ -83,14 +83,12 @@ git: - __path__: ['C:\\Users\\ek\\source\\repos\\GitPython\\git'] - __spec__: ModuleSpec(name='git', loader=<_frozen_importlib_external.SourceFileLoader object at 0x...>, origin='C:\\Users\\ek\\source\\repos\\GitPython\\git\\__init__.py', submodule_search_locations=['C:\\Users\\ek\\source\\repos\\GitPython\\git']) - __version__: 'git' -- base: - cmd: - compat: - config: - db: - diff: - exc: -- fun: - head: - index: - log: -@@ -106,9 +104,8 @@ git: - symbolic: - tag: - to_hex_sha: -- typ: - types: -- util: -+ util: - - - git.cmd: diff --git a/git/index/__init__.py b/git/index/__init__.py index ba48110fd..2086d67f9 100644 --- a/git/index/__init__.py +++ b/git/index/__init__.py @@ -4,6 +4,10 @@ """Initialize the index package.""" __all__ = [ + "base", + "fun", + "typ", + "util", "BaseIndexEntry", "BlobFilter", "CheckoutError", @@ -12,5 +16,6 @@ "StageType", ] +from . import base, fun, typ, util from .base import CheckoutError, IndexFile from .typ import BaseIndexEntry, BlobFilter, IndexEntry, StageType From 1c9bda222077a6227beed2e06df6f26e2e864807 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 13:59:43 -0400 Subject: [PATCH 05/34] Improve relative order of import groups, and __all__, in git.index --- git/index/base.py | 5 +++-- git/index/fun.py | 41 ++++++++++++++++------------------------- git/index/typ.py | 3 ++- git/index/util.py | 7 ++----- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/git/index/base.py b/git/index/base.py index 38c14252b..b49841435 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -19,6 +19,9 @@ import sys import tempfile +from gitdb.base import IStream +from gitdb.db import MemoryDB + from git.compat import defenc, force_bytes import git.diff as git_diff from git.exc import CheckoutError, GitCommandError, GitError, InvalidGitRepositoryError @@ -33,8 +36,6 @@ unbare_repo, to_bin_sha, ) -from gitdb.base import IStream -from gitdb.db import MemoryDB from .fun import ( S_IFGITLINK, diff --git a/git/index/fun.py b/git/index/fun.py index 001e8f6f2..b24e803a3 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -4,22 +4,28 @@ """Standalone functions to accompany the index implementation and make it more versatile.""" +__all__ = ( + "write_cache", + "read_cache", + "write_tree_from_cache", + "entry_key", + "stat_mode_to_index_mode", + "S_IFGITLINK", + "run_commit_hook", + "hook_path", +) + from io import BytesIO import os import os.path as osp from pathlib import Path -from stat import ( - S_IFDIR, - S_IFLNK, - S_ISLNK, - S_ISDIR, - S_IFMT, - S_IFREG, - S_IXUSR, -) +from stat import S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_ISDIR, S_ISLNK, S_IXUSR import subprocess import sys +from gitdb.base import IStream +from gitdb.typ import str_tree_type + from git.cmd import handle_process_output, safer_popen from git.compat import defenc, force_bytes, force_text, safe_decode from git.exc import HookExecutionError, UnmergedEntriesError @@ -29,8 +35,6 @@ tree_to_stream, ) from git.util import IndexFileSHA1Writer, finalize_process -from gitdb.base import IStream -from gitdb.typ import str_tree_type from .typ import BaseIndexEntry, IndexEntry, CE_NAMEMASK, CE_STAGESHIFT from .util import pack, unpack @@ -42,31 +46,18 @@ from git.types import PathLike if TYPE_CHECKING: - from .base import IndexFile from git.db import GitCmdObjectDB from git.objects.tree import TreeCacheTup - # from git.objects.fun import EntryTupOrNone + from .base import IndexFile # ------------------------------------------------------------------------------------ - S_IFGITLINK = S_IFLNK | S_IFDIR """Flags for a submodule.""" CE_NAMEMASK_INV = ~CE_NAMEMASK -__all__ = ( - "write_cache", - "read_cache", - "write_tree_from_cache", - "entry_key", - "stat_mode_to_index_mode", - "S_IFGITLINK", - "run_commit_hook", - "hook_path", -) - def hook_path(name: str, git_dir: PathLike) -> str: """:return: path to the given named hook in the given git repository directory""" diff --git a/git/index/typ.py b/git/index/typ.py index eb0dd4341..0fbcd69f0 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -8,9 +8,10 @@ from binascii import b2a_hex from pathlib import Path -from .util import pack, unpack from git.objects import Blob +from .util import pack, unpack + # typing ---------------------------------------------------------------------- from typing import NamedTuple, Sequence, TYPE_CHECKING, Tuple, Union, cast diff --git a/git/index/util.py b/git/index/util.py index 0bad11571..4aee61bce 100644 --- a/git/index/util.py +++ b/git/index/util.py @@ -3,6 +3,8 @@ """Index utilities.""" +__all__ = ("TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir") + import contextlib from functools import wraps import os @@ -22,14 +24,9 @@ # --------------------------------------------------------------------------------- - -__all__ = ("TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir") - # { Aliases pack = struct.pack unpack = struct.unpack - - # } END aliases From 8b51af36a4943f75fb66d553d55e381859fe3a34 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 14:31:12 -0400 Subject: [PATCH 06/34] Improve order of imports and __all__ in git.refs submodules --- git/refs/head.py | 12 ++++++------ git/refs/log.py | 17 ++++++++--------- git/refs/reference.py | 9 +++++---- git/refs/remote.py | 11 +++++------ git/refs/symbolic.py | 11 ++++++----- git/refs/tag.py | 8 +++----- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/git/refs/head.py b/git/refs/head.py index 86321d9ea..7e6fc3377 100644 --- a/git/refs/head.py +++ b/git/refs/head.py @@ -6,12 +6,14 @@ Note the distinction between the :class:`HEAD` and :class:`Head` classes. """ +__all__ = ["HEAD", "Head"] + from git.config import GitConfigParser, SectionConstraint -from git.util import join_path from git.exc import GitCommandError +from git.util import join_path -from .symbolic import SymbolicReference from .reference import Reference +from .symbolic import SymbolicReference # typing --------------------------------------------------- @@ -26,8 +28,6 @@ # ------------------------------------------------------------------- -__all__ = ["HEAD", "Head"] - def strip_quotes(string: str) -> str: if string.startswith('"') and string.endswith('"'): @@ -36,8 +36,8 @@ def strip_quotes(string: str) -> str: class HEAD(SymbolicReference): - """Special case of a SymbolicReference representing the repository's HEAD - reference.""" + """Special case of a :class:`~git.refs.symbolic.SymbolicReference` representing the + repository's HEAD reference.""" _HEAD_NAME = "HEAD" _ORIG_HEAD_NAME = "ORIG_HEAD" diff --git a/git/refs/log.py b/git/refs/log.py index f98f56f11..17e3a94b3 100644 --- a/git/refs/log.py +++ b/git/refs/log.py @@ -1,44 +1,43 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ["RefLog", "RefLogEntry"] + from mmap import mmap +import os.path as osp import re import time as _time from git.compat import defenc from git.objects.util import ( - parse_date, Serializable, altz_to_utctz_str, + parse_date, ) from git.util import ( Actor, LockedFD, LockFile, assure_directory_exists, - to_native_path, bin_to_hex, file_contents_ro_filepath, + to_native_path, ) -import os.path as osp - - # typing ------------------------------------------------------------------ -from typing import Iterator, List, Tuple, Union, TYPE_CHECKING +from typing import Iterator, List, Tuple, TYPE_CHECKING, Union from git.types import PathLike if TYPE_CHECKING: from io import BytesIO - from git.refs import SymbolicReference + from git.config import GitConfigParser, SectionConstraint + from git.refs import SymbolicReference # ------------------------------------------------------------------------------ -__all__ = ["RefLog", "RefLogEntry"] - class RefLogEntry(Tuple[str, str, Actor, Tuple[int, int], str]): """Named tuple allowing easy access to the revlog data fields.""" diff --git a/git/refs/reference.py b/git/refs/reference.py index 62fb58420..e5d473779 100644 --- a/git/refs/reference.py +++ b/git/refs/reference.py @@ -1,7 +1,10 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ["Reference"] + from git.util import IterableObj, LazyMixin + from .symbolic import SymbolicReference, T_References # typing ------------------------------------------------------------------ @@ -15,8 +18,6 @@ # ------------------------------------------------------------------------------ -__all__ = ["Reference"] - # { Utilities @@ -34,7 +35,7 @@ def wrapper(self: T_References, *args: Any) -> _T: return wrapper -# }END utilities +# } END utilities class Reference(SymbolicReference, LazyMixin, IterableObj): @@ -142,7 +143,7 @@ def iter_items( but will return non-detached references as well.""" return cls._iter_items(repo, common_path) - # }END interface + # } END interface # { Remote Interface diff --git a/git/refs/remote.py b/git/refs/remote.py index 3f9c6c6be..b4f4f7b36 100644 --- a/git/refs/remote.py +++ b/git/refs/remote.py @@ -3,24 +3,23 @@ """Module implementing a remote object allowing easy access to git remotes.""" +__all__ = ["RemoteReference"] + import os from git.util import join_path from .head import Head - -__all__ = ["RemoteReference"] - # typing ------------------------------------------------------------------ -from typing import Any, Iterator, NoReturn, Union, TYPE_CHECKING -from git.types import PathLike +from typing import Any, Iterator, NoReturn, TYPE_CHECKING, Union +from git.types import PathLike if TYPE_CHECKING: + from git.remote import Remote from git.repo import Repo - from git import Remote # ------------------------------------------------------------------------------ diff --git a/git/refs/symbolic.py b/git/refs/symbolic.py index 2701f9f2b..510850b2e 100644 --- a/git/refs/symbolic.py +++ b/git/refs/symbolic.py @@ -1,10 +1,14 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ["SymbolicReference"] + import os +from gitdb.exc import BadName, BadObject + from git.compat import defenc -from git.objects import Object +from git.objects.base import Object from git.objects.commit import Commit from git.refs.log import RefLog from git.util import ( @@ -15,7 +19,6 @@ join_path_native, to_native_path_linux, ) -from gitdb.exc import BadName, BadObject # typing ------------------------------------------------------------------ @@ -30,6 +33,7 @@ Union, cast, ) + from git.types import AnyGitObject, PathLike if TYPE_CHECKING: @@ -45,9 +49,6 @@ # ------------------------------------------------------------------------------ -__all__ = ["SymbolicReference"] - - def _git_dir(repo: "Repo", path: Union[PathLike, None]) -> PathLike: """Find the git dir that is appropriate for the path.""" name = f"{path}" diff --git a/git/refs/tag.py b/git/refs/tag.py index f653d4e7d..1e38663ae 100644 --- a/git/refs/tag.py +++ b/git/refs/tag.py @@ -8,10 +8,10 @@ :mod:`git.objects.tag` module. """ -from .reference import Reference - __all__ = ["TagReference", "Tag"] +from .reference import Reference + # typing ------------------------------------------------------------------ from typing import Any, TYPE_CHECKING, Type, Union @@ -19,12 +19,10 @@ from git.types import AnyGitObject, PathLike if TYPE_CHECKING: - from git.objects import Commit - from git.objects import TagObject + from git.objects import Commit, TagObject from git.refs import SymbolicReference from git.repo import Repo - # ------------------------------------------------------------------------------ From b25dd7e6c1b5453fce111a470832e8942430c934 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 14:43:05 -0400 Subject: [PATCH 07/34] Replace wildcard imports in git.refs This uses explicit imports rather than wildcard imports in git.refs for names from its submodules. A comment suggested that the import order was deliberate. But each of the modules being imported from defined __all__, and there was no overlap in the names across any of them. The other main reason to import in a specific order is an order dependency in the side effects, but that does not appear to apply, at least not at this time. (In addition, at least for now, this adds explicit imports for the Python submodules of git.refs, so it is clear that they can always be accessed directly in git.refs without further imports, if desired. For clarity, those appear first, and that makes the order of the "from" imports not relevant to such side effects, due to the "from" imports no longer causing modules to be loaded for the first time. However, this is a much less important reason to consider the other imports' reordering okay, because these module imports may end up being removed later during this refactoring; their clarity benefit might not be justified, because if production code outside GitPython ill-advisedly uses wildcard imports, the bad effect of doing so could be increased.) --- git/refs/__init__.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/git/refs/__init__.py b/git/refs/__init__.py index b0233e902..04279901a 100644 --- a/git/refs/__init__.py +++ b/git/refs/__init__.py @@ -1,12 +1,28 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -# Import all modules in order, fix the names they require. +__all__ = [ + "head", + "log", + "reference", + "remote", + "symbolic", + "tag", + "HEAD", + "Head", + "RefLog", + "RefLogEntry", + "Reference", + "RemoteReference", + "SymbolicReference", + "Tag", + "TagReference", +] -from .symbolic import * # noqa: F401 F403 -from .reference import * # noqa: F401 F403 -from .head import * # noqa: F401 F403 -from .tag import * # noqa: F401 F403 -from .remote import * # noqa: F401 F403 - -from .log import * # noqa: F401 F403 +from . import head, log, reference, remote, symbolic, tag +from .head import HEAD, Head +from .log import RefLog, RefLogEntry +from .reference import Reference +from .remote import RemoteReference +from .symbolic import SymbolicReference +from .tag import Tag, TagReference From b32ef6528c0da39b91b6399c5593d10ca41c5865 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 15:01:33 -0400 Subject: [PATCH 08/34] Improve order of imports and __all__ in git.repo submodules --- git/repo/base.py | 4 ++-- git/repo/fun.py | 30 ++++++++++++++++-------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/git/repo/base.py b/git/repo/base.py index ce164274e..63b21fdbf 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -5,6 +5,8 @@ from __future__ import annotations +__all__ = ("Repo",) + import gc import logging import os @@ -92,8 +94,6 @@ _logger = logging.getLogger(__name__) -__all__ = ("Repo",) - class BlameEntry(NamedTuple): commit: Dict[str, Commit] diff --git a/git/repo/fun.py b/git/repo/fun.py index 738e5816d..1defcb1ac 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -5,18 +5,31 @@ from __future__ import annotations +__all__ = ( + "rev_parse", + "is_git_dir", + "touch", + "find_submodule_git_dir", + "name_to_object", + "short_to_long", + "deref_tag", + "to_commit", + "find_worktree_git_dir", +) + import os import os.path as osp from pathlib import Path import stat from string import digits +from gitdb.exc import BadName, BadObject + from git.cmd import Git from git.exc import WorkTreeRepositoryUnsupported from git.objects import Object from git.refs import SymbolicReference -from git.util import hex_to_bin, bin_to_hex, cygpath -from gitdb.exc import BadName, BadObject +from git.util import cygpath, bin_to_hex, hex_to_bin # Typing ---------------------------------------------------------------------- @@ -29,22 +42,11 @@ from git.objects import Commit, TagObject from git.refs.reference import Reference from git.refs.tag import Tag + from .base import Repo # ---------------------------------------------------------------------------- -__all__ = ( - "rev_parse", - "is_git_dir", - "touch", - "find_submodule_git_dir", - "name_to_object", - "short_to_long", - "deref_tag", - "to_commit", - "find_worktree_git_dir", -) - def touch(filename: str) -> str: with open(filename, "ab"): From 0ba06e9425e90f7f787ac39f48562af54ff41038 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 15:03:49 -0400 Subject: [PATCH 09/34] Add git.repo.__all__ and make submodules explicit - No need to import Repo "as Repo". Some tools only recognize this to give the name conceptually public status when it appears in type stub files (.pyi files), and listing it in the newly created __all__ is sufficient to let humans and all tools know it has that status. - As very recently done in git.refs, this explicitly imports the submodules, so it is clear they are available and don't have to be explicitly imported. (Fundamental to the way they are used is that they will end up being imported in order to import Repo.) However, also as in git.refs, it may be that the problems this could cause in some inherently flawed but plausible uses are too greater for it to be justified. So this may be revisited. --- git/repo/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/git/repo/__init__.py b/git/repo/__init__.py index 50a8c6f86..3b784b975 100644 --- a/git/repo/__init__.py +++ b/git/repo/__init__.py @@ -3,4 +3,7 @@ """Initialize the repo package.""" -from .base import Repo as Repo # noqa: F401 +__all__ = ["base", "fun", "Repo"] + +from . import base, fun +from .base import Repo From c946906c4e92512e4294f587eaccaf0029a425df Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 15:29:45 -0400 Subject: [PATCH 10/34] Improve order of imports and __all__ in git.objects.* But not yet in git.objects.submodule.* modules. --- git/objects/submodule/base.py | 4 ++-- git/objects/submodule/root.py | 5 +++-- git/objects/submodule/util.py | 24 +++++++++++------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/git/objects/submodule/base.py b/git/objects/submodule/base.py index d01aa448f..fa60bcdaf 100644 --- a/git/objects/submodule/base.py +++ b/git/objects/submodule/base.py @@ -1,6 +1,8 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ["Submodule", "UpdateProgress"] + import gc from io import BytesIO import logging @@ -68,8 +70,6 @@ # ----------------------------------------------------------------------------- -__all__ = ["Submodule", "UpdateProgress"] - _logger = logging.getLogger(__name__) diff --git a/git/objects/submodule/root.py b/git/objects/submodule/root.py index ae56e5ef4..d93193fa3 100644 --- a/git/objects/submodule/root.py +++ b/git/objects/submodule/root.py @@ -1,10 +1,13 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ["RootModule", "RootUpdateProgress"] + import logging import git from git.exc import InvalidGitRepositoryError + from .base import Submodule, UpdateProgress from .util import find_first_remote_branch @@ -20,8 +23,6 @@ # ---------------------------------------------------------------------------- -__all__ = ["RootModule", "RootUpdateProgress"] - _logger = logging.getLogger(__name__) diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 10b994e9b..fda81249b 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -1,12 +1,20 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -import git -from git.exc import InvalidGitRepositoryError -from git.config import GitConfigParser +__all__ = ( + "sm_section", + "sm_name", + "mkhead", + "find_first_remote_branch", + "SubmoduleConfigParser", +) + from io import BytesIO import weakref +import git +from git.config import GitConfigParser +from git.exc import InvalidGitRepositoryError # typing ----------------------------------------------------------------------- @@ -22,15 +30,6 @@ from git import Remote from git.refs import RemoteReference - -__all__ = ( - "sm_section", - "sm_name", - "mkhead", - "find_first_remote_branch", - "SubmoduleConfigParser", -) - # { Utilities @@ -65,7 +64,6 @@ def find_first_remote_branch(remotes: Sequence["Remote"], branch_name: str) -> " # } END utilities - # { Classes From 4e9a2f2facb0da3adb9c6fe5165dcfb5c6627cb7 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 15:42:09 -0400 Subject: [PATCH 11/34] Improve order of imports and __all__ in git.object.submodule.* --- git/objects/base.py | 12 ++++----- git/objects/blob.py | 6 ++--- git/objects/commit.py | 9 ++++--- git/objects/fun.py | 19 +++++++------- git/objects/submodule/util.py | 9 ++++--- git/objects/tag.py | 14 +++++++--- git/objects/tree.py | 9 +++---- git/objects/util.py | 49 +++++++++++++++++++---------------- 8 files changed, 68 insertions(+), 59 deletions(-) diff --git a/git/objects/base.py b/git/objects/base.py index 22d939aa6..07ff639e5 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -3,15 +3,17 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -import gitdb.typ as dbtyp +__all__ = ("Object", "IndexObject") + import os.path as osp +import gitdb.typ as dbtyp + from git.exc import WorkTreeRepositoryUnsupported -from git.util import LazyMixin, join_path_native, stream_copy, bin_to_hex +from git.util import LazyMixin, bin_to_hex, join_path_native, stream_copy from .util import get_object_type_by_name - # typing ------------------------------------------------------------------ from typing import Any, TYPE_CHECKING, Union @@ -24,16 +26,14 @@ from git.refs.reference import Reference from git.repo import Repo - from .tree import Tree from .blob import Blob from .submodule.base import Submodule + from .tree import Tree IndexObjUnion = Union["Tree", "Blob", "Submodule"] # -------------------------------------------------------------------------- -__all__ = ("Object", "IndexObject") - class Object(LazyMixin): """Base class for classes representing git object types. diff --git a/git/objects/blob.py b/git/objects/blob.py index 122d5f731..50500f550 100644 --- a/git/objects/blob.py +++ b/git/objects/blob.py @@ -3,17 +3,17 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ("Blob",) + from mimetypes import guess_type import sys -from . import base - if sys.version_info >= (3, 8): from typing import Literal else: from typing_extensions import Literal -__all__ = ("Blob",) +from . import base class Blob(base.IndexObject): diff --git a/git/objects/commit.py b/git/objects/commit.py index 6a60c30bd..b22308726 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -3,6 +3,8 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ("Commit",) + from collections import defaultdict import datetime from io import BytesIO @@ -14,10 +16,12 @@ from time import altzone, daylight, localtime, time, timezone from gitdb import IStream + from git.cmd import Git from git.diff import Diffable -from git.util import hex_to_bin, Actor, Stats, finalize_process +from git.util import Actor, Stats, finalize_process, hex_to_bin +from . import base from .tree import Tree from .util import ( Serializable, @@ -27,7 +31,6 @@ parse_actor_and_date, parse_date, ) -from . import base # typing ------------------------------------------------------------------ @@ -59,8 +62,6 @@ _logger = logging.getLogger(__name__) -__all__ = ("Commit",) - class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): """Wraps a git commit object. diff --git a/git/objects/fun.py b/git/objects/fun.py index 5bd8a3d62..9bd2d8f10 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -3,8 +3,14 @@ """Functions that are supposed to be as fast as possible.""" -from stat import S_ISDIR +__all__ = ( + "tree_to_stream", + "tree_entries_from_data", + "traverse_trees_recursive", + "traverse_tree_recursive", +) +from stat import S_ISDIR from git.compat import safe_decode, defenc @@ -23,22 +29,15 @@ if TYPE_CHECKING: from _typeshed import ReadableBuffer + from git import GitCmdObjectDB -EntryTup = Tuple[bytes, int, str] # same as TreeCacheTup in tree.py +EntryTup = Tuple[bytes, int, str] # Same as TreeCacheTup in tree.py. EntryTupOrNone = Union[EntryTup, None] # --------------------------------------------------- -__all__ = ( - "tree_to_stream", - "tree_entries_from_data", - "traverse_trees_recursive", - "traverse_tree_recursive", -) - - def tree_to_stream(entries: Sequence[EntryTup], write: Callable[["ReadableBuffer"], Union[int, None]]) -> None: """Write the given list of entries into a stream using its ``write`` method. diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index fda81249b..3a09d6a17 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -23,12 +23,13 @@ from git.types import PathLike if TYPE_CHECKING: - from .base import Submodule from weakref import ReferenceType + + from git.refs import Head, RemoteReference + from git.remote import Remote from git.repo import Repo - from git.refs import Head - from git import Remote - from git.refs import RemoteReference + + from .base import Submodule # { Utilities diff --git a/git/objects/tag.py b/git/objects/tag.py index 52d79751f..5ad311590 100644 --- a/git/objects/tag.py +++ b/git/objects/tag.py @@ -9,12 +9,17 @@ For lightweight tags, see the :mod:`git.refs.tag` module. """ +__all__ = ("TagObject",) + import sys +from git.compat import defenc +from git.util import hex_to_bin + from . import base from .util import get_object_type_by_name, parse_actor_and_date -from ..util import hex_to_bin -from ..compat import defenc + +# typing ---------------------------------------------- from typing import List, TYPE_CHECKING, Union @@ -26,11 +31,12 @@ if TYPE_CHECKING: from git.repo import Repo from git.util import Actor - from .commit import Commit + from .blob import Blob + from .commit import Commit from .tree import Tree -__all__ = ("TagObject",) +# --------------------------------------------------- class TagObject(base.Object): diff --git a/git/objects/tree.py b/git/objects/tree.py index c74df58a9..18ccf2f30 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -3,16 +3,18 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ("TreeModifier", "Tree") + import sys import git.diff as git_diff from git.util import IterableList, join_path, to_bin_sha +from . import util from .base import IndexObjUnion, IndexObject from .blob import Blob from .fun import tree_entries_from_data, tree_to_stream from .submodule.base import Submodule -from . import util # typing ------------------------------------------------- @@ -39,23 +41,20 @@ if TYPE_CHECKING: from io import BytesIO + from git.repo import Repo 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) # -------------------------------------------------------- - cmp: Callable[[str, str], int] = lambda a, b: (a > b) - (a < b) -__all__ = ("TreeModifier", "Tree") - class TreeModifier: """A utility class providing methods to alter the underlying cache in a list-like diff --git a/git/objects/util.py b/git/objects/util.py index 297b33b70..6f6927b47 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -5,26 +5,40 @@ """General utility functions.""" +__all__ = ( + "get_object_type_by_name", + "parse_date", + "parse_actor_and_date", + "ProcessStreamAdapter", + "Traversable", + "altz_to_utctz_str", + "utctz_to_altz", + "verify_utctz", + "Actor", + "tzoffset", + "utc", +) + from abc import ABC, abstractmethod import calendar from collections import deque from datetime import datetime, timedelta, tzinfo -from string import digits import re +from string import digits import time import warnings -from git.util import IterableList, IterableObj, Actor +from git.util import Actor, IterableList, IterableObj # typing ------------------------------------------------------------ + from typing import ( Any, Callable, Deque, - Iterator, # Generic, + Iterator, NamedTuple, - overload, Sequence, TYPE_CHECKING, Tuple, @@ -32,19 +46,22 @@ TypeVar, Union, cast, + overload, ) from git.types import Has_id_attribute, Literal # , _T if TYPE_CHECKING: from io import BytesIO, StringIO - from .commit import Commit - from .blob import Blob - from .tag import TagObject - from .tree import Tree, TraversedTreeTup from subprocess import Popen - from .submodule.base import Submodule + from git.types import Protocol, runtime_checkable + + from .blob import Blob + from .commit import Commit + from .submodule.base import Submodule + from .tag import TagObject + from .tree import TraversedTreeTup, Tree else: # Protocol = Generic[_T] # Needed for typing bug #572? Protocol = ABC @@ -68,20 +85,6 @@ class TraverseNT(NamedTuple): # -------------------------------------------------------------------- -__all__ = ( - "get_object_type_by_name", - "parse_date", - "parse_actor_and_date", - "ProcessStreamAdapter", - "Traversable", - "altz_to_utctz_str", - "utctz_to_altz", - "verify_utctz", - "Actor", - "tzoffset", - "utc", -) - ZERO = timedelta(0) # { Functions From c58be4c0b6e82dc1c0445f9e41e53263f5d2ec4e Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 15:44:52 -0400 Subject: [PATCH 12/34] Remove a bit of old commented-out code in git.objects.* --- git/objects/tree.py | 4 ---- git/objects/util.py | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/git/objects/tree.py b/git/objects/tree.py index 18ccf2f30..d8320b319 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -48,9 +48,6 @@ 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) - # -------------------------------------------------------- cmp: Callable[[str, str], int] = lambda a, b: (a > b) - (a < b) @@ -124,7 +121,6 @@ def add(self, sha: bytes, mode: int, name: str, force: bool = False) -> "TreeMod index = self._index_by_name(name) item = (sha, mode, name) - # assert is_tree_cache(item) if index == -1: self._cache.append(item) diff --git a/git/objects/util.py b/git/objects/util.py index 6f6927b47..9491b067c 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -36,7 +36,6 @@ Any, Callable, Deque, - # Generic, Iterator, NamedTuple, Sequence, @@ -49,7 +48,7 @@ overload, ) -from git.types import Has_id_attribute, Literal # , _T +from git.types import Has_id_attribute, Literal if TYPE_CHECKING: from io import BytesIO, StringIO @@ -63,7 +62,6 @@ from .tag import TagObject from .tree import TraversedTreeTup, Tree else: - # Protocol = Generic[_T] # Needed for typing bug #572? Protocol = ABC def runtime_checkable(f): From 01c95ebcc7d41bc68e1f64f9b1ec01c30f29d591 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 16:38:39 -0400 Subject: [PATCH 13/34] Don't patch IndexObject and Object into git.objects.submodule.util Such patching, which was introduced in 9519f18, seems no longer to be necessary. Since git.objects.submodule.util.__all__ has existed for a long time without including those names, they are not conceptually public attributes of git.objects.submodule.util, so they should not be in use by any code outside GitPython either. The modattrs.py script shows the change, as expected, showing these two names as no longer being in the git.objects.submodule.util module dictionary, in output of: python modattrs.py >b git diff --no-index a b However, because the removal is intentional, this is okay. --- git/objects/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/git/objects/__init__.py b/git/objects/__init__.py index 1061ec874..d636873a0 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -8,17 +8,10 @@ from .base import * # noqa: F403 from .blob import * # noqa: F403 from .commit import * # noqa: F403 -from .submodule import util as smutil from .submodule.base import * # noqa: F403 from .submodule.root import * # noqa: F403 from .tag import * # noqa: F403 from .tree import * # noqa: F403 -# Fix import dependency - add IndexObject to the util module, so that it can be imported -# by the submodule.base. -smutil.IndexObject = IndexObject # type: ignore[attr-defined] # noqa: F405 -smutil.Object = Object # type: ignore[attr-defined] # noqa: F405 -del smutil - # Must come after submodule was made available. __all__ = [name for name, obj in locals().items() if not (name.startswith("_") or inspect.ismodule(obj))] From f89d065742ba5b62056f1aa679e1197124bf7bcf Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 18:50:37 -0400 Subject: [PATCH 14/34] Fix git.objects.__all__ and make submodules explicit The submodules being made explicit here are of course Python submodules, not git submodules. The git.objects.submodule Python submodule (which is *about* git submodules) is made explicit here (as are the names imported from that Python submodule's own Python submodules) along with the other Python submodules of git.objects. Unlike some other submodules, but like the top-level git module until #1659, git.objects already defined __all__ but it was dynamically constructed. As with git.__all__ previously (as noted in #1859), I have used https://github.com/EliahKagan/deltall here to check that it is safe, sufficient, and probably necessary to replace this dynamically constructed __all__ with a literal list of strings in which all of the original elements are included. See: https://gist.github.com/EliahKagan/e627603717ca5f9cafaf8de9d9f27ad7 Running the modattrs.py script, and diffing against the output from before the current import refactoring began, reveals two changes to module contents as a result of the change here: - git.objects no longer contains `inspect`, which it imported just to build the dynamically constructed __all__. Because this was not itself included in that __all__ or otherwise made public or used out of git.objects, that is okay. This is exactly analogous to the situation in 8197e90, which removed it from the top-level git module after #1659. - The top-level git module now has has new attributes blob, commit, submodule, and tree, aliasing the corresponding modules. This has happened as a result of them being put in git.objects.__all__ along with various names imported from them. (As noted in some prior commits, there are tradeoffs associated with doing this, and it may be that such elements of __all__ should be removed, here and elsewhere.) --- git/objects/__init__.py | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/git/objects/__init__.py b/git/objects/__init__.py index d636873a0..692a468d6 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -3,15 +3,30 @@ """Import all submodules' main classes into the package space.""" -import inspect +__all__ = [ + "base", + "blob", + "commit", + "submodule", + "tag", + "tree", + "IndexObject", + "Object", + "Blob", + "Commit", + "Submodule", + "UpdateProgress", + "RootModule", + "RootUpdateProgress", + "TagObject", + "Tree", + "TreeModifier", +] -from .base import * # noqa: F403 -from .blob import * # noqa: F403 -from .commit import * # noqa: F403 -from .submodule.base import * # noqa: F403 -from .submodule.root import * # noqa: F403 -from .tag import * # noqa: F403 -from .tree import * # noqa: F403 - -# Must come after submodule was made available. -__all__ = [name for name, obj in locals().items() if not (name.startswith("_") or inspect.ismodule(obj))] +from .base import IndexObject, Object +from .blob import Blob +from .commit import Commit +from .submodule.base import Submodule, UpdateProgress +from .submodule.root import RootModule, RootUpdateProgress +from .tag import TagObject +from .tree import Tree, TreeModifier From 3786307f965d38cde9e251d191f71ff26903935d Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 19:10:17 -0400 Subject: [PATCH 15/34] Make git.objects.util module docstring more specific So git.objects.util is less likely to be confused with git.util. (The modattrs.py script reveals the change in the value of git.objects.util.__doc__, as expected.) --- git/objects/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/objects/util.py b/git/objects/util.py index 9491b067c..08aef132a 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -"""General utility functions.""" +"""General utility functions for working with git objects.""" __all__ = ( "get_object_type_by_name", From de540b78caa71c9f1a0f098c0d1608287c8927ac Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 19:17:48 -0400 Subject: [PATCH 16/34] Add __all__ and imports in git.objects.submodule This is the top-level of the git.objects.submodule subpackage, for its own Python submodules. This appears only not to have been done before because of a cyclic import problem involving imports that are no longer present. Doing it improves consistency, since all the other subpackages under the top-level git package already do this. The modattrs.py script reveals the four new attributes of git.objects.submodule for the four classes obtained by the new imports, as expected. (The explicit module imports do not change the attribues because they are the same attributes as come into existence due to those Python submodules being imported anywhere in any style.) --- git/objects/__init__.py | 3 +-- git/objects/submodule/__init__.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/git/objects/__init__.py b/git/objects/__init__.py index 692a468d6..4f18c8754 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -26,7 +26,6 @@ from .base import IndexObject, Object from .blob import Blob from .commit import Commit -from .submodule.base import Submodule, UpdateProgress -from .submodule.root import RootModule, RootUpdateProgress +from .submodule import RootModule, RootUpdateProgress, Submodule, UpdateProgress from .tag import TagObject from .tree import Tree, TreeModifier diff --git a/git/objects/submodule/__init__.py b/git/objects/submodule/__init__.py index b11b568f2..383439545 100644 --- a/git/objects/submodule/__init__.py +++ b/git/objects/submodule/__init__.py @@ -1,5 +1,16 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -# NOTE: Cannot import anything here as the top-level __init__ has to handle -# our dependencies. +__all__ = [ + "base", + "root", + "util", + "Submodule", + "UpdateProgress", + "RootModule", + "RootUpdateProgress", +] + +from . import base, root, util +from .base import Submodule, UpdateProgress +from .root import RootModule, RootUpdateProgress From a05597a1aeacec0c18772944208ab2757b0db34b Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 19:48:07 -0400 Subject: [PATCH 17/34] Improve how imports and __all__ are written in git.util + Update the detailed comment about the unused import situation. --- git/util.py | 102 +++++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/git/util.py b/git/util.py index 37986edaa..cf5949693 100644 --- a/git/util.py +++ b/git/util.py @@ -3,6 +3,32 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +import sys + +__all__ = [ + "stream_copy", + "join_path", + "to_native_path_linux", + "join_path_native", + "Stats", + "IndexFileSHA1Writer", + "IterableObj", + "IterableList", + "BlockingLockFile", + "LockFile", + "Actor", + "get_user_id", + "assure_directory_exists", + "RemoteProgress", + "CallableRemoteProgress", + "rmtree", + "unbare_repo", + "HIDE_WINDOWS_KNOWN_ERRORS", +] + +if sys.platform == "win32": + __all__.append("to_native_path_windows") + from abc import abstractmethod import contextlib from functools import wraps @@ -16,11 +42,27 @@ import shutil import stat import subprocess -import sys import time from urllib.parse import urlsplit, urlunsplit import warnings +# NOTE: Unused imports can be improved now that CI testing has fully resumed. Some of +# these be used indirectly through other GitPython modules, which avoids having to write +# gitdb all the time in their imports. They are not in __all__, at least currently, +# because they could be removed or changed at any time, and so should not be considered +# conceptually public to code outside GitPython. Linters of course do not like it. +from gitdb.util import ( # noqa: F401 # @IgnorePep8 + LazyMixin, # @UnusedImport + LockedFD, # @UnusedImport + bin_to_hex, # @UnusedImport + file_contents_ro_filepath, # @UnusedImport + file_contents_ro, # @UnusedImport + hex_to_bin, # @UnusedImport + make_sha, + to_bin_sha, # @UnusedImport + to_hex_sha, # @UnusedImport +) + # typing --------------------------------------------------------- from typing import ( @@ -37,73 +79,36 @@ Pattern, Sequence, Tuple, + TYPE_CHECKING, TypeVar, Union, - TYPE_CHECKING, cast, overload, ) if TYPE_CHECKING: + from git.cmd import Git + from git.config import GitConfigParser, SectionConstraint from git.remote import Remote from git.repo.base import Repo - from git.config import GitConfigParser, SectionConstraint - from git import Git -from .types import ( +from git.types import ( + Files_TD, + Has_id_attribute, + HSH_TD, Literal, - SupportsIndex, - Protocol, - runtime_checkable, # because behind py version guards PathLike, - HSH_TD, + Protocol, + SupportsIndex, Total_TD, - Files_TD, # aliases - Has_id_attribute, + runtime_checkable, ) # --------------------------------------------------------------------- -from gitdb.util import ( # noqa: F401 # @IgnorePep8 - make_sha, - LockedFD, # @UnusedImport - file_contents_ro, # @UnusedImport - file_contents_ro_filepath, # @UnusedImport - LazyMixin, # @UnusedImport - to_hex_sha, # @UnusedImport - to_bin_sha, # @UnusedImport - bin_to_hex, # @UnusedImport - hex_to_bin, # @UnusedImport -) - T_IterableObj = TypeVar("T_IterableObj", bound=Union["IterableObj", "Has_id_attribute"], covariant=True) # So IterableList[Head] is subtype of IterableList[IterableObj]. -# NOTE: Some of the unused imports might be used/imported by others. -# Handle once test-cases are back up and running. -# Most of these are unused here, but are for use by git-python modules so these -# don't see gitdb all the time. Flake of course doesn't like it. -__all__ = [ - "stream_copy", - "join_path", - "to_native_path_linux", - "join_path_native", - "Stats", - "IndexFileSHA1Writer", - "IterableObj", - "IterableList", - "BlockingLockFile", - "LockFile", - "Actor", - "get_user_id", - "assure_directory_exists", - "RemoteProgress", - "CallableRemoteProgress", - "rmtree", - "unbare_repo", - "HIDE_WINDOWS_KNOWN_ERRORS", -] - _logger = logging.getLogger(__name__) @@ -292,7 +297,6 @@ def to_native_path_linux(path: PathLike) -> str: path = str(path) return path.replace("\\", "/") - __all__.append("to_native_path_windows") to_native_path = to_native_path_windows else: # No need for any work on Linux. From 2053a3d66c1667f3b130347e9984779f94875719 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 19:58:34 -0400 Subject: [PATCH 18/34] Remove old commented-out change_type assertions in git.diff --- git/diff.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/git/diff.py b/git/diff.py index a6322ff57..54a5cf9af 100644 --- a/git/diff.py +++ b/git/diff.py @@ -40,11 +40,6 @@ Lit_change_type = Literal["A", "D", "C", "M", "R", "T", "U"] - -# def is_change_type(inp: str) -> TypeGuard[Lit_change_type]: -# # return True -# return inp in ['A', 'D', 'C', 'M', 'R', 'T', 'U'] - # ------------------------------------------------------------------------ @@ -693,7 +688,6 @@ def _handle_diff_line(lines_bytes: bytes, repo: "Repo", index: DiffIndex) -> Non # Change type can be R100 # R: status letter # 100: score (in case of copy and rename) - # assert is_change_type(_change_type[0]), f"Unexpected value for change_type received: {_change_type[0]}" change_type: Lit_change_type = cast(Lit_change_type, _change_type[0]) score_str = "".join(_change_type[1:]) score = int(score_str) if score_str.isdigit() else None From b8bab43db59c8a110d11dd16e24d61adf65c18fd Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 20:02:55 -0400 Subject: [PATCH 19/34] Remove old commented-out flagKeyLiteral assertions in git.remote --- git/remote.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/git/remote.py b/git/remote.py index 2c452022e..65bafc0e9 100644 --- a/git/remote.py +++ b/git/remote.py @@ -50,10 +50,6 @@ flagKeyLiteral = Literal[" ", "!", "+", "-", "*", "=", "t", "?"] -# def is_flagKeyLiteral(inp: str) -> TypeGuard[flagKeyLiteral]: -# return inp in [' ', '!', '+', '-', '=', '*', 't', '?'] - - # ------------------------------------------------------------- _logger = logging.getLogger(__name__) @@ -415,7 +411,6 @@ def _from_line(cls, repo: "Repo", line: str, fetch_line: str) -> "FetchInfo": remote_local_ref_str, note, ) = match.groups() - # assert is_flagKeyLiteral(control_character), f"{control_character}" control_character = cast(flagKeyLiteral, control_character) try: _new_hex_sha, _fetch_operation, fetch_note = fetch_line.split("\t") From 3d4e47623ef7dc76883abfd5d44fcaef92e7bd1c Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 20:07:17 -0400 Subject: [PATCH 20/34] Improve how second-level imports and __all__ are written These are in the modules that are directly under the top-level git module (and are not themselves subpackages, with their own submodules in the Python sense), except for: - git.util, where this change was very recently made. - git.compat, where no improvements of this kind were needed. - git.types, which currently has no __all__ and will only benefit from it if what should go in it is carefully considered (and where the imports themselves are grouped, sorted, and formatted already). --- git/cmd.py | 15 +++++++-------- git/config.py | 7 ++++--- git/db.py | 13 ++++++------- git/diff.py | 24 +++++++++++------------- git/exc.py | 4 +++- git/remote.py | 4 ++-- 6 files changed, 33 insertions(+), 34 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index 2862b1600..7459fae97 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -5,18 +5,20 @@ from __future__ import annotations -import re +__all__ = ("Git",) + import contextlib import io import itertools import logging import os +import re import signal -from subprocess import Popen, PIPE, DEVNULL import subprocess +from subprocess import DEVNULL, PIPE, Popen import sys -import threading from textwrap import dedent +import threading from git.compat import defenc, force_bytes, safe_decode from git.exc import ( @@ -57,12 +59,11 @@ overload, ) -from git.types import PathLike, Literal, TBD +from git.types import Literal, PathLike, TBD if TYPE_CHECKING: - from git.repo.base import Repo from git.diff import DiffIndex - + from git.repo.base import Repo # --------------------------------------------------------------------------------- @@ -84,8 +85,6 @@ _logger = logging.getLogger(__name__) -__all__ = ("Git",) - # ============================================================================== ## @name Utilities diff --git a/git/config.py b/git/config.py index f74d290cc..1c9761c1b 100644 --- a/git/config.py +++ b/git/config.py @@ -5,6 +5,8 @@ """Parser for reading and writing configuration files.""" +__all__ = ("GitConfigParser", "SectionConstraint") + import abc import configparser as cp import fnmatch @@ -40,9 +42,10 @@ from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, assert_never, _T if TYPE_CHECKING: - from git.repo.base import Repo from io import BytesIO + from git.repo.base import Repo + T_ConfigParser = TypeVar("T_ConfigParser", bound="GitConfigParser") T_OMD_value = TypeVar("T_OMD_value", str, bytes, int, float, bool) @@ -58,8 +61,6 @@ # ------------------------------------------------------------- -__all__ = ("GitConfigParser", "SectionConstraint") - _logger = logging.getLogger(__name__) CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository") diff --git a/git/db.py b/git/db.py index 5b2ca4de2..302192bdf 100644 --- a/git/db.py +++ b/git/db.py @@ -3,27 +3,26 @@ """Module with our own gitdb implementation - it uses the git command.""" -from git.util import bin_to_hex, hex_to_bin -from gitdb.base import OInfo, OStream -from gitdb.db import GitDB -from gitdb.db import LooseObjectDB +__all__ = ("GitCmdObjectDB", "GitDB") +from gitdb.base import OInfo, OStream +from gitdb.db import GitDB, LooseObjectDB from gitdb.exc import BadObject + +from git.util import bin_to_hex, hex_to_bin from git.exc import GitCommandError # typing------------------------------------------------- from typing import TYPE_CHECKING + from git.types import PathLike if TYPE_CHECKING: from git.cmd import Git - # -------------------------------------------------------- -__all__ = ("GitCmdObjectDB", "GitDB") - class GitCmdObjectDB(LooseObjectDB): """A database representing the default git object store, which includes loose diff --git a/git/diff.py b/git/diff.py index 54a5cf9af..1381deca0 100644 --- a/git/diff.py +++ b/git/diff.py @@ -3,17 +3,17 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = ("DiffConstants", "NULL_TREE", "INDEX", "Diffable", "DiffIndex", "Diff") + import enum import re from git.cmd import handle_process_output from git.compat import defenc +from git.objects.blob import Blob +from git.objects.util import mode_str_to_int from git.util import finalize_process, hex_to_bin -from .objects.blob import Blob -from .objects.util import mode_str_to_int - - # typing ------------------------------------------------------------------ from typing import ( @@ -23,29 +23,27 @@ Match, Optional, Tuple, + TYPE_CHECKING, TypeVar, Union, - TYPE_CHECKING, cast, ) from git.types import Literal, PathLike if TYPE_CHECKING: - from .objects.tree import Tree - from .objects import Commit - from git.repo.base import Repo - from git.objects.base import IndexObject from subprocess import Popen - from git import Git + + from git.cmd import Git + from git.objects.base import IndexObject + from git.objects.commit import Commit + from git.objects.tree import Tree + from git.repo.base import Repo Lit_change_type = Literal["A", "D", "C", "M", "R", "T", "U"] # ------------------------------------------------------------------------ -__all__ = ("DiffConstants", "NULL_TREE", "INDEX", "Diffable", "DiffIndex", "Diff") - - @enum.unique class DiffConstants(enum.Enum): """Special objects for :meth:`Diffable.diff`. diff --git a/git/exc.py b/git/exc.py index 9f6462b39..583eee8c1 100644 --- a/git/exc.py +++ b/git/exc.py @@ -42,12 +42,14 @@ ParseError, UnsupportedOperation, ) + from git.compat import safe_decode from git.util import remove_password_if_present # typing ---------------------------------------------------- -from typing import List, Sequence, Tuple, Union, TYPE_CHECKING +from typing import List, Sequence, Tuple, TYPE_CHECKING, Union + from git.types import PathLike if TYPE_CHECKING: diff --git a/git/remote.py b/git/remote.py index 65bafc0e9..54a5c459c 100644 --- a/git/remote.py +++ b/git/remote.py @@ -5,6 +5,8 @@ """Module implementing a remote object allowing easy access to git remotes.""" +__all__ = ("RemoteProgress", "PushInfo", "FetchInfo", "Remote") + import contextlib import logging import re @@ -54,8 +56,6 @@ _logger = logging.getLogger(__name__) -__all__ = ("RemoteProgress", "PushInfo", "FetchInfo", "Remote") - # { Utilities From 6318eea9743efec557bd7dbfdea46ddc21f654f2 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 20:27:20 -0400 Subject: [PATCH 21/34] Make F401 "unused import" suppressions more specific In git.compat and git.util. This applies them individually to each name that is known to be an unused import, rather than to entire import statements (except where the entire statement fit on one line and everything it imported is known to be unused). Either Ruff has the ability to accept this more granular style of suppression, or I had been unaware of it from flake8 (used before). I have veriifed that the suppressions are not superfluous: with no suppressions, Ruff does warn. This commit makes no change in git.types because it looks like no suppression at all may be needed there anymore; that will be covered in the next commit. This also removes the old `@UnusedImport` comments, which had been kept before because they were more granular; this specificity is now achieved by comments the tools being used can recognize. --- git/compat.py | 14 +++++++------- git/util.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/git/compat.py b/git/compat.py index 6f5376c9d..4ede8c985 100644 --- a/git/compat.py +++ b/git/compat.py @@ -14,18 +14,18 @@ import os import sys -from gitdb.utils.encoding import force_bytes, force_text # noqa: F401 # @UnusedImport +from gitdb.utils.encoding import force_bytes, force_text # noqa: F401 # typing -------------------------------------------------------------------- -from typing import ( # noqa: F401 - Any, +from typing import ( + Any, # noqa: F401 AnyStr, - Dict, - IO, + Dict, # noqa: F401 + IO, # noqa: F401 Optional, - Tuple, - Type, + Tuple, # noqa: F401 + Type, # noqa: F401 Union, overload, ) diff --git a/git/util.py b/git/util.py index cf5949693..a56b63d69 100644 --- a/git/util.py +++ b/git/util.py @@ -51,16 +51,16 @@ # gitdb all the time in their imports. They are not in __all__, at least currently, # because they could be removed or changed at any time, and so should not be considered # conceptually public to code outside GitPython. Linters of course do not like it. -from gitdb.util import ( # noqa: F401 # @IgnorePep8 - LazyMixin, # @UnusedImport - LockedFD, # @UnusedImport - bin_to_hex, # @UnusedImport - file_contents_ro_filepath, # @UnusedImport - file_contents_ro, # @UnusedImport - hex_to_bin, # @UnusedImport +from gitdb.util import ( + LazyMixin, # noqa: F401 + LockedFD, # noqa: F401 + bin_to_hex, # noqa: F401 + file_contents_ro_filepath, # noqa: F401 + file_contents_ro, # noqa: F401 + hex_to_bin, # noqa: F401 make_sha, - to_bin_sha, # @UnusedImport - to_hex_sha, # @UnusedImport + to_bin_sha, # noqa: F401 + to_hex_sha, # noqa: F401 ) # typing --------------------------------------------------------- From 31bc8a4e062841e041e9fff664bb08366d8fce48 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 20:33:33 -0400 Subject: [PATCH 22/34] Remove unneeded F401 "Unused import" suppressions In git.types. It appears Ruff, unlike flake8, recognizes "import X as X" to mean that "X" should not be considered unused, even when it appears outside a .pyi type stub file where that notation is more commonly used. Those imports in git.types may benefit from being changed in some way that uses a syntax whose intent is clearer in context, and depending on how that is done it may even be necessary to bring back suppressions. If so, they can be written more specifically. (For now, changing them would express more than is known about what names that are only present in git.types becuase it imports them should continue to be imported, should be considered conceptually public, or should be condered suitable for use within GitPython.) --- git/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git/types.py b/git/types.py index e3ae9e3d5..230422dff 100644 --- a/git/types.py +++ b/git/types.py @@ -3,7 +3,7 @@ import os import sys -from typing import ( # noqa: F401 +from typing import ( Any, Callable, Dict, @@ -17,7 +17,7 @@ ) if sys.version_info >= (3, 8): - from typing import ( # noqa: F401 + from typing import ( Literal, Protocol, SupportsIndex as SupportsIndex, @@ -25,7 +25,7 @@ runtime_checkable, ) else: - from typing_extensions import ( # noqa: F401 + from typing_extensions import ( Literal, Protocol, SupportsIndex as SupportsIndex, From abbe74d030a647664f646a8d43e071608593b6a3 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 20:40:13 -0400 Subject: [PATCH 23/34] Fix a tiny import sorting nit --- git/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/util.py b/git/util.py index a56b63d69..5839f9720 100644 --- a/git/util.py +++ b/git/util.py @@ -55,8 +55,8 @@ LazyMixin, # noqa: F401 LockedFD, # noqa: F401 bin_to_hex, # noqa: F401 - file_contents_ro_filepath, # noqa: F401 file_contents_ro, # noqa: F401 + file_contents_ro_filepath, # noqa: F401 hex_to_bin, # noqa: F401 make_sha, to_bin_sha, # noqa: F401 From 774525087bcc0389bf585b88ee38eabf69933183 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 22:09:52 -0400 Subject: [PATCH 24/34] Replace wildcard imports in top-level git module - Use explicit imports instead of * imports. - Remove now-unneeded linter suppressions. - Alphabetize inside the try-block, though this will be undone. This currently fails to import due to a cyclic import error, so the third change, alphabetizing the imports, will have to be undone (at least in the absence of other changes). It is not merely that they should not be reordered in a way that makes them cross into or out of the try-block, but that within the try block not all orders will work. There will be more to do for backward compatibility, but the modattrs.py script, which imports the top-level git module, cannot be run until the cyclic import problem is fixed. --- git/__init__.py | 95 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/git/__init__.py b/git/__init__.py index ca5bed7a3..ebcaf89a2 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -5,7 +5,7 @@ # @PydevCodeAnalysisIgnore -__all__ = [ # noqa: F405 +__all__ = [ "Actor", "AmbiguousObjectName", "BadName", @@ -88,32 +88,93 @@ __version__ = "git" -from typing import List, Optional, Sequence, Tuple, Union, TYPE_CHECKING +from typing import List, Optional, Sequence, TYPE_CHECKING, Tuple, Union from gitdb.util import to_hex_sha -from git.exc import * # noqa: F403 # @NoMove @IgnorePep8 + +from git.exc import ( + AmbiguousObjectName, + BadName, + BadObject, + BadObjectType, + CacheError, + CheckoutError, + CommandError, + GitCommandError, + GitCommandNotFound, + GitError, + HookExecutionError, + InvalidDBRoot, + InvalidGitRepositoryError, + NoSuchPathError, + ODBError, + ParseError, + RepositoryDirtyError, + UnmergedEntriesError, + UnsafeOptionError, + UnsafeProtocolError, + UnsupportedOperation, + WorkTreeRepositoryUnsupported, +) from git.types import PathLike try: - from git.compat import safe_decode # @NoMove @IgnorePep8 - from git.config import GitConfigParser # @NoMove @IgnorePep8 - from git.objects import * # noqa: F403 # @NoMove @IgnorePep8 - from git.refs import * # noqa: F403 # @NoMove @IgnorePep8 - from git.diff import * # noqa: F403 # @NoMove @IgnorePep8 - from git.db import * # noqa: F403 # @NoMove @IgnorePep8 - from git.cmd import Git # @NoMove @IgnorePep8 - from git.repo import Repo # @NoMove @IgnorePep8 - from git.remote import * # noqa: F403 # @NoMove @IgnorePep8 - from git.index import * # noqa: F403 # @NoMove @IgnorePep8 - from git.util import ( # @NoMove @IgnorePep8 - LockFile, + from git.cmd import Git # @NoMove + from git.compat import safe_decode # @NoMove + from git.config import GitConfigParser # @NoMove + from git.db import GitCmdObjectDB, GitDB # @NoMove + from git.diff import ( # @NoMove + INDEX, + NULL_TREE, + Diff, + DiffConstants, + DiffIndex, + Diffable, + ) + from git.index import ( # @NoMove + BaseIndexEntry, + BlobFilter, + CheckoutError, + IndexEntry, + IndexFile, + StageType, + util, # noqa: F401 # For backward compatibility. + ) + from git.objects import ( # @NoMove + Blob, + Commit, + IndexObject, + Object, + RootModule, + RootUpdateProgress, + Submodule, + TagObject, + Tree, + TreeModifier, + UpdateProgress, + ) + from git.refs import ( # @NoMove + HEAD, + Head, + RefLog, + RefLogEntry, + Reference, + RemoteReference, + SymbolicReference, + Tag, + TagReference, + ) + from git.remote import FetchInfo, PushInfo, Remote, RemoteProgress # @NoMove + from git.repo import Repo # @NoMove + from git.util import ( # @NoMove + Actor, BlockingLockFile, + LockFile, Stats, - Actor, remove_password_if_present, rmtree, ) -except GitError as _exc: # noqa: F405 +except GitError as _exc: raise ImportError("%s: %s" % (_exc.__class__.__name__, _exc)) from _exc # { Initialize git executable path From 64c9efdad216954b19cb91a4c04ea2de9d598159 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 22:13:45 -0400 Subject: [PATCH 25/34] Restore relative order to fix circular import error This still uses all explicit rather than wildcard imports (and still omits suppressions that are no longer needed due to wildcard imports not being used). But it brings back the old relative order of the `from ... import ...` statements inside the try-block. Since this fixes the circular import problem, it is possible to run the modattrs.py script to check for changes. New changes since replacing wildcard imports, which are probably undesirable, are the removal of these attributes pointing to indirect Python submodules of the git module: base -> git.index.base fun -> git.index.fun head -> git.refs.head log -> git.refs.log reference -> git.refs.reference symbolic -> git.refs.symbolic tag -> git.refs.tag typ -> git.index.typ --- git/__init__.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/git/__init__.py b/git/__init__.py index ebcaf89a2..7e07dcf13 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -119,27 +119,8 @@ from git.types import PathLike try: - from git.cmd import Git # @NoMove from git.compat import safe_decode # @NoMove from git.config import GitConfigParser # @NoMove - from git.db import GitCmdObjectDB, GitDB # @NoMove - from git.diff import ( # @NoMove - INDEX, - NULL_TREE, - Diff, - DiffConstants, - DiffIndex, - Diffable, - ) - from git.index import ( # @NoMove - BaseIndexEntry, - BlobFilter, - CheckoutError, - IndexEntry, - IndexFile, - StageType, - util, # noqa: F401 # For backward compatibility. - ) from git.objects import ( # @NoMove Blob, Commit, @@ -164,8 +145,27 @@ Tag, TagReference, ) - from git.remote import FetchInfo, PushInfo, Remote, RemoteProgress # @NoMove + from git.diff import ( # @NoMove + INDEX, + NULL_TREE, + Diff, + DiffConstants, + DiffIndex, + Diffable, + ) + from git.db import GitCmdObjectDB, GitDB # @NoMove + from git.cmd import Git # @NoMove from git.repo import Repo # @NoMove + from git.remote import FetchInfo, PushInfo, Remote, RemoteProgress # @NoMove + from git.index import ( # @NoMove + BaseIndexEntry, + BlobFilter, + CheckoutError, + IndexEntry, + IndexFile, + StageType, + util, # noqa: F401 # For backward compatibility. + ) from git.util import ( # @NoMove Actor, BlockingLockFile, From 31f89a1fa9f7fea7a13ba7aa9079364c22fb0e68 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 23:17:09 -0400 Subject: [PATCH 26/34] Add the nonpublic indirect submodule aliases back for now These should definitely never be used by code inside or outside of GitPython, as they have never been public, having even been omitted by the dynamic (and expansive) technique used to build git.__all__ in the past (which omitted modules intentionally). However, to ease compatibility, for now they are back. This is so that the change of making all imports explicit rather than using wildcards does not break anything. However, code using these names could never be relied on to work, and these should be considered eligible for removal, at least from the perspective of code outside GitPython. That is for the indirect submodules whose absence as a same-named direct submodule or attribute listed in __all__ was readily discoverable. The more difficult case is util. git.util is a module, git/util.py, which is how it is treated when it appears immediately after the "from" keyword, or as a key in sys.modules. However, the expression `git.util`, and a `from git import util` import, instead access the separate git.index.util module, which due to a wildcard import has been an attribute of the top-level git module for a long time. It may not be safe to change this, because although any code anywhere is better off not relying on this, this situation hasn't been (and isn't) immediately clear. To help with it somewhat, this also includes a detailed comment over where util is imported from git.index, explaining the situation. --- git/__init__.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/git/__init__.py b/git/__init__.py index 7e07dcf13..a13030456 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -144,6 +144,11 @@ SymbolicReference, Tag, TagReference, + head, # noqa: F401 # Nonpublic. May disappear! Use git.refs.head. + log, # noqa: F401 # Nonpublic. May disappear! Use git.refs.log. + reference, # noqa: F401 # Nonpublic. May disappear! Use git.refs.reference. + symbolic, # noqa: F401 # Nonpublic. May disappear! Use git.refs.symbolic. + tag, # noqa: F401 # Nonpublic. May disappear! Use git.refs.tag. ) from git.diff import ( # @NoMove INDEX, @@ -164,7 +169,21 @@ IndexEntry, IndexFile, StageType, - util, # noqa: F401 # For backward compatibility. + base, # noqa: F401 # Nonpublic. May disappear! Use git.index.base. + fun, # noqa: F401 # Nonpublic. May disappear! Use git.index.fun. + typ, # noqa: F401 # Nonpublic. May disappear! Use git.index.typ. + # + # NOTE: The expression `git.util` evaluates to git.index.util, and the import + # `from git import util` imports git.index.util, NOT git.util. It may not be + # feasible to change this until the next major version, to avoid breaking code + # inadvertently relying on it. If git.index.util really is what you want, use or + # import from that name, to avoid confusion. To use the "real" git.util module, + # write `from git.util import ...`, or access it as `sys.modules["git.util"]`. + # (This differs from other historical indirect-submodule imports that are + # unambiguously nonpublic and are subject to immediate removal. Here, the public + # git.util module, even though different, makes it less discoverable that the + # expression `git.util` refers to a non-public attribute of the git module.) + util, # noqa: F401 ) from git.util import ( # @NoMove Actor, From 9bbbcb5e847c9f0e4c91cb75d1c93be2b9cb1f57 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 23:28:09 -0400 Subject: [PATCH 27/34] Further improve git.objects.util module docstring --- git/objects/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/objects/util.py b/git/objects/util.py index 08aef132a..2cba89a9f 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -"""General utility functions for working with git objects.""" +"""Utility functions for working with git objects.""" __all__ = ( "get_object_type_by_name", From 00f4cbcf46426311f3cb6748912a1ea675639cf2 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 23:32:06 -0400 Subject: [PATCH 28/34] Add missing submodule imports in git.objects Since they are listed in __all__. (They are imported either way because names are imported from them, and this caused them to be present with the same effect.) Though they are proably about to be removed along with the corresponding entries in __all__. --- git/objects/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/git/objects/__init__.py b/git/objects/__init__.py index 4f18c8754..d146901a1 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -23,6 +23,7 @@ "TreeModifier", ] +from . import base, blob, commit, submodule, tag, tree from .base import IndexObject, Object from .blob import Blob from .commit import Commit From fcc741838dbffa45648f3f224c01b9cb8941adc2 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 23:35:58 -0400 Subject: [PATCH 29/34] Don't explicitly list direct submodules in __all__ This is for non-toplevel __all__, as git.__all__ already did not do this. As noted in some of the previous commit messags that added them, omitting them might be a bit safer in terms of the impact of bugs bugs in code using GitPython, in that unexpected modules, some of which have the same name as other modules within GitPython, won't be imported due to wildcard imports from intermediate subpackages (those that are below the top-level git package/module but collect non-subpackage modules). Though it is hard to know, since some of these would have been imported before, when an __all__ was not defined at that level. However, a separate benefit of omitting them is consistency with git.__all__, which does not list the direct Python submodules of the git module. This does not affect the output of the modattrs.py script, because the attributes exist with the same objects as their values as a result of those Python submodules being imported (in "from" imports and otherwise), including when importing the top-level git module. --- git/index/__init__.py | 5 ----- git/objects/__init__.py | 7 ------- git/objects/submodule/__init__.py | 11 +---------- git/refs/__init__.py | 7 ------- git/repo/__init__.py | 3 +-- 5 files changed, 2 insertions(+), 31 deletions(-) diff --git a/git/index/__init__.py b/git/index/__init__.py index 2086d67f9..ba48110fd 100644 --- a/git/index/__init__.py +++ b/git/index/__init__.py @@ -4,10 +4,6 @@ """Initialize the index package.""" __all__ = [ - "base", - "fun", - "typ", - "util", "BaseIndexEntry", "BlobFilter", "CheckoutError", @@ -16,6 +12,5 @@ "StageType", ] -from . import base, fun, typ, util from .base import CheckoutError, IndexFile from .typ import BaseIndexEntry, BlobFilter, IndexEntry, StageType diff --git a/git/objects/__init__.py b/git/objects/__init__.py index d146901a1..4447ca50d 100644 --- a/git/objects/__init__.py +++ b/git/objects/__init__.py @@ -4,12 +4,6 @@ """Import all submodules' main classes into the package space.""" __all__ = [ - "base", - "blob", - "commit", - "submodule", - "tag", - "tree", "IndexObject", "Object", "Blob", @@ -23,7 +17,6 @@ "TreeModifier", ] -from . import base, blob, commit, submodule, tag, tree from .base import IndexObject, Object from .blob import Blob from .commit import Commit diff --git a/git/objects/submodule/__init__.py b/git/objects/submodule/__init__.py index 383439545..c0604e76f 100644 --- a/git/objects/submodule/__init__.py +++ b/git/objects/submodule/__init__.py @@ -1,16 +1,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = [ - "base", - "root", - "util", - "Submodule", - "UpdateProgress", - "RootModule", - "RootUpdateProgress", -] +__all__ = ["Submodule", "UpdateProgress", "RootModule", "RootUpdateProgress"] -from . import base, root, util from .base import Submodule, UpdateProgress from .root import RootModule, RootUpdateProgress diff --git a/git/refs/__init__.py b/git/refs/__init__.py index 04279901a..d6157e6f3 100644 --- a/git/refs/__init__.py +++ b/git/refs/__init__.py @@ -2,12 +2,6 @@ # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ __all__ = [ - "head", - "log", - "reference", - "remote", - "symbolic", - "tag", "HEAD", "Head", "RefLog", @@ -19,7 +13,6 @@ "TagReference", ] -from . import head, log, reference, remote, symbolic, tag from .head import HEAD, Head from .log import RefLog, RefLogEntry from .reference import Reference diff --git a/git/repo/__init__.py b/git/repo/__init__.py index 3b784b975..66319ef95 100644 --- a/git/repo/__init__.py +++ b/git/repo/__init__.py @@ -3,7 +3,6 @@ """Initialize the repo package.""" -__all__ = ["base", "fun", "Repo"] +__all__ = ["Repo"] -from . import base, fun from .base import Repo From 78055a8b8473a25e8f36a3846b920a698e8ba66b Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Mon, 18 Mar 2024 23:52:57 -0400 Subject: [PATCH 30/34] Pick a consistent type for __all__ (for now, list) This makes all __all__ everywhere in the git package lists. Before, roughly half were lists and half were tuples. There are reasonable theoretical arguments for both, and in practice all tools today support both. Traditionally using a list is far more common, and it remains at least somewhat more common. Furthermore, git/util.py uses a list and is currently written to append an element to it that is conditionally defined on Windows (though it would probably be fine for that to be the only list, since it reflects an actual relevant difference about it). The goal here is just to remove inconsistency that does not signify anything and is the result of drift over time. If a reason (even preference) arises to make them all tuples in the future, then that is also probably fine. --- git/cmd.py | 2 +- git/config.py | 2 +- git/db.py | 2 +- git/diff.py | 2 +- git/index/base.py | 2 +- git/index/fun.py | 4 ++-- git/index/typ.py | 2 +- git/index/util.py | 2 +- git/objects/base.py | 2 +- git/objects/blob.py | 2 +- git/objects/commit.py | 2 +- git/objects/fun.py | 4 ++-- git/objects/submodule/util.py | 4 ++-- git/objects/tag.py | 2 +- git/objects/tree.py | 2 +- git/objects/util.py | 4 ++-- git/remote.py | 2 +- git/repo/base.py | 2 +- git/repo/fun.py | 4 ++-- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index 7459fae97..03e5d7ffc 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -5,7 +5,7 @@ from __future__ import annotations -__all__ = ("Git",) +__all__ = ["Git"] import contextlib import io diff --git a/git/config.py b/git/config.py index 1c9761c1b..3ce9b123f 100644 --- a/git/config.py +++ b/git/config.py @@ -5,7 +5,7 @@ """Parser for reading and writing configuration files.""" -__all__ = ("GitConfigParser", "SectionConstraint") +__all__ = ["GitConfigParser", "SectionConstraint"] import abc import configparser as cp diff --git a/git/db.py b/git/db.py index 302192bdf..cacd030d0 100644 --- a/git/db.py +++ b/git/db.py @@ -3,7 +3,7 @@ """Module with our own gitdb implementation - it uses the git command.""" -__all__ = ("GitCmdObjectDB", "GitDB") +__all__ = ["GitCmdObjectDB", "GitDB"] from gitdb.base import OInfo, OStream from gitdb.db import GitDB, LooseObjectDB diff --git a/git/diff.py b/git/diff.py index 1381deca0..0e39fe7a8 100644 --- a/git/diff.py +++ b/git/diff.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ("DiffConstants", "NULL_TREE", "INDEX", "Diffable", "DiffIndex", "Diff") +__all__ = ["DiffConstants", "NULL_TREE", "INDEX", "Diffable", "DiffIndex", "Diff"] import enum import re diff --git a/git/index/base.py b/git/index/base.py index b49841435..b8161ea52 100644 --- a/git/index/base.py +++ b/git/index/base.py @@ -6,7 +6,7 @@ """Module containing :class:`IndexFile`, an Index implementation facilitating all kinds of index manipulations such as querying and merging.""" -__all__ = ("IndexFile", "CheckoutError", "StageType") +__all__ = ["IndexFile", "CheckoutError", "StageType"] import contextlib import datetime diff --git a/git/index/fun.py b/git/index/fun.py index b24e803a3..59cce6ae6 100644 --- a/git/index/fun.py +++ b/git/index/fun.py @@ -4,7 +4,7 @@ """Standalone functions to accompany the index implementation and make it more versatile.""" -__all__ = ( +__all__ = [ "write_cache", "read_cache", "write_tree_from_cache", @@ -13,7 +13,7 @@ "S_IFGITLINK", "run_commit_hook", "hook_path", -) +] from io import BytesIO import os diff --git a/git/index/typ.py b/git/index/typ.py index 0fbcd69f0..974252528 100644 --- a/git/index/typ.py +++ b/git/index/typ.py @@ -3,7 +3,7 @@ """Additional types used by the index.""" -__all__ = ("BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType") +__all__ = ["BlobFilter", "BaseIndexEntry", "IndexEntry", "StageType"] from binascii import b2a_hex from pathlib import Path diff --git a/git/index/util.py b/git/index/util.py index 4aee61bce..e59cb609f 100644 --- a/git/index/util.py +++ b/git/index/util.py @@ -3,7 +3,7 @@ """Index utilities.""" -__all__ = ("TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir") +__all__ = ["TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir"] import contextlib from functools import wraps diff --git a/git/objects/base.py b/git/objects/base.py index 07ff639e5..eeaebc09b 100644 --- a/git/objects/base.py +++ b/git/objects/base.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ("Object", "IndexObject") +__all__ = ["Object", "IndexObject"] import os.path as osp diff --git a/git/objects/blob.py b/git/objects/blob.py index 50500f550..58de59642 100644 --- a/git/objects/blob.py +++ b/git/objects/blob.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ("Blob",) +__all__ = ["Blob"] from mimetypes import guess_type import sys diff --git a/git/objects/commit.py b/git/objects/commit.py index b22308726..8de52980c 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ("Commit",) +__all__ = ["Commit"] from collections import defaultdict import datetime diff --git a/git/objects/fun.py b/git/objects/fun.py index 9bd2d8f10..fe57da13a 100644 --- a/git/objects/fun.py +++ b/git/objects/fun.py @@ -3,12 +3,12 @@ """Functions that are supposed to be as fast as possible.""" -__all__ = ( +__all__ = [ "tree_to_stream", "tree_entries_from_data", "traverse_trees_recursive", "traverse_tree_recursive", -) +] from stat import S_ISDIR diff --git a/git/objects/submodule/util.py b/git/objects/submodule/util.py index 3a09d6a17..c021510d8 100644 --- a/git/objects/submodule/util.py +++ b/git/objects/submodule/util.py @@ -1,13 +1,13 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ( +__all__ = [ "sm_section", "sm_name", "mkhead", "find_first_remote_branch", "SubmoduleConfigParser", -) +] from io import BytesIO import weakref diff --git a/git/objects/tag.py b/git/objects/tag.py index 5ad311590..a3bb0b882 100644 --- a/git/objects/tag.py +++ b/git/objects/tag.py @@ -9,7 +9,7 @@ For lightweight tags, see the :mod:`git.refs.tag` module. """ -__all__ = ("TagObject",) +__all__ = ["TagObject"] import sys diff --git a/git/objects/tree.py b/git/objects/tree.py index d8320b319..09184a781 100644 --- a/git/objects/tree.py +++ b/git/objects/tree.py @@ -3,7 +3,7 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -__all__ = ("TreeModifier", "Tree") +__all__ = ["TreeModifier", "Tree"] import sys diff --git a/git/objects/util.py b/git/objects/util.py index 2cba89a9f..5c56e6134 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -5,7 +5,7 @@ """Utility functions for working with git objects.""" -__all__ = ( +__all__ = [ "get_object_type_by_name", "parse_date", "parse_actor_and_date", @@ -17,7 +17,7 @@ "Actor", "tzoffset", "utc", -) +] from abc import ABC, abstractmethod import calendar diff --git a/git/remote.py b/git/remote.py index 54a5c459c..f2ecd0f36 100644 --- a/git/remote.py +++ b/git/remote.py @@ -5,7 +5,7 @@ """Module implementing a remote object allowing easy access to git remotes.""" -__all__ = ("RemoteProgress", "PushInfo", "FetchInfo", "Remote") +__all__ = ["RemoteProgress", "PushInfo", "FetchInfo", "Remote"] import contextlib import logging diff --git a/git/repo/base.py b/git/repo/base.py index 63b21fdbf..51ea76901 100644 --- a/git/repo/base.py +++ b/git/repo/base.py @@ -5,7 +5,7 @@ from __future__ import annotations -__all__ = ("Repo",) +__all__ = ["Repo"] import gc import logging diff --git a/git/repo/fun.py b/git/repo/fun.py index 1defcb1ac..e44d9c644 100644 --- a/git/repo/fun.py +++ b/git/repo/fun.py @@ -5,7 +5,7 @@ from __future__ import annotations -__all__ = ( +__all__ = [ "rev_parse", "is_git_dir", "touch", @@ -15,7 +15,7 @@ "deref_tag", "to_commit", "find_worktree_git_dir", -) +] import os import os.path as osp From ecdb6aa25a95e2cb3a650a26f3ffd9c45e127ed1 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Tue, 19 Mar 2024 00:05:15 -0400 Subject: [PATCH 31/34] Save diff of non-__all__ attributes across import changes This commits the diff of the output of the modattrs.py script between when the script was introduced in 1e5a944, and now (with the import refactoring finished and no wildcard imports remaining outside the test suite, and only there in one import statement for test helpers). Neither this diff nor modattrs.py itself will be retained. Both will be removed in the next commit. This is committed here to facilitate inspection and debugging (especially if turns out there are thus-far undetected regressions). --- ab.diff | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 ab.diff diff --git a/ab.diff b/ab.diff new file mode 100644 index 000000000..1f2885b90 --- /dev/null +++ b/ab.diff @@ -0,0 +1,42 @@ +diff --git a/a b/b +index 81b3f984..099fa33b 100644 +--- a/a ++++ b/b +@@ -634,7 +634,6 @@ git.objects: + blob: + commit: + fun: +- inspect: + submodule: + tag: + tree: +@@ -770,6 +769,10 @@ git.objects.fun: + + + git.objects.submodule: ++ RootModule: ++ RootUpdateProgress: ++ Submodule: ++ UpdateProgress: + __builtins__: {'__name__': 'builtins', '__doc__': "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.", '__package__': '', '__loader__': , '__spec__': ModuleSpec(name='builtins', loader=, origin='built-in'), '__build_class__': , '__import__': , 'abs': , 'all': , 'any': , 'ascii': , 'bin': , 'breakpoint': , 'callable': , 'chr': , 'compile': , 'delattr': , 'dir': , 'divmod': , 'eval': , 'exec': , 'format': , 'getattr': , 'globals': , 'hasattr': , 'hash': , 'hex': , 'id': , 'input': , 'isinstance': , 'issubclass': , 'iter': , 'aiter': , 'len': , 'locals': , 'max': , 'min': , 'next': , 'anext': , 'oct': , 'ord': , 'pow': , 'print': , 'repr': , 'round': , 'setattr': , 'sorted': , 'sum': , 'vars': , 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': , 'memoryview': , 'bytearray': , 'bytes': , 'classmethod': , 'complex': , 'dict': , 'enumerate': , 'filter': , 'float': , 'frozenset': , 'property': , 'int': , 'list': , 'map': , 'object': , 'range': , 'reversed': , 'set': , 'slice': , 'staticmethod': , 'str': , 'super': , 'tuple': , 'type': , 'zip': , '__debug__': True, 'BaseException': , 'BaseExceptionGroup': , 'Exception': , 'GeneratorExit': , 'KeyboardInterrupt': , 'SystemExit': , 'ArithmeticError': , 'AssertionError': , 'AttributeError': , 'BufferError': , 'EOFError': , 'ImportError': , 'LookupError': , 'MemoryError': , 'NameError': , 'OSError': , 'ReferenceError': , 'RuntimeError': , 'StopAsyncIteration': , 'StopIteration': , 'SyntaxError': , 'SystemError': , 'TypeError': , 'ValueError': , 'Warning': , 'FloatingPointError': , 'OverflowError': , 'ZeroDivisionError': , 'BytesWarning': , 'DeprecationWarning': , 'EncodingWarning': , 'FutureWarning': , 'ImportWarning': , 'PendingDeprecationWarning': , 'ResourceWarning': , 'RuntimeWarning': , 'SyntaxWarning': , 'UnicodeWarning': , 'UserWarning': , 'BlockingIOError': , 'ChildProcessError': , 'ConnectionError': , 'FileExistsError': , 'FileNotFoundError': , 'InterruptedError': , 'IsADirectoryError': , 'NotADirectoryError': , 'PermissionError': , 'ProcessLookupError': , 'TimeoutError': , 'IndentationError': , 'IndexError': , 'KeyError': , 'ModuleNotFoundError': , 'NotImplementedError': , 'RecursionError': , 'UnboundLocalError': , 'UnicodeError': , 'BrokenPipeError': , 'ConnectionAbortedError': , 'ConnectionRefusedError': , 'ConnectionResetError': , 'TabError': , 'UnicodeDecodeError': , 'UnicodeEncodeError': , 'UnicodeTranslateError': , 'ExceptionGroup': , 'EnvironmentError': , 'IOError': , 'WindowsError': , 'open': , 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2023 Python Software Foundation.?All Rights Reserved.??Copyright (c) 2000 BeOpen.com.?All Rights Reserved.??Copyright (c) 1995-2001 Corporation for National Research Initiatives.?All Rights Reserved.??Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.?All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands? for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.} + __cached__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\submodule\\__pycache__\\__init__.cpython-312.pyc' + __doc__: None +@@ -881,9 +884,7 @@ git.objects.submodule.util: + Any: typing.Any + BytesIO: + GitConfigParser: +- IndexObject: + InvalidGitRepositoryError: +- Object: + PathLike: typing.Union[str, ForwardRef('os.PathLike[str]')] + Sequence: typing.Sequence + SubmoduleConfigParser: +@@ -998,7 +999,7 @@ git.objects.util: + ZERO: datetime.timedelta(0) + __builtins__: {'__name__': 'builtins', '__doc__': "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.", '__package__': '', '__loader__': , '__spec__': ModuleSpec(name='builtins', loader=, origin='built-in'), '__build_class__': , '__import__': , 'abs': , 'all': , 'any': , 'ascii': , 'bin': , 'breakpoint': , 'callable': , 'chr': , 'compile': , 'delattr': , 'dir': , 'divmod': , 'eval': , 'exec': , 'format': , 'getattr': , 'globals': , 'hasattr': , 'hash': , 'hex': , 'id': , 'input': , 'isinstance': , 'issubclass': , 'iter': , 'aiter': , 'len': , 'locals': , 'max': , 'min': , 'next': , 'anext': , 'oct': , 'ord': , 'pow': , 'print': , 'repr': , 'round': , 'setattr': , 'sorted': , 'sum': , 'vars': , 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': , 'memoryview': , 'bytearray': , 'bytes': , 'classmethod': , 'complex': , 'dict': , 'enumerate': , 'filter': , 'float': , 'frozenset': , 'property': , 'int': , 'list': , 'map': , 'object': , 'range': , 'reversed': , 'set': , 'slice': , 'staticmethod': , 'str': , 'super': , 'tuple': , 'type': , 'zip': , '__debug__': True, 'BaseException': , 'BaseExceptionGroup': , 'Exception': , 'GeneratorExit': , 'KeyboardInterrupt': , 'SystemExit': , 'ArithmeticError': , 'AssertionError': , 'AttributeError': , 'BufferError': , 'EOFError': , 'ImportError': , 'LookupError': , 'MemoryError': , 'NameError': , 'OSError': , 'ReferenceError': , 'RuntimeError': , 'StopAsyncIteration': , 'StopIteration': , 'SyntaxError': , 'SystemError': , 'TypeError': , 'ValueError': , 'Warning': , 'FloatingPointError': , 'OverflowError': , 'ZeroDivisionError': , 'BytesWarning': , 'DeprecationWarning': , 'EncodingWarning': , 'FutureWarning': , 'ImportWarning': , 'PendingDeprecationWarning': , 'ResourceWarning': , 'RuntimeWarning': , 'SyntaxWarning': , 'UnicodeWarning': , 'UserWarning': , 'BlockingIOError': , 'ChildProcessError': , 'ConnectionError': , 'FileExistsError': , 'FileNotFoundError': , 'InterruptedError': , 'IsADirectoryError': , 'NotADirectoryError': , 'PermissionError': , 'ProcessLookupError': , 'TimeoutError': , 'IndentationError': , 'IndexError': , 'KeyError': , 'ModuleNotFoundError': , 'NotImplementedError': , 'RecursionError': , 'UnboundLocalError': , 'UnicodeError': , 'BrokenPipeError': , 'ConnectionAbortedError': , 'ConnectionRefusedError': , 'ConnectionResetError': , 'TabError': , 'UnicodeDecodeError': , 'UnicodeEncodeError': , 'UnicodeTranslateError': , 'ExceptionGroup': , 'EnvironmentError': , 'IOError': , 'WindowsError': , 'open': , 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2023 Python Software Foundation.?All Rights Reserved.??Copyright (c) 2000 BeOpen.com.?All Rights Reserved.??Copyright (c) 1995-2001 Corporation for National Research Initiatives.?All Rights Reserved.??Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.?All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands? for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.} + __cached__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\__pycache__\\util.cpython-312.pyc' +- __doc__: 'General utility functions.' ++ __doc__: 'Utility functions for working with git objects.' + __file__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\util.py' + __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x...> + __name__: 'git.objects.util' From f705fd6dde6047c7787809aeb691988fa4af80d8 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Tue, 19 Mar 2024 00:15:50 -0400 Subject: [PATCH 32/34] Remove modattrs.py and related Now that it has served its purpose. (Of course, it can also be brought back from the history at any time if needed.) Changes: - Delete the modattrs.py script that was used to check for unintended changes to what module attributes existed and what objects they referred to, while doing the import refactoring. - Delete the ab.diff file showing the overall diff, which was temporarily introduced in the previous commit. - Remove the "a" and "b" temporary entries in .gitignore that were used to facilitate efficient use of modattrs.py while carrying out the import refactoring. --- .gitignore | 4 ---- ab.diff | 42 ------------------------------------------ modattrs.py | 53 ----------------------------------------------------- 3 files changed, 99 deletions(-) delete mode 100644 ab.diff delete mode 100755 modattrs.py diff --git a/.gitignore b/.gitignore index 1be4f3201..7765293d8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,3 @@ output.txt # Finder metadata .DS_Store - -# Output files for modattrs.py (these entries will be removed soon) -a -b diff --git a/ab.diff b/ab.diff deleted file mode 100644 index 1f2885b90..000000000 --- a/ab.diff +++ /dev/null @@ -1,42 +0,0 @@ -diff --git a/a b/b -index 81b3f984..099fa33b 100644 ---- a/a -+++ b/b -@@ -634,7 +634,6 @@ git.objects: - blob: - commit: - fun: -- inspect: - submodule: - tag: - tree: -@@ -770,6 +769,10 @@ git.objects.fun: - - - git.objects.submodule: -+ RootModule: -+ RootUpdateProgress: -+ Submodule: -+ UpdateProgress: - __builtins__: {'__name__': 'builtins', '__doc__': "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.", '__package__': '', '__loader__': , '__spec__': ModuleSpec(name='builtins', loader=, origin='built-in'), '__build_class__': , '__import__': , 'abs': , 'all': , 'any': , 'ascii': , 'bin': , 'breakpoint': , 'callable': , 'chr': , 'compile': , 'delattr': , 'dir': , 'divmod': , 'eval': , 'exec': , 'format': , 'getattr': , 'globals': , 'hasattr': , 'hash': , 'hex': , 'id': , 'input': , 'isinstance': , 'issubclass': , 'iter': , 'aiter': , 'len': , 'locals': , 'max': , 'min': , 'next': , 'anext': , 'oct': , 'ord': , 'pow': , 'print': , 'repr': , 'round': , 'setattr': , 'sorted': , 'sum': , 'vars': , 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': , 'memoryview': , 'bytearray': , 'bytes': , 'classmethod': , 'complex': , 'dict': , 'enumerate': , 'filter': , 'float': , 'frozenset': , 'property': , 'int': , 'list': , 'map': , 'object': , 'range': , 'reversed': , 'set': , 'slice': , 'staticmethod': , 'str': , 'super': , 'tuple': , 'type': , 'zip': , '__debug__': True, 'BaseException': , 'BaseExceptionGroup': , 'Exception': , 'GeneratorExit': , 'KeyboardInterrupt': , 'SystemExit': , 'ArithmeticError': , 'AssertionError': , 'AttributeError': , 'BufferError': , 'EOFError': , 'ImportError': , 'LookupError': , 'MemoryError': , 'NameError': , 'OSError': , 'ReferenceError': , 'RuntimeError': , 'StopAsyncIteration': , 'StopIteration': , 'SyntaxError': , 'SystemError': , 'TypeError': , 'ValueError': , 'Warning': , 'FloatingPointError': , 'OverflowError': , 'ZeroDivisionError': , 'BytesWarning': , 'DeprecationWarning': , 'EncodingWarning': , 'FutureWarning': , 'ImportWarning': , 'PendingDeprecationWarning': , 'ResourceWarning': , 'RuntimeWarning': , 'SyntaxWarning': , 'UnicodeWarning': , 'UserWarning': , 'BlockingIOError': , 'ChildProcessError': , 'ConnectionError': , 'FileExistsError': , 'FileNotFoundError': , 'InterruptedError': , 'IsADirectoryError': , 'NotADirectoryError': , 'PermissionError': , 'ProcessLookupError': , 'TimeoutError': , 'IndentationError': , 'IndexError': , 'KeyError': , 'ModuleNotFoundError': , 'NotImplementedError': , 'RecursionError': , 'UnboundLocalError': , 'UnicodeError': , 'BrokenPipeError': , 'ConnectionAbortedError': , 'ConnectionRefusedError': , 'ConnectionResetError': , 'TabError': , 'UnicodeDecodeError': , 'UnicodeEncodeError': , 'UnicodeTranslateError': , 'ExceptionGroup': , 'EnvironmentError': , 'IOError': , 'WindowsError': , 'open': , 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2023 Python Software Foundation.?All Rights Reserved.??Copyright (c) 2000 BeOpen.com.?All Rights Reserved.??Copyright (c) 1995-2001 Corporation for National Research Initiatives.?All Rights Reserved.??Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.?All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands? for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.} - __cached__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\submodule\\__pycache__\\__init__.cpython-312.pyc' - __doc__: None -@@ -881,9 +884,7 @@ git.objects.submodule.util: - Any: typing.Any - BytesIO: - GitConfigParser: -- IndexObject: - InvalidGitRepositoryError: -- Object: - PathLike: typing.Union[str, ForwardRef('os.PathLike[str]')] - Sequence: typing.Sequence - SubmoduleConfigParser: -@@ -998,7 +999,7 @@ git.objects.util: - ZERO: datetime.timedelta(0) - __builtins__: {'__name__': 'builtins', '__doc__': "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.", '__package__': '', '__loader__': , '__spec__': ModuleSpec(name='builtins', loader=, origin='built-in'), '__build_class__': , '__import__': , 'abs': , 'all': , 'any': , 'ascii': , 'bin': , 'breakpoint': , 'callable': , 'chr': , 'compile': , 'delattr': , 'dir': , 'divmod': , 'eval': , 'exec': , 'format': , 'getattr': , 'globals': , 'hasattr': , 'hash': , 'hex': , 'id': , 'input': , 'isinstance': , 'issubclass': , 'iter': , 'aiter': , 'len': , 'locals': , 'max': , 'min': , 'next': , 'anext': , 'oct': , 'ord': , 'pow': , 'print': , 'repr': , 'round': , 'setattr': , 'sorted': , 'sum': , 'vars': , 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': , 'memoryview': , 'bytearray': , 'bytes': , 'classmethod': , 'complex': , 'dict': , 'enumerate': , 'filter': , 'float': , 'frozenset': , 'property': , 'int': , 'list': , 'map': , 'object': , 'range': , 'reversed': , 'set': , 'slice': , 'staticmethod': , 'str': , 'super': , 'tuple': , 'type': , 'zip': , '__debug__': True, 'BaseException': , 'BaseExceptionGroup': , 'Exception': , 'GeneratorExit': , 'KeyboardInterrupt': , 'SystemExit': , 'ArithmeticError': , 'AssertionError': , 'AttributeError': , 'BufferError': , 'EOFError': , 'ImportError': , 'LookupError': , 'MemoryError': , 'NameError': , 'OSError': , 'ReferenceError': , 'RuntimeError': , 'StopAsyncIteration': , 'StopIteration': , 'SyntaxError': , 'SystemError': , 'TypeError': , 'ValueError': , 'Warning': , 'FloatingPointError': , 'OverflowError': , 'ZeroDivisionError': , 'BytesWarning': , 'DeprecationWarning': , 'EncodingWarning': , 'FutureWarning': , 'ImportWarning': , 'PendingDeprecationWarning': , 'ResourceWarning': , 'RuntimeWarning': , 'SyntaxWarning': , 'UnicodeWarning': , 'UserWarning': , 'BlockingIOError': , 'ChildProcessError': , 'ConnectionError': , 'FileExistsError': , 'FileNotFoundError': , 'InterruptedError': , 'IsADirectoryError': , 'NotADirectoryError': , 'PermissionError': , 'ProcessLookupError': , 'TimeoutError': , 'IndentationError': , 'IndexError': , 'KeyError': , 'ModuleNotFoundError': , 'NotImplementedError': , 'RecursionError': , 'UnboundLocalError': , 'UnicodeError': , 'BrokenPipeError': , 'ConnectionAbortedError': , 'ConnectionRefusedError': , 'ConnectionResetError': , 'TabError': , 'UnicodeDecodeError': , 'UnicodeEncodeError': , 'UnicodeTranslateError': , 'ExceptionGroup': , 'EnvironmentError': , 'IOError': , 'WindowsError': , 'open': , 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2023 Python Software Foundation.?All Rights Reserved.??Copyright (c) 2000 BeOpen.com.?All Rights Reserved.??Copyright (c) 1995-2001 Corporation for National Research Initiatives.?All Rights Reserved.??Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.?All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands? for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object.} - __cached__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\__pycache__\\util.cpython-312.pyc' -- __doc__: 'General utility functions.' -+ __doc__: 'Utility functions for working with git objects.' - __file__: 'C:\\Users\\ek\\source\\repos\\GitPython\\git\\objects\\util.py' - __loader__: <_frozen_importlib_external.SourceFileLoader object at 0x...> - __name__: 'git.objects.util' diff --git a/modattrs.py b/modattrs.py deleted file mode 100755 index 245f68912..000000000 --- a/modattrs.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -"""Script to get the names and "stabilized" reprs of module attributes in GitPython. - -Run with :envvar:`PYTHONHASHSEED` set to ``0`` for fully comparable results. These are -only still meaningful for comparing if the same platform and Python version are used. - -The output of this script should probably not be committed, because within the reprs of -objects found in modules, it may contain sensitive information, such as API keys stored -in environment variables. The "sanitization" performed here is only for common forms of -whitespace that clash with the output format. -""" - -# fmt: off - -__all__ = ["git", "main"] - -import itertools -import re -import sys - -import git - - -def main(): - # This assumes `import git` causes all of them to be loaded. - gitpython_modules = sorted( - (module_name, module) - for module_name, module in sys.modules.items() - if re.match(r"git(?:\.|$)", module_name) - ) - - # We will print two blank lines between successive module reports. - separators = itertools.chain(("",), itertools.repeat("\n\n")) - - # Report each module's contents. - for (module_name, module), separator in zip(gitpython_modules, separators): - print(f"{separator}{module_name}:") - - attributes = sorted( - (name, value) - for name, value in module.__dict__.items() - if name != "__all__" # Because we are deliberately adding these. - ) - - for name, value in attributes: - sanitized_repr = re.sub(r"[\r\n\v\f]", "?", repr(value)) - normalized_repr = re.sub(r" at 0x[0-9a-fA-F]+", " at 0x...", sanitized_repr) - print(f" {name}: {normalized_repr}") - - -if __name__ == "__main__": - main() From 4a4d880fec4364c7d49e7708238a962942ae69f3 Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Tue, 19 Mar 2024 00:45:40 -0400 Subject: [PATCH 33/34] Improve test suite import grouping/sorting, __all__ placement There is only one __all__ in the test suite, so this is mostly the change to imports, grouping and sorting them in a fully consistent style that is the same as the import style in the code under test. --- test/lib/helper.py | 32 ++++++++++++++++---------------- test/performance/__init__.py | 2 ++ test/performance/lib.py | 5 +++-- test/performance/test_commit.py | 6 ++++-- test/performance/test_odb.py | 2 +- test/performance/test_streams.py | 8 ++++---- test/test_actor.py | 3 ++- test/test_base.py | 10 +++++----- test/test_blob.py | 3 ++- test/test_clone.py | 5 +---- test/test_commit.py | 20 +++++++++++--------- test/test_config.py | 2 +- test/test_db.py | 5 +++-- test/test_diff.py | 1 + test/test_docs.py | 3 +-- test/test_exc.py | 7 ++++--- test/test_fun.py | 17 ++++++++--------- test/test_git.py | 3 ++- test/test_index.py | 14 ++++---------- test/test_reflog.py | 5 +++-- test/test_refs.py | 23 ++++++++++++----------- test/test_remote.py | 3 +-- test/test_repo.py | 7 ++----- test/test_stats.py | 3 ++- test/test_submodule.py | 1 + test/test_tree.py | 3 ++- test/test_util.py | 1 + 27 files changed, 99 insertions(+), 95 deletions(-) diff --git a/test/lib/helper.py b/test/lib/helper.py index 45a778b7d..5d91447ea 100644 --- a/test/lib/helper.py +++ b/test/lib/helper.py @@ -3,6 +3,22 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +__all__ = [ + "fixture_path", + "fixture", + "StringProcessAdapter", + "with_rw_directory", + "with_rw_repo", + "with_rw_and_rw_remote_repo", + "TestBase", + "VirtualEnvironment", + "TestCase", + "SkipTest", + "skipIf", + "GIT_REPO", + "GIT_DAEMON_PORT", +] + import contextlib from functools import wraps import gc @@ -31,22 +47,6 @@ GIT_REPO = os.environ.get("GIT_PYTHON_TEST_GIT_REPO_BASE", ospd(ospd(ospd(__file__)))) GIT_DAEMON_PORT = os.environ.get("GIT_PYTHON_TEST_GIT_DAEMON_PORT", "19418") -__all__ = ( - "fixture_path", - "fixture", - "StringProcessAdapter", - "with_rw_directory", - "with_rw_repo", - "with_rw_and_rw_remote_repo", - "TestBase", - "VirtualEnvironment", - "TestCase", - "SkipTest", - "skipIf", - "GIT_REPO", - "GIT_DAEMON_PORT", -) - _logger = logging.getLogger(__name__) # { Routines diff --git a/test/performance/__init__.py b/test/performance/__init__.py index e69de29bb..56b5d89db 100644 --- a/test/performance/__init__.py +++ b/test/performance/__init__.py @@ -0,0 +1,2 @@ +# This module is part of GitPython and is released under the +# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ diff --git a/test/performance/lib.py b/test/performance/lib.py index d08e1027f..2ca3c409b 100644 --- a/test/performance/lib.py +++ b/test/performance/lib.py @@ -5,13 +5,14 @@ import logging import os +import os.path as osp import tempfile from git import Repo from git.db import GitCmdObjectDB, GitDB -from test.lib import TestBase from git.util import rmtree -import os.path as osp + +from test.lib import TestBase # { Invariants diff --git a/test/performance/test_commit.py b/test/performance/test_commit.py index 00d768f0a..b943f1975 100644 --- a/test/performance/test_commit.py +++ b/test/performance/test_commit.py @@ -10,9 +10,11 @@ from time import time import sys -from .lib import TestBigRepoRW -from git import Commit from gitdb import IStream + +from git import Commit + +from test.performance.lib import TestBigRepoRW from test.test_commit import TestCommitSerialization diff --git a/test/performance/test_odb.py b/test/performance/test_odb.py index 00e245fb7..fdbbeb8c3 100644 --- a/test/performance/test_odb.py +++ b/test/performance/test_odb.py @@ -6,7 +6,7 @@ import sys from time import time -from .lib import TestBigRepoR +from test.performance.lib import TestBigRepoR class TestObjDBPerformance(TestBigRepoR): diff --git a/test/performance/test_streams.py b/test/performance/test_streams.py index 56b5274ec..f6ffeba8e 100644 --- a/test/performance/test_streams.py +++ b/test/performance/test_streams.py @@ -5,18 +5,18 @@ import gc import os +import os.path as osp import subprocess import sys from time import time -from test.lib import with_rw_repo -from git.util import bin_to_hex from gitdb import LooseObjectDB, IStream from gitdb.test.lib import make_memory_file -import os.path as osp +from git.util import bin_to_hex -from .lib import TestBigRepoR +from test.lib import with_rw_repo +from test.performance.lib import TestBigRepoR class TestObjDBPerformance(TestBigRepoR): diff --git a/test/test_actor.py b/test/test_actor.py index caf095739..5e6635709 100644 --- a/test/test_actor.py +++ b/test/test_actor.py @@ -3,9 +3,10 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -from test.lib import TestBase from git import Actor +from test.lib import TestBase + class TestActor(TestBase): def test_from_string_should_separate_name_and_email(self): diff --git a/test/test_base.py b/test/test_base.py index e477b4837..86bcc5c79 100644 --- a/test/test_base.py +++ b/test/test_base.py @@ -5,18 +5,18 @@ import gc import os +import os.path as osp import sys import tempfile from unittest import skipIf from git import Repo -from git.objects import Blob, Tree, Commit, TagObject +from git.objects import Blob, Commit, TagObject, Tree +import git.objects.base as base from git.objects.util import get_object_type_by_name -from test.lib import TestBase as _TestBase, with_rw_repo, with_rw_and_rw_remote_repo -from git.util import hex_to_bin, HIDE_WINDOWS_FREEZE_ERRORS +from git.util import HIDE_WINDOWS_FREEZE_ERRORS, hex_to_bin -import git.objects.base as base -import os.path as osp +from test.lib import TestBase as _TestBase, with_rw_and_rw_remote_repo, with_rw_repo class TestBase(_TestBase): diff --git a/test/test_blob.py b/test/test_blob.py index ff59c67ea..affaa60fc 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -3,9 +3,10 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -from test.lib import TestBase from git import Blob +from test.lib import TestBase + class TestBlob(TestBase): def test_mime_type_should_return_mime_type_for_known_types(self): diff --git a/test/test_clone.py b/test/test_clone.py index be2e6b19b..126ef0063 100644 --- a/test/test_clone.py +++ b/test/test_clone.py @@ -6,10 +6,7 @@ import git -from .lib import ( - TestBase, - with_rw_directory, -) +from test.lib import TestBase, with_rw_directory class TestClone(TestBase): diff --git a/test/test_commit.py b/test/test_commit.py index 5571b9ecb..5832258de 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -6,23 +6,25 @@ import copy from datetime import datetime from io import BytesIO +import os.path as osp import re import sys import time from unittest.mock import Mock -from git import ( - Commit, - Actor, -) -from git import Repo +from gitdb import IStream + +from git import Actor, Commit, Repo from git.objects.util import tzoffset, utc from git.repo.fun import touch -from test.lib import TestBase, with_rw_repo, fixture_path, StringProcessAdapter -from test.lib import with_rw_directory -from gitdb import IStream -import os.path as osp +from test.lib import ( + StringProcessAdapter, + TestBase, + fixture_path, + with_rw_directory, + with_rw_repo, +) class TestCommitSerialization(TestBase): diff --git a/test/test_config.py b/test/test_config.py index ac19a7fa8..0911d0262 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -15,8 +15,8 @@ from git import GitConfigParser from git.config import _OMD, cp from git.util import rmfile -from test.lib import SkipTest, TestCase, fixture_path, with_rw_directory +from test.lib import SkipTest, TestCase, fixture_path, with_rw_directory _tc_lock_fpaths = osp.join(osp.dirname(__file__), "fixtures/*.lock") diff --git a/test/test_db.py b/test/test_db.py index de093cbd8..72d63b44b 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -3,12 +3,13 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +import os.path as osp + from git.db import GitCmdObjectDB from git.exc import BadObject -from test.lib import TestBase from git.util import bin_to_hex -import os.path as osp +from test.lib import TestBase class TestDB(TestBase): diff --git a/test/test_diff.py b/test/test_diff.py index 96fbc60e3..928a9f428 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -14,6 +14,7 @@ from git import NULL_TREE, Diff, DiffIndex, Diffable, GitCommandError, Repo, Submodule from git.cmd import Git + from test.lib import StringProcessAdapter, TestBase, fixture, with_rw_directory diff --git a/test/test_docs.py b/test/test_docs.py index 409f66bb3..b3547c1de 100644 --- a/test/test_docs.py +++ b/test/test_docs.py @@ -5,6 +5,7 @@ import gc import os +import os.path import sys import pytest @@ -12,8 +13,6 @@ from test.lib import TestBase from test.lib.helper import with_rw_directory -import os.path - class Tutorials(TestBase): def tearDown(self): diff --git a/test/test_exc.py b/test/test_exc.py index 3f4d0b803..c1eae7240 100644 --- a/test/test_exc.py +++ b/test/test_exc.py @@ -3,9 +3,11 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ +from itertools import product import re import ddt + from git.exc import ( InvalidGitRepositoryError, WorkTreeRepositoryUnsupported, @@ -20,9 +22,8 @@ RepositoryDirtyError, ) from git.util import remove_password_if_present -from test.lib import TestBase -import itertools as itt +from test.lib import TestBase _cmd_argvs = ( @@ -79,7 +80,7 @@ def test_ExceptionsHaveBaseClass(self): for ex_class in exception_classes: self.assertTrue(issubclass(ex_class, GitError)) - @ddt.data(*list(itt.product(_cmd_argvs, _causes_n_substrings, _streams_n_substrings))) + @ddt.data(*list(product(_cmd_argvs, _causes_n_substrings, _streams_n_substrings))) def test_CommandError_unicode(self, case): argv, (cause, subs), stream = case cls = CommandError diff --git a/test/test_fun.py b/test/test_fun.py index 2d30d355a..b8593b400 100644 --- a/test/test_fun.py +++ b/test/test_fun.py @@ -2,27 +2,26 @@ # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ from io import BytesIO -from stat import S_IFDIR, S_IFREG, S_IFLNK, S_IXUSR +from stat import S_IFDIR, S_IFLNK, S_IFREG, S_IXUSR from os import stat import os.path as osp +from gitdb.base import IStream +from gitdb.typ import str_tree_type + from git import Git from git.index import IndexFile -from git.index.fun import ( - aggressive_tree_merge, - stat_mode_to_index_mode, -) +from git.index.fun import aggressive_tree_merge, stat_mode_to_index_mode from git.objects.fun import ( traverse_tree_recursive, traverse_trees_recursive, - tree_to_stream, tree_entries_from_data, + tree_to_stream, ) from git.repo.fun import find_worktree_git_dir -from test.lib import TestBase, with_rw_repo, with_rw_directory from git.util import bin_to_hex, cygpath, join_path_native -from gitdb.base import IStream -from gitdb.typ import str_tree_type + +from test.lib import TestBase, with_rw_directory, with_rw_repo class TestFun(TestBase): diff --git a/test/test_git.py b/test/test_git.py index dae0f6a39..112e2f0eb 100644 --- a/test/test_git.py +++ b/test/test_git.py @@ -25,8 +25,9 @@ import ddt -from git import Git, refresh, GitCommandError, GitCommandNotFound, Repo, cmd +from git import Git, GitCommandError, GitCommandNotFound, Repo, cmd, refresh from git.util import cwd, finalize_process + from test.lib import TestBase, fixture_path, with_rw_directory diff --git a/test/test_index.py b/test/test_index.py index 622e7ca9a..b92258c92 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -17,18 +17,12 @@ import sys import tempfile +from gitdb.base import IStream + import ddt import pytest -from git import ( - BlobFilter, - Diff, - Git, - IndexFile, - Object, - Repo, - Tree, -) +from git import BlobFilter, Diff, Git, IndexFile, Object, Repo, Tree from git.exc import ( CheckoutError, GitCommandError, @@ -41,7 +35,7 @@ from git.index.util import TemporaryFileSwap from git.objects import Blob from git.util import Actor, cwd, hex_to_bin, rmtree -from gitdb.base import IStream + from test.lib import ( TestBase, VirtualEnvironment, diff --git a/test/test_reflog.py b/test/test_reflog.py index 1bd2e5dab..7ce64219a 100644 --- a/test/test_reflog.py +++ b/test/test_reflog.py @@ -5,9 +5,10 @@ import tempfile from git.objects import IndexObject -from git.refs import RefLogEntry, RefLog +from git.refs import RefLog, RefLogEntry +from git.util import Actor, hex_to_bin, rmtree + from test.lib import TestBase, fixture_path -from git.util import Actor, rmtree, hex_to_bin class TestRefLog(TestBase): diff --git a/test/test_refs.py b/test/test_refs.py index 28db70c6e..08096e69e 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -4,27 +4,28 @@ # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ from itertools import chain +import os.path as osp from pathlib import Path +import tempfile + +from gitdb.exc import BadName from git import ( - Reference, - Head, - TagReference, - RemoteReference, Commit, - SymbolicReference, GitCommandError, - RefLog, GitConfigParser, + Head, + RefLog, + Reference, + RemoteReference, + SymbolicReference, + TagReference, ) from git.objects.tag import TagObject -from test.lib import TestBase, with_rw_repo +import git.refs as refs from git.util import Actor -from gitdb.exc import BadName -import git.refs as refs -import os.path as osp -import tempfile +from test.lib import TestBase, with_rw_repo class TestRefs(TestBase): diff --git a/test/test_remote.py b/test/test_remote.py index f84452deb..5ddb41bc0 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -28,7 +28,7 @@ ) from git.cmd import Git from git.exc import UnsafeOptionError, UnsafeProtocolError -from git.util import rmtree, HIDE_WINDOWS_FREEZE_ERRORS, IterableList +from git.util import HIDE_WINDOWS_FREEZE_ERRORS, IterableList, rmtree from test.lib import ( GIT_DAEMON_PORT, TestBase, @@ -37,7 +37,6 @@ with_rw_repo, ) - # Make sure we have repeatable results. random.seed(0) diff --git a/test/test_repo.py b/test/test_repo.py index 238f94712..e38da5bb6 100644 --- a/test/test_repo.py +++ b/test/test_repo.py @@ -36,13 +36,10 @@ Submodule, Tree, ) -from git.exc import ( - BadObject, - UnsafeOptionError, - UnsafeProtocolError, -) +from git.exc import BadObject, UnsafeOptionError, UnsafeProtocolError from git.repo.fun import touch from git.util import bin_to_hex, cwd, cygpath, join_path_native, rmfile, rmtree + from test.lib import TestBase, fixture, with_rw_directory, with_rw_repo diff --git a/test/test_stats.py b/test/test_stats.py index 4efb6f313..eec73c802 100644 --- a/test/test_stats.py +++ b/test/test_stats.py @@ -3,10 +3,11 @@ # This module is part of GitPython and is released under the # 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/ -from test.lib import TestBase, fixture from git import Stats from git.compat import defenc +from test.lib import TestBase, fixture + class TestStats(TestBase): def test_list_from_string(self): diff --git a/test/test_submodule.py b/test/test_submodule.py index ee7795dbb..d88f9dab0 100644 --- a/test/test_submodule.py +++ b/test/test_submodule.py @@ -27,6 +27,7 @@ from git.objects.submodule.root import RootModule, RootUpdateProgress from git.repo.fun import find_submodule_git_dir, touch from git.util import HIDE_WINDOWS_KNOWN_ERRORS, join_path_native, to_native_path_linux + from test.lib import TestBase, with_rw_directory, with_rw_repo diff --git a/test/test_tree.py b/test/test_tree.py index 0c06b950c..73158113d 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -8,8 +8,9 @@ from pathlib import Path import subprocess -from git.objects import Tree, Blob +from git.objects import Blob, Tree from git.util import cwd + from test.lib import TestBase, with_rw_directory diff --git a/test/test_util.py b/test/test_util.py index 369896581..dad2f3dcd 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -38,6 +38,7 @@ remove_password_if_present, rmtree, ) + from test.lib import TestBase, with_rw_repo From d524c76a460b339b224c59b6753607eb2e546b2a Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Tue, 19 Mar 2024 01:02:20 -0400 Subject: [PATCH 34/34] Fix slightly unsorted imports in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 143206653..f28fedb85 100755 --- a/setup.py +++ b/setup.py @@ -1,8 +1,8 @@ #!/usr/bin/env python import os -import sys from pathlib import Path +import sys from typing import Sequence from setuptools import setup, find_packages