Skip to content

Commit

Permalink
Test invoking PyJulia with incompatible Python executable
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf committed Oct 27, 2018
1 parent 65f11f5 commit 698e05b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ env:
- JULIA_VERSION=nightly
global:
- TOXENV=py
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=/usr/bin/python2
matrix:
# Python environment is not functional on OS X
include:
Expand All @@ -29,12 +30,14 @@ matrix:
- language: generic
env:
- PYTHON=python3
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=python2
- JULIA_VERSION=1.0
# - JULIA_VERSION=nightly
os: osx
- language: generic
env:
- PYTHON=python3
- PYJULIA_TEST_INCOMPATIBLE_PYTHONS=python2
- JULIA_VERSION=0.6.4
- CROSS_VERSION=1
os: osx
Expand All @@ -59,6 +62,15 @@ before_script:
- julia --color=yes -e 'VERSION >= v"0.7.0-DEV.5183" && using Pkg; Pkg.add("PyCall")'
script:

# Point PYJULIA_TEST_INCOMPATIBLE_PYTHONS to incompatible Python
# executable (see: test/test_compatible_exe.py).
- if [ "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS" = "$PYTHON" ]; then
PYJULIA_TEST_INCOMPATIBLE_PYTHONS="";
elif ! which "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS"; then
PYJULIA_TEST_INCOMPATIBLE_PYTHONS="";
fi
- echo "$PYJULIA_TEST_INCOMPATIBLE_PYTHONS"

# "py,py27" below would be redundant when the main interpreter is
# Python 2.7 but it simplifies the CI setup.
- if [ "$CROSS_VERSION" = "1" ]; then
Expand Down
5 changes: 5 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ environment:
TOX_TESTENV_PASSENV: DISTUTILS_USE_SDK MSSdk INCLUDE LIB
# https://packaging.python.org/guides/supporting-windows-using-appveyor/#testing-with-tox

# Point PYJULIA_TEST_INCOMPATIBLE_PYTHONS to incompatible Python
# executable (see: test/test_compatible_exe.py). MUST specify
# Python versions NOT listed in the matrix below:
PYJULIA_TEST_INCOMPATIBLE_PYTHONS: python3.7

# for more python versions have a look at
# https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml
matrix:
Expand Down
1 change: 1 addition & 0 deletions ci/appveyor/win32/python3.7.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@C:\Python37\python.exe %*
1 change: 1 addition & 0 deletions ci/appveyor/win64/python3.7.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@C:\Python37-x64\python.exe %*
120 changes: 120 additions & 0 deletions test/test_compatible_exe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from __future__ import print_function

import os
import subprocess
import sys
import textwrap

import pytest

from .test_core import julia
from julia.core import _enviorn, which

is_linux = sys.platform.startswith("linux")
is_windows = os.name == "nt"
is_apple = sys.platform == "darwin"


def _get_paths(path):
return filter(None, path.split(":"))


# Environment variable PYJULIA_TEST_INCOMPATIBLE_PYTHONS is the
# :-separated list of Python executables incompatible with the current
# Python:
incompatible_pythons = _get_paths(os.getenv("PYJULIA_TEST_INCOMPATIBLE_PYTHONS", ""))


try:
from types import SimpleNamespace
except ImportError:
from argparse import Namespace as SimpleNamespace


def _run_fallback(args, input=None, **kwargs):
process = subprocess.Popen(args, stdin=subprocess.PIPE, **kwargs)
stdout, stderr = process.communicate(input)
retcode = process.wait()
return SimpleNamespace(args=args, stdout=stdout, stderr=stderr, returncode=retcode)


try:
from subprocess import run
except ImportError:
run = _run_fallback


def runcode(python, code):
"""Run `code` in `python`."""
return run(
[python],
input=textwrap.dedent(code),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
env=dict(
_enviorn,
# Make PyJulia importable:
PYTHONPATH=os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
),
)


def print_completed_proc(proc):
# Print output (pytest will hide it by default):
print("Ran:", *proc.args)
if proc.stdout:
print("# --- STDOUT from", *proc.args)
print(proc.stdout)
if proc.stderr:
print("# --- STDERR from", *proc.args)
print(proc.stderr)
print("# ---")


def is_dynamically_linked(executable):
path = which(executable)
assert os.path.exists(path)
if is_linux and which("ldd"):
proc = run(
["ldd", path], stdout=subprocess.PIPE, env=_enviorn, universal_newlines=True
)
print_completed_proc(proc)
return "libpython" in proc.stdout
elif is_apple and which("otool"):
proc = run(
["otool", "-L", path],
stdout=subprocess.PIPE,
env=_enviorn,
universal_newlines=True,
)
print_completed_proc(proc)
return "libpython" in proc.stdout or "/Python" in proc.stdout
# TODO: support Windows
return None


@pytest.mark.parametrize("python", incompatible_pythons)
def test_incompatible_python(python):
if julia.eval("(VERSION.major, VERSION.minor)") == (0, 6):
# Julia 0.6 implements mixed version
return

python = which(python)
proc = runcode(
python,
"""
import os
from julia import Julia
Julia(runtime=os.getenv("JULIA_EXE"), debug=True)
""",
)
print_completed_proc(proc)

assert proc.returncode == 1
assert "It seems your Julia and PyJulia setup are not supported." in proc.stderr
dynamic = is_dynamically_linked(python)
if dynamic is True:
assert "`libpython` have to match" in proc.stderr
elif dynamic is False:
assert "is statically linked to libpython" in proc.stderr
3 changes: 3 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ passenv =
PYJULIA_TEST_REBUILD
JULIA_EXE

# See: test/test_compatible_exe.py
PYJULIA_TEST_INCOMPATIBLE_PYTHONS

# See: https://coveralls-python.readthedocs.io/en/latest/usage/tox.html#travisci
TRAVIS
TRAVIS_*
Expand Down

0 comments on commit 698e05b

Please sign in to comment.