MagicStack / MagicStack/uvloop

inconsistent file descriptor blocking state when running in asyncio vs uvloop

Open
#712 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Cython
Stars
11.9k
Forks
616
PR merge metrics
No merged PRs in 30d

Description

When running under uvloop, registering `stdin`, `stdout`, or `stderr` with `loop.add_reader()` / `loop.add_writer()` implicitly changes their blocking mode from blocking to non-blocking. This behaviour does not occur under the standard asyncio selector event loop.

This is causing some issues when running with rich/promptkit in [fast-agent](https://github.com/evalstate/fast-agent)

Under the following test code

```python
import anyio
import sys
import os
import asyncio

async def main():

loop = asyncio.get_running_loop()
print(f"Using event loop: {type(loop)}")

stdin = sys.stdin.fileno()
stdout = sys.stdout.fileno()
stderr = sys.stderr.fileno()

print(f"initial:\tstdin fd: {stdin}, stdout fd: {stdout}, stderr fd: {stderr}")
print(f"blocking:\tstdin fd: {os.get_blocking(stdin)}, stdout fd: {os.get_blocking(stdout)}, stderr fd: {os.get_blocking(stderr)}")

loop.add_writer(stdin, lambda: None)
loop.add_writer(stdout, lambda: None)
loop.add_writer(stderr, lambda: None)

print(f"writer: \tstdin fd: {stdin}, stdout fd: {stdout}, stderr fd: {stderr}")
print(f"blocking:\tstdin fd: {os.get_blocking(stdin)}, stdout fd: {os.get_blocking(stdout)}, stderr fd: {os.get_blocking(stderr)}")

loop.add_reader(stdin, lambda: None)
loop.add_reader(stdout, lambda: None)
loop.add_reader(stderr, lambda: None)

print(f"reader: \tstdin fd: {stdin}, stdout fd: {stdout}, stderr fd: {stderr}")
print(f"blocking:\tstdin fd: {os.get_blocking(stdin)}, stdout fd: {os.get_blocking(stdout)}, stderr fd: {os.get_blocking(stderr)}")

anyio.run(main, backend="asyncio", backend_options={"use_uvloop": False})
print("-" * 40)
anyio.run(main, backend="asyncio", backend_options={"use_uvloop": True})
```

I see the following output, where the blocking status of the stdio file descriptors are changed from blocking to non blocking when running under uvloop but not asyncio.

```
Using event loop:
initial: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: True, stdout fd: True, stderr fd: True
writer: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: True, stdout fd: True, stderr fd: True
reader: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: True, stdout fd: True, stderr fd: True
----------------------------------------
Using event loop:
initial: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: True, stdout fd: True, stderr fd: True
writer: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: False, stdout fd: False, stderr fd: False
reader: stdin fd: 0, stdout fd: 1, stderr fd: 2
blocking: stdin fd: False, stdout fd: False, stderr fd: False
```

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the supplied asyncio versus uvloop reproduction and inspect the uvloop entry points for loop.add_reader() and loop.add_writer(). Compare their file-descriptor handling with the standard asyncio selector loop; done means registering stdin, stdout, and stderr preserves their blocking state under uvloop.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.