Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Git.refresh GitCommandNotFound indicates "git" even for other commands #1809

Closed
@EliahKagan

Description

@EliahKagan

The bug

Even when the Git command thatGit.refresh runs and checks is notgit, its command line is reported asgit in theGitCommandNotFound exception, when such an exception is raised:

GitPython/git/cmd.py

Lines 483 to 485 ind28c20b

# After the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is no longer
# None) we raise an exception.
raiseGitCommandNotFound("git",err)

Steps to reproduce

The effect happens when the exception is raised due to a subsequent call toGit.refresh, since that is whenGit.refresh raisesGitCommandNotFound rather thanImportError. Thecommand attribute is directly part of the traceback message, presented ascmdline. This can be produced by running a shell one-liner:

$ GIT_PYTHON_GIT_EXECUTABLE=git-2.43 GIT_PYTHON_REFRESH=quiet python -c 'import git; git.refresh()'Traceback (most recent call last):  File "<string>", line 1, in <module>  File "/home/ek/repos-wsl/GitPython/git/__init__.py", line 127, in refresh    if not Git.refresh(path=path):           ^^^^^^^^^^^^^^^^^^^^^^  File "/home/ek/repos-wsl/GitPython/git/cmd.py", line 485, in refresh    raise GitCommandNotFound("git", err)git.exc.GitCommandNotFound: Cmd('git') not found due to: 'Bad git executable.The git executable must be specified in one of the following ways:    - be included in your $PATH    - be set via $GIT_PYTHON_GIT_EXECUTABLE    - explicitly set via git.refresh()'  cmdline: git

Although often a custom Git command may be a full path whose last component is exactlygit (or sometimesgit.exe on Windows), the above shows a realistic scenario in which even that may not be the case. I do not have a command namedgit-2.43 installed, so it is correct that I get an error, but the error should not state the name of the command asgit.

Verification

Verbose debugging confirms that the command given inGIT_PYTHON_GIT_EXECUTABLE really is running, and that the only problem is that the hard-coded command namegit is printed instead:

$ GIT_PYTHON_GIT_EXECUTABLE=git-2.43 GIT_PYTHON_REFRESH=quiet GIT_PYTHON_TRACE=full python -c 'import logging; logging.basicConfig(level=logging.DEBUG); import git; git.refresh()'DEBUG:git.cmd:Popen(['git-2.43', 'version'], cwd=/home/ek/repos-wsl/GitPython, stdin=None, shell=False, universal_newlines=False)DEBUG:git.cmd:Popen(['git-2.43', 'version'], cwd=/home/ek/repos-wsl/GitPython, stdin=None, shell=False, universal_newlines=False)Traceback (most recent call last):  ...  cmdline: git

Capturing thesubprocess.Popenaudit event verifies that no other logging bug in GitPython is confusing the analysis:

$ GIT_PYTHON_GIT_EXECUTABLE=git-2.43 GIT_PYTHON_REFRESH=quiet python -c 'import sys, git; sys.addaudithook(lambda event, args: event == "subprocess.Popen" and print(args[:2])); git.refresh()'('git-2.43', ['git-2.43', 'version'])Traceback (most recent call last):  ...  cmdline: git

(For both the above runs, I replaced most of the traceback with....)

Why this is a bug

Since it's possible, however unlikely, that someone has come to rely on the exception object'scommand attribute having the exact value"git" in this situation, it shouldarguably only be changed if it really is a bug for it to hold that value.

I believe it is a bug, for three reasons:

1. It is presented as the command line

When users see this in theGitCommandNotFound exception message, I believe they will assume it is the command that was run:

  cmdline: git

2. It differs from any otherGitCommandNotFound

In other situations that raiseGitCommandNotFound, it comes fromGit.execute, and the exception is constructed with the actual command line ascommand (except that redactions are performed, such as for passwords):

GitPython/git/cmd.py

Lines 1065 to 1079 ind28c20b

try:
proc=safer_popen(
command,
env=env,
cwd=cwd,
bufsize=-1,
stdin=(istreamorDEVNULL),
stderr=PIPE,
stdout=stdout_sink,
shell=shell,
universal_newlines=universal_newlines,
**subprocess_kwargs,
)
exceptcmd_not_found_exceptionaserr:
raiseGitCommandNotFound(redacted_command,err)fromerr

3. The superclass documents thecommand argument

GitCommandNotFound inherits itscommand attribute from itsCommandError superclass. AlthoughCommandError does not document thecommand attribute directly, it does document thecommand argument from which the value of that attribute is derived:

GitPython/git/exc.py

Lines 83 to 88 ind28c20b

classCommandError(GitError):
"""Base class for exceptions thrown at every stage of `Popen()` execution.
:param command:
A non-empty list of argv comprising the command-line.
"""

This does not really guarantee the specific meaning of thecommandattribute. (Likskov substitutability is usually a goal for objects that have already been constructed/initialized; subclass__new__ and__init__ are not expected to treat arguments the same ways as the superclass.) But it is a further reason to consider the current behavior inGit.refresh unexpected.

Possible fixes

This could be fixed by havingGit.refresh construct theGitCommandNotFoundError with the same command line that itscls().version() call causesGit.execute to use, which is given byGIT_PYTHON_GIT_EXECUTABLE.

Another approach, though, would be to keep a reference to theGitCommandNotFound exception that occurred occurred due to runningcls().version():

GitPython/git/cmd.py

Lines 381 to 385 ind28c20b

try:
cls().version()
has_git=True
except (GitCommandNotFound,PermissionError):
pass

Then that same exception object, with its original information, could be reraised instead of constructing and raising a newGitCommandNotFound object.

If this is to be done, then some refactoring may be in order, and a decision about whether thatGitCommandNotFound object should be used as thecause of anImportError in the case that is raised should probably also be made:

raiseImportError(err)

Thus, in addition to the other changes, that statement could be changed to theraise ... from ... form.

There may be reasons to avoid this approach, in whole or in part, that I am not aware of.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp