python-trio / python-trio/trio

Consider converting lowest-level I/O primitives from global functions into methods

Open
#475 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

design discussion low-level potential API breaker
Dominant language
Python
Stars
7.3k
Forks
431
Avg merge
2d 17h
Merged PRs (30d)
6

Description

This issue is about Trio's handful of primitive I/O functions: wait_{readable,writable}, wait_socket_{readable,writable}, and a few more exotic ones like wait_kevent, wait_overlapped, etc.

Currently, the public API for these is global functions in trio.hazmat:

trio.hazmat.wait_readable(fd)

I'm wondering if we should change it so that they're methods on a global object you can fetch:

trio.hazmat.current_io_manager().wait_readable(fd)

Implementation-wise, this would be a pretty simple refactoring, because currently the wait_readable function is implemented like:

@enable_ki_protection
def wait_readable(*args, **kwargs):
    try:
        meth = GLOBAL_RUN_CONTEXT.io_manager.wait_readable
    except AttributeError:
        raise RuntimeError("must be called from async context") from None
    return meth(*args, **kwargs)

So in this approach we'd instead have:

def current_io_manager():
    return GLOBAL_RUN_CONTEXT.io_manager

and that's basically it. Though we would need to go through and add explicit @enable_ki_protection decorators to all the methods, since currently they're inheriting it from the wrapper. And make sure that all the private methods are underscored or something. But it's fundamentally not that big a change.

The bigger implication is for the public API:

  • Currently you can check which primitives are available by doing checks like hasattr(trio.hazmat, "wait_readable"), and this is a static fact for the life of the process. For example, you can do it at import time. Now this wouldn't be true; you'd have to do the checks on the I/O manager object, and you couldn't do it until you were inside an async context.

  • And, of course, this is the whole point: it would make it possible for us to delay deciding which I/O manager we were using until runtime. I'm still not excited about the idea of adding a Qt-backed I/O manager (see #399 for more discussion), but at least it would be structurally possible if we decided to do it at some point in the future.

Contributor guide

Open the contributing guide

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 in trio/_core/_run.py at the wait_readable wrapper and trace the related primitives in trio.hazmat and the I/O manager implementations. Review issue #399 and the public API implications described here; done requires a decided migration design, including runtime availability checks and the affected method decorators and names.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.