rosetrap provides an efficient, non-polling mechanism for inter-process communication (IPC) and variable synchronization across Python scripts, leveraging FastAPI on the server and the requests library on the client.
It implements a robust, event-driven pattern that allows a consuming process to block indefinitely without consuming CPU cycles until a producing process updates the required variable.
- Asynchronous Blocking: Uses
asyncio.Eventon the FastAPI server to suspend client requests, eliminating busy-waiting and high CPU load typically associated with polling. - Zero-Config IPC: No need for complex message queues (Redis, Kafka); state is managed centrally by the lightweight
DATA_HUB. - Deadlock Prevention: Robust client logic handles race conditions where data arrives between the initial status check and the blocking request.
- Simple API: Client-side functions are reduced to two clear commands:
printToServer()(Write) andreceiveFromServer()(Read/Block). - Independent Variables: State management is fully independent for each variable name, allowing concurrent, synchronized access by multiple clients.
-
Install dependencies:
pip install fastapi requests pydantic uvicorn
-
Start the Server: The
server_data_hub.pyscript must be running first.python server_data_hub.py # Server runs on http://0.0.0.0:8000
This example demonstrates how Client A blocks waiting for Client C's data, and then how Client B blocks waiting for Client A's computed result, all without polling.
Ensure server_data_hub.py and core_utils.py are in your project directory.
Create these three files in the same directory:
import time
from core_utils import receiveFromServer, printToServer, set_client_id
set_client_id("CLIENT A")
# 1. Block until 'X_Data' is ready
print("CLIENT A: Waiting to receive X_Data...")
x_data = receiveFromServer("X_Data", timeout_seconds=60)
if x_data:
print(f"CLIENT A: Successfully received X_Data: {x_data}")
# 2. Compute a new value 'Y_Data'
time.sleep(1) # Simulate computation time
y_data = str(int(x_data) * 2)
print(f"CLIENT A: Calculated Y_Data = X_Data * 2 = {y_data}")
# 3. Send the new value 'Y_Data'
print("CLIENT A: Sending Y_Data...")
printToServer("Y_Data", y_data)
print("CLIENT A: Finished sending Y_Data.")
else:
print("CLIENT A: Failed to receive X_Data.")from core_utils import receiveFromServer, set_client_id
set_client_id("CLIENT B")
# 1. Block until 'Y_Data' is ready
print("CLIENT B: Waiting to receive Y_Data...")
y_data = receiveFromServer("Y_Data", timeout_seconds=60)
if y_data:
print(f"CLIENT B: Successfully received Y_Data: {y_data}")
print("CLIENT B: Final result received. Execution complete.")
else:
print("CLIENT B: Failed to receive Y_Data. Timeout or error occurred.")import time
from core_utils import printToServer, set_client_id
set_client_id("CLIENT C")
# 1. Compute the initial value
initial_value = 50
time.sleep(4) # Simulate initial computation delay
# 2. Send the value 'X_Data'
print(f"CLIENT C: Computation finished. Sending X_Data: {initial_value}")
printToServer("X_Data", initial_value)
print("CLIENT C: Finished sending X_Data.")The terminal output will clearly show the blocking and release mechanism.
-
Start Blocking Clients:
python client_A.py & python client_B.py &
Client A and B will print their "Waiting" messages and then immediately stop consuming CPU, as they are blocked on the server's
asyncio.Event.wait(). -
Trigger the Chain:
python client_C.py
- Client C sends "50" for
X_Dataafter a 4-second delay. - The Server sees the
X_Datalookout is True (set by Client A) and signals the event. - Client A unblocks, receives "50", computes the result (100), and sends "100" for
Y_Data. - The Server sees the
Y_Datalookout is True (set by Client B) and signals the event. - Client B unblocks, receives "100", and finishes.