Skip to content

Commit a9b77e4

Browse files
authored
Add options for the Julia binary and project path (#667)
* Add options for the Julia binary and project path This makes using JuliaPkg optional. * fixup! Add options for the Julia binary and project path * nitpicks * allow exe option to be found in the path --------- Co-authored-by: Christopher Doris <github.com/cjdoris>
1 parent ad228ce commit a9b77e4

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

.github/workflows/docs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ jobs:
2323
- run: |
2424
julia --project=docs -e '
2525
using Pkg
26-
Pkg.develop(PackageSpec(path=pwd()))
2726
Pkg.instantiate()'
2827
- run: |
2928
julia --project=docs -e '

.github/workflows/tests.yml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,22 @@ jobs:
6060
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
6161

6262
python:
63-
name: Test Python (${{ matrix.pyversion }}, ${{ matrix.os }})
63+
name: Test Python (${{ matrix.pyversion }}, ${{ matrix.os }}, ${{ matrix.juliaexe }})
6464
runs-on: ${{ matrix.os }}
6565
strategy:
6666
fail-fast: false
6767
matrix:
6868
os: [ubuntu-latest, windows-latest, macos-latest]
6969
pyversion: ["3", "3.9"]
70+
juliaexe: ["@JuliaPkg"]
71+
include:
72+
- os: ubuntu-latest
73+
pyversion: 3
74+
juliaexe: julia
75+
env:
76+
MANUAL_TEST_PROJECT: /tmp/juliacall-test-project
77+
PYTHON_JULIACALL_THREADS: '2'
78+
PYTHON_JULIACALL_HANDLE_SIGNALS: 'yes'
7079

7180
steps:
7281
- uses: actions/checkout@v5
@@ -82,10 +91,17 @@ jobs:
8291
python-version: ${{ matrix.pyversion }}
8392

8493
- name: Set up Julia
94+
id: setup_julia
8595
uses: julia-actions/setup-julia@v2
8696
with:
8797
version: '1'
8898

99+
- name: Set up test Julia project
100+
if: ${{ matrix.juliaexe == 'julia' }}
101+
run: |
102+
mkdir ${{ env.MANUAL_TEST_PROJECT }}
103+
julia --project=${{ env.MANUAL_TEST_PROJECT }} -e 'import Pkg; Pkg.develop(path="."); Pkg.instantiate()'
104+
89105
- name: Install dependencies
90106
run: |
91107
cp pysrc/juliacall/juliapkg-dev.json pysrc/juliacall/juliapkg.json
@@ -98,12 +114,18 @@ jobs:
98114
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
99115
uv run flake8 ./pysrc ./pytest --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
100116
101-
- name: Run tests
117+
- name: Run tests (with JuliaPkg)
118+
if: ${{ matrix.juliaexe == '@JuliaPkg' }}
119+
run: |
120+
uv run pytest -s --nbval --cov=pysrc ./pytest/
121+
122+
- name: Run tests (without JuliaPkg)
123+
if: ${{ matrix.juliaexe == 'julia' }}
102124
run: |
103125
uv run pytest -s --nbval --cov=pysrc ./pytest/
104126
env:
105-
PYTHON_JULIACALL_THREADS: '2'
106-
PYTHON_JULIACALL_HANDLE_SIGNALS: 'yes'
127+
PYTHON_JULIACALL_EXE: "${{ steps.setup_julia.outputs.julia-bindir }}/julia"
128+
PYTHON_JULIACALL_PROJECT: ${{ env.MANUAL_TEST_PROJECT }}
107129

108130
- name: Upload coverage to Codecov
109131
uses: codecov/codecov-action@v5

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
44

55
[compat]
66
Documenter = "1"
7+
8+
[sources]
9+
PythonCall = {path = ".."}

docs/src/juliacall.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ What to read next:
7373

7474
## [Managing Julia dependencies](@id julia-deps)
7575

76-
JuliaCall manages its Julia dependencies using [JuliaPkg](https://github.com/JuliaPy/PyJuliaPkg).
76+
By default JuliaCall manages its Julia dependencies using
77+
[JuliaPkg](https://github.com/JuliaPy/PyJuliaPkg).
7778

7879
It will automatically download a suitable version of Julia if required.
7980

@@ -100,6 +101,15 @@ Alternatively you can use `add`, `rm`, etc. from JuliaPkg to edit this file.
100101

101102
See [JuliaPkg](https://github.com/JuliaPy/PyJuliaPkg) for more details.
102103

104+
### Using existing environments
105+
106+
It's possible to override the defaults and disable JuliaPkg entirely by setting
107+
the `PYTHON_JULIACALL_EXE` and `PYTHON_JULIACALL_PROJECT` options (both must be
108+
set together). This is particularly useful when using shared environments on HPC
109+
systems that may be readonly. Note that the project set in
110+
`PYTHON_JULIACALL_PROJECT` *must* already have PythonCall.jl installed and it
111+
*must* match the JuliaCall version, otherwise loading Julia will fail.
112+
103113
## [Configuration](@id julia-config)
104114

105115
Some features of the Julia process, such as the optimization level or number of threads, may
@@ -125,6 +135,8 @@ be configured in two ways:
125135
| `-X juliacall-warn-overwrite=<yes\|no>` | `PYTHON_JULIACALL_WARN_OVERWRITE=<yes\|no>` | Enable or disable method overwrite warnings. |
126136
| `-X juliacall-autoload-ipython-extension=<yes\|no>` | `PYTHON_JULIACALL_AUTOLOAD_IPYTHON_EXTENSION=<yes\|no>` | Enable or disable IPython extension autoloading. |
127137
| `-X juliacall-heap-size-hint=<N>` | `PYTHON_JULIACALL_HEAP_SIZE_HINT=<N>` | Hint for initial heap size in bytes. |
138+
| `-X juliacall-exe=<file>` | `PYTHON_JULIACALL_EXE=<file>` | Path to Julia binary to use (overrides JuliaPkg). |
139+
| `-X juliacall-project=<dir>` | `PYTHON_JULIACALL_PROJECT=<dir>` | Path to the Julia project to use (overrides JuliaPkg). |
128140

129141
## [Multi-threading](@id py-multi-threading)
130142

docs/src/releasenotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Added `juliacall.ArrayValue` support for Julia arrays of `InlineDateTime64` and `InlineTimeDelta64`.
88
* If `JULIA_PYTHONCALL_EXE` is a relative path, it is now considered relative to the active project.
99
* Added option `JULIA_PYTHONCALL_EXE=@venv` to use a Python virtual environment relative to the active project.
10+
* Added `PYTHON_JULIACALL_EXE` and `PYTHON_JULIACALL_PROJECT` for specifying the Julia binary and project to override JuliaPkg.
1011
* Bug fixes.
1112
* Internal: switch from Requires.jl to package extensions.
1213

pysrc/juliacall/__init__.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ def path_option(name, default=None, check_exists=False, **kw):
9696
raise ValueError(f'{s}: path does not exist')
9797
return os.path.abspath(path), s
9898
return default, s
99+
100+
def executable_option(name, default=None, **kw):
101+
import shutil
102+
_path, s = option(name, **kw)
103+
if _path is not None:
104+
path = shutil.which(_path)
105+
if path is None:
106+
raise ValueError(f'{s}: executable not found')
107+
return os.path.abspath(path), s
108+
return default, s
99109

100110
def int_option(name, *, accept_auto=False, **kw):
101111
val, s = option(name, **kw)
@@ -147,18 +157,31 @@ def args_from_config(config):
147157
CONFIG['opt_handle_signals'] = choice('handle_signals', ['yes', 'no'])[0]
148158
CONFIG['opt_startup_file'] = choice('startup_file', ['yes', 'no'])[0]
149159
CONFIG['opt_heap_size_hint'] = option('heap_size_hint')[0]
160+
CONFIG['project'] = path_option('project', check_exists=True)[0]
161+
CONFIG['exepath'] = executable_option('exe')[0]
150162

151163
# Stop if we already initialised
152164
if CONFIG['inited']:
153165
return
154166

155-
# we don't import this at the top level because it is not required when juliacall is
156-
# loaded by PythonCall and won't be available
157-
import juliapkg
167+
have_exepath = CONFIG['exepath'] is not None
168+
have_project = CONFIG['project'] is not None
169+
if have_exepath and have_project:
170+
pass
171+
elif (not have_exepath) and (not have_project):
172+
# we don't import this at the top level because it is not required when
173+
# juliacall is loaded by PythonCall and won't be available, or if both
174+
# `exepath` and `project` are set by the user.
175+
import juliapkg
176+
177+
# Find the Julia executable and project
178+
CONFIG['exepath'] = juliapkg.executable()
179+
CONFIG['project'] = juliapkg.project()
180+
else:
181+
raise Exception("Both PYTHON_JULIACALL_PROJECT and PYTHON_JULIACALL_EXE must be set together, not only one of them.")
158182

159-
# Find the Julia executable and project
160-
CONFIG['exepath'] = exepath = juliapkg.executable()
161-
CONFIG['project'] = project = juliapkg.project()
183+
exepath = CONFIG['exepath']
184+
project = CONFIG['project']
162185

163186
# Find the Julia library
164187
cmd = [exepath, '--project='+project, '--startup-file=no', '-O0', '--compile=min',

0 commit comments

Comments
 (0)