Skip to content

Commit 3c2eee8

Browse files
committed
Test invoking PyJulia with incompatible Python executable
1 parent 65f11f5 commit 3c2eee8

File tree

6 files changed

+142
-0
lines changed

6 files changed

+142
-0
lines changed

.travis.yml

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ env:
1111
- JULIA_VERSION=nightly
1212
global:
1313
- TOXENV=py
14+
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=/usr/bin/python2
1415
matrix:
1516
# Python environment is not functional on OS X
1617
include:
@@ -29,12 +30,14 @@ matrix:
2930
- language: generic
3031
env:
3132
- PYTHON=python3
33+
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=python2
3234
- JULIA_VERSION=1.0
3335
# - JULIA_VERSION=nightly
3436
os: osx
3537
- language: generic
3638
env:
3739
- PYTHON=python3
40+
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=python2
3841
- JULIA_VERSION=0.6.4
3942
- CROSS_VERSION=1
4043
os: osx
@@ -59,6 +62,15 @@ before_script:
5962
- julia --color=yes -e 'VERSION >= v"0.7.0-DEV.5183" && using Pkg; Pkg.add("PyCall")'
6063
script:
6164

65+
# Point PYJULIA_TEST_INCOMPATIBLE_PYTHONS to incompatible Python
66+
# executable (see: test/test_compatible_exe.py).
67+
- if [ "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS" = "$PYTHON" ]; then
68+
PYJULIA_TEST_INCOMPATIBLE_PYTHONS="";
69+
elif ! which "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS"; then
70+
PYJULIA_TEST_INCOMPATIBLE_PYTHONS="";
71+
fi
72+
- echo "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS"
73+
6274
# "py,py27" below would be redundant when the main interpreter is
6375
# Python 2.7 but it simplifies the CI setup.
6476
- if [ "$CROSS_VERSION" = "1" ]; then

appveyor.yml

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ environment:
44
TOX_TESTENV_PASSENV: DISTUTILS_USE_SDK MSSdk INCLUDE LIB
55
# https://packaging.python.org/guides/supporting-windows-using-appveyor/#testing-with-tox
66

7+
# Point PYJULIA_TEST_INCOMPATIBLE_PYTHONS to incompatible Python
8+
# executable (see: test/test_compatible_exe.py). MUST specify
9+
# Python versions NOT listed in the matrix below:
10+
PYJULIA_TEST_INCOMPATIBLE_PYTHONS: python3.7.bat
11+
712
# for more python versions have a look at
813
# https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml
914
matrix:

ci/appveyor/win32/python3.7.bat

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@C:\Python37\python.exe %*

ci/appveyor/win64/python3.7.bat

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@C:\Python37-x64\python.exe %*

test/test_compatible_exe.py

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from __future__ import print_function
2+
3+
import os
4+
import subprocess
5+
import sys
6+
import textwrap
7+
8+
import pytest
9+
10+
from .test_core import julia
11+
from julia.core import _enviorn, which
12+
13+
is_linux = sys.platform.startswith("linux")
14+
is_windows = os.name == "nt"
15+
is_apple = sys.platform == "darwin"
16+
17+
18+
def _get_paths(path):
19+
return filter(None, path.split(":"))
20+
21+
22+
# Environment variable PYJULIA_TEST_INCOMPATIBLE_PYTHONS is the
23+
# :-separated list of Python executables incompatible with the current
24+
# Python:
25+
incompatible_pythons = _get_paths(os.getenv("PYJULIA_TEST_INCOMPATIBLE_PYTHONS", ""))
26+
27+
28+
try:
29+
from types import SimpleNamespace
30+
except ImportError:
31+
from argparse import Namespace as SimpleNamespace
32+
33+
34+
def _run_fallback(args, input=None, **kwargs):
35+
process = subprocess.Popen(args, stdin=subprocess.PIPE, **kwargs)
36+
stdout, stderr = process.communicate(input)
37+
retcode = process.wait()
38+
return SimpleNamespace(args=args, stdout=stdout, stderr=stderr, returncode=retcode)
39+
40+
41+
try:
42+
from subprocess import run
43+
except ImportError:
44+
run = _run_fallback
45+
46+
47+
def runcode(python, code):
48+
"""Run `code` in `python`."""
49+
return run(
50+
[python],
51+
input=textwrap.dedent(code),
52+
stdout=subprocess.PIPE,
53+
stderr=subprocess.PIPE,
54+
universal_newlines=True,
55+
env=dict(
56+
_enviorn,
57+
# Make PyJulia importable:
58+
PYTHONPATH=os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
59+
),
60+
)
61+
62+
63+
def print_completed_proc(proc):
64+
# Print output (pytest will hide it by default):
65+
print("Ran:", *proc.args)
66+
if proc.stdout:
67+
print("# --- STDOUT from", *proc.args)
68+
print(proc.stdout)
69+
if proc.stderr:
70+
print("# --- STDERR from", *proc.args)
71+
print(proc.stderr)
72+
print("# ---")
73+
74+
75+
def is_dynamically_linked(executable):
76+
path = which(executable)
77+
assert os.path.exists(path)
78+
if is_linux and which("ldd"):
79+
proc = run(
80+
["ldd", path], stdout=subprocess.PIPE, env=_enviorn, universal_newlines=True
81+
)
82+
print_completed_proc(proc)
83+
return "libpython" in proc.stdout
84+
elif is_apple and which("otool"):
85+
proc = run(
86+
["otool", "-L", path],
87+
stdout=subprocess.PIPE,
88+
env=_enviorn,
89+
universal_newlines=True,
90+
)
91+
print_completed_proc(proc)
92+
return "libpython" in proc.stdout or "/Python" in proc.stdout
93+
# TODO: support Windows
94+
return None
95+
96+
97+
@pytest.mark.parametrize("python", incompatible_pythons)
98+
def test_incompatible_python(python):
99+
if julia.eval("(VERSION.major, VERSION.minor)") == (0, 6):
100+
# Julia 0.6 implements mixed version
101+
return
102+
103+
python = which(python)
104+
proc = runcode(
105+
python,
106+
"""
107+
import os
108+
from julia import Julia
109+
Julia(runtime=os.getenv("JULIA_EXE"), debug=True)
110+
""",
111+
)
112+
print_completed_proc(proc)
113+
114+
assert proc.returncode == 1
115+
assert "It seems your Julia and PyJulia setup are not supported." in proc.stderr
116+
dynamic = is_dynamically_linked(python)
117+
if dynamic is True:
118+
assert "`libpython` have to match" in proc.stderr
119+
elif dynamic is False:
120+
assert "is statically linked to libpython" in proc.stderr

tox.ini

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ passenv =
3131
PYJULIA_TEST_REBUILD
3232
JULIA_EXE
3333

34+
# See: test/test_compatible_exe.py
35+
PYJULIA_TEST_INCOMPATIBLE_PYTHONS
36+
3437
# See: https://coveralls-python.readthedocs.io/en/latest/usage/tox.html#travisci
3538
TRAVIS
3639
TRAVIS_*

0 commit comments

Comments
 (0)