Skip to content

Commit 05237b2

Browse files
author
Christopher Doris
committed
juliacall tweaks
1 parent cb207df commit 05237b2

File tree

4 files changed

+58
-44
lines changed

4 files changed

+58
-44
lines changed

juliacall/init.py

+55-41
Original file line numberDiff line numberDiff line change
@@ -18,70 +18,84 @@
1818

1919
# Determine where to put the julia environment
2020
# TODO: Can we more direcly figure out the environment from which python was called? Maybe find the first PATH entry containing python?
21-
venvprefix = os.environ.get("VIRTUAL_ENV")
22-
condaprefix = os.environ.get("CONDA_PREFIX")
23-
if venvprefix and condaprefix:
24-
raise Exception("You appear to be using both a virtual environment and a conda environment.")
25-
elif venvprefix:
26-
prefix = venvprefix
27-
elif condaprefix:
28-
prefix = condaprefix
29-
else:
21+
prefixes = [os.environ.get('VIRTUAL_ENV'), os.environ.get('CONDA_PREFIX'), os.environ.get('MAMBA_PREFIX')]
22+
prefixes = [x for x in prefixes if x is not None]
23+
if len(prefixes) == 0:
3024
prefix = None
25+
elif len(prefixes) == 1:
26+
prefix = prefixes[0]
27+
else:
28+
raise Exception('You are using some mix of virtual, conda and mamba environments, cannot figure out which to use!')
3129
if prefix is None:
3230
jlenv = os.path.join(jldepot, "environments", "PythonCall")
3331
else:
3432
jlenv = os.path.join(prefix, "julia_env")
3533
CONFIG['jlenv'] = os.path.join(jlenv)
36-
CONFIG['meta'] = os.path.join(jlenv, "PythonCallPyMeta")
34+
CONFIG['meta'] = os.path.join(jlenv, ".PythonCallJuliaCallMeta")
3735

3836
# Determine whether or not to skip resolving julia/package versions
3937
skip = deps.can_skip_resolve()
4038

41-
# Find the Julia library, possibly installing Julia
39+
# Find the Julia library
4240
libpath = os.environ.get('PYTHON_JULIACALL_LIB')
4341
if libpath is not None:
4442
if not os.path.exists(libpath):
45-
raise ValueError('PYTHON_JULIACALL_LIB={!r} does not exist'.format(libpath))
43+
raise ValueError(f'PYTHON_JULIACALL_LIB={libpath!r} does not exist')
4644
else:
4745
# Find the Julia executable
48-
exepath = os.environ.get('PYTHON_JULIACALL_EXE')
49-
if exepath is not None:
50-
v = deps.julia_version_str(exepath)
51-
if v is None:
52-
raise ValueError("PYTHON_JULIACALL_EXE={!r} does not exist".format(exepath))
53-
else:
54-
CONFIG["exever"] = v
55-
else:
46+
exepath = os.environ.get('PYTHON_JULIACALL_EXE', '@auto')
47+
if exepath.startswith('@'):
48+
if exepath not in ('@auto', '@system', '@best'):
49+
raise ValueError(f"PYTHON_JULIACALL_EXE={exepath!r} is not valid (can be @auto, @system or @best)")
50+
method = exepath[1:]
51+
exepath = None
5652
compat = deps.required_julia()
5753
# Default scenario
5854
if skip:
5955
# Already know where Julia is
6056
exepath = skip["jlexe"]
6157
else:
62-
# Find the best available version
63-
exepath = None
64-
exever, exeverinfo = install.best_julia_version(compat)
65-
default_exeprefix = os.path.join(jlprefix, 'julia-'+exever)
66-
default_exepath = os.path.join(default_exeprefix, 'bin', 'julia.exe' if os.name=='nt' else 'julia')
67-
for x in [default_exepath, 'julia']:
58+
# @auto and @system try the system julia and succeed if it matches the compat bound
59+
if exepath is None and method in ('auto', 'system'):
60+
x = shutil.which('julia')
61+
if x is not None:
62+
v = deps.julia_version_str(x)
63+
if compat is None or semver.Version(v) in compat:
64+
print(f'Found Julia v{v} at {x!r}')
65+
exepath = x
66+
else:
67+
print(f'Incompatible Julia v{v} at {x!r}, require: {compat.jlstr()}')
68+
# @auto and @best look for the best available version
69+
if exepath is None and method in ('auto', 'system'):
70+
exever, exeverinfo = install.best_julia_version(compat)
71+
default_exeprefix = os.path.join(jlprefix, 'julia-'+exever)
72+
x = os.path.join(default_exeprefix, 'bin', 'julia.exe' if os.name=='nt' else 'julia')
6873
v = deps.julia_version_str(x)
6974
if v is not None and v == exever:
70-
print(f'Found Julia {v} at {x!r}')
75+
print(f'Found Julia v{v} at {x!r}')
7176
exepath = x
72-
break
7377
elif v is not None:
74-
print(f'Found Julia {v} at {x!r} (but looking for Julia {exever})')
75-
# If no such version, install it
76-
if exepath is None:
77-
install.install_julia(exeverinfo, default_exeprefix)
78-
exepath = default_exepath
79-
if not os.path.isfile(exepath):
80-
raise Exception(f'Installed Julia in {default_exeprefix!r} but cannot find it')
78+
print(f'Incompatible Julia v{v} at {x!r}, require: = {exever}')
79+
# If no such version, install it
80+
if exepath is None:
81+
install.install_julia(exeverinfo, default_exeprefix)
82+
exepath = x
83+
if not os.path.isfile(exepath):
84+
raise Exception(f'Installed Julia in {default_exeprefix!r} but cannot find it')
85+
# Failed to find Julia
86+
if exepath is None:
87+
raise Exception('Could not find a compatible version of Julia')
8188
# Check the version is compatible
8289
v = deps.julia_version_str(exepath)
8390
assert v is not None and (compat is None or semver.Version(v) in compat)
8491
CONFIG['exever'] = v
92+
else:
93+
v = deps.julia_version_str(exepath)
94+
if v is None:
95+
raise ValueError(f"PYTHON_JULIACALL_EXE={exepath!r} is not a Julia executable")
96+
else:
97+
CONFIG["exever"] = v
98+
assert exepath is not None
8599
CONFIG['exepath'] = exepath
86100
libpath = subprocess.run([exepath, '--startup-file=no', '-O0', '--compile=min', '-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")))'], check=True, stdout=subprocess.PIPE).stdout.decode('utf8')
87101

@@ -120,12 +134,12 @@
120134
with open(os.path.join(jlenv, "Project.toml"), "wt") as fp:
121135
print('[deps]', file=fp)
122136
for pkg in pkgs:
123-
print('{} = "{}"'.format(pkg.name, pkg.uuid), file=fp)
137+
print(f'{pkg.name} = "{pkg.uuid}"', file=fp)
124138
print(file=fp)
125139
print('[compat]', file=fp)
126140
for pkg in pkgs:
127141
if pkg.version:
128-
print('{} = "{}"'.format(pkg.name, pkg.version), file=fp)
142+
print(f'{pkg.name} = "{pkg.version}"', file=fp)
129143
print(file=fp)
130144
# Create install command
131145
dev_pkgs = [pkg.jlstr() for pkg in pkgs if pkg.dev]
@@ -140,23 +154,23 @@
140154
install = ''
141155
os.environ['JULIA_PYTHONCALL_LIBPTR'] = str(c.pythonapi._handle)
142156
os.environ['JULIA_PYTHONCALL_EXE'] = sys.executable or ''
143-
script = '''
157+
script = f'''
144158
try
145159
if ENV["JULIA_PYTHONCALL_EXE"] != "" && get(ENV, "PYTHON", "") == ""
146160
# Ensures PyCall uses the same Python executable
147161
ENV["PYTHON"] = ENV["JULIA_PYTHONCALL_EXE"]
148162
end
149163
import Pkg
150-
Pkg.activate(raw"{}", io=devnull)
151-
{}
164+
Pkg.activate(raw"{jlenv}", io=devnull)
165+
{install}
152166
import PythonCall
153167
catch err
154168
print(stdout, "ERROR: ")
155169
showerror(stdout, err, catch_backtrace())
156170
flush(stdout)
157171
rethrow()
158172
end
159-
'''.format(jlenv, install, c.pythonapi._handle)
173+
'''
160174
res = lib.jl_eval_string(script.encode('utf8'))
161175
if res is None:
162176
raise Exception('PythonCall.jl did not start properly')

juliacall/install.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def compatible_julia_versions(compat=None):
9696
def best_julia_version(compat=None):
9797
vers = compatible_julia_versions(compat)
9898
if not vers:
99-
raise Exception('no compatible Julia version found')
99+
raise Exception(f'no version of Julia is compatible with: {compat.jlstr()}')
100100
v = sorted(vers.keys(), key=Version, reverse=True)[0]
101101
return v, vers[v]
102102

juliacall/juliacalldeps.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"julia": "1"
2+
"julia": "1.6"
33
}

juliacall/semver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def jlstr(self):
9595
def isempty(self):
9696
return False
9797
def __and__(self, other):
98-
if self in other:
98+
if self.v in other:
9999
return self
100100
else:
101101
return Range(Version('0'), Version('0'))

0 commit comments

Comments
 (0)