On a quest for agency in Bellairs

Python bits

+1
py-template/.python-version
···
+
3.9.6
+1
py-template/README.md
···
+
This is a uv project to get started with Capnproto
+34
py-template/bellairs.capnp
···
+
@0xa59e1c95e37fb55d;
+
+
interface Directory {
+
# Represents a directory in the filesystem
+
+
list @0 () -> (entries :List(Entry));
+
# Lists all entries in the directory
+
+
struct Entry {
+
name @0 :Text; # Name of the entry
+
file @1 :File; # Reference to the file object
+
}
+
+
create @1 (name :Text) -> (file :File);
+
# Creates a new file with the given name
+
+
open @2 (name :Text) -> (file :File);
+
# Opens an existing file with the given name
+
+
delete @3 (name :Text);
+
# Deletes a file with the given name
+
}
+
+
interface File {
+
# Represents a file in the filesystem
+
+
size @0 () -> (size :UInt64);
+
# Returns the size of the file in bytes
+
+
read @1 (off :UInt64 = 0, len :UInt64 = 0xffffffffffffffff) -> (data :Data);
+
# Reads data from the file, optionally starting at offset and reading up to len bytes
+
# Default is to read the entire file
+
}
+
+58
py-template/client-and-mock-server.py
···
+
import asyncio
+
import socket
+
+
import capnp
+
+
capnp.remove_import_hook()
+
+
bellairs_capnp = capnp.load("bellairs.capnp")
+
+
"""
+
Mock file server so I can test this.
+
"""
+
+
+
class TestServer(bellairs_capnp.File.Server):
+
def __init__(self):
+
self.data = "Voop"
+
+
async def size(self, **kwargs):
+
return len(self.data)
+
+
async def read(self, offset, len, **kwargs):
+
return self.data
+
+
+
async def client(sock):
+
# See https://capnproto.github.io/pycapnp/capnp.html?highlight=twopartyclient#capnp.TwoPartyClient
+
# AP: I am a bit unsure why the only thing available here
+
# is a two-party client, but this works as a hack.
+
client = capnp.TwoPartyClient(sock)
+
cap = client.bootstrap()
+
cap = cap.cast_as(bellairs_capnp.File)
+
result = await cap.size()
+
size = result.size
+
print("Got %d" % size)
+
assert size == 4
+
+
+
async def main():
+
# Create a UNIX socket pair, because the network is
+
# more complex.
+
client_end, server_end = socket.socketpair(socket.AF_UNIX)
+
# Create AsyncIoStreams, which is really a wrapper around
+
# the C++ Capnproto connection. See https://github.com/capnproto/pycapnp/blob/59a639fa977e4a2e19c6cc60b44cbc9926418710/capnp/lib/capnp.pyx#L1314
+
client_end = await capnp.AsyncIoStream.create_connection(sock=client_end)
+
server_end = await capnp.AsyncIoStream.create_connection(sock=server_end)
+
# Create a TwoPartyServer. Better options
+
# are available (e.g., see https://github.com/capnproto/pycapnp/blob/master/examples/async_ssl_server.py#L60), but
+
# I did this for now. I think we will need to use
+
# `AsyncIoStream.create_server` and do some amount
+
# of indirection in there
+
_ = capnp.TwoPartyServer(server_end, bootstrap=TestServer())
+
print("Started file server")
+
await client(client_end)
+
+
+
if __name__ == "__main__":
+
asyncio.run(capnp.run(main()))
+7
py-template/pyproject.toml
···
+
[project]
+
name = "mvp-py"
+
version = "0.1.0"
+
description = "Add your description here"
+
readme = "README.md"
+
requires-python = ">=3.9.6"
+
dependencies = []
+8
py-template/uv.lock
···
+
version = 1
+
revision = 1
+
requires-python = ">=3.9.6"
+
+
[[package]]
+
name = "mvp-py"
+
version = "0.1.0"
+
source = { virtual = "." }