Skip to content

Commit 2e80398

Browse files
authored
Fix infinite os.execv when unsuitable dependencies are specified in PYTHONPATH (#151)
Remove incompatible Python tools from environment Whenever a Python tool is about to be installed with `pip` in the virtualenv, get rid of previous installation of this tool in the environment if any (sys.path and PYTHONPATH environment variable) so that it doesn't interference with pip installation. The change also detects the conflicts encountered by pip to ensure that unsuitable dependencies of the package are also excluded from the environment. This change solves an issue where an unsuitable version of black was provided via PYTHONPATH, and pip was not able to uninstall it because provided from outside. The required version of black also needed a newer version of `pathspec` provided in PYTHONPATH. Therefore, despite the `pip install proper-version-of-black`, `pip` considered that the conditions were not met, and the loop was going on forever.
1 parent 82b157f commit 2e80398

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

cpp/lib.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,34 @@
2828
DEFAULT_RE_EXTRACT_VERSION = "([0-9]+\\.[0-9]+(\\.[0-9]+)?[ab]?)"
2929

3030

31+
def forget_python_pkg(package):
32+
"""
33+
exclude from PYTHONPATH environment variable
34+
and sys.path trace of the specified package
35+
36+
Args:
37+
package: name of the Python package to exclude
38+
"""
39+
import pkg_resources
40+
41+
try:
42+
dist = pkg_resources.get_distribution(package)
43+
except pkg_resources.DistributionNotFound:
44+
return
45+
PYTHONPATH = os.environ.get("PYTHONPATH")
46+
if PYTHONPATH is not None and dist.location in PYTHONPATH:
47+
logging.debug(
48+
"Remove incompatible version of %s in PYTHONPATH: %s",
49+
package,
50+
dist.location,
51+
)
52+
os.environ["PYTHONPATH"] = PYTHONPATH.replace(dist.location, "")
53+
try:
54+
sys.path.remove(dist.location)
55+
except ValueError:
56+
pass
57+
58+
3159
@functools.lru_cache()
3260
def source_dir():
3361
"""
@@ -370,6 +398,9 @@ def is_requirement_met(self, requirement) -> bool:
370398
return True
371399
except ImportError:
372400
self._install_requirement("setuptools", restart=True)
401+
except pkg_resources.ContextualVersionConflict as conflict:
402+
forget_python_pkg(conflict.req.name)
403+
return False
373404
except (pkg_resources.VersionConflict, pkg_resources.DistributionNotFound):
374405
return False
375406

@@ -708,6 +739,7 @@ def configure(self):
708739
self._path, self._version = self.find_tool_in_path()
709740
except FileNotFoundError as e:
710741
if self.requirement:
742+
forget_python_pkg(self.requirement.name)
711743
BBPProject.virtualenv().ensure_requirement(self.requirement)
712744
self._path, self._version = self.find_tool_in_path(
713745
[BBPProject.virtualenv().bin_dir]

0 commit comments

Comments
 (0)