Skip to content

Fix locating system python when it's not in $PATH (weird but happens, apparently) #1856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from Jun 10, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 48 additions & 5 deletions pythonforandroid/pythonpackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@


from io import open # needed for python 2
import functools
import os
from pep517.envbuild import BuildEnvironment
from pep517.wrappers import Pep517HookCaller
Expand Down Expand Up @@ -172,11 +173,23 @@ def _get_system_python_executable():

def python_binary_from_folder(path):
def binary_is_usable(python_bin):
""" Helper function to see if a given binary name refers
to a usable python interpreter binary
"""

# Abort if path isn't present at all or a directory:
if not os.path.exists(
os.path.join(path, python_bin)
) or os.path.isdir(os.path.join(path, python_bin)):
return
# We should check file not found anyway trying to run it,
# since it might be a dead symlink:
try:
filenotfounderror = FileNotFoundError
except NameError: # Python 2
filenotfounderror = OSError
try:
# Run it and see if version output works with no error:
subprocess.check_output([
os.path.join(path, python_bin), "--version"
], stderr=subprocess.STDOUT)
Expand All @@ -202,19 +215,26 @@ def binary_is_usable(python_bin):
bad_candidates = []
good_candidates = []
ever_had_nonvenv_path = False
ever_had_path_starting_with_prefix = False
for p in os.environ.get("PATH", "").split(":"):
# Skip if not possibly the real system python:
if not os.path.normpath(p).startswith(
os.path.normpath(search_prefix)
):
continue

ever_had_path_starting_with_prefix = True

# First folders might be virtualenv/venv we want to avoid:
if not ever_had_nonvenv_path:
sep = os.path.sep
if ("system32" not in p.lower() and "usr" not in p) or \
{"home", ".tox"}.intersection(set(p.split(sep))) or \
"users" in p.lower():
if (
("system32" not in p.lower() and
"usr" not in p and
not p.startswith("/opt/python")) or
{"home", ".tox"}.intersection(set(p.split(sep))) or
"users" in p.lower()
):
# Doesn't look like bog-standard system path.
if (p.endswith(os.path.sep + "bin") or
p.endswith(os.path.sep + "bin" + os.path.sep)):
Expand All @@ -226,14 +246,37 @@ def binary_is_usable(python_bin):

good_candidates.append(p)

# If we have a bad env with PATH not containing any reference to our
# real python (travis, why would you do that to me?) then just guess
# based from the search prefix location itself:
if not ever_had_path_starting_with_prefix:
# ... and yes we're scanning all the folders for that, it's dumb
# but i'm not aware of a better way: (@JonasT)
for root, dirs, files in os.walk(search_prefix, topdown=True):
for name in dirs:
bad_candidates.append(os.path.join(root, name))

# Sort candidates by length (to prefer shorter ones):
def candidate_cmp(a, b):
return len(a) - len(b)
good_candidates = sorted(
good_candidates, key=functools.cmp_to_key(candidate_cmp)
)
bad_candidates = sorted(
bad_candidates, key=functools.cmp_to_key(candidate_cmp)
)

# See if we can now actually find the system python:
for p in good_candidates + bad_candidates:
result = python_binary_from_folder(p)
if result is not None:
return result

raise RuntimeError("failed to locate system python in: " +
sys.real_prefix)
raise RuntimeError(
"failed to locate system python in: {}"
" - checked candidates were: {}, {}"
.format(sys.real_prefix, good_candidates, bad_candidates)
)


def get_package_as_folder(dependency):
Expand Down