-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1b30c6f
commit 6a92054
Showing
4 changed files
with
187 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
@static if VERSION >= v"1.7.0" | ||
const _which = Sys.which | ||
else | ||
function _which(program_name::String) | ||
if isempty(program_name) | ||
return nothing | ||
end | ||
# Build a list of program names that we're going to try | ||
program_names = String[] | ||
base_pname = basename(program_name) | ||
if Sys.iswindows() | ||
# If the file already has an extension, try that name first | ||
if !isempty(splitext(base_pname)[2]) | ||
push!(program_names, base_pname) | ||
end | ||
|
||
# But also try appending .exe and .com` | ||
for pe in (".exe", ".com") | ||
push!(program_names, string(base_pname, pe)) | ||
end | ||
else | ||
# On non-windows, we just always search for what we've been given | ||
push!(program_names, base_pname) | ||
end | ||
|
||
path_dirs = String[] | ||
program_dirname = dirname(program_name) | ||
# If we've been given a path that has a directory name in it, then we | ||
# check to see if that path exists. Otherwise, we search the PATH. | ||
if isempty(program_dirname) | ||
# If we have been given just a program name (not a relative or absolute | ||
# path) then we should search `PATH` for it here: | ||
pathsep = Sys.iswindows() ? ';' : ':' | ||
path_dirs = abspath.(split(get(ENV, "PATH", ""), pathsep)) | ||
|
||
# On windows we always check the current directory as well | ||
if Sys.iswindows() | ||
pushfirst!(path_dirs, pwd()) | ||
end | ||
else | ||
push!(path_dirs, abspath(program_dirname)) | ||
end | ||
|
||
# Here we combine our directories with our program names, searching for the | ||
# first match among all combinations. | ||
for path_dir in path_dirs | ||
for pname in program_names | ||
program_path = joinpath(path_dir, pname) | ||
# If we find something that matches our name and we can execute | ||
if isfile(program_path) && Sys.isexecutable(program_path) | ||
return program_path | ||
end | ||
end | ||
end | ||
|
||
# If we couldn't find anything, don't return anything | ||
nothing | ||
end | ||
|
||
_which(program_name::AbstractString) = _which(String(program_name)) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
using PyCall, Test | ||
|
||
|
||
function test_venv_has_python(path) | ||
newpython = PyCall.python_cmd(venv=path).exec[1] | ||
if !isfile(newpython) | ||
@info """ | ||
Python executable $newpython does not exists. | ||
This directory contains only the following files: | ||
$(join(readdir(dirname(newpython)), '\n')) | ||
""" | ||
end | ||
@test isfile(newpython) | ||
end | ||
|
||
|
||
function test_venv_activation(path) | ||
newpython = PyCall.python_cmd(venv=path).exec[1] | ||
|
||
# Run a fresh Julia process with new Python environment | ||
code = """ | ||
$(Base.load_path_setup_code()) | ||
using PyCall | ||
println(PyCall.pyimport("sys").executable) | ||
println(PyCall.pyimport("sys").exec_prefix) | ||
println(PyCall.pyimport("pip").__file__) | ||
""" | ||
# Note that `pip` is just some arbitrary non-standard | ||
# library. Using standard library like `os` does not work | ||
# because those files are not created. | ||
env = copy(ENV) | ||
env["PYCALL_JL_RUNTIME_PYTHON"] = newpython | ||
jlcmd = setenv(`$(Base.julia_cmd()) --startup-file=no -e $code`, env) | ||
if Sys.iswindows() | ||
# Marking the test broken in Windows. It seems that | ||
# venv copies .dll on Windows and libpython check in | ||
# PyCall.__init__ detects that. | ||
@test_broken begin | ||
output = read(jlcmd, String) | ||
sys_executable, exec_prefix, mod_file = split(output, "\n") | ||
newpython == sys_executable | ||
end | ||
else | ||
output = read(jlcmd, String) | ||
sys_executable, exec_prefix, mod_file = split(output, "\n") | ||
@test newpython == sys_executable | ||
@test startswith(exec_prefix, path) | ||
@test startswith(mod_file, path) | ||
end | ||
end | ||
|
||
|
||
@testset "virtualenv activation" begin | ||
pyname = "python$(pyversion.major).$(pyversion.minor)" | ||
if Sys.which("virtualenv") === nothing | ||
@info "No virtualenv command. Skipping the test..." | ||
elseif Sys.which(pyname) === nothing | ||
@info "No $pyname command. Skipping the test..." | ||
else | ||
mktempdir() do tmppath | ||
if PyCall.pyversion.major == 2 | ||
path = joinpath(tmppath, "kind") | ||
else | ||
path = joinpath(tmppath, "ϵνιℓ") | ||
end | ||
run(`virtualenv --python=$pyname $path`) | ||
test_venv_has_python(path) | ||
|
||
newpython = PyCall.python_cmd(venv=path).exec[1] | ||
venv_libpython = PyCall.find_libpython(newpython) | ||
if venv_libpython != PyCall.libpython | ||
@info """ | ||
virtualenv created an environment with incompatible libpython: | ||
$venv_libpython | ||
""" | ||
return | ||
end | ||
|
||
test_venv_activation(path) | ||
end | ||
end | ||
end | ||
|
||
|
||
@testset "venv activation" begin | ||
# In case PyCall is built with a Python executable created by | ||
# `virtualenv`, let's try to find the original Python executable. | ||
# Otherwise, `venv` does not work with this Python executable: | ||
# https://bugs.python.org/issue30811 | ||
sys = PyCall.pyimport("sys") | ||
if hasproperty(sys, :real_prefix) | ||
# sys.real_prefix is set by virtualenv and does not exist in | ||
# standard Python: | ||
# https://github.com/pypa/virtualenv/blob/16.0.0/virtualenv_embedded/site.py#L554 | ||
candidates = [ | ||
PyCall.venv_python(sys.real_prefix, "$(pyversion.major).$(pyversion.minor)"), | ||
PyCall.venv_python(sys.real_prefix, "$(pyversion.major)"), | ||
PyCall.venv_python(sys.real_prefix), | ||
PyCall.pyprogramname, # must exists | ||
] | ||
python = candidates[findfirst(isfile, candidates)] | ||
else | ||
python = PyCall.pyprogramname | ||
end | ||
|
||
if PyCall.conda | ||
@info "Skip venv test with conda." | ||
elseif !success(PyCall.python_cmd(`-c "import venv"`, python=python)) | ||
@info "Skip venv test since venv package is missing." | ||
else | ||
mktempdir() do tmppath | ||
if PyCall.pyversion.major == 2 | ||
path = joinpath(tmppath, "kind") | ||
else | ||
path = joinpath(tmppath, "ϵνιℓ") | ||
end | ||
# Create a new virtual environment | ||
run(PyCall.python_cmd(`-m venv $path`, python=python)) | ||
test_venv_has_python(path) | ||
test_venv_activation(path) | ||
end | ||
end | ||
end |