Skip to main content

uipath-ipc

Python client and server for UiPath.Ipc — an interface-based RPC framework with .NET server and client, TypeScript client, and now a Python client and server.

This package speaks the same wire protocol as the .NET package, so a Python client can talk to any UiPath.Ipc server (and a Python IpcServer can host services for any client).

Status

  • Scope: client (IpcClient) and server (IpcServer), with bidirectional callbacks. Stream uploads/downloads are not implemented.
  • Transports: Named Pipe, TCP. (WebSocket is on the roadmap.)
  • Python: 3.10+.

Install

pip install uipath-ipc

Quick start

1. Define a contract

The contract is a Python ABC whose method names exactly match the .NET interface methods. Each method must be async def.

from abc import ABC, abstractmethod


class IComputingService(ABC):
    @abstractmethod
    async def AddFloats(self, x: float, y: float) -> float: ...

    @abstractmethod
    async def Wait(self, duration: float) -> bool: ...

2. Create a client and call methods

import asyncio
from uipath_ipc import IpcClient, NamedPipeClientTransport


async def main() -> None:
    transport = NamedPipeClientTransport(pipe_name="test")
    async with IpcClient(transport) as client:
        svc = client.get_proxy(IComputingService)

        result = await svc.AddFloats(1.5, 2.5)
        print(result)  # 4.0


asyncio.run(main())

The proxy returned by get_proxy(IComputingService) looks like an instance of the contract to your editor and type checker — call its methods normally.

Features

Cancellation

Cancellation in Python is task-based, not token-based. You cancel by cancelling the task that's awaiting:

task = asyncio.create_task(svc.Wait(10.0))
await asyncio.sleep(0.1)
task.cancel()              # CancelledError propagates up through await

When the proxy observes CancelledError it re-raises it locally. Whether the server is also told to cancel depends on the method: an @ipc_cancellable method (see below) sends a CancellationRequest frame matching the in-flight request id; an unmarked method like Wait above cancels locally only (the server keeps running).

@ipc_cancellable and .NET CancellationToken

Because cancellation is task-based, a Python contract never declares a CancellationToken parameter — it's delivered out-of-band, not as an argument. When a method's .NET counterpart ends with a CancellationToken, mark it with @ipc_cancellable:

from uipath_ipc import ipc_cancellable

class IRobotService(ABC):
    @ipc_cancellable
    @abstractmethod
    async def LongRunning(self, count: int) -> int: ...
    # .NET: Task<int> LongRunning(int count, CancellationToken ct = default)

The marker controls whether a local cancellation is forwarded to the peer. Cancel (or time out) the task awaiting an @ipc_cancellable call and the client sends a CancellationRequest so the peer can cancel its handler. Cancel an unmarked call and nothing is sent — the cancellation stays local, because a peer with no CancellationToken has nothing to act on.

It does not change the request's arguments: the token is never a parameter, so Request.Parameters is unaffected. The .NET server fills the missing trailing CancellationToken slot with a default and injects the real token by type; a Python server ignores the empty-string slot a .NET client sends for its token.

One constraint: .NET accepts a CancellationToken at any position (matched by type), but the Python signature omits it, so in a .NET↔Python pairing the token must be the last .NET parameter — otherwise the trailing arguments misalign on the wire.

Timeouts

Configure a per-client default:

async with IpcClient(transport, request_timeout=5.0) as client:
    ...

Or override per-call with asyncio.timeout (3.11+) / asyncio.wait_for:

async with asyncio.timeout(1.0):
    await svc.Wait(10.0)   # raises TimeoutError after 1s

In both cases the call raises locally. The server is notified via a CancellationRequest only for an @ipc_cancellable method; for an unmarked method like Wait the timeout is local-only and the server runs to completion.

For a single call, pass a Message argument carrying the timeout — it overrides the client default for that call only:

from uipath_ipc import Message, INFINITE_REQUEST_TIMEOUT

await svc.Install(pkg, Message(request_timeout=1200))                       # 20-minute call
await svc.SignIn(creds, Message(request_timeout=INFINITE_REQUEST_TIMEOUT))  # no deadline

INFINITE_REQUEST_TIMEOUT is the .NET Timeout.InfiniteTimeSpan rendition: no client-side deadline, and the server reads it as "no timeout". A request_timeout of 0 means "use the server's default" (it does not override the client default).

Exception propagation

Server-side exceptions surface as RemoteException:

from uipath_ipc import RemoteException

try:
    await svc.DivideByZero()
except RemoteException as ex:
    print(ex.message)       # "Attempted to divide by zero."
    print(ex.type_name)     # "System.DivideByZeroException"
    print(ex.stack_trace)   # the .NET stack
    print(ex.inner)         # inner RemoteException (chain), or None
    ex.is_remote_type("System.DivideByZeroException")   # True — .NET Is<T>() analog

__cause__ is set on the exception chain so Python tracebacks display the inner errors naturally.

Callbacks (server → client)

The server can invoke methods on objects that the client hosts. Define the callback contract, pass an instance to IpcClient(callbacks={...}), and the proxy on the server side can call into your Python object:

from abc import ABC, abstractmethod


class IClientCallback(ABC):
    @abstractmethod
    async def EchoToClient(self, value: str) -> str: ...


class EchoHandler:
    async def EchoToClient(self, value: str) -> str:
        return f"echoed: {value}"


async with IpcClient(transport, callbacks={IClientCallback: EchoHandler()}) as client:
    tester = client.get_proxy(ICallbackTester)
    print(await tester.TriggerEcho("hi"))   # "echoed: hi"

Callback methods must be async def (like service handlers): a synchronous handler runs inline on the event loop, blocking the whole connection for its duration and escaping the request timeout. Exceptions raised inside the handler are wired back to the server as RemoteException. Server-initiated cancellations cancel the in-flight handler task.

Hooks

Two optional hooks let you observe or gate the client (the analog of .NET's BeforeConnect / BeforeOutgoingCall). Each may be sync or async; raising in a hook aborts the connect/call.

from uipath_ipc import CallInfo

async def launch_server() -> None:
    ...  # e.g. lazily start the server before the first connect (self-healing)

def log_call(ci: CallInfo) -> None:
    print(ci.endpoint, ci.method_name, ci.arguments, ci.new_connection)

async with IpcClient(transport, before_connect=launch_server, before_call=log_call) as client:
    ...

before_connect runs before each (re)connect; before_call runs before each outgoing call with a CallInfo (endpoint, method_name, arguments, and new_connection — True only on the call that opened the connection).

Custom serialization (advanced)

The proxy materializes results into a contract's declared return type via reflection — bytes, UUID, datetime, Decimal, enums, and dataclasses all round-trip (see Features above). If you need to (de)serialize values yourself, the same primitives are exported as from_wire(value, hint) / to_wire(value). The contract vocabulary is intentionally narrow — plain JSON values and dataclasses — so the IPC layer stays decoupled from any modeling framework (pydantic, ORM entities, …); map IPC DTOs to your own validated/domain types at your boundary if you need them.

Auto-reconnect

The client opens a connection lazily on the first call and reuses it. If the underlying stream drops (server restart, network blip), the next call transparently re-dials via the transport. The proxy instance remains valid across reconnects.

In-flight calls when the drop happens propagate the underlying error rather than silently retrying — that's the caller's policy choice.

Transports

from uipath_ipc import NamedPipeClientTransport, TcpClientTransport

NamedPipeClientTransport(pipe_name="test")                  # local
NamedPipeClientTransport(pipe_name="test", server_name="REMOTE")  # remote (Windows)
TcpClientTransport(host="127.0.0.1", port=5050)

Custom transports are easy: subclass ClientTransport and implement connect().

What's NOT implemented (yet)

  • Streams (UploadRequest / DownloadResponse message types). Add on demand.
  • WebSocket transport. Pending; will be an optional extra.
  • Configurable max message size — the 2 MB cap (matching .NET's default) is fixed; .NET's MaxReceivedMessageSizeInMegabytes knob isn't exposed yet.

Not supported / undefined behaviour (by design)

Distinct from the "not yet" list above — these are deliberate boundaries. The contract is a shared agreement between both peers, and the library trusts it rather than policing every misuse. This is IPC, not a versioned schema layer like gRPC/protobuf.

  • Both peers must agree on the contract. Every argument and return must be serializable in both counterparts' worlds. A contract/wire mismatch — a field the other side can't decode, a value-typed return answered with empty Data, a result that doesn't match the declared type — is undefined behaviour: you may get a raw value, a None, or an opaque error. The library does not hand-hold with clean per-case diagnostics; iterate the contract until both sides round-trip.
  • Variadic *args are delivered undecoded. A handler's *args elements arrive as their raw JSON-parsed values, not materialized to the declared element type — decoding *args: T is unsupported (no .NET/TS use case). Use explicit, individually-typed parameters.
  • Argument count must match the contract. Too few args for a required parameter raises a loud TypeError (rather than silently filling a default); extra trailing args are ignored. A wrong arg count is a contract mismatch, not something the library papers over.
  • Request ids are library-managed — unique per connection, generated by the proxy. Cancellation and response correlation key off them, so don't fabricate or reuse them on the low-level Request API.
  • A CancellationToken must be the last .NET parameter in a .NET↔Python pairing — see the @ipc_cancellable marker under Cancellation.

Development

# Clone, set up env
py -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"

# Run tests
pytest

# Build wheel + sdist
pip install build
python -m build

Wire protocol cheat sheet

  • Frame: 5-byte header + UTF-8 JSON payload.
  • Header: [MessageType: uint8][PayloadLength: int32 LE].
  • Message types: Request=0, Response=1, CancellationRequest=2, UploadRequest=3, DownloadResponse=4.
  • Request.Parameters is a list of individually JSON-encoded strings — [\"1.5\", \"\\\"hi\\\"\"], not [1.5, \"hi\"].

License

MIT.

Metadata

Release files for uipath-ipc 2.5.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uipath-ipc 2.5.3
File Size Uploaded
uipath_ipc-2.5.3.tar.gz 86.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for uipath-ipc 2.5.3
File Interpreter ABI Platform
uipath_ipc-2.5.3-py3-none-any.whl Python 3 none any Details

Total release size: 136.7 kB

Release files / uipath_ipc-2.5.3.tar.gz

Download URL uipath_ipc-2.5.3.tar.gz
Size 86.4 kB
Tags Source
SHA-256 checksum
How to use checksums
823cdd1192c72ca786bfd3cbff65f88e2b02c33f450046f13e572022b7e6b870
BLAKE2b-256 checksum
How to use checksums
55ee182b16ff0cd205cb172def92349e7625e36d5b2ba11bdcb98bee43ccf548
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release files / uipath_ipc-2.5.3-py3-none-any.whl

Download URL uipath_ipc-2.5.3-py3-none-any.whl
Size 50.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
139852a85c8235f7f98959053a97ed748e9a051ddef952b99e49b6097c36d7ad
BLAKE2b-256 checksum
How to use checksums
5033eebf6d9b5021d0517d5b7ca0f27bac48dda68530a2322bc04fd3c636e737
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.5.3 This release

2 release files

2.5.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page