Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath-runtime"
version = "0.0.23"
version = "0.1.0"
description = "Runtime abstractions and interfaces for building agents and automation scripts in the UiPath ecosystem"
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
4 changes: 4 additions & 0 deletions src/uipath/runtime/debug/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ async def wait_for_resume(self) -> Any:
"""Wait for resume command from debugger."""
...

async def wait_for_terminate(self) -> None:
"""Wait until the user has requested to terminate debugging."""
...

def get_breakpoints(self) -> list[str] | Literal["*"]:
"""Get nodes to suspend execution at.

Expand Down
40 changes: 15 additions & 25 deletions src/uipath/runtime/debug/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,36 +251,32 @@ async def _poll_trigger(
while True:
attempt += 1

await self.debug_bridge.emit_state_update(
UiPathRuntimeStateEvent(
node_name="<polling>",
payload={
"status": "polling",
"attempt": attempt,
},
)
)

try:
resume_data = await reader.read_trigger(trigger)

if resume_data is not None:
return resume_data

await self.debug_bridge.emit_state_update(
UiPathRuntimeStateEvent(
node_name="<polling>",
payload={
"attempt": attempt,
},
)
)

await self._wait_with_quit_check()

except UiPathDebugQuitError:
logger.info("Quit requested during polling")
raise
except Exception as e:
logger.error(f"Error polling trigger: {e}", exc_info=True)
await self.debug_bridge.emit_state_update(
UiPathRuntimeStateEvent(
node_name="<polling>",
payload={
"status": "poll_error",
"attempt": attempt,
"error": str(e),
"info": str(e),
},
)
)
Expand All @@ -294,25 +290,19 @@ async def _wait_with_quit_check(self) -> None:
UiPathDebugQuitError: If quit is requested during wait
"""
sleep_task = asyncio.create_task(asyncio.sleep(self.trigger_poll_interval))
resume_task = asyncio.create_task(self.debug_bridge.wait_for_resume())
term_task = asyncio.create_task(self.debug_bridge.wait_for_terminate())

done, pending = await asyncio.wait(
{sleep_task, resume_task}, return_when=asyncio.FIRST_COMPLETED
{sleep_task, term_task},
return_when=asyncio.FIRST_COMPLETED,
)

for task in pending:
task.cancel()
try:
await task
except asyncio.CancelledError:
# Expected when cancelling pending tasks; safe to ignore.
pass

# Check if quit was triggered
if resume_task in done:
try:
await (
resume_task
) # This will raise UiPathDebugQuitError if it was a quit
except UiPathDebugQuitError:
raise
if term_task in done:
raise UiPathDebugQuitError("Debugging terminated during polling.")
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.