Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ad228ce

Browse files
authored
feat(exe): add support for @venv and relative paths in JULIA_PYTHONCALL_EXE (#670)
Add new option `@venv` to use a Python virtual environment located at `.venv` in the active project directory. Relative paths for `JULIA_PYTHONCALL_EXE` are now resolved relative to the active project. Includes documentation updates and release notes. Co-authored-by: Christopher Doris <github.com/cjdoris>
1 parent 3633da0 commit ad228ce

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

docs/src/faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# FAQ & Troubleshooting
22

3-
## Can I use PythonCall and PyCall together?
3+
## [Can I use PythonCall and PyCall together?](@id faq-pycall)
44

55
Yes, you can use both PyCall and PythonCall in the same Julia session. This is platform-dependent:
66
- On most systems the Python interpreter used by PythonCall and PyCall must be the same (see below).

docs/src/pythoncall.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,21 @@ to your current Julia project containing Python and any required Python packages
240240
ENV["JULIA_CONDAPKG_BACKEND"] = "Null"
241241
ENV["JULIA_PYTHONCALL_EXE"] = "/path/to/python" # optional
242242
ENV["JULIA_PYTHONCALL_EXE"] = "@PyCall" # optional
243+
ENV["JULIA_PYTHONCALL_EXE"] = "@venv" # optional
243244
```
244245

245246
By setting the CondaPkg backend to Null, it will never install any Conda packages. In this
246247
case, PythonCall will use whichever Python is currently installed and in your `PATH`. You
247248
must have already installed any Python packages that you need.
248249

249250
If `python` is not in your `PATH`, you will also need to set `JULIA_PYTHONCALL_EXE` to its
250-
path.
251+
path. Relative paths are resolved relative to the current active project.
251252

252253
If you also use PyCall, you can set `JULIA_PYTHONCALL_EXE=@PyCall` to use the same Python
253-
interpreter.
254+
interpreter. [See here](@ref faq-pycall).
255+
256+
If you have a Python virtual environment at `.venv` in your current active project, you
257+
can set `JULIA_PYTHONCALL_EXE=@venv` to use it.
254258

255259
#### If you already have a Conda environment
256260

docs/src/releasenotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Added `pyconvert` rules for NumpyDates types.
66
* Added `PyArray` support for NumPy arrays of `datetime64` and `timedelta64`.
77
* Added `juliacall.ArrayValue` support for Julia arrays of `InlineDateTime64` and `InlineTimeDelta64`.
8+
* If `JULIA_PYTHONCALL_EXE` is a relative path, it is now considered relative to the active project.
9+
* Added option `JULIA_PYTHONCALL_EXE=@venv` to use a Python virtual environment relative to the active project.
810
* Bug fixes.
911
* Internal: switch from Requires.jl to package extensions.
1012

src/C/context.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,38 @@ function init_context()
7575
exe_path = PyCall.python::String
7676
CTX.lib_path = PyCall.libpython::String
7777
CTX.which = :PyCall
78+
elseif exe_path == "@venv"
79+
# load from a .venv in the active project
80+
exe_path = abspath(dirname(Base.active_project()), ".venv")
81+
if Sys.iswindows()
82+
exe_path = abspath(exe_path, "Scripts", "python.exe")::String
83+
else
84+
exe_path = abspath(exe_path, "bin", "python")::String
85+
end
7886
elseif startswith(exe_path, "@")
7987
error("invalid JULIA_PYTHONCALL_EXE=$exe_path")
8088
else
8189
# Otherwise we use the Python specified
8290
CTX.which = :unknown
91+
if isabspath(exe_path)
92+
# nothing to do
93+
elseif '/' in exe_path || '\\' in exe_path
94+
# it's a relative path, interpret it as relative to the current project
95+
exe_path = abspath(dirname(Base.active_project()), exe_path)::String
96+
else
97+
# it's a command, find it in the PATH
98+
given_exe_path = exe_path
99+
exe_path = Sys.which(exe_path)
100+
exe_path === nothing &&
101+
error("Python executable $(repr(given_exe_path)) not found.")
102+
exe_path::String
103+
end
83104
end
84105

85106
# Ensure Python is runnable
107+
if !ispath(exe_path)
108+
error("Python executable $(repr(exe_path)) does not exist.")
109+
end
86110
try
87111
run(pipeline(`$exe_path --version`, stdout = devnull, stderr = devnull))
88112
catch

0 commit comments

Comments
 (0)