From 6e0d58794e99bfa373cd2689b4ba9414d5f082ec Mon Sep 17 00:00:00 2001 From: Eduard Dumitru Date: Wed, 16 Sep 2026 11:51:18 +0200 Subject: [PATCH 1/3] feat(deps): make uipath-ipc a required dependency uipath-ipc sits behind the `ipc` extra because of a review note on #1809 (2026-07-23): at the time the package was only on the internal feed, so a hard dependency would have broken `pip install uipath` for anyone outside UiPath. uipath-ipc 2.5.2 was published to public PyPI on 2026-07-24, five days before #1809 merged, and the extra stayed behind. That costs us downstream. A consumer cannot tell from the uipath version whether an environment can actually speak IPC: a project declaring plain `uipath` resolves uipath and uipath-runtime but not uipath-ipc, so any caller of `uipath server --ipc-pipe` or `uipath run --handler-ipc-pipe` has to either fail at import time or silently fall back. Version-gating is the normal way to negotiate these channels and it cannot work while the transport is optional. The `ipc` extra stays declared, so an existing `uipath[ipc]` requirement still resolves. Removed from the dev group, where it is now redundant. --- packages/uipath/pyproject.toml | 5 +++-- packages/uipath/src/uipath/_cli/_job_api.py | 3 ++- packages/uipath/src/uipath/_cli/cli_server_ipc.py | 7 ++++--- packages/uipath/uv.lock | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index 0b4331572..defc1977f 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.14.19" +version = "2.14.20" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" @@ -8,6 +8,7 @@ dependencies = [ "uipath-core>=0.5.30, <0.6.0", "uipath-runtime>=0.13.5, <0.14.0", "uipath-platform>=0.2.30, <0.3.0", + "uipath-ipc>=2.5.1, <2.6.0", "click>=8.3.3, <9.0.0", "httpx>=0.28.1", "pyjwt>=2.13.0, <3.0.0", @@ -46,6 +47,7 @@ Documentation = "https://uipath.github.io/uipath-python/" [project.scripts] uipath = "uipath._cli:cli" +# Kept so an existing `uipath[ipc]` requirement still resolves; uipath-ipc is a base dependency now. [project.optional-dependencies] ipc = ["uipath-ipc>=2.5.1,<2.6.0"] @@ -82,7 +84,6 @@ dev = [ "types-PyYAML>=6.0", "pytest-timeout>=2.4.0", "packaging>=24.0", - "uipath-ipc>=2.5.1,<2.6.0", ] [tool.hatch.build.targets.wheel] diff --git a/packages/uipath/src/uipath/_cli/_job_api.py b/packages/uipath/src/uipath/_cli/_job_api.py index 1db129062..04bfc393e 100644 --- a/packages/uipath/src/uipath/_cli/_job_api.py +++ b/packages/uipath/src/uipath/_cli/_job_api.py @@ -350,7 +350,8 @@ def connect_handler_ipc(pipe: str, job_id: str | None) -> _HandlerIpcConnection: from uipath_ipc import IpcClient, NamedPipeClientTransport except ImportError as e: raise RuntimeError( - "--handler-ipc-pipe requires the 'uipath-ipc' package. Install it (pip install 'uipath[ipc]')." + "--handler-ipc-pipe requires the 'uipath-ipc' package, which ships with uipath " + "but is missing from this environment. Reinstall uipath." ) from e loop = _new_ipc_event_loop() diff --git a/packages/uipath/src/uipath/_cli/cli_server_ipc.py b/packages/uipath/src/uipath/_cli/cli_server_ipc.py index cd489bd17..862ddf72f 100644 --- a/packages/uipath/src/uipath/_cli/cli_server_ipc.py +++ b/packages/uipath/src/uipath/_cli/cli_server_ipc.py @@ -9,7 +9,8 @@ if TYPE_CHECKING: from uipath_ipc import Message else: - # Optional dep: the annotation stays a string forward-ref, so this placeholder is never subscripted. + # uipath-ipc is a base dependency, so this only fires on a broken install. The annotation stays a + # string forward-ref, so this placeholder is never subscripted. try: from uipath_ipc import Message except ImportError: # pragma: no cover - no IPC means Register is never dispatched @@ -170,8 +171,8 @@ async def start_ipc_server(pipe_name: str) -> None: except ImportError as e: raise RuntimeError( "The uipath-ipc channel was requested (--ipc-pipe) but the 'uipath-ipc' " - "package is not installed in this environment. Install it (pip install " - "'uipath[ipc]') or omit --ipc-pipe to serve HTTP only." + "package is missing from this environment. It ships with uipath, so reinstall " + "uipath, or omit --ipc-pipe to serve HTTP only." ) from e _state.init() diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index 80937ab6d..d349677b3 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2599,7 +2599,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.19" +version = "2.14.20" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2620,6 +2620,7 @@ dependencies = [ { name = "tenacity" }, { name = "truststore" }, { name = "uipath-core" }, + { name = "uipath-ipc" }, { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -2657,7 +2658,6 @@ dev = [ { name = "tomli-w" }, { name = "types-pyyaml" }, { name = "types-toml" }, - { name = "uipath-ipc" }, { name = "virtualenv" }, ] @@ -2681,6 +2681,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "truststore", specifier = ">=0.10.1" }, { name = "uipath-core", editable = "../uipath-core" }, + { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-ipc", marker = "extra == 'ipc'", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-platform", editable = "../uipath-platform" }, { name = "uipath-runtime", specifier = ">=0.13.5,<0.14.0" }, @@ -2715,7 +2716,6 @@ dev = [ { name = "tomli-w", specifier = ">=1.2.0" }, { name = "types-pyyaml", specifier = ">=6.0" }, { name = "types-toml", specifier = ">=0.10.8" }, - { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "virtualenv", specifier = ">=20.36.1" }, ] From 256b78391be30699f7623533be3f0b539e90b1e4 Mon Sep 17 00:00:00 2001 From: Eduard Dumitru Date: Wed, 16 Sep 2026 11:57:14 +0200 Subject: [PATCH 2/3] feat(deps): regenerate the editable sample locks The three samples that resolve uipath as an editable path dependency had locked its old dependency set, so a locked sync would have installed a uipath without uipath-ipc. greeter is left alone: it resolves uipath from the registry, so this change does not reach it. --- .../samples/attachment_evaluation_test/uv.lock | 16 +++++++++++++--- .../samples/csv_employee_generator/uv.lock | 16 +++++++++++++--- .../uipath/samples/line_by_line_test/uv.lock | 16 +++++++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/uipath/samples/attachment_evaluation_test/uv.lock b/packages/uipath/samples/attachment_evaluation_test/uv.lock index ee5de2354..7e3658511 100644 --- a/packages/uipath/samples/attachment_evaluation_test/uv.lock +++ b/packages/uipath/samples/attachment_evaluation_test/uv.lock @@ -1454,7 +1454,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.18" +version = "2.14.20" source = { editable = "../../" } dependencies = [ { name = "applicationinsights" }, @@ -1475,6 +1475,7 @@ dependencies = [ { name = "tenacity" }, { name = "truststore" }, { name = "uipath-core" }, + { name = "uipath-ipc" }, { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -1499,6 +1500,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "truststore", specifier = ">=0.10.1" }, { name = "uipath-core", editable = "../../../uipath-core" }, + { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-ipc", marker = "extra == 'ipc'", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-platform", editable = "../../../uipath-platform" }, { name = "uipath-runtime", specifier = ">=0.13.5,<0.14.0" }, @@ -1533,7 +1535,6 @@ dev = [ { name = "tomli-w", specifier = ">=1.2.0" }, { name = "types-pyyaml", specifier = ">=6.0" }, { name = "types-toml", specifier = ">=0.10.8" }, - { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "virtualenv", specifier = ">=20.36.1" }, ] @@ -1569,9 +1570,18 @@ dev = [ { name = "rust-just", specifier = ">=1.39.0" }, ] +[[package]] +name = "uipath-ipc" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/ee/182b16ff0cd205cb172def92349e7625e36d5b2ba11bdcb98bee43ccf548/uipath_ipc-2.5.3.tar.gz", hash = "sha256:823cdd1192c72ca786bfd3cbff65f88e2b02c33f450046f13e572022b7e6b870", size = 86363, upload-time = "2026-09-15T15:32:58.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/33/eebf6d9b5021d0517d5b7ca0f27bac48dda68530a2322bc04fd3c636e737/uipath_ipc-2.5.3-py3-none-any.whl", hash = "sha256:139852a85c8235f7f98959053a97ed748e9a051ddef952b99e49b6097c36d7ad", size = 50342, upload-time = "2026-09-15T15:32:56.962Z" }, +] + [[package]] name = "uipath-platform" -version = "0.2.29" +version = "0.2.30" source = { editable = "../../../uipath-platform" } dependencies = [ { name = "anyio" }, diff --git a/packages/uipath/samples/csv_employee_generator/uv.lock b/packages/uipath/samples/csv_employee_generator/uv.lock index 076d156fa..d83b9de1c 100644 --- a/packages/uipath/samples/csv_employee_generator/uv.lock +++ b/packages/uipath/samples/csv_employee_generator/uv.lock @@ -1454,7 +1454,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.18" +version = "2.14.20" source = { editable = "../../" } dependencies = [ { name = "applicationinsights" }, @@ -1475,6 +1475,7 @@ dependencies = [ { name = "tenacity" }, { name = "truststore" }, { name = "uipath-core" }, + { name = "uipath-ipc" }, { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -1499,6 +1500,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "truststore", specifier = ">=0.10.1" }, { name = "uipath-core", editable = "../../../uipath-core" }, + { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-ipc", marker = "extra == 'ipc'", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-platform", editable = "../../../uipath-platform" }, { name = "uipath-runtime", specifier = ">=0.13.5,<0.14.0" }, @@ -1533,7 +1535,6 @@ dev = [ { name = "tomli-w", specifier = ">=1.2.0" }, { name = "types-pyyaml", specifier = ">=6.0" }, { name = "types-toml", specifier = ">=0.10.8" }, - { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "virtualenv", specifier = ">=20.36.1" }, ] @@ -1569,9 +1570,18 @@ dev = [ { name = "rust-just", specifier = ">=1.39.0" }, ] +[[package]] +name = "uipath-ipc" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/ee/182b16ff0cd205cb172def92349e7625e36d5b2ba11bdcb98bee43ccf548/uipath_ipc-2.5.3.tar.gz", hash = "sha256:823cdd1192c72ca786bfd3cbff65f88e2b02c33f450046f13e572022b7e6b870", size = 86363, upload-time = "2026-09-15T15:32:58.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/33/eebf6d9b5021d0517d5b7ca0f27bac48dda68530a2322bc04fd3c636e737/uipath_ipc-2.5.3-py3-none-any.whl", hash = "sha256:139852a85c8235f7f98959053a97ed748e9a051ddef952b99e49b6097c36d7ad", size = 50342, upload-time = "2026-09-15T15:32:56.962Z" }, +] + [[package]] name = "uipath-platform" -version = "0.2.29" +version = "0.2.30" source = { editable = "../../../uipath-platform" } dependencies = [ { name = "anyio" }, diff --git a/packages/uipath/samples/line_by_line_test/uv.lock b/packages/uipath/samples/line_by_line_test/uv.lock index 7cb79f167..82ea2553c 100644 --- a/packages/uipath/samples/line_by_line_test/uv.lock +++ b/packages/uipath/samples/line_by_line_test/uv.lock @@ -1454,7 +1454,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.14.18" +version = "2.14.20" source = { editable = "../../" } dependencies = [ { name = "applicationinsights" }, @@ -1475,6 +1475,7 @@ dependencies = [ { name = "tenacity" }, { name = "truststore" }, { name = "uipath-core" }, + { name = "uipath-ipc" }, { name = "uipath-platform" }, { name = "uipath-runtime" }, ] @@ -1499,6 +1500,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "truststore", specifier = ">=0.10.1" }, { name = "uipath-core", editable = "../../../uipath-core" }, + { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-ipc", marker = "extra == 'ipc'", specifier = ">=2.5.1,<2.6.0" }, { name = "uipath-platform", editable = "../../../uipath-platform" }, { name = "uipath-runtime", specifier = ">=0.13.5,<0.14.0" }, @@ -1533,7 +1535,6 @@ dev = [ { name = "tomli-w", specifier = ">=1.2.0" }, { name = "types-pyyaml", specifier = ">=6.0" }, { name = "types-toml", specifier = ">=0.10.8" }, - { name = "uipath-ipc", specifier = ">=2.5.1,<2.6.0" }, { name = "virtualenv", specifier = ">=20.36.1" }, ] @@ -1569,9 +1570,18 @@ dev = [ { name = "rust-just", specifier = ">=1.39.0" }, ] +[[package]] +name = "uipath-ipc" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/ee/182b16ff0cd205cb172def92349e7625e36d5b2ba11bdcb98bee43ccf548/uipath_ipc-2.5.3.tar.gz", hash = "sha256:823cdd1192c72ca786bfd3cbff65f88e2b02c33f450046f13e572022b7e6b870", size = 86363, upload-time = "2026-09-15T15:32:58.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/33/eebf6d9b5021d0517d5b7ca0f27bac48dda68530a2322bc04fd3c636e737/uipath_ipc-2.5.3-py3-none-any.whl", hash = "sha256:139852a85c8235f7f98959053a97ed748e9a051ddef952b99e49b6097c36d7ad", size = 50342, upload-time = "2026-09-15T15:32:56.962Z" }, +] + [[package]] name = "uipath-platform" -version = "0.2.29" +version = "0.2.30" source = { editable = "../../../uipath-platform" } dependencies = [ { name = "anyio" }, From 348262fd934d5dd18dde7df6cedb4a7d9594526b Mon Sep 17 00:00:00 2001 From: Eduard Dumitru Date: Wed, 16 Sep 2026 13:41:24 +0200 Subject: [PATCH 3/3] feat(deps): drop the ImportError guards that uipath-ipc made unreachable connect_handler_ipc and start_ipc_server each caught ImportError on the uipath-ipc import and re-raised it as a RuntimeError telling the caller to install the extra. Neither can fire now that the package is a base dependency: a uipath new enough to have these code paths already brings it. Both imports stay inside their functions, so nothing new is imported at module load, and a broken install now surfaces as a plain ImportError pointing at the line. Their two tests go with them. The module-level Message import is deliberately left alone. Only its ImportError arm is dead: when uipath-ipc is present the else branch is what binds Message at runtime, and the dispatcher resolves the contract's annotations against it. --- packages/uipath/src/uipath/_cli/_job_api.py | 8 +------- packages/uipath/src/uipath/_cli/cli_server_ipc.py | 9 +-------- packages/uipath/tests/cli/test_job_api.py | 6 ------ packages/uipath/tests/cli/test_server_ipc.py | 9 --------- 4 files changed, 2 insertions(+), 30 deletions(-) diff --git a/packages/uipath/src/uipath/_cli/_job_api.py b/packages/uipath/src/uipath/_cli/_job_api.py index 04bfc393e..f288b330d 100644 --- a/packages/uipath/src/uipath/_cli/_job_api.py +++ b/packages/uipath/src/uipath/_cli/_job_api.py @@ -346,13 +346,7 @@ def connect_handler_ipc(pipe: str, job_id: str | None) -> _HandlerIpcConnection: f"--handler-ipc-pipe needs UIPATH_JOB_KEY to be a job id; got {job_id!r}." ) - try: - from uipath_ipc import IpcClient, NamedPipeClientTransport - except ImportError as e: - raise RuntimeError( - "--handler-ipc-pipe requires the 'uipath-ipc' package, which ships with uipath " - "but is missing from this environment. Reinstall uipath." - ) from e + from uipath_ipc import IpcClient, NamedPipeClientTransport loop = _new_ipc_event_loop() thread = threading.Thread( diff --git a/packages/uipath/src/uipath/_cli/cli_server_ipc.py b/packages/uipath/src/uipath/_cli/cli_server_ipc.py index 862ddf72f..52f9a94bf 100644 --- a/packages/uipath/src/uipath/_cli/cli_server_ipc.py +++ b/packages/uipath/src/uipath/_cli/cli_server_ipc.py @@ -166,14 +166,7 @@ async def StopJob(self, request: PythonServerStopJobRequest) -> bool: async def start_ipc_server(pipe_name: str) -> None: """Serve the Python runtime over a uipath-ipc named pipe until it is closed.""" - try: - from uipath_ipc import IpcServer, NamedPipeServerTransport - except ImportError as e: - raise RuntimeError( - "The uipath-ipc channel was requested (--ipc-pipe) but the 'uipath-ipc' " - "package is missing from this environment. It ships with uipath, so reinstall " - "uipath, or omit --ipc-pipe to serve HTTP only." - ) from e + from uipath_ipc import IpcServer, NamedPipeServerTransport _state.init() diff --git a/packages/uipath/tests/cli/test_job_api.py b/packages/uipath/tests/cli/test_job_api.py index 0e6918818..46a29ff34 100644 --- a/packages/uipath/tests/cli/test_job_api.py +++ b/packages/uipath/tests/cli/test_job_api.py @@ -443,12 +443,6 @@ def live() -> int: assert live() == before -def test_connect_without_uipath_ipc_raises(monkeypatch): - monkeypatch.setitem(sys.modules, "uipath_ipc", None) - with pytest.raises(RuntimeError, match="uipath-ipc"): - _job_api.connect_handler_ipc("pipe", JOB_ID) - - def test_a_broken_log_channel_is_reported_once_to_stderr(monkeypatch): _isolated_output_sinks(monkeypatch) diff --git a/packages/uipath/tests/cli/test_server_ipc.py b/packages/uipath/tests/cli/test_server_ipc.py index 711e0fd58..c48e76354 100644 --- a/packages/uipath/tests/cli/test_server_ipc.py +++ b/packages/uipath/tests/cli/test_server_ipc.py @@ -15,7 +15,6 @@ import json import logging import os -import sys import threading import time from typing import Any, Awaitable, Callable, cast @@ -120,14 +119,6 @@ def main(input: Input) -> str: """ -def test_start_ipc_server_fails_fast_without_uipath_ipc(monkeypatch): - """--ipc-pipe with uipath-ipc absent must fail loudly, not silently no-op.""" - monkeypatch.setitem(sys.modules, "uipath_ipc", None) - coro = start_ipc_server(_unique_pipe()) - with pytest.raises(RuntimeError, match="uipath-ipc"): - asyncio.run(coro) - - JOB_ID = "3f2504e0-4f89-11d3-9a0c-0305e82c3301"