diff --git a/python/omp-rpc/src/omp_rpc/client.py b/python/omp-rpc/src/omp_rpc/client.py index eaac8921f..e1906465c 100644 --- a/python/omp-rpc/src/omp_rpc/client.py +++ b/python/omp-rpc/src/omp_rpc/client.py @@ -278,6 +278,9 @@ class RpcClient: session_dir: str | Path | None = None, cwd: str | Path | None = None, env: Mapping[str, str] | None = None, + user: int | str | None = None, + group: int | str | None = None, + extra_groups: Sequence[int | str] | None = None, thinking: ThinkingLevel | None = None, append_system_prompt: str | None = None, provider_session_id: str | None = None, @@ -302,6 +305,9 @@ class RpcClient: self._session_dir = Path(session_dir) if session_dir is not None else None self._cwd = Path(cwd) if cwd is not None else None self._env = dict(env or {}) + self._user = user + self._group = group + self._extra_groups = list(extra_groups) if extra_groups is not None else None self._thinking = thinking self._append_system_prompt = append_system_prompt self._provider_session_id = provider_session_id @@ -403,6 +409,9 @@ class RpcClient: list(self._build_command()), cwd=str(self._cwd) if self._cwd is not None else None, env={**os.environ, **self._env}, + user=self._user, + group=self._group, + extra_groups=self._extra_groups, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/python/omp-rpc/tests/test_user_group.py b/python/omp-rpc/tests/test_user_group.py new file mode 100644 index 000000000..8f6b41fdc --- /dev/null +++ b/python/omp-rpc/tests/test_user_group.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from unittest.mock import patch + +import pytest + +from omp_rpc import RpcClient + + +class _Sentinel(Exception): + pass + + +def _start_and_capture(**kwargs): + client = RpcClient(**kwargs) + with patch("omp_rpc.client.subprocess.Popen", side_effect=_Sentinel("aborted")) as mock_popen: + with pytest.raises(_Sentinel): + client.start() + assert mock_popen.call_count == 1 + return mock_popen.call_args + + +def test_no_user_group_defaults_to_none(): + call = _start_and_capture(executable="omp") + assert call.kwargs["user"] is None + assert call.kwargs["group"] is None + assert call.kwargs["extra_groups"] is None + + +def test_user_and_group_kwargs_threaded(): + call = _start_and_capture( + executable="omp", + user=2001, + group="omp", + extra_groups=[2000, "docker"], + ) + assert call.kwargs["user"] == 2001 + assert call.kwargs["group"] == "omp" + assert call.kwargs["extra_groups"] == [2000, "docker"] + + +def test_extra_groups_none_distinct_from_empty(): + call = _start_and_capture(executable="omp", extra_groups=[]) + # [] means an empty supplementary group list and differs from None. + assert call.kwargs["extra_groups"] == []