API reference
Applications and shared pool
Independent threaded asyncio applications sharing one Python runtime.
UniteIO gives each application class one singleton instance, one daemon
thread, and one asyncio event loop. All applications share the singleton
UIOPool for synchronous callable work while coroutine work remains on the
application’s own loop. On free-threaded CPython, callbacks belonging to
different loop threads can execute Python code in parallel when their workload
permits it. Process-wide objects remain shared and require normal thread-safe
access when mutable.
Example
Define, start, submit work to, and stop an application:
import asyncio
from uniteio import UniteIO
class Service(UniteIO, prefix="SVC"):
async def __call__(self):
await asyncio.Event().wait()
service = Service()
future = service.submit(asyncio.sleep, 0, result="ready")
assert future.result() == "ready"
service.stop()
The package targets CPython free-threaded builds. Separate loops still provide scheduling isolation with the GIL enabled, but the GIL prevents parallel Python-bytecode execution between their threads. Closing an application loop uses a CPython-specific executor-detachment step so the shared pool remains available to other applications.
- class uniteio.uniteio.UniteIO(**kwargs)
Bases:
UIOArchetypeBase class for singleton applications running dedicated asyncio loops.
Each concrete subclass owns one active daemon thread and event loop. On a free-threaded CPython build, work on different application threads can run in parallel while sharing process-wide state; mutable shared state must be synchronized by the application. The subclass’s asynchronous
__call__()method is scheduled automatically during construction. Synchronous callables submitted throughsubmit()run on the sharedUIOPool; coroutines run on the application’s loop.Keyword arguments accepted by a subclass can be forwarded to this base constructor. Keys that do not already exist become both attributes and item-accessible configuration values.
- Parameters:
**kwargs – Application configuration exposed through attributes and
app[key]access.
Example
Create an application, submit both kinds of work, and stop it:
import asyncio from uniteio import UniteIO class Processor(UniteIO, prefix="PROC"): def __init__(self, endpoint): super().__init__(endpoint=endpoint) async def __call__(self): await asyncio.Event().wait() app = Processor(endpoint="local") assert app.endpoint == "local" assert app["endpoint"] == "local" assert app.submit(sum, [1, 2, 3]).result() == 6 app.stop()
- taskname()
Return a unique task name using the application’s prefix.
- Returns:
A name such as
"API:3".- Return type:
str
- __init__(**kwargs)
Initialize thread state, lifecycle events, and configuration.
- Parameters:
**kwargs – Configuration values to expose as attributes and through
__getitem__(). Existing attributes are not overwritten.- Return type:
None
- run()
Run and ultimately close the application’s event loop.
This is the target executed by
threading.Thread; callers should not invoke it directly. On exit it cancels pending tasks, finalizes asynchronous generators, detaches the shared executor, closes the loop, and unregisters the application.- Return type:
None
- stop(wait=True, timeout=None)
Stop this application while leaving the shared executor active.
When called outside the application thread,
wait=Truewaits for task cancellation, loop closure, and thread termination. Calls made by the application itself never block waiting for their own thread.- Parameters:
wait (bool) – Whether an external caller should join the application thread before returning. Ignored when called by the application itself.
timeout (float | None) – Maximum seconds to wait for thread termination, or
Noneto wait indefinitely.
- Returns:
Truewhen teardown has completed before return; otherwiseFalse. A self-stop request returnsFalsebecause cleanup continues after the current callback yields control.- Return type:
bool
Example
Request shutdown and wait at most two seconds:
if not app.stop(timeout=2): print("application is still stopping")
- async asleep(delay, result=None)
Sleep asynchronously on the application’s event loop.
- Parameters:
delay – Number of seconds to suspend the current coroutine.
result – Value returned after the delay. A falsey value selects the application instance instead.
- Returns:
resultwhen truthy, otherwiseself.
Example
await self.asleep(0.1, "ready")returns"ready".
- sleep(delay, result=None)
Block the current thread for a specified duration.
Use this helper only in synchronous work submitted to
UIOPool; calling it on an event-loop thread blocks that loop.- Parameters:
delay – Number of seconds to block the current thread.
result – Value returned after the delay. A falsey value selects the application instance instead.
- Returns:
resultwhen truthy, otherwiseself.
Example
app.submit(app.sleep, 0.1, "ready").result()returns"ready".
- exception_handler(loop, context)
Record an asyncio exception context and delegate default reporting.
- Parameters:
loop (BaseEventLoop) – Event loop that reported the exception.
context – Asyncio exception-handler context dictionary.
- Return type:
None
Example
Install the handler from
run()withloop.set_exception_handler(self.exception_handler).
- submit(target: Coroutine, /, *, name: str | None = None, eager: bool = False) Future
- submit(target: Iterable[Coroutine], /, *, name: str | None = None, eager: bool = False) Future
Submit coroutine or synchronous work to the appropriate runtime.
Coroutine objects and coroutine functions execute on this application’s event loop. An iterable of coroutine objects executes inside an
asyncio.TaskGroup. Other callables execute on the sharedUIOPool.- Parameters:
target (Callable) – Coroutine object, coroutine function, iterable of coroutine objects, or synchronous callable.
*args – Positional arguments passed to a coroutine function or synchronous callable.
name (str | None) – Optional asyncio task name. Batch child names append their zero-based index, for example
"batch:0".context (Context) – Optional
contextvars.Contextused for coroutine execution. It does not apply to synchronous executor work.eager (bool) – Request eager task start when supported by asyncio.
**kwargs – Keyword arguments passed to a coroutine function or synchronous callable.
- Returns:
A
concurrent.futures.Future. A batch future resolves to an insertion-ordered mapping from each original coroutine object to its return value.- Raises:
TypeError – If
targetis unsupported or a batch contains a non-coroutine member.RuntimeError – If coroutine work is submitted after the application event loop has closed, or synchronous work is submitted after the shared executor has shut down.
- Return type:
Future
Example
Submit coroutine and synchronous work from another thread:
async def fetch(identifier): return identifier one = app.submit(fetch, 1, name="fetch-one").result() many = app.submit( [fetch(2), fetch(3)], name="fetch-batch", ).result() total = app.submit(sum, [one, *many.values()]).result()
- class uniteio.uniteio.UIOPool(max_workers=None)
Bases:
AdjustableThreadPoolExecutorProcess-wide executor shared by active
UniteIOapplications.The pool is a restartable singleton: repeated construction returns the active executor, while construction after
shutdown()creates a new executor. Each application still owns a distinct asyncio event loop.- Parameters:
max_workers – Maximum number of executor worker threads.
Noneuses the standardThreadPoolExecutordefault.
Example
Submit synchronous work directly to the shared pool:
from uniteio import UIOPool pool = UIOPool(max_workers=4) assert pool.submit(sum, [1, 2, 3]).result() == 6 pool.shutdown()
- __init__(max_workers=None)
Initialize the shared executor and application registry.
- Parameters:
max_workers – Maximum number of worker threads, or
Noneto use the standard executor default.
- discard(app)
Remove an application without affecting the executor.
- Parameters:
app (UIOArchetype) – Application to remove. A stale or unknown instance is ignored.
- Return type:
None
- shutdown(wait=True, *, cancel_futures=False)
Stop every application before shutting down the executor.
Stop requests are broadcast to all registered applications first, then every application thread is joined. Only after their loops are closed is
ThreadPoolExecutorshutdown invoked. The pool is removed from the singleton registry after successful completion.- Parameters:
wait – Passed to
ThreadPoolExecutor.shutdown. Applications are always joined first, even when this argument isFalse.cancel_futures – Whether queued executor futures that have not begun execution should be cancelled.
- Raises:
RuntimeError – If called from one of the registered application threads, because that thread cannot synchronously join itself.
Example
Stop all applications and replace the exhausted singleton:
old_pool = UIOPool() old_pool.shutdown(cancel_futures=True) new_pool = UIOPool() assert new_pool is not old_pool
Adjustable executor
Thread-pool executor whose worker ceiling can be increased at runtime.
The implementation intentionally relies on CPython’s private
ThreadPoolExecutor state. It changes the ceiling used when deciding
whether new workers may be created; it never terminates existing workers.
- class uniteio.adjustable_pool.AdjustableThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=(), **ctxkwargs)
Bases:
ThreadPoolExecutorA
ThreadPoolExecutorwith a mutable worker-creation ceiling.Lowering the ceiling is allowed only when the requested value is not below the number of worker threads that already exist. Existing workers are never retired.
- Parameters:
max_workers – Initial maximum number of worker threads. All constructor arguments are inherited from
ThreadPoolExecutor.
Example
Increase a running executor from two workers to four:
from uniteio import AdjustableThreadPoolExecutor with AdjustableThreadPoolExecutor(max_workers=2) as pool: old, new = pool.set_max_workers(4) assert (old, new) == (2, 4)
- property max_workers: int
Return the current worker-creation ceiling.
- Returns:
The maximum number of threads the executor may create.
- set_max_workers(value)
Change the ceiling used for future worker creation.
This method does not create workers immediately and does not retire workers that already exist.
- Parameters:
value (int) – New positive integer worker ceiling. Objects implementing
__index__are accepted; booleans are rejected explicitly.- Returns:
A
(previous, current)tuple containing the old and new limits.- Raises:
TypeError – If
valueis not an integer or is a boolean.ValueError – If
valueis not positive or is smaller than the number of workers already created.RuntimeError – If the executor has shut down or is broken.
- Return type:
tuple[int, int]
Example
Resize an executor before submitting additional work:
pool = AdjustableThreadPoolExecutor(max_workers=1) try: pool.set_max_workers(3) result = pool.submit(pow, 2, 8).result() finally: pool.shutdown()