I/O in Trio

Note

Please excuse our dust! geocities-construction-worker.gif

You’re looking at the documentation for trio’s development branch, which is currently about half-way through implementing a proper high-level networking API. If you want to know how to do networking in trio right now, then you might want to jump down to read about trio.socket, which is the already-working lower-level API. Alternatively, you can read on for a (somewhat disorganized) preview of coming attractions.

The abstract Stream API

Trio provides a set of abstract base classes that define a standard interface for unidirectional and bidirectional byte streams.

Why is this useful? Because it lets you write generic protocol implementations that can work over arbitrary transports, and easily create complex transport configurations. Here’s some examples:

  • trio.SocketStream wraps a raw socket (like a TCP connection over the network), and converts it to the standard stream interface.

  • trio.ssl.SSLStream is a “stream adapter” that can take any object that implements the trio.abc.Stream interface, and convert it into an encrypted stream. In trio the standard way to speak SSL over the network is to wrap an SSLStream around a SocketStream.

  • If you spawn a subprocess then you can get a SendStream that lets you write to its stdin, and a ReceiveStream that lets you read from its stdout. If for some reason you wanted to speak SSL to a subprocess, you could use a StapledStream to combine its stdin/stdout into a single bidirectional Stream, and then wrap that in an SSLStream:

    ssl_context = trio.ssl.create_default_context()
    ssl_context.check_hostname = False
    s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context)
    

    [Note: subprocess support is not implemented yet, but that’s the plan. Unless it is implemented, and I forgot to remove this note.]

  • It sometimes happens that you want to connect to an HTTPS server, but you have to go through a web proxy… and the proxy also uses HTTPS. So you end up having to do SSL-on-top-of-SSL. In trio this is trivial – just wrap your first SSLStream in a second SSLStream:

    # Get a raw SocketStream connection to the proxy:
    s0 = await open_tcp_stream("proxy", 443)
    
    # Set up SSL connection to proxy:
    s1 = SSLStream(s0, proxy_ssl_context, server_hostname="proxy")
    # Request a connection to the website
    await s1.send_all(b"CONNECT website:443 / HTTP/1.0\r\n")
    await check_CONNECT_response(s1)
    
    # Set up SSL connection to the real website. Notice that s1 is
    # already an SSLStream object, and here we're wrapping a second
    # SSLStream object around it.
    s2 = SSLStream(s1, website_ssl_context, server_hostname="website")
    # Make our request
    await s2.send_all("GET /index.html HTTP/1.0\r\n")
    ...
    
  • The trio.testing module provides a set of flexible in-memory stream object implementations, so if you have a protocol implementation to test then you can can spawn two tasks, set up a virtual “socket” connecting them, and then do things like inject random-but-repeatable delays into the connection.

Abstract base classes

class trio.abc.AsyncResource

A standard interface for resources that needs to be cleaned up, and where that cleanup may require blocking operations.

This class distinguishes between “graceful” closes, which may perform I/O and thus block, and a “forceful” close, which cannot. For example, cleanly shutting down a TLS-encrypted connection requires sending a “goodbye” message; but if a peer has become non-responsive, then sending this message might block forever, so we may want to just drop the connection instead. Therefore the graceful_close() method is unusual in that it should always close the connection (or at least make its best attempt) even if it fails; failure indicates a failure to achieve grace, not a failure to close the connection.

Objects that implement this interface can be used as async context managers, i.e., you can write:

async with create_resource() as some_async_resource:
    ...

Entering the context manager is synchronous (not a checkpoint); exiting it calls graceful_close(). The default implementations of __aenter__ and __aexit__ should be adequate for all subclasses.

abstractmethod forceful_close()

Force an immediate close of this resource.

This will never block, but (depending on the resource in question) it might be a “rude” shutdown.

If the resource is already closed, then this method should silently succeed.

await graceful_close()

Close this resource, gracefully.

This may block in order to perform a “graceful” shutdown (for example, sending a “goodbye” message). But, if this fails (e.g., due to being cancelled), then it still must close the underlying resource, possibly by calling forceful_close().

If the resource is already closed, then this method should silently succeed.

AsyncResource provides a default implementation of this method that’s suitable for resources that don’t distinguish between graceful and forceful closure: it simply calls forceful_close() and then executes a checkpoint.

class trio.abc.SendStream

Bases: trio.abc.AsyncResource

A standard interface for sending data on a byte stream.

The underlying stream may be unidirectional, or bidirectional. If it’s bidirectional, then you probably want to also implement ReceiveStream, which makes your object a Stream.

Every SendStream also implements the AsyncResource interface.

abstractmethod await send_all(data)

Sends the given data through the stream, blocking if necessary.

Parameters:data (bytes, bytearray, or memoryview) – The data to send.
Raises:trio.ResourceBusyError – if another task is already executing a send_all(), wait_send_all_might_not_block(), or HalfCloseableStream.send_eof() on this stream.
abstractmethod await wait_send_all_might_not_block()

Block until it’s possible that send_all() might not block.

This method may return early: it’s possible that after it returns, send_all() will still block. (In the worst case, if no better implementation is available, then it might always return immediately without blocking. It’s nice to do better than that when possible, though.)

This method must not return late: if it’s possible for send_all() to complete without blocking, then it must return. When implementing it, err on the side of returning early.

Raises:trio.ResourceBusyError – if another task is already executing a send_all(), wait_send_all_might_not_block(), or HalfCloseableStream.send_eof() on this stream.

Note

This method is intended to aid in implementing protocols that want to delay choosing which data to send until the last moment. E.g., suppose you’re working on an implemention of a remote display server like VNC, and the network connection is currently backed up so that if you call send_all() now then it will sit for 0.5 seconds before actually sending anything. In this case it doesn’t make sense to take a screenshot, then wait 0.5 seconds, and then send it, because the screen will keep changing while you wait; it’s better to wait 0.5 seconds, then take the screenshot, and then send it, because this way the data you deliver will be more up-to-date. Using wait_send_all_might_not_block() makes it possible to implement the better strategy.

If you use this method, you might also want to read up on TCP_NOTSENT_LOWAT.

Further reading:

class trio.abc.ReceiveStream

Bases: trio.abc.AsyncResource

A standard interface for receiving data on a byte stream.

The underlying stream may be unidirectional, or bidirectional. If it’s bidirectional, then you probably want to also implement SendStream, which makes your object a Stream.

Every ReceiveStream also implements the AsyncResource interface.

abstractmethod await receive_some(max_bytes)

Wait until there is data available on this stream, and then return at most max_bytes of it.

A return value of b"" (an empty bytestring) indicates that the stream has reached end-of-file. Implementations should be careful that they return b"" if, and only if, the stream has reached end-of-file!

This method will return as soon as any data is available, so it may return fewer than max_bytes of data. But it will never return more.

Parameters:

max_bytes (int) – The maximum number of bytes to return. Must be greater than zero.

Returns:

The data received.

Return type:

bytes or bytearray

Raises:
class trio.abc.Stream

Bases: trio.abc.SendStream, trio.abc.ReceiveStream

A standard interface for interacting with bidirectional byte streams.

A Stream is an object that implements both the SendStream and ReceiveStream interfaces.

If implementing this interface, you should consider whether you can go one step further and implement HalfCloseableStream.

class trio.abc.HalfCloseableStream

Bases: trio.abc.Stream

This interface extends Stream to also allow closing the send part of the stream without closing the receive part.

abstractmethod await send_eof()

Send an end-of-file indication on this stream, if possible.

The difference between send_eof() and graceful_close() is that send_eof() is a unidirectional end-of-file indication. After you call this method, you shouldn’t try sending any more data on this stream, and your remote peer should receive an end-of-file indication (eventually, after receiving all the data you sent before that). But, they may continue to send data to you, and you can continue to receive it by calling receive_some(). You can think of it as calling graceful_close() on just the SendStream “half” of the stream object (and in fact that’s literally how trio.StapledStream implements it).

Examples:

  • On a socket, this corresponds to shutdown(..., SHUT_WR) (man page).
  • The SSH protocol provides the ability to multiplex bidirectional “channels” on top of a single encrypted connection. A trio implementation of SSH could expose these channels as HalfCloseableStream objects, and calling send_eof() would send an SSH_MSG_CHANNEL_EOF request (see RFC 4254 §5.3).
  • On an SSL/TLS-encrypted connection, the protocol doesn’t provide any way to do a unidirectional shutdown without closing the connection entirely, so SSLStream implements Stream, not HalfCloseableStream.

If an EOF has already been sent, then this method should silently succeed.

Raises:
exception trio.BrokenStreamError

Raised when an attempt to use a stream a stream fails due to external circumstances.

For example, you might get this if you try to send data on a stream where the remote side has already closed the connection.

You don’t get this error if you closed the stream – in that case you get ClosedStreamError.

This exception’s __cause__ attribute will often contain more information about the underlying error.

exception trio.ClosedStreamError

Raised when an attempt to use a stream a stream fails because the stream was already closed locally.

You only get this error if your code closed the stream object you’re attempting to use by calling graceful_close() or similar. (send_all() might also raise this if you already called send_eof().) Therefore this exception generally indicates a bug in your code.

If a problem arises elsewhere, for example due to a network failure or a misbehaving peer, then you get BrokenStreamError instead.

Generic stream implementations

Trio currently provides one generic utility class for working with streams. And if you want to test code that’s written against the streams interface, you should also check out Streams in trio.testing.

class trio.StapledStream(send_stream, receive_stream)

Bases: trio.abc.HalfCloseableStream

This class staples together two unidirectional streams to make single bidirectional stream.

Parameters:
  • send_stream (SendStream) – The stream to use for sending.
  • receive_stream (ReceiveStream) – The stream to use for receiving.

Example

A silly way to make a stream that echoes back whatever you write to it:

sock1, sock2 = trio.socket.socketpair()
echo_stream = StapledStream(SocketStream(sock1), SocketStream(sock2))
await echo_stream.send_all(b"x")
assert await echo_stream.receive_some(1) == b"x"

StapledStream objects implement the methods in the HalfCloseableStream interface. They also have two additional public attributes:

send_stream

The underlying SendStream. send_all() and wait_send_all_might_not_block() are delegated to this object.

receive_stream

The underlying ReceiveStream. receive_some() is delegated to this object.

await send_all(data)

Calls self.send_stream.send_all.

await wait_send_all_might_not_block()

Calls self.send_stream.wait_send_all_might_not_block.

await send_eof()

Shuts down the send side of the stream.

If self.send_stream.send_eof exists, then calls it. Otherwise, calls self.send_stream.graceful_close().

await receive_some(max_bytes)

Calls self.receive_stream.receive_some.

forceful_close()

Calls forceful_close on both underlying streams.

await graceful_close()

Calls graceful_close on both underlying streams.

Sockets and networking

The high-level network interface is built on top of our stream abstraction.

class trio.SocketStream(sock)

Bases: trio.abc.HalfCloseableStream

An implementation of the trio.abc.HalfCloseableStream interface based on a raw network socket.

Parameters:sock (trio.socket.SocketType) – The trio socket object to wrap. Must have type SOCK_STREAM, and be connected.

By default, SocketStream enables TCP_NODELAY, and (on platforms where it’s supported) enables TCP_NOTSENT_LOWAT with a reasonable buffer size (currently 16 KiB) – see issue #72 for discussion. You can of course override these defaults by calling setsockopt().

Once a SocketStream object is constructed, it implements the full trio.abc.HalfCloseableStream interface. In addition, it provides a few extra features:

socket

The trio.socket.SocketType object that this stream wraps.

setsockopt(level, option, value)

Set an option on the underlying socket.

See socket.socket.setsockopt() for details.

getsockopt(level, option, buffersize=0)

Check the current value of an option on the underlying socket.

See socket.socket.getsockopt() for details.

trio.socket_stream_pair()

Returns a pair of connected SocketStream objects.

This is a convenience function that uses socket.socketpair() to create the sockets, and then converts the result into SocketStreams.

SSL / TLS support

The trio.ssl module implements SSL/TLS support for Trio, using the standard library ssl module. It re-exports most of ssl´s API, with the notable exception is ssl.SSLContext, which has unsafe defaults; if you really want to use ssl.SSLContext you can import it from ssl, but normally you should create your contexts using trio.ssl.create_default_context.

Instead of using ssl.SSLContext.wrap_socket(), though, you create a SSLStream:

class trio.ssl.SSLStream(transport_stream, ssl_context, *, server_hostname=None, server_side=False, https_compatible=False, max_refill_bytes=32768)

Bases: trio.abc.Stream

Encrypted communication using SSL/TLS.

SSLStream wraps an arbitrary Stream, and allows you to perform encrypted communication over it using the usual Stream interface. You pass regular data to send_all(), then it encrypts it and sends the encrypted data on the underlying Stream; receive_some() takes encrypted data out of the underlying Stream and decrypts it before returning it.

You should read the standard library’s ssl documentation carefully before attempting to use this class, and probably other general documentation on SSL/TLS as well. SSL/TLS is subtle and quick to anger. Really. I’m not kidding.

Parameters:
  • transport_stream (Stream) – The stream used to transport encrypted data. Required.
  • ssl_context (SSLContext) – The SSLContext used for this connection. Required. Usually created by calling trio.ssl.create_default_context().
  • server_hostname (str or None) – The name of the server being connected to. Used for SNI and for validating the server’s certificate (if hostname checking is enabled). This is effectively mandatory for clients, and actually mandatory if ssl_context.check_hostname is True.
  • server_side (bool) – Whether this stream is acting as a client or server. Defaults to False, i.e. client mode.
  • https_compatible (bool) –

    There are two versions of SSL/TLS commonly encountered in the wild: the standard version, and the version used for HTTPS (HTTP-over-SSL/TLS).

    Standard-compliant SSL/TLS implementations always send a cryptographically signed close_notify message before closing the connection. This is important because if the underlying transport were simply closed, then there wouldn’t be any way for the other side to know whether the connection was intentionally closed by the peer that they negotiated a cryptographic connection to, or by some man-in-the-middle attacker who can’t manipulate the cryptographic stream, but can manipulate the transport layer (a so-called “truncation attack”).

    However, this part of the standard is widely ignored by real-world HTTPS implementations, which means that if you want to interoperate with them, then you NEED to ignore it too.

    Fortunately this isn’t as bad as it sounds, because the HTTP protocol already includes its own equivalent of close_notify, so doing this again at the SSL/TLS level is redundant. But not all protocols do! Therefore, by default Trio implements the safer standard-compliant version (https_compatible=False). But if you’re speaking HTTPS or some other protocol where close_notifys are commonly skipped, then you should set https_compatible=True; with this setting, Trio will neither expect nor send close_notify messages.

    If you have code that was written to use ssl.SSLSocket and now you’re porting it to Trio, then it may be useful to know that a difference between SSLStream and ssl.SSLSocket is that SSLSocket implements the https_compatible=True behavior by default.

  • max_refill_bytes (int) – SSLSocket maintains an internal buffer of incoming data, and when it runs low then it calls receive_some() on the underlying transport stream to refill it. This argument lets you set the max_bytes argument passed to the underlying receive_some() call. It doesn’t affect calls to this class’s receive_some(), or really anything else user-observable except possibly performance. You probably don’t need to worry about this.
transport_stream

trio.abc.Stream – The underlying transport stream that was passed to __init__. An example of when this would be useful is if you’re using SSLStream over a SocketStream and want to call the SocketStream’s setsockopt() method.

Internally, this class is implemented using an instance of ssl.SSLObject, and all of SSLObject’s methods and attributes are re-exported as methods and attributes on this class.

This also means that you register a SNI callback using set_servername_callback(), then the first argument your callback receives will be a ssl.SSLObject.

await do_handshake()

Ensure that the initial handshake has completed.

The SSL protocol requires an initial handshake to exchange certificates, select cryptographic keys, and so forth, before any actual data can be sent or received. You don’t have to call this method; if you don’t, then SSLStream will automatically peform the handshake as needed, the first time you try to send or receive data. But if you want to trigger it manually – for example, because you want to look at the peer’s certificate before you start talking to them – then you can call this method.

If the initial handshake is already in progress in another task, this waits for it to complete and then returns.

If the initial handshake has already completed, this returns immediately without doing anything (except executing a checkpoint).

Warning

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenStreamError.

await receive_some(max_bytes)

Read some data from the underlying transport, decrypt it, and return it.

See trio.abc.ReceiveStream.receive_some() for details.

Warning

If this method is cancelled while the initial handshake or a renegotiation are in progress, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenStreamError.

await send_all(data)

Encrypt some data and then send it on the underlying transport.

See trio.abc.SendStream.send_all() for details.

Warning

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any attempt to use the object will raise trio.BrokenStreamError.

await unwrap()

Cleanly close down the SSL/TLS encryption layer, allowing the underlying stream to be used for unencrypted communication.

You almost certainly don’t need this.

Returns:A pair (transport_stream, trailing_bytes), where transport_stream is the underlying transport stream, and trailing_bytes is a byte string. Since SSLStream doesn’t necessarily know where the end of the encrypted data will be, it can happen that it accidentally reads too much from the underlying stream. trailing_bytes contains this extra data; you should process it as if it was returned from a call to transport_stream.receive_some(...).
forceful_close()

Forcefully closes the underlying transport and marks this stream as closed.

await graceful_close()

Gracefully shut down this connection, and close the underlying transport.

If https_compatible is False (the default), then this attempts to first send a close_notify and then close the underlying stream by calling its graceful_close() method.

If https_compatible is set to True, then this simply closes the underlying stream and marks this stream as closed.

await wait_send_all_might_not_block()

See trio.abc.SendStream.wait_send_all_might_not_block().

Low-level sockets and networking

The trio.socket module provides trio’s basic low-level networking API. If you’re doing ordinary things with stream-oriented connections over IPv4/IPv6/Unix domain sockets, then you probably want to stick to the high-level API described above. If you want to use UDP, or exotic address families like AF_BLUETOOTH, or otherwise get direct access to all the quirky bits of your system’s networking API, then you’re in the right place.

trio.socket: top-level exports

Generally, the API exposed by trio.socket mirrors that of the standard library socket module. Most constants (like SOL_SOCKET) and simple utilities (like inet_aton()) are simply re-exported unchanged. But there are also some differences, which are described here.

All functions that return socket objects (e.g. socket.socket(), socket.socketpair(), …) are modified to return trio socket objects instead. In addition, there is a new function to directly convert a standard library socket into a trio socket:

trio.socket.from_stdlib_socket(sock)

Convert a standard library socket.socket() into a trio socket.

For name lookup, Trio provides the standard functions, but with some changes:

await trio.socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

Look up a numeric address given a name.

Arguments and return values are identical to socket.getaddrinfo(), except that this version is async.

Also, trio.socket.getaddrinfo() correctly uses IDNA 2008 to process non-ASCII domain names. (socket.getaddrinfo() uses IDNA 2003, which can give the wrong result in some cases and cause you to connect to a different host than the one you intended; see bpo-17305.)

await trio.socket.getnameinfo(sockaddr, flags)

Look up a name given a numeric address.

Arguments and return values are identical to socket.getnameinfo(), except that this version is async.

await trio.socket.getprotobyname(name)

Look up a protocol number by name. (Rarely used.)

Like socket.getprotobyname(), but async.

Trio intentionally DOES NOT include some obsolete, redundant, or broken features:

Socket objects

class trio.socket.SocketType

Trio socket objects are overall very similar to the standard library socket objects, with a few important differences:

Async all the things: Most obviously, everything is made “trio-style”: blocking methods become async methods, and the following attributes are not supported:

  • setblocking(): trio sockets always act like blocking sockets; if you need to read/write from multiple sockets at once, then create multiple tasks.
  • settimeout(): see Cancellation and timeouts instead.
  • makefile(): Python’s file-like API is synchronous, so it can’t be implemented on top of an async socket.

No implicit name resolution: In the standard library socket API, there are number of methods that take network addresses as arguments. When given a numeric address this is fine:

# OK
sock.bind(("127.0.0.1", 80))
sock.connect(("2607:f8b0:4000:80f::200e", 80))

But in the standard library, these methods also accept hostnames, and in this case implicitly trigger a DNS lookup to find the IP address:

# Might block!
sock.bind(("localhost", 80))
sock.connect(("google.com", 80))

This is problematic because DNS lookups are a blocking operation.

For simplicity, trio forbids such usages: hostnames must be “pre-resolved” to numeric addresses before they are passed to socket methods like bind() or connect(). In most cases this can be easily accomplished by calling either resolve_local_address() or resolve_remote_address().

await resolve_local_address(address)

Resolve the given address into a numeric address suitable for passing to bind().

This performs the same address resolution that the standard library bind() call would do, taking into account the current socket’s settings (e.g. if this is an IPv6 socket then it returns IPv6 addresses). In particular, a hostname of None is mapped to the wildcard address.

await resolve_remote_address(address)

Resolve the given address into a numeric address suitable for passing to connect() or similar.

This performs the same address resolution that the standard library connect() call would do, taking into account the current socket’s settings (e.g. if this is an IPv6 socket then it returns IPv6 addresses). In particular, a hostname of None is mapped to the localhost address.

Modern defaults: And finally, we took the opportunity to update the defaults for several socket options that were stuck in the 1980s. You can always use setsockopt() to change these back, but for trio sockets:

  1. Everywhere except Windows, SO_REUSEADDR is enabled by default. This is almost always what you want, but if you’re in one of the rare cases where this is undesireable then you can always disable SO_REUSEADDR manually:

    sock.setsockopt(trio.socket.SOL_SOCKET, trio.socket.SO_REUSEADDR, False)
    

    On Windows, SO_EXCLUSIVEADDR is enabled by default. Unfortunately, this means that if you stop and restart a server you may have trouble reacquiring listen ports (i.e., it acts like Unix without SO_REUSEADDR). To get the Unix-style SO_REUSEADDR semantics on Windows, you can disable SO_EXCLUSIVEADDR:

    sock.setsockopt(trio.socket.SOL_SOCKET, trio.socket.SO_EXCLUSIVEADDR, False)
    

    but be warned that this may leave your application vulnerable to port hijacking attacks.

  2. IPV6_V6ONLY is disabled, i.e., by default on dual-stack hosts a AF_INET6 socket is able to communicate with both IPv4 and IPv6 peers, where the IPv4 peers appear to be in the “IPv4-mapped” portion of IPv6 address space. To make an IPv6-only socket, use something like:

    sock = trio.socket.socket(trio.socket.AF_INET6)
    sock.setsockopt(trio.socket.IPPROTO_IPV6, trio.socket.IPV6_V6ONLY, True)
    

    This makes trio applications behave more consistently across different environments.

See issue #72 for discussion of these defaults.

The following methods are similar, but not identical, to the equivalents in socket.socket():

bind(address)

Bind this socket to the given address.

Unlike the stdlib connect(), this method requires a pre-resolved address. See resolve_local_address().

await connect(address)

Connect the socket to a remote address.

Similar to socket.socket.connect(), except async and requiring a pre-resolved address. See resolve_remote_address().

Warning

Due to limitations of the underlying operating system APIs, it is not always possible to properly cancel a connection attempt once it has begun. If connect() is cancelled, and is unable to abort the connection attempt, then it will:

  1. forcibly close the socket to prevent accidental re-use
  2. raise Cancelled.

tl;dr: if connect() is cancelled then you should throw away that socket and make a new one.

await sendall(data, flags=0)

Send the data to the socket, blocking until all of it has been accepted by the operating system.

flags are passed on to send.

Most low-level operations in trio provide a guarantee: if they raise trio.Cancelled, this means that they had no effect, so the system remains in a known state. This is not true for sendall(). If this operation raises trio.Cancelled (or any other exception for that matter), then it may have sent some, all, or none of the requested data, and there is no way to know which.

sendfile()

Not implemented yet!

The following methods are identical to their equivalents in socket.socket(), except async, and the ones that take address arguments require pre-resolved addresses:

All methods and attributes not mentioned above are identical to their equivalents in socket.socket():

Asynchronous disk I/O

await trio.open_file(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

Asynchronous version of open().

Returns:An asynchronous file object

Example:

async with await trio.open_file(filename) as f:
    async for line in f:
        pass

assert f.closed
trio.wrap_file(file)

This wraps any file object in a wrapper that provides an asynchronous file object interface.

Parameters:file – a file object
Returns:An asynchronous file object that wraps file

Example:

async_file = trio.wrap_file(StringIO('asdf'))

assert await async_file.read() == 'asdf'

Asynchronous path objects

class trio.Path(*args)

A Path wrapper that executes blocking Path methods in trio.run_in_worker_thread().

await open(*args, **kwargs)

Open the file pointed to by the path, like the trio.open_file() function does.

as_posix()

Return the string representation of the path with forward (/) slashes.

as_uri()

Return the path as a ‘file’ URI.

await chmod(*args, **kwargs)

Like chmod(), but async.

classmethod cwd()

Return a new path pointing to the current working directory (as returned by os.getcwd()).

await exists(*args, **kwargs)

Like exists(), but async.

await expanduser(*args, **kwargs)

Like expanduser(), but async.

await glob(*args, **kwargs)

Like glob(), but async.

await group(*args, **kwargs)

Like group(), but async.

classmethod home()

Return a new path pointing to the user’s home directory (as returned by os.path.expanduser(‘~’)).

is_absolute()

True if the path is absolute (has both a root and, if applicable, a drive).

await is_block_device(*args, **kwargs)

Like is_block_device(), but async.

await is_char_device(*args, **kwargs)

Like is_char_device(), but async.

await is_dir(*args, **kwargs)

Like is_dir(), but async.

await is_fifo(*args, **kwargs)

Like is_fifo(), but async.

await is_file(*args, **kwargs)

Like is_file(), but async.

is_reserved()

Return True if the path contains one of the special names reserved by the system, if any.

await is_socket(*args, **kwargs)

Like is_socket(), but async.

Like is_symlink(), but async.

await iterdir(*args, **kwargs)

Like iterdir(), but async.

joinpath(*args)

Combine this path with one or several arguments, and return a new path representing either a subpath (if all arguments are relative paths) or a totally different path (if one of the arguments is anchored).

await lchmod(*args, **kwargs)

Like lchmod(), but async.

await lstat(*args, **kwargs)

Like lstat(), but async.

match(path_pattern)

Return True if this path matches the given pattern.

await mkdir(*args, **kwargs)

Like mkdir(), but async.

await owner(*args, **kwargs)

Like owner(), but async.

await read_bytes(*args, **kwargs)

Like read_bytes(), but async.

await read_text(*args, **kwargs)

Like read_text(), but async.

relative_to(*other)

Return the relative path to another path identified by the passed arguments. If the operation is not possible (because this is not a subpath of the other path), raise ValueError.

await rename(*args, **kwargs)

Like rename(), but async.

await replace(*args, **kwargs)

Like replace(), but async.

await resolve(*args, **kwargs)

Like resolve(), but async.

await rglob(*args, **kwargs)

Like rglob(), but async.

await rmdir(*args, **kwargs)

Like rmdir(), but async.

await samefile(*args, **kwargs)

Like samefile(), but async.

await stat(*args, **kwargs)

Like stat(), but async.

Like symlink_to(), but async.

await touch(*args, **kwargs)

Like touch(), but async.

Like unlink(), but async.

with_name(name)

Return a new path with the file name changed.

with_suffix(suffix)

Return a new path with the file suffix changed (or added, if none).

await write_bytes(*args, **kwargs)

Like write_bytes(), but async.

await write_text(*args, **kwargs)

Like write_text(), but async.

Subprocesses

Not implemented yet!

Signals

with trio.catch_signals(signals) as batched_signal_aiter

A context manager for catching signals.

Entering this context manager starts listening for the given signals and returns an async iterator; exiting the context manager stops listening.

The async iterator blocks until at least one signal has arrived, and then yields a set containing all of the signals that were received since the last iteration. (This is generally similar to how UnboundedQueue works, but since Unix semantics are that identical signals can/should be coalesced, here we use a set for storage instead of a list.)

Note that if you leave the with block while the iterator has unextracted signals still pending inside it, then they will be re-delivered using Python’s regular signal handling logic. This avoids a race condition when signals arrives just before we exit the with block.

Parameters:signals – a set of signals to listen for.
Raises:RuntimeError – if you try to use this anywhere except Python’s main thread. (This is a Python limitation.)

Example

A common convention for Unix daemon is that they should reload their configuration when they receive a SIGHUP. Here’s a sketch of what that might look like using catch_signals():

with trio.catch_signals({signal.SIGHUP}) as batched_signal_aiter:
    async for batch in batched_signal_aiter:
        # We're only listening for one signal, so the batch is always
        # {signal.SIGHUP}, but if we were listening to more signals
        # then it could vary.
        for signum in batch:
            assert signum == signal.SIGHUP
            reload_configuration()