Gitpython: Import Error on Linux

Created on 24 Dec 2017  路  14Comments  路  Source: gitpython-developers/GitPython

gitpython version: 2.1.8
git version: 1.8.3.1
python version: 2.7.5

I'm using REL7. This is not an issue in REL6 (not sure if I should post here or somewhere else...)

To reproduce on REL7:

  • create repo in your home dir with 700 permissions
  • cd $repo
  • sudo -u $someotheruser python -c "import git"
    here, someotheruser is not able to read $repo

I get this error: `Traceback (most recent call last):

  File "<string>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/git/__init__.py", line 82, in <module>
    refresh()
  File "/usr/lib64/python2.7/site-packages/git/__init__.py", line 73, in refresh
    if not Git.refresh(path=path):
  File "/usr/lib64/python2.7/site-packages/git/cmd.py", line 293, in refresh
    raise ImportError(err)
ImportError: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
    - quiet|q|silence|s|none|n|0: for no warning or exception
    - warn|w|warning|1: for a printed warning
    - error|e|raise|r|2: for a raised exception

Example:
    export GIT_PYTHON_REFRESH=quiet
acknowledged help wanted

Most helpful comment

I have encountered the same problem under Ubuntu 14.04.

Git version: 1.9.1
GitPython version: 2.1.8
Python version: 2.7.6

In my case, I am attempting to invoke GitPython as another user, but from within my own home directory. To illustrate:

# confirm current user and path
$ whoami
ianw
$ pwd
/home/ianw
# confirm path is set when running commands as other user
$ sudo -u ubuntu echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
# confirm other user has git in path
$ sudo -u ubuntu which git
/usr/bin/git
# attempt to import GitPython as other user
$ sudo -u ubuntu python -c "import git"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/git/__init__.py", line 82, in <module>
    refresh()
  File "/usr/local/lib/python2.7/dist-packages/git/__init__.py", line 73, in refresh
    if not Git.refresh(path=path):
  File "/usr/local/lib/python2.7/dist-packages/git/cmd.py", line 293, in refresh
    raise ImportError(err)
ImportError: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
    - quiet|q|silence|s|none|n|0: for no warning or exception
    - warn|w|warning|1: for a printed warning
    - error|e|raise|r|2: for a raised exception

Example:
    export GIT_PYTHON_REFRESH=quiet

I concur with @tomkcook's analysis that Popen() is throwing PermissionError because it can't read cwd, not because it can't find git. Thus, the error message shown above is inaccurate. To wit:

  1. On initialization of the module, the refresh() function is called with no arguments, meaning path defaults to None.
  2. That means that the Git.refresh() classmethod is also called with path set to None.
  3. Which, in turn, means that the Git class is instantiated with working_dir set to None, so its self._working_dir attribute is None as well.
  4. So when Git.refresh() attempts to invoke the "git version" command as a sanity test, it means execute() has no value for self._working_dir, so it falls back to using os.getcwd().
  5. Thus, in the case where cwd is a path that the user cannot access, the call to Popen() fails with the ambiguous PermissionError.

Aside from the error message being inaccurate, I question whether GitPython actually needs to use the current working directory in this context. If there's a need to preserve defaulting to the current working directory, perhaps it's worth confirming that it can be accessed (e.g. with os.access()) before passing it to Popen()?

All 14 comments

Thanks for the detailed descriptions, which I am sure would help to reproduce it.

It appears this issue is not entirely complete, the example seems to be truncated - is this intended?
Also I am wondering what the desired behavior would be.
Thanks for the clarification.

Yes, I only mean to import git. The issue is that I am unable to import git in that specific situation (import git from an unreadable directory). The desired behavior is no error when trying to import git.

Thank you! I wonder why git does not appear to be in the PATH in that case, which is the source of the trouble.
When looking at the sudo ... invocation, I wonder what would happen if you would also specify the -i flag, like so: sudo -i -u $someotheruser python -c "import git". That should source some additional configuration files and hopefully set the PATH accordingly.

The way I see it right now, GitPython clearly communicates what the problem is, and allows the issue to be alleviated by following the instructions. Changing the defaults to be silent would certainly just delay the inevitable.
However, maybe there is a better way to deal with this, even though I am wondering what the value added would be.

This happens because explicitly passing a directory to which the user does not have permission as the cwd to POpen causes a PermissionError to be thrown. Compare:

$ mkdir temp
$ chmod go-rwx temp
$ pwd
/home/me/temp
$ sudo -u another_user python3 'import subprocess; subprocess.call(["pwd"], cwd=None)'
/home/me/temp
$ sudo -u another_user python3 'import subprocess; subprocess.call(["pwd"], cwd=".")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.5/subprocess.py", line 557, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/lib/python3.5/subprocess.py", line 947, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.5/subprocess.py", line 1551, in _execute_child
    raise child_exception_type(errno_num, err_msg)
PermissionError: [Errno 13] Permission denied

I think you could fix this by changing cmd.py:691 to cwd = self._working_dir but haven't had a chance to test it.

I have encountered the same problem under Ubuntu 14.04.

Git version: 1.9.1
GitPython version: 2.1.8
Python version: 2.7.6

In my case, I am attempting to invoke GitPython as another user, but from within my own home directory. To illustrate:

# confirm current user and path
$ whoami
ianw
$ pwd
/home/ianw
# confirm path is set when running commands as other user
$ sudo -u ubuntu echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
# confirm other user has git in path
$ sudo -u ubuntu which git
/usr/bin/git
# attempt to import GitPython as other user
$ sudo -u ubuntu python -c "import git"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/git/__init__.py", line 82, in <module>
    refresh()
  File "/usr/local/lib/python2.7/dist-packages/git/__init__.py", line 73, in refresh
    if not Git.refresh(path=path):
  File "/usr/local/lib/python2.7/dist-packages/git/cmd.py", line 293, in refresh
    raise ImportError(err)
ImportError: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
    - quiet|q|silence|s|none|n|0: for no warning or exception
    - warn|w|warning|1: for a printed warning
    - error|e|raise|r|2: for a raised exception

Example:
    export GIT_PYTHON_REFRESH=quiet

I concur with @tomkcook's analysis that Popen() is throwing PermissionError because it can't read cwd, not because it can't find git. Thus, the error message shown above is inaccurate. To wit:

  1. On initialization of the module, the refresh() function is called with no arguments, meaning path defaults to None.
  2. That means that the Git.refresh() classmethod is also called with path set to None.
  3. Which, in turn, means that the Git class is instantiated with working_dir set to None, so its self._working_dir attribute is None as well.
  4. So when Git.refresh() attempts to invoke the "git version" command as a sanity test, it means execute() has no value for self._working_dir, so it falls back to using os.getcwd().
  5. Thus, in the case where cwd is a path that the user cannot access, the call to Popen() fails with the ambiguous PermissionError.

Aside from the error message being inaccurate, I question whether GitPython actually needs to use the current working directory in this context. If there's a need to preserve defaulting to the current working directory, perhaps it's worth confirming that it can be accessed (e.g. with os.access()) before passing it to Popen()?

@yarikoptic I totally agree, testing before failing is certainly a viable fix for the issue. Maybe it's even possible to handle the CWD differently, even though I would be afraid of subtle breakage if that behaviour would indeed change.
My recommendation is to check with os.access() and _do the right thing鈩笍_ depending on the outcome.

Thanks for thinking about me @Byron, but did you mean ianwestcott ? or should I analyze the situation as well and provide feedback?

Indeed, I meant to reply to @ianwestcott, thanks for pointing it out 馃 !

Nice digging @ianwestcott - I'm having this same issue, but in another scenario where I have a puppet agent running what is effectively a GitPython wrapper, and notably doesn't have an interactive shell which tickles the Popen() PermissionError discussed above. In order to resolve it, I had to specify a cwd to my exec, but just poking my head in here to suggest perhaps an upstream fix/better error message.

Somehow I vaguely remember that a PR landed to fix this kind of issue. Maybe it's worth trying again with the latest release v2.1.11 .

I'm still encountering this problem with GitPython 2.1.11 (with Python 2.7.6 on Ubuntu 14.04.1), when trying to invoke a script via sudo (e.g. sudo -Hu scriptuser /home/scriptuser/bin/script.py foo).

Initially very confused because the script's path definitely included the dir with the git executable.

Thanks to @ianwestcott 's deep dive, I was able to solve the problem by sticking these two lines at the very beginning of the script:

import os
os.chdir('/tmp')

@ianwestcott Given your analysis, why not do as I suggested and pass self._working_dir instead of os.getcwd()? In this case _working_dir will be None and this avoids the PermissionError.

[root@a25d0d47c918 app]# python3
```Python 3.6.8 (default, Aug 7 2019, 17:28:10)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.

```>>> import git```

Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 83, in
refresh()
File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 73, in refresh
if not Git.refresh(path=path):
File "/usr/local/lib/python3.6/site-packages/git/cmd.py", line 278, in refresh
raise ImportError(err)
ImportError: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
- quiet|q|silence|s|none|n|0: for no warning or exception
- warn|w|warning|1: for a printed warning
- error|e|raise|r|2: for a raised exception

Example:
export GIT_PYTHON_REFRESH=quiet

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 85, in
raise ImportError('Failed to initialize: {0}'.format(exc))
ImportError: Failed to initialize: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
- quiet|q|silence|s|none|n|0: for no warning or exception
- warn|w|warning|1: for a printed warning
- error|e|raise|r|2: for a raised exception

Example:
export GIT_PYTHON_REFRESH=quiet
```

I have to write export GIT_PYTHON_REFRESH=quiet after every reboot , I am trying this in docker:centos 7 , any way i can fix this permanently in centos 7.

[root@a25d0d47c918 app]# python3

[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import git

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 83, in <module>
    refresh()
  File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 73, in refresh
    if not Git.refresh(path=path):
  File "/usr/local/lib/python3.6/site-packages/git/cmd.py", line 278, in refresh
    raise ImportError(err)
ImportError: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
    - quiet|q|silence|s|none|n|0: for no warning or exception
    - warn|w|warning|1: for a printed warning
    - error|e|raise|r|2: for a raised exception

Example:
    export GIT_PYTHON_REFRESH=quiet


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/git/__init__.py", line 85, in <module>
    raise ImportError('Failed to initialize: {0}'.format(exc))
ImportError: Failed to initialize: 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()

All git commands will error until this is rectified.

This initial warning can be silenced or aggravated in the future by setting the
$GIT_PYTHON_REFRESH environment variable. Use one of the following values:
    - quiet|q|silence|s|none|n|0: for no warning or exception
    - warn|w|warning|1: for a printed warning
    - error|e|raise|r|2: for a raised exception

Example:
    export GIT_PYTHON_REFRESH=quiet

I have to write export GIT_PYTHON_REFRESH=quiet after every reboot , I am trying this in docker:centos 7 , any way i can fix this permanently in centos 7.

Actually I had not installed git in the os therefore it was throwing this warning. Check if you all have installed git in your os.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marcwebbie picture marcwebbie  路  6Comments

aaronlelevier picture aaronlelevier  路  5Comments

luator picture luator  路  6Comments

connordelacruz picture connordelacruz  路  5Comments

NiQ1 picture NiQ1  路  4Comments