Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add native_tls option to Pythonx.uv_init/2 (#40)
  • Loading branch information
GenericJam committed Jan 16, 2026
commit 8c0b6922663fb5eaa804ea4f64c96c73c509f4a2
23 changes: 22 additions & 1 deletion lib/pythonx.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ defmodule Pythonx do
]
""")

In some environments, you may need to pass additional flags to the `uv sync`
command. For example, in corporate environments with strict security policies,
you might need to use native TLS:

Pythonx.uv_init(
"""
[project]
name = "project"
version = "0.0.0"
requires-python = "==3.13.*"
dependencies = [
"numpy==2.2.2"
]
""",
native_tls: true
)

Comment thread
jonatanklosko marked this conversation as resolved.
Outdated
For more configuration options, refer to the [uv documentation](https://docs.astral.sh/uv/concepts/projects/dependencies/).

## Options
Expand All @@ -54,10 +71,14 @@ defmodule Pythonx do
* `:uv_version` - select the version of the uv package manager to use.
Defaults to `#{inspect(Pythonx.Uv.default_uv_version())}`.

* `:native_tls` - if true, uses the system's native TLS implementation instead
of vendored rustls. This is useful in corporate environments where the system
certificate store must be used. Defaults to `false`.

'''
@spec uv_init(String.t(), keyword()) :: :ok
def uv_init(pyproject_toml, opts \\ []) when is_binary(pyproject_toml) and is_list(opts) do
opts = Keyword.validate!(opts, force: false, uv_version: Pythonx.Uv.default_uv_version())
opts = Keyword.validate!(opts, force: false, uv_version: Pythonx.Uv.default_uv_version(), native_tls: false)
Comment thread
jonatanklosko marked this conversation as resolved.
Outdated

Pythonx.Uv.fetch(pyproject_toml, false, opts)
install_paths = Pythonx.Uv.init(pyproject_toml, false, Keyword.take(opts, [:uv_version]))
Expand Down
7 changes: 5 additions & 2 deletions lib/pythonx/uv.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Pythonx.Uv do
"""
@spec fetch(String.t(), boolean(), keyword()) :: :ok
def fetch(pyproject_toml, priv?, opts \\ []) do
opts = Keyword.validate!(opts, force: false, uv_version: default_uv_version())
opts = Keyword.validate!(opts, force: false, uv_version: default_uv_version(), native_tls: false)
Comment thread
jonatanklosko marked this conversation as resolved.
Outdated

project_dir = project_dir(pyproject_toml, priv?, opts[:uv_version])
python_install_dir = python_install_dir(priv?, opts[:uv_version])
Expand All @@ -28,7 +28,10 @@ defmodule Pythonx.Uv do
File.write!(Path.join(project_dir, "pyproject.toml"), pyproject_toml)

# We always use uv-managed Python, so the paths are predictable.
if run!(["sync", "--managed-python", "--no-config"],
base_args = ["sync", "--managed-python", "--no-config"]
uv_args = if opts[:native_tls], do: base_args ++ ["--native-tls"], else: base_args

if run!(uv_args,
cd: project_dir,
env: %{"UV_PYTHON_INSTALL_DIR" => python_install_dir},
uv_version: opts[:uv_version]
Expand Down
50 changes: 50 additions & 0 deletions test/pythonx_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,56 @@ defmodule PythonxTest do
end
end

describe "uv_init/2 native_tls option" do
test "accepts native_tls option" do
# Test that native_tls is recognized as a valid option
opts = Keyword.validate!([native_tls: true, force: false],
force: false,
uv_version: Pythonx.Uv.default_uv_version(),
native_tls: false)

assert opts[:native_tls] == true
assert opts[:force] == false
end

test "defaults native_tls to false" do
opts = Keyword.validate!([],
force: false,
uv_version: Pythonx.Uv.default_uv_version(),
native_tls: false)

assert opts[:native_tls] == false
end

test "native_tls true adds --native-tls flag to uv command" do
# Simulate how Pythonx.Uv.fetch constructs the uv command arguments
base_args = ["sync", "--managed-python", "--no-config"]
native_tls = true

uv_args = if native_tls, do: base_args ++ ["--native-tls"], else: base_args

assert uv_args == ["sync", "--managed-python", "--no-config", "--native-tls"]
end

test "native_tls false does not add --native-tls flag" do
base_args = ["sync", "--managed-python", "--no-config"]
native_tls = false

uv_args = if native_tls, do: base_args ++ ["--native-tls"], else: base_args

assert uv_args == ["sync", "--managed-python", "--no-config"]
end

test "raises error for unknown options" do
assert_raise ArgumentError, ~r/unknown keys/, fn ->
Keyword.validate!([unknown_option: true],
force: false,
uv_version: Pythonx.Uv.default_uv_version(),
native_tls: false)
end
end
end

Comment thread
jonatanklosko marked this conversation as resolved.
Outdated
defp repr(object) do
assert %Pythonx.Object{} = object

Expand Down