| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,7 @@ | |||
| 5 | 5 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php | |
| 6 | 6 | from __future__ import annotations | |
| 7 | 7 | import re | |
| 8 | - from contextlib import contextmanager | ||
| 8 | + import contextlib | ||
| 9 | 9 | import io | |
| 10 | 10 | import logging | |
| 11 | 11 | import os | |
@@ -14,6 +14,7 @@ | |||
| 14 | 14 | import subprocess | |
| 15 | 15 | import threading | |
| 16 | 16 | from textwrap import dedent | |
| 17 | + import unittest.mock | ||
| 17 | 18 | ||
| 18 | 19 | from git.compat import ( | |
| 19 | 20 | defenc, | |
@@ -963,8 +964,11 @@ def execute( | |||
| 963 | 964 | redacted_command, | |
| 964 | 965 | '"kill_after_timeout" feature is not supported on Windows.', | |
| 965 | 966 | ) | |
| 967 | + # Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value. | ||
| 968 | + patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"}) | ||
| 966 | 969 | else: | |
| 967 | 970 | cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable | |
| 971 | + patch_caller_env = contextlib.nullcontext() | ||
| 968 | 972 | # end handle | |
| 969 | 973 | ||
| 970 | 974 | stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb") | |
@@ -980,21 +984,21 @@ def execute( | |||
| 980 | 984 | istream_ok, | |
| 981 | 985 | ) | |
| 982 | 986 | try: | |
| 983 | - proc = Popen( | ||
| 984 | - command, | ||
| 985 | - env=env, | ||
| 986 | - cwd=cwd, | ||
| 987 | - bufsize=-1, | ||
| 988 | - stdin=istream or DEVNULL, | ||
| 989 | - stderr=PIPE, | ||
| 990 | - stdout=stdout_sink, | ||
| 991 | - shell=shell is not None and shell or self.USE_SHELL, | ||
| 992 | - close_fds=is_posix, # unsupported on windows | ||
| 993 | - universal_newlines=universal_newlines, | ||
| 994 | - creationflags=PROC_CREATIONFLAGS, | ||
| 995 | - **subprocess_kwargs, | ||
| 996 | - ) | ||
| 997 | - | ||
| 987 | + with patch_caller_env: | ||
| 988 | + proc = Popen( | ||
| 989 | + command, | ||
| 990 | + env=env, | ||
| 991 | + cwd=cwd, | ||
| 992 | + bufsize=-1, | ||
| 993 | + stdin=istream or DEVNULL, | ||
| 994 | + stderr=PIPE, | ||
| 995 | + stdout=stdout_sink, | ||
| 996 | + shell=shell is not None and shell or self.USE_SHELL, | ||
| 997 | + close_fds=is_posix, # unsupported on windows | ||
| 998 | + universal_newlines=universal_newlines, | ||
| 999 | + creationflags=PROC_CREATIONFLAGS, | ||
| 1000 | + **subprocess_kwargs, | ||
| 1001 | + ) | ||
| 998 | 1002 | except cmd_not_found_exception as err: | |
| 999 | 1003 | raise GitCommandNotFound(redacted_command, err) from err | |
| 1000 | 1004 | else: | |
@@ -1144,7 +1148,7 @@ def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]: | |||
| 1144 | 1148 | del self._environment[key] | |
| 1145 | 1149 | return old_env | |
| 1146 | 1150 | ||
| 1147 | - @contextmanager | ||
| 1151 | + @contextlib.contextmanager | ||
| 1148 | 1152 | def custom_environment(self, **kwargs: Any) -> Iterator[None]: | |
| 1149 | 1153 | """ | |
| 1150 | 1154 | A context manager around the above ``update_environment`` method to restore the | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,10 +4,12 @@ | |||
| 4 | 4 | # | |
| 5 | 5 | # This module is part of GitPython and is released under | |
| 6 | 6 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php | |
| 7 | + import contextlib | ||
| 7 | 8 | import os | |
| 9 | + import shutil | ||
| 8 | 10 | import subprocess | |
| 9 | 11 | import sys | |
| 10 | - from tempfile import TemporaryFile | ||
| 12 | + from tempfile import TemporaryDirectory, TemporaryFile | ||
| 11 | 13 | from unittest import mock | |
| 12 | 14 | ||
| 13 | 15 | from git import Git, refresh, GitCommandError, GitCommandNotFound, Repo, cmd | |
@@ -20,6 +22,17 @@ | |||
| 20 | 22 | from git.compat import is_win | |
| 21 | 23 | ||
| 22 | 24 | ||
| 25 | + @contextlib.contextmanager | ||
| 26 | + def _chdir(new_dir): | ||
| 27 | + """Context manager to temporarily change directory. Not reentrant.""" | ||
| 28 | + old_dir = os.getcwd() | ||
| 29 | + os.chdir(new_dir) | ||
| 30 | + try: | ||
| 31 | + yield | ||
| 32 | + finally: | ||
| 33 | + os.chdir(old_dir) | ||
| 34 | + | ||
| 35 | + | ||
| 23 | 36 | class TestGit(TestBase): | |
| 24 | 37 | @classmethod | |
| 25 | 38 | def setUpClass(cls): | |
@@ -75,6 +88,23 @@ def test_it_transforms_kwargs_into_git_command_arguments(self): | |||
| 75 | 88 | def test_it_executes_git_to_shell_and_returns_result(self): | |
| 76 | 89 | self.assertRegex(self.git.execute(["git", "version"]), r"^git version [\d\.]{2}.*$") | |
| 77 | 90 | ||
| 91 | + def test_it_executes_git_not_from_cwd(self): | ||
| 92 | + with TemporaryDirectory() as tmpdir: | ||
| 93 | + if is_win: | ||
| 94 | + # Copy an actual binary executable that is not git. | ||
| 95 | + other_exe_path = os.path.join(os.getenv("WINDIR"), "system32", "hostname.exe") | ||
| 96 | + impostor_path = os.path.join(tmpdir, "git.exe") | ||
| 97 | + shutil.copy(other_exe_path, impostor_path) | ||
| 98 | + else: | ||
| 99 | + # Create a shell script that doesn't do anything. | ||
| 100 | + impostor_path = os.path.join(tmpdir, "git") | ||
| 101 | + with open(impostor_path, mode="w", encoding="utf-8") as file: | ||
| 102 | + print("#!/bin/sh", file=file) | ||
| 103 | + os.chmod(impostor_path, 0o755) | ||
| 104 | + | ||
| 105 | + with _chdir(tmpdir): | ||
| 106 | + self.assertRegex(self.git.execute(["git", "version"]), r"^git version\b") | ||
| 107 | + | ||
| 78 | 108 | def test_it_accepts_stdin(self): | |
| 79 | 109 | filename = fixture_path("cat_file_blob") | |
| 80 | 110 | with open(filename, "r") as fh: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments