Skip to content

Fix GitHub issue 280#330

Closed
NiveditJain wants to merge 3 commits intomainfrom
cursor/fix-github-issue-280-374a
Closed

Fix GitHub issue 280#330
NiveditJain wants to merge 3 commits intomainfrom
cursor/fix-github-issue-280-374a

Conversation

@NiveditJain
Copy link
Member

Add documentation for Runtime.start() blocking behavior and provide non-blocking examples, fixing #280.


Slack Thread

Open in Cursor Open in Web

cursoragent and others added 2 commits August 31, 2025 11:49
…ments

Co-authored-by: nivedit <nivedit@aikin.club>
Co-authored-by: nivedit <nivedit@aikin.club>
@cursor
Copy link

cursor bot commented Aug 31, 2025

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 31, 2025

📝 Walkthrough

Summary by CodeRabbit

  • Documentation
    • Clarified runtime start blocking behavior across environments (synchronous scripts vs. interactive notebooks) in README and Getting Started.
    • Added guidance for achieving non-blocking startup, including running in a background thread and offloading from async contexts.
    • Provided practical usage examples for interactive environments, standard scripts, and async code paths.
    • Emphasized that these are documentation-only updates with no changes to code or public APIs.

Walkthrough

Documentation updates add a note about Runtime.start() blocking behavior across README and getting-started guides, including examples for interactive (async) environments, synchronous scripts using a background thread, and async contexts using asyncio.to_thread. No code or API changes.

Changes

Cohort / File(s) Summary
Docs: Runtime.start() blocking behavior note
README.md, docs/docs/getting-started.md, python-sdk/README.md
Added documentation clarifying when Runtime.start() blocks vs returns an asyncio.Task, with examples for interactive environments, synchronous scripts using a daemon thread, and async contexts using asyncio.to_thread. No implementation or API changes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related issues

Possibly related PRs

Poem

I twitch my ears at threads and loops,
Hop-start the runtime—no blocking hoops!
In notebooks I glide, a Task on the breeze,
In scripts I burrow with daemonized ease.
Async meadows, to_thread I race—
Carrots queued, events in place.
Thump-thump: docs keep steady pace.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cursor/fix-github-issue-280-374a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Aug 31, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@NiveditJain NiveditJain marked this pull request as ready for review August 31, 2025 11:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b6f1db8 and e84014c.

📒 Files selected for processing (3)
  • README.md (1 hunks)
  • docs/docs/getting-started.md (1 hunks)
  • python-sdk/README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
python-sdk/README.md

[grammar] ~62-~62: Use correct spacing
Context: ...) ``` ### Note on blocking behavior of Runtime.start() `Runtime.start()` blocks the main thread when no asyncio ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~64-~64: There might be a problem here.
Context: ...ent loop (e.g., Jupyter), it returns an asyncio.Task and does not block. - Jupyter/interactive ...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~64-~64: Use correct spacing
Context: ...ns an asyncio.Task and does not block. - Jupyter/interactive (non-blocking): `...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~66-~66: Use correct spacing
Context: .... - Jupyter/interactive (non-blocking): python runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) task = runtime.start() # background asyncio.Task # await task # optionally wait on it later - Regular sync script (non-blocking via th...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~74-~74: Use correct spacing
Context: ...r sync script (non-blocking via thread): python from threading import Thread runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) Thread(target=runtime.start, daemon=True).start() - From async code (offload to a thread): ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~83-~83: Use correct spacing
Context: ...- From async code (offload to a thread): python import asyncio runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) await asyncio.to_thread(runtime.start) ## Environment Configuration The SDK requi...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

docs/docs/getting-started.md

[grammar] ~89-~89: Use correct spacing
Context: ...) ``` ### Note on blocking behavior of Runtime.start() By design, `Runtime.start()` runs the ru...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~91-~91: There might be a problem here.
Context: ...otebooks), Runtime.start() returns an asyncio.Task and does not block. - If you're in an asyn...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~91-~91: Use correct spacing
Context: ...ns an asyncio.Task and does not block. - If you're in an async/interactive enviro...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~93-~93: Use correct spacing
Context: ...e.g., Jupyter/REPL with a running loop): python # Jupyter/async environment runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) task = runtime.start() # task is an asyncio.Task running in the background # You can continue interacting, and optionally await/cancel the task later # await task # if you want to wait on it - If you need a non-blocking start from a ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~103-~103: Use correct spacing
Context: ...c script, run it in a background thread: python from threading import Thread runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) Thread(target=runtime.start, daemon=True).start() # continue with other work in the main thread - Alternatively, from an async context you...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~113-~113: There might be a mistake here.
Context: ...d - Alternatively, from an async context you can offload to a thread: pyth...

(QB_NEW_EN_OTHER)


[grammar] ~113-~113: Use correct spacing
Context: ...ync context you can offload to a thread: python import asyncio runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) await asyncio.to_thread(runtime.start) ## Next Steps Now that you have the basics...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

README.md

[grammar] ~74-~74: There might be a problem here.
Context: ...ive loop (e.g., Jupyter), it returns an asyncio.Task and does not block. For non-blocking usage ...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~74-~74: Use correct spacing
Context: ..., you can run it in a background thread: python from threading import Thread runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode]) Thread(target=runtime.start, daemon=True).start() - ### Define your first graph Graphs are ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🔇 Additional comments (2)
python-sdk/README.md (1)

62-65: Verified: Runtime.start() correctly calls asyncio.get_running_loop() inside a try‐block, returns loop.create_task(self._start()) when a loop exists, and falls back to asyncio.run(self._start()) otherwise, matching the README description.

docs/docs/getting-started.md (1)

91-92: Blocking/Task semantics verified
Runtime.start() correctly returns an asyncio.Task when called inside an existing event loop (non-blocking) and blocks the main thread via asyncio.run (returning None) otherwise.

Comment on lines +118 to +120
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
await asyncio.to_thread(runtime.start)
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

await asyncio.to_thread(...) blocks; schedule it background

This example contradicts “offload to a thread” by awaiting the call. Schedule it as a background task instead.

-  await asyncio.to_thread(runtime.start)
+  task = asyncio.create_task(asyncio.to_thread(runtime.start))
+  # optionally await/cancel later
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
await asyncio.to_thread(runtime.start)
```
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
task = asyncio.create_task(asyncio.to_thread(runtime.start))
# optionally await or cancel the task later
🤖 Prompt for AI Agents
In docs/docs/getting-started.md around lines 118 to 120, the example awaits
asyncio.to_thread(runtime.start) which blocks the coroutine instead of
offloading startup to the background; change it to schedule the call as a
background task (e.g., wrap the to_thread call in a task creation so
runtime.start runs on a thread without awaiting it) so the example demonstrates
non-blocking background scheduling.

Comment on lines +74 to +81
- Regular sync script (non-blocking via thread):

```python
from threading import Thread

runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
Thread(target=runtime.start, daemon=True).start()
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Keep a thread handle; call out daemon trade-offs

Create and name the thread so users can observe/coordinate shutdown. Daemon threads can terminate abruptly; a handle enables optional joining.

-  Thread(target=runtime.start, daemon=True).start()
+  t = Thread(target=runtime.start, name="exosphere-runtime", daemon=True)
+  t.start()
+  # on shutdown you may: t.join(timeout=5)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Regular sync script (non-blocking via thread):
```python
from threading import Thread
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
Thread(target=runtime.start, daemon=True).start()
```
from threading import Thread
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
t = Thread(target=runtime.start, name="exosphere-runtime", daemon=True)
t.start()
# on shutdown you may: t.join(timeout=5)
🧰 Tools
🪛 LanguageTool

[grammar] ~74-~74: Use correct spacing
Context: ...r sync script (non-blocking via thread): python from threading import Thread runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) Thread(target=runtime.start, daemon=True).start() - From async code (offload to a thread): ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🤖 Prompt for AI Agents
In python-sdk/README.md around lines 74 to 81, the example spawns a daemon
thread without keeping a handle or naming it; update the guidance to create and
assign the Thread to a variable, give it a descriptive name, and avoid using
daemon=True by default (or clearly call out the abrupt-termination trade-off) so
callers can optionally call thread.join() during shutdown to coordinate clean
termination; mention that if daemon is used it should be an explicit choice with
warning about abrupt exit.

Comment on lines +85 to +90
```python
import asyncio

runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
await asyncio.to_thread(runtime.start)
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Awaiting asyncio.to_thread will block; schedule it instead

await asyncio.to_thread(runtime.start) blocks the current coroutine indefinitely if start() runs forever. Schedule it as a background task.

-  await asyncio.to_thread(runtime.start)
+  task = asyncio.create_task(asyncio.to_thread(runtime.start))
+  # optionally: await task  # later, if you want to join/cancel

Alternative: asyncio.get_running_loop().run_in_executor(None, runtime.start) (also schedule without awaiting).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```python
import asyncio
runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode])
await asyncio.to_thread(runtime.start)
```
🤖 Prompt for AI Agents
In python-sdk/README.md around lines 85 to 90, the example uses "await
asyncio.to_thread(runtime.start)" which will block the current coroutine if
runtime.start() runs indefinitely; instead schedule it as a background task or
run it in an executor without awaiting. Replace the await with creating a
background task (e.g., asyncio.create_task(asyncio.to_thread(runtime.start))) or
use the running loop's run_in_executor and do not await its result, ensuring
runtime.start runs concurrently and does not block the calling coroutine.

Comment on lines +74 to +81
Note: `Runtime.start()` will block the main thread in normal scripts (no running event loop). In interactive environments with an active loop (e.g., Jupyter), it returns an `asyncio.Task` and does not block. For non-blocking usage from a sync script, you can run it in a background thread:

```python
from threading import Thread

runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode])
Thread(target=runtime.start, daemon=True).start()
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Background-thread sample: capture the thread handle and note shutdown behavior

Starting a daemon thread is fine for quickstarts, but users should keep a handle for observability/shutdown. Replace the one-liner with two lines.

-  Thread(target=runtime.start, daemon=True).start()
+  t = Thread(target=runtime.start, name="exosphere-runtime", daemon=True)
+  t.start()  # optionally join on shutdown: t.join(timeout=5)

If Runtime.start() truly returns an asyncio.Task when a loop is running, please verify in code (e.g., look for loop-detection and asyncio.create_task paths) so the note remains accurate across releases.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Note: `Runtime.start()` will block the main thread in normal scripts (no running event loop). In interactive environments with an active loop (e.g., Jupyter), it returns an `asyncio.Task` and does not block. For non-blocking usage from a sync script, you can run it in a background thread:
```python
from threading import Thread
runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode])
Thread(target=runtime.start, daemon=True).start()
```
from threading import Thread
runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode])
t = Thread(target=runtime.start, name="exosphere-runtime", daemon=True)
t.start() # optionally join on shutdown: t.join(timeout=5)
🧰 Tools
🪛 LanguageTool

[grammar] ~74-~74: There might be a problem here.
Context: ...ive loop (e.g., Jupyter), it returns an asyncio.Task and does not block. For non-blocking usage ...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~74-~74: Use correct spacing
Context: ..., you can run it in a background thread: python from threading import Thread runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode]) Thread(target=runtime.start, daemon=True).start() - ### Define your first graph Graphs are ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🤖 Prompt for AI Agents
In README.md around lines 74 to 81, replace the one-liner daemon-thread example
with a two-line pattern that stores the Thread object for observability and
graceful shutdown (i.e., create the Thread, assign it to a variable, then start
it), and add a brief note about shutdown behavior and why keeping the handle
matters; additionally, verify the assertion that Runtime.start() returns an
asyncio.Task when an event loop is running by inspecting the runtime
implementation for loop-detection logic and any paths that call
asyncio.create_task (or similar), and if that behavior differs, update the
README note to reflect the actual behavior.

@NiveditJain NiveditJain reopened this Aug 31, 2025
@NiveditJain NiveditJain closed this Sep 1, 2025
@NiveditJain NiveditJain deleted the cursor/fix-github-issue-280-374a branch September 1, 2025 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants