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_connectionTrue 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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uipath_ipc-2.5.2.tar.gz (86.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uipath_ipc-2.5.2-py3-none-any.whl (50.3 kB view details)

Uploaded Python 3

File details

Details for the file uipath_ipc-2.5.2.tar.gz.

File metadata

  • Download URL: uipath_ipc-2.5.2.tar.gz
  • Upload date:
  • Size: 86.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for uipath_ipc-2.5.2.tar.gz
Algorithm Hash digest
SHA256 d69c3d7c1ad1a25ef7f9f1d78c505f5d2ed7daa7227bb202404fa3d47e0ba12e
MD5 e63e2110ffe99a76135898aae7c253dc
BLAKE2b-256 2c6b53d9725d6abd1dab447300a7f999332f530678e3fabd38fc31171a5a9a6f

See more details on using hashes here.

Provenance

The following attestation bundles were made for uipath_ipc-2.5.2.tar.gz:

Publisher: cd.yml on UiPath/coreipc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file uipath_ipc-2.5.2-py3-none-any.whl.

File metadata

  • Download URL: uipath_ipc-2.5.2-py3-none-any.whl
  • Upload date:
  • Size: 50.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for uipath_ipc-2.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 617f25f35377d87956165a875b0966a3cd69ae6724fcd533c93a7a6a9460fc4f
MD5 2876f8cff61c81c7e4b0eef029e62ffd
BLAKE2b-256 29eb6def505a0d351119da27b342c11eb0767a31a7f100022d35e349c5194eb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for uipath_ipc-2.5.2-py3-none-any.whl:

Publisher: cd.yml on UiPath/coreipc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page