From d78009c960ebb652d86dcfc6cd3b55b1ae28659c Mon Sep 17 00:00:00 2001 From: Michael Ruoss Date: Wed, 5 Nov 2025 20:38:43 +0100 Subject: [PATCH] make uv_init return a struct with required paths --- lib/pythonx.ex | 25 +++++++++- lib/pythonx/uv.ex | 118 +++++++++++++++++++++++++--------------------- 2 files changed, 88 insertions(+), 55 deletions(-) diff --git a/lib/pythonx.ex b/lib/pythonx.ex index 1e5453a..587c819 100644 --- a/lib/pythonx.ex +++ b/lib/pythonx.ex @@ -8,8 +8,21 @@ defmodule Pythonx do @moduledoc readme_docs + defstruct [ + :python_dl_path, + :python_home_path, + :python_executable_path, + :sys_paths + ] + alias Pythonx.Object + @type python_config :: %__MODULE__{ + python_dl_path: String.t(), + python_home_path: String.t(), + python_executable_path: String.t(), + sys_paths: [String.t()] + } @type encoder :: (term(), encoder() -> Object.t()) @doc ~s''' @@ -90,7 +103,7 @@ defmodule Pythonx do # (`sys.path`). Defaults to `[]`. # @doc false - @spec init(String.t(), String.t(), keyword()) :: :ok + @spec init(String.t(), String.t(), String.t(), keyword()) :: :ok def init(python_dl_path, python_home_path, python_executable_path, opts \\ []) when is_binary(python_dl_path) and is_binary(python_home_path) when is_binary(python_executable_path) and is_list(opts) do @@ -111,6 +124,16 @@ defmodule Pythonx do Pythonx.NIF.init(python_dl_path, python_home_path, python_executable_path, opts[:sys_paths]) end + @spec init(python_config()) :: :ok + def init(%__MODULE__{ + python_dl_path: python_dl_path, + python_home_path: python_home_path, + python_executable_path: python_executable_path, + sys_paths: sys_paths + }) do + init(python_dl_path, python_home_path, python_executable_path, sys_paths: sys_paths) + end + @doc ~S''' Evaluates the Python `code`. diff --git a/lib/pythonx/uv.ex b/lib/pythonx/uv.ex index 68a648a..fcebd56 100644 --- a/lib/pythonx/uv.ex +++ b/lib/pythonx/uv.ex @@ -66,7 +66,7 @@ defmodule Pythonx.Uv do Initializes the interpreter using Python and dependencies previously fetched by `fetch/3`. """ - @spec init(String.t(), boolean()) :: :ok + @spec init(String.t(), boolean()) :: Pythonx.python_config() def init(pyproject_toml, priv?, opts \\ []) do opts = Keyword.validate!(opts, uv_version: default_uv_version()) project_dir = project_dir(pyproject_toml, priv?, opts[:uv_version]) @@ -95,59 +95,69 @@ defmodule Pythonx.Uv do root_dir = Path.join(python_install_dir(priv?, opts[:uv_version]), versioned_dir_name) - case :os.type() do - {:win32, _osname} -> - # Note that we want the version-specific DLL, rather than the - # "forwarder DLL" python3.dll, otherwise symbols cannot be - # found directly. - python_dl_path = - root_dir - |> Path.join("python3?*.dll") - |> wildcard_one!() - |> make_windows_slashes() - - python_home_path = make_windows_slashes(root_dir) - - python_executable_path = - project_dir - |> Path.join(".venv/Scripts/python.exe") - |> make_windows_slashes() - - venv_packages_path = - project_dir - |> Path.join(".venv/Lib/site-packages") - |> make_windows_slashes() - - Pythonx.init(python_dl_path, python_home_path, python_executable_path, - sys_paths: [venv_packages_path] - ) - - {:unix, osname} -> - dl_extension = - case osname do - :darwin -> ".dylib" - :linux -> ".so" - end - - python_dl_path = - root_dir - |> Path.join("lib/libpython3.*" <> dl_extension) - |> wildcard_one!() - |> Path.expand() - - python_home_path = root_dir - - python_executable_path = Path.join(project_dir, ".venv/bin/python") - - venv_packages_path = - project_dir - |> Path.join(".venv/lib/python3*/site-packages") - |> wildcard_one!() - - Pythonx.init(python_dl_path, python_home_path, python_executable_path, - sys_paths: [venv_packages_path] - ) - end + python_config = + case :os.type() do + {:win32, _osname} -> + # Note that we want the version-specific DLL, rather than the + # "forwarder DLL" python3.dll, otherwise symbols cannot be + # found directly. + python_dl_path = + root_dir + |> Path.join("python3?*.dll") + |> wildcard_one!() + |> make_windows_slashes() + + python_home_path = make_windows_slashes(root_dir) + + python_executable_path = + project_dir + |> Path.join(".venv/Scripts/python.exe") + |> make_windows_slashes() + + venv_packages_path = + project_dir + |> Path.join(".venv/Lib/site-packages") + |> make_windows_slashes() + + %Pythonx{ + python_dl_path: python_dl_path, + python_home_path: python_home_path, + python_executable_path: python_executable_path, + sys_paths: [venv_packages_path] + } + + {:unix, osname} -> + dl_extension = + case osname do + :darwin -> ".dylib" + :linux -> ".so" + end + + python_dl_path = + root_dir + |> Path.join("lib/libpython3.*" <> dl_extension) + |> wildcard_one!() + |> Path.expand() + + python_home_path = root_dir + + python_executable_path = Path.join(project_dir, ".venv/bin/python") + + venv_packages_path = + project_dir + |> Path.join(".venv/lib/python3*/site-packages") + |> wildcard_one!() + + %Pythonx{ + python_dl_path: python_dl_path, + python_home_path: python_home_path, + python_executable_path: python_executable_path, + sys_paths: [venv_packages_path] + } + end + + Pythonx.init(python_config) + python_config end defp wildcard_one!(path) do