Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/git/index/util.py: 68%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This module is part of GitPython and is released under the
2# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
4"""Index utilities."""
6__all__ = ["TemporaryFileSwap", "post_clear_cache", "default_index", "git_working_dir"]
8import contextlib
9from functools import wraps
10import os
11import os.path as osp
12import struct
13import tempfile
14from types import TracebackType
16# typing ----------------------------------------------------------------------
18from typing import Any, Callable, TYPE_CHECKING, Optional, Type
20from git.types import Literal, PathLike, _T
22if TYPE_CHECKING:
23 from git.index import IndexFile
25# ---------------------------------------------------------------------------------
27# { Aliases
28pack = struct.pack
29unpack = struct.unpack
30# } END aliases
33class TemporaryFileSwap:
34 """Utility class moving a file to a temporary location within the same directory and
35 moving it back on to where on object deletion."""
37 __slots__ = ("file_path", "tmp_file_path")
39 def __init__(self, file_path: PathLike) -> None:
40 self.file_path = file_path
41 dirname, basename = osp.split(file_path)
42 fd, self.tmp_file_path = tempfile.mkstemp(prefix=basename, dir=dirname)
43 os.close(fd)
44 with contextlib.suppress(OSError): # It may be that the source does not exist.
45 os.replace(self.file_path, self.tmp_file_path)
47 def __enter__(self) -> "TemporaryFileSwap":
48 return self
50 def __exit__(
51 self,
52 exc_type: Optional[Type[BaseException]],
53 exc_val: Optional[BaseException],
54 exc_tb: Optional[TracebackType],
55 ) -> Literal[False]:
56 if osp.isfile(self.tmp_file_path):
57 os.replace(self.tmp_file_path, self.file_path)
58 return False
61# { Decorators
64def post_clear_cache(func: Callable[..., _T]) -> Callable[..., _T]:
65 """Decorator for functions that alter the index using the git command.
67 When a git command alters the index, this invalidates our possibly existing entries
68 dictionary, which is why it must be deleted to allow it to be lazily reread later.
69 """
71 @wraps(func)
72 def post_clear_cache_if_not_raised(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
73 rval = func(self, *args, **kwargs)
74 self._delete_entries_cache()
75 return rval
77 # END wrapper method
79 return post_clear_cache_if_not_raised
82def default_index(func: Callable[..., _T]) -> Callable[..., _T]:
83 """Decorator ensuring the wrapped method may only run if we are the default
84 repository index.
86 This is as we rely on git commands that operate on that index only.
87 """
89 @wraps(func)
90 def check_default_index(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
91 if self._file_path != self._index_path():
92 raise AssertionError(
93 "Cannot call %r on indices that do not represent the default git index" % func.__name__
94 )
95 return func(self, *args, **kwargs)
97 # END wrapper method
99 return check_default_index
102def git_working_dir(func: Callable[..., _T]) -> Callable[..., _T]:
103 """Decorator which changes the current working dir to the one of the git
104 repository in order to ensure relative paths are handled correctly."""
106 @wraps(func)
107 def set_git_working_dir(self: "IndexFile", *args: Any, **kwargs: Any) -> _T:
108 cur_wd = os.getcwd()
109 os.chdir(str(self.repo.working_tree_dir))
110 try:
111 return func(self, *args, **kwargs)
112 finally:
113 os.chdir(cur_wd)
114 # END handle working dir
116 # END wrapper
118 return set_git_working_dir
121# } END decorators