Skip to content

feat(porcelain): discover modules by typed RPC specs - #4009

Open
TomCC7 wants to merge 1 commit into
mainfrom
feat/spec-rpc-discovery
Open

feat(porcelain): discover modules by typed RPC specs#4009
TomCC7 wants to merge 1 commit into
mainfrom
feat/spec-rpc-discovery

Conversation

@TomCC7

@TomCC7 TomCC7 commented Sep 8, 2026

Copy link
Copy Markdown
Member

Contribution path

Split from #3944. Related discussion: #3923

Problem

Clients should be able to find a module by its Spec without also knowing its implementation name.

Solution

app.get_module(MySpec) finds the deployed module whose RPC signatures match the Spec, using the same checks as blueprint wiring.

Multiple matches require instance_name=. The module's class must be importable in the client. Name-based lookup still works.

The Arm SDK in #3944 builds on this change.

How to test

Use this branch's provisioned environment. Run only one blueprint on the transport bus.

In terminal one, start the mock dual-arm stack:

source .venv/bin/activate
dimos run dual-xarm6-planner-coordinator

Once startup finishes, open a shell in terminal two:

source .venv/bin/activate
dimos shell

Find the motion module by Spec:

from dimos.manipulation.manipulation_spec import ManipulationSpec

motion = app.get_module(ManipulationSpec)
motion.list_planning_groups()

You should see the left and right planning groups. This does not move either arm.

Check that name-based and explicit-instance lookup return the same proxy:

app.get_module("ManipulationModule") is motion

app.get_module(
    ManipulationSpec,
    instance_name=motion.remote_name,
) is motion

Both expressions should return True.

Check a missing instance:

app.get_module(ManipulationSpec, instance_name="missing")

This should raise LookupError.

Exit the shell, reopen it, and repeat the first lookup. The runtime should still be running. When finished, exit the shell and stop the test runtime with Ctrl-C in terminal one.

AI assistance

Codex (GPT-5-based coding agent) implemented the change with user direction.

Checklist

  • I have read and approved the CLA.

@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

❌ 7 Tests Failed:

Tests completed Failed Passed Skipped
5316 7 5309 102
View the top 3 failed test(s) by shortest run time
dimos.experimental.memory.test_rust_recorder_e2e::test_cli_recording_uses_existing_binary_for_both_formats[mcap]
Stack Traces | 0s run time
@pytest.fixture(scope="module")
    def rust_recorder_executable() -> Path:
>       subprocess.run(
            [
                "nix",
                "--extra-experimental-features",
                "nix-command flakes",
                "build",
                "-L",
                ".#dimos-memory-recorder",
                "--no-write-lock-file",
            ],
            cwd=_RUST_PACKAGE,
            check=True,
        )


.../experimental/memory/test_rust_recorder_e2e.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:548: in run
    with Popen(*popenargs, **kwargs) as process:
        capture_output = False
        check      = True
        input      = None
        kwargs     = {'cwd': PosixPath('.../experimental/memory/rust')}
        popenargs  = (['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...],)
        timeout    = None
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1026: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
        args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
        bufsize    = -1
        c2pread    = -1
        c2pwrite   = -1
        close_fds  = True
        creationflags = 0
        cwd        = PosixPath('.../experimental/memory/rust')
        encoding   = None
        env        = None
        errors     = None
        errread    = -1
        errwrite   = -1
        executable = None
        extra_groups = None
        gid        = None
        gids       = None
        group      = None
        p2cread    = -1
        p2cwrite   = -1
        pass_fds   = ()
        pipesize   = -1
        preexec_fn = None
        process_group = -1
        restore_signals = True
        self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
        shell      = False
        start_new_session = False
        startupinfo = None
        stderr     = None
        stdin      = None
        stdout     = None
        text       = None
        uid        = None
        umask      = -1
        universal_newlines = None
        user       = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
args = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
executable = b'nix', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = PosixPath('.../experimental/memory/rust')
env = None, startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False, process_group = -1

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session, process_group):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '....../system/bin/sh'.
            unix_shell = ('....../system/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and process_group == -1
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        process_group, gid, gids, uid, umask,
                        preexec_fn, _USE_VFORK)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                if err_msg == "noexec:chdir":
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                elif err_msg == "noexec":
                    err_msg = ""
                    err_filename = None
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                if err_filename is not None:
>                   raise child_exception_type(errno_num, err_msg, err_filename)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'nix'

args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
c2pread    = -1
c2pwrite   = -1
child_exception_type = <class 'OSError'>
close_fds  = True
creationflags = 0
cwd        = PosixPath('.../experimental/memory/rust')
env        = None
env_list   = None
err_filename = 'nix'
err_msg    = 'No such file or directory'
errno_num  = 2
errpipe_data = bytearray(b'OSError:2:')
errpipe_read = 294
errpipe_write = 295
errread    = -1
errwrite   = -1
exception_name = bytearray(b'OSError')
executable = b'nix'
executable_list = (b'.../dimos/dimos/.venv/bin/nix', b'....../Users/ec2-user/.local.../uv/python/nix', b'/...l/uv/0.12.11/aarch64/nix', b'....../Users/ec2-user/.local/bin/nix', b'.../homebrew/bin/nix', b'.../homebrew/sbin/nix', ...)
fds_to_keep = {295}
gid        = None
gids       = None
hex_errno  = bytearray(b'2')
low_fds_to_close = []
orig_executable = 'nix'
p2cread    = -1
p2cwrite   = -1
part       = b''
pass_fds   = ()
pid        = 80483
preexec_fn = None
process_group = -1
restore_signals = True
self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
shell      = False
start_new_session = False
startupinfo = None
sts        = 65280
uid        = None
umask      = -1

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1955: FileNotFoundError
dimos.experimental.memory.test_rust_recorder_e2e::test_cli_recording_uses_existing_binary_for_both_formats[sqlite]
Stack Traces | 0s run time
@pytest.fixture(scope="module")
    def rust_recorder_executable() -> Path:
>       subprocess.run(
            [
                "nix",
                "--extra-experimental-features",
                "nix-command flakes",
                "build",
                "-L",
                ".#dimos-memory-recorder",
                "--no-write-lock-file",
            ],
            cwd=_RUST_PACKAGE,
            check=True,
        )


.../experimental/memory/test_rust_recorder_e2e.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:548: in run
    with Popen(*popenargs, **kwargs) as process:
        capture_output = False
        check      = True
        input      = None
        kwargs     = {'cwd': PosixPath('.../experimental/memory/rust')}
        popenargs  = (['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...],)
        timeout    = None
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1026: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
        args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
        bufsize    = -1
        c2pread    = -1
        c2pwrite   = -1
        close_fds  = True
        creationflags = 0
        cwd        = PosixPath('.../experimental/memory/rust')
        encoding   = None
        env        = None
        errors     = None
        errread    = -1
        errwrite   = -1
        executable = None
        extra_groups = None
        gid        = None
        gids       = None
        group      = None
        p2cread    = -1
        p2cwrite   = -1
        pass_fds   = ()
        pipesize   = -1
        preexec_fn = None
        process_group = -1
        restore_signals = True
        self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
        shell      = False
        start_new_session = False
        startupinfo = None
        stderr     = None
        stdin      = None
        stdout     = None
        text       = None
        uid        = None
        umask      = -1
        universal_newlines = None
        user       = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
args = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
executable = b'nix', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = PosixPath('.../experimental/memory/rust')
env = None, startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False, process_group = -1

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session, process_group):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '....../system/bin/sh'.
            unix_shell = ('....../system/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and process_group == -1
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        process_group, gid, gids, uid, umask,
                        preexec_fn, _USE_VFORK)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                if err_msg == "noexec:chdir":
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                elif err_msg == "noexec":
                    err_msg = ""
                    err_filename = None
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                if err_filename is not None:
>                   raise child_exception_type(errno_num, err_msg, err_filename)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'nix'

args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
c2pread    = -1
c2pwrite   = -1
child_exception_type = <class 'OSError'>
close_fds  = True
creationflags = 0
cwd        = PosixPath('.../experimental/memory/rust')
env        = None
env_list   = None
err_filename = 'nix'
err_msg    = 'No such file or directory'
errno_num  = 2
errpipe_data = bytearray(b'OSError:2:')
errpipe_read = 294
errpipe_write = 295
errread    = -1
errwrite   = -1
exception_name = bytearray(b'OSError')
executable = b'nix'
executable_list = (b'.../dimos/dimos/.venv/bin/nix', b'....../Users/ec2-user/.local.../uv/python/nix', b'/...l/uv/0.12.11/aarch64/nix', b'....../Users/ec2-user/.local/bin/nix', b'.../homebrew/bin/nix', b'.../homebrew/sbin/nix', ...)
fds_to_keep = {295}
gid        = None
gids       = None
hex_errno  = bytearray(b'2')
low_fds_to_close = []
orig_executable = 'nix'
p2cread    = -1
p2cwrite   = -1
part       = b''
pass_fds   = ()
pid        = 80483
preexec_fn = None
process_group = -1
restore_signals = True
self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
shell      = False
start_new_session = False
startupinfo = None
sts        = 65280
uid        = None
umask      = -1

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1955: FileNotFoundError
dimos.experimental.memory.test_rust_recorder_e2e::test_rust_artifact_is_readable_by_python_memory2[mcap]
Stack Traces | 0s run time
@pytest.fixture(scope="module")
    def rust_recorder_executable() -> Path:
>       subprocess.run(
            [
                "nix",
                "--extra-experimental-features",
                "nix-command flakes",
                "build",
                "-L",
                ".#dimos-memory-recorder",
                "--no-write-lock-file",
            ],
            cwd=_RUST_PACKAGE,
            check=True,
        )


.../experimental/memory/test_rust_recorder_e2e.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:548: in run
    with Popen(*popenargs, **kwargs) as process:
        capture_output = False
        check      = True
        input      = None
        kwargs     = {'cwd': PosixPath('.../experimental/memory/rust')}
        popenargs  = (['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...],)
        timeout    = None
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1026: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
        args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
        bufsize    = -1
        c2pread    = -1
        c2pwrite   = -1
        close_fds  = True
        creationflags = 0
        cwd        = PosixPath('.../experimental/memory/rust')
        encoding   = None
        env        = None
        errors     = None
        errread    = -1
        errwrite   = -1
        executable = None
        extra_groups = None
        gid        = None
        gids       = None
        group      = None
        p2cread    = -1
        p2cwrite   = -1
        pass_fds   = ()
        pipesize   = -1
        preexec_fn = None
        process_group = -1
        restore_signals = True
        self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
        shell      = False
        start_new_session = False
        startupinfo = None
        stderr     = None
        stdin      = None
        stdout     = None
        text       = None
        uid        = None
        umask      = -1
        universal_newlines = None
        user       = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
args = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
executable = b'nix', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = PosixPath('.../experimental/memory/rust')
env = None, startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False, process_group = -1

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session, process_group):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '....../system/bin/sh'.
            unix_shell = ('....../system/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and process_group == -1
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        process_group, gid, gids, uid, umask,
                        preexec_fn, _USE_VFORK)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                if err_msg == "noexec:chdir":
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                elif err_msg == "noexec":
                    err_msg = ""
                    err_filename = None
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                if err_filename is not None:
>                   raise child_exception_type(errno_num, err_msg, err_filename)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'nix'

args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
c2pread    = -1
c2pwrite   = -1
child_exception_type = <class 'OSError'>
close_fds  = True
creationflags = 0
cwd        = PosixPath('.../experimental/memory/rust')
env        = None
env_list   = None
err_filename = 'nix'
err_msg    = 'No such file or directory'
errno_num  = 2
errpipe_data = bytearray(b'OSError:2:')
errpipe_read = 294
errpipe_write = 295
errread    = -1
errwrite   = -1
exception_name = bytearray(b'OSError')
executable = b'nix'
executable_list = (b'.../dimos/dimos/.venv/bin/nix', b'....../Users/ec2-user/.local.../uv/python/nix', b'/...l/uv/0.12.11/aarch64/nix', b'....../Users/ec2-user/.local/bin/nix', b'.../homebrew/bin/nix', b'.../homebrew/sbin/nix', ...)
fds_to_keep = {295}
gid        = None
gids       = None
hex_errno  = bytearray(b'2')
low_fds_to_close = []
orig_executable = 'nix'
p2cread    = -1
p2cwrite   = -1
part       = b''
pass_fds   = ()
pid        = 80483
preexec_fn = None
process_group = -1
restore_signals = True
self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
shell      = False
start_new_session = False
startupinfo = None
sts        = 65280
uid        = None
umask      = -1

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1955: FileNotFoundError
dimos.experimental.memory.test_rust_recorder_e2e::test_tf_records_over_zenoh_and_replays_through_python
Stack Traces | 0s run time
@pytest.fixture(scope="module")
    def rust_recorder_executable() -> Path:
>       subprocess.run(
            [
                "nix",
                "--extra-experimental-features",
                "nix-command flakes",
                "build",
                "-L",
                ".#dimos-memory-recorder",
                "--no-write-lock-file",
            ],
            cwd=_RUST_PACKAGE,
            check=True,
        )


.../experimental/memory/test_rust_recorder_e2e.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:548: in run
    with Popen(*popenargs, **kwargs) as process:
        capture_output = False
        check      = True
        input      = None
        kwargs     = {'cwd': PosixPath('.../experimental/memory/rust')}
        popenargs  = (['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...],)
        timeout    = None
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1026: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
        args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
        bufsize    = -1
        c2pread    = -1
        c2pwrite   = -1
        close_fds  = True
        creationflags = 0
        cwd        = PosixPath('.../experimental/memory/rust')
        encoding   = None
        env        = None
        errors     = None
        errread    = -1
        errwrite   = -1
        executable = None
        extra_groups = None
        gid        = None
        gids       = None
        group      = None
        p2cread    = -1
        p2cwrite   = -1
        pass_fds   = ()
        pipesize   = -1
        preexec_fn = None
        process_group = -1
        restore_signals = True
        self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
        shell      = False
        start_new_session = False
        startupinfo = None
        stderr     = None
        stdin      = None
        stdout     = None
        text       = None
        uid        = None
        umask      = -1
        universal_newlines = None
        user       = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
args = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
executable = b'nix', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = PosixPath('.../experimental/memory/rust')
env = None, startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False, process_group = -1

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session, process_group):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '....../system/bin/sh'.
            unix_shell = ('....../system/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and process_group == -1
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        process_group, gid, gids, uid, umask,
                        preexec_fn, _USE_VFORK)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                if err_msg == "noexec:chdir":
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                elif err_msg == "noexec":
                    err_msg = ""
                    err_filename = None
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                if err_filename is not None:
>                   raise child_exception_type(errno_num, err_msg, err_filename)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'nix'

args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
c2pread    = -1
c2pwrite   = -1
child_exception_type = <class 'OSError'>
close_fds  = True
creationflags = 0
cwd        = PosixPath('.../experimental/memory/rust')
env        = None
env_list   = None
err_filename = 'nix'
err_msg    = 'No such file or directory'
errno_num  = 2
errpipe_data = bytearray(b'OSError:2:')
errpipe_read = 294
errpipe_write = 295
errread    = -1
errwrite   = -1
exception_name = bytearray(b'OSError')
executable = b'nix'
executable_list = (b'.../dimos/dimos/.venv/bin/nix', b'....../Users/ec2-user/.local.../uv/python/nix', b'/...l/uv/0.12.11/aarch64/nix', b'....../Users/ec2-user/.local/bin/nix', b'.../homebrew/bin/nix', b'.../homebrew/sbin/nix', ...)
fds_to_keep = {295}
gid        = None
gids       = None
hex_errno  = bytearray(b'2')
low_fds_to_close = []
orig_executable = 'nix'
p2cread    = -1
p2cwrite   = -1
part       = b''
pass_fds   = ()
pid        = 80483
preexec_fn = None
process_group = -1
restore_signals = True
self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
shell      = False
start_new_session = False
startupinfo = None
sts        = 65280
uid        = None
umask      = -1

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1955: FileNotFoundError
dimos.hardware.whole_body.dual_openyam_damiao.test_adapter::test_adapter_connects_complete_dual_yam_topology
Stack Traces | 0.001s run time
mocker = <pytest_mock.plugin.MockerFixture object at 0x3b038fbf0>

    @pytest.fixture
    def adapter(mocker: MockerFixture) -> Iterator[DualOpenYamDamiaoAdapter]:
>       mocker.patch.object(can_motor_control, "SocketCanBus", can_motor_control.MockCanBus)

mocker     = <pytest_mock.plugin.MockerFixture object at 0x3b038fbf0>

.../whole_body/dual_openyam_damiao/test_adapter.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.12....../site-packages/pytest_mock/plugin.py:294: in object
    return self._start_patch(
        attribute  = 'SocketCanBus'
        autospec   = None
        create     = False
        kwargs     = {}
        new        = <class 'can_motor_control.MockCanBus'>
        new_callable = None
        self       = <pytest_mock.plugin.MockerFixture._Patcher object at 0x3b038f6b0>
        spec       = None
        spec_set   = None
        target     = <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>
.venv/lib/python3.12....../site-packages/pytest_mock/plugin.py:263: in _start_patch
    mocked: MockType = p.start()
        args       = (<module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>, 'SocketCanBus')
        kwargs     = {'autospec': None, 'create': False, 'new': <class 'can_motor_control.MockCanBus'>, 'new_callable': None, ...}
        mock_func  = <function _patch_object at 0x1034bb2e0>
        p          = <unittest.mock._patch object at 0x3b038f8c0>
        self       = <pytest_mock.plugin.MockerFixture._Patcher object at 0x3b038f6b0>
        warn_on_mock_enter = True
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1624: in start
    result = self.__enter__()
        self       = <unittest.mock._patch object at 0x3b038f8c0>
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1467: in __enter__
    original, local = self.get_original()
        autospec   = None
        kwargs     = {}
        new        = <class 'can_motor_control.MockCanBus'>
        new_callable = None
        self       = <unittest.mock._patch object at 0x3b038f8c0>
        spec       = None
        spec_set   = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <unittest.mock._patch object at 0x3b038f8c0>

    def get_original(self):
        target = self.getter()
        name = self.attribute
    
        original = DEFAULT
        local = False
    
        try:
            original = target.__dict__[name]
        except (AttributeError, KeyError):
            original = getattr(target, name, DEFAULT)
        else:
            local = True
    
        if name in _builtins and isinstance(target, ModuleType):
            self.create = True
    
        if not self.create and original is DEFAULT:
>           raise AttributeError(
                "%s does not have the attribute %r" % (target, name)
            )
E           AttributeError: <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'> does not have the attribute 'SocketCanBus'

local      = False
name       = 'SocketCanBus'
original   = sentinel.DEFAULT
self       = <unittest.mock._patch object at 0x3b038f8c0>
target     = <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1437: AttributeError
dimos.hardware.whole_body.openarm_damiao.test_adapter::test_openarm_feedback_limits_match_urdf_joint_limits
Stack Traces | 0.001s run time
mocker = <pytest_mock.plugin.MockerFixture object at 0x3b03fd4c0>

    @pytest.fixture
    def openarm_adapter(mocker: MockerFixture) -> Iterator[OpenArmDamiaoAdapter]:
>       mocker.patch.object(can_motor_control, "SocketCanBus", can_motor_control.MockCanBus)

mocker     = <pytest_mock.plugin.MockerFixture object at 0x3b03fd4c0>

.../whole_body/openarm_damiao/test_adapter.py:33: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.12....../site-packages/pytest_mock/plugin.py:294: in object
    return self._start_patch(
        attribute  = 'SocketCanBus'
        autospec   = None
        create     = False
        kwargs     = {}
        new        = <class 'can_motor_control.MockCanBus'>
        new_callable = None
        self       = <pytest_mock.plugin.MockerFixture._Patcher object at 0x3b03fd460>
        spec       = None
        spec_set   = None
        target     = <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>
.venv/lib/python3.12....../site-packages/pytest_mock/plugin.py:263: in _start_patch
    mocked: MockType = p.start()
        args       = (<module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>, 'SocketCanBus')
        kwargs     = {'autospec': None, 'create': False, 'new': <class 'can_motor_control.MockCanBus'>, 'new_callable': None, ...}
        mock_func  = <function _patch_object at 0x1034bb2e0>
        p          = <unittest.mock._patch object at 0x3b03fd430>
        self       = <pytest_mock.plugin.MockerFixture._Patcher object at 0x3b03fd460>
        warn_on_mock_enter = True
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1624: in start
    result = self.__enter__()
        self       = <unittest.mock._patch object at 0x3b03fd430>
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1467: in __enter__
    original, local = self.get_original()
        autospec   = None
        kwargs     = {}
        new        = <class 'can_motor_control.MockCanBus'>
        new_callable = None
        self       = <unittest.mock._patch object at 0x3b03fd430>
        spec       = None
        spec_set   = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <unittest.mock._patch object at 0x3b03fd430>

    def get_original(self):
        target = self.getter()
        name = self.attribute
    
        original = DEFAULT
        local = False
    
        try:
            original = target.__dict__[name]
        except (AttributeError, KeyError):
            original = getattr(target, name, DEFAULT)
        else:
            local = True
    
        if name in _builtins and isinstance(target, ModuleType):
            self.create = True
    
        if not self.create and original is DEFAULT:
>           raise AttributeError(
                "%s does not have the attribute %r" % (target, name)
            )
E           AttributeError: <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'> does not have the attribute 'SocketCanBus'

local      = False
name       = 'SocketCanBus'
original   = sentinel.DEFAULT
self       = <unittest.mock._patch object at 0x3b03fd430>
target     = <module 'can_motor_control' from '.../dimos/dimos/.venv/lib/python3.12............/site-packages/can_motor_control/__init__.py'>

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/unittest/mock.py:1437: AttributeError
dimos.experimental.memory.test_rust_recorder_e2e::test_rust_artifact_is_readable_by_python_memory2[sqlite]
Stack Traces | 0.017s run time
@pytest.fixture(scope="module")
    def rust_recorder_executable() -> Path:
>       subprocess.run(
            [
                "nix",
                "--extra-experimental-features",
                "nix-command flakes",
                "build",
                "-L",
                ".#dimos-memory-recorder",
                "--no-write-lock-file",
            ],
            cwd=_RUST_PACKAGE,
            check=True,
        )


.../experimental/memory/test_rust_recorder_e2e.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:548: in run
    with Popen(*popenargs, **kwargs) as process:
        capture_output = False
        check      = True
        input      = None
        kwargs     = {'cwd': PosixPath('.../experimental/memory/rust')}
        popenargs  = (['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...],)
        timeout    = None
../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1026: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
        args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
        bufsize    = -1
        c2pread    = -1
        c2pwrite   = -1
        close_fds  = True
        creationflags = 0
        cwd        = PosixPath('.../experimental/memory/rust')
        encoding   = None
        env        = None
        errors     = None
        errread    = -1
        errwrite   = -1
        executable = None
        extra_groups = None
        gid        = None
        gids       = None
        group      = None
        p2cread    = -1
        p2cwrite   = -1
        pass_fds   = ()
        pipesize   = -1
        preexec_fn = None
        process_group = -1
        restore_signals = True
        self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
        shell      = False
        start_new_session = False
        startupinfo = None
        stderr     = None
        stdin      = None
        stdout     = None
        text       = None
        uid        = None
        umask      = -1
        universal_newlines = None
        user       = None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
args = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
executable = b'nix', preexec_fn = None, close_fds = True, pass_fds = ()
cwd = PosixPath('.../experimental/memory/rust')
env = None, startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False, process_group = -1

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session, process_group):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '....../system/bin/sh'.
            unix_shell = ('....../system/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and process_group == -1
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        process_group, gid, gids, uid, umask,
                        preexec_fn, _USE_VFORK)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                if err_msg == "noexec:chdir":
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                elif err_msg == "noexec":
                    err_msg = ""
                    err_filename = None
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
                if err_filename is not None:
>                   raise child_exception_type(errno_num, err_msg, err_filename)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'nix'

args       = ['nix', '--extra-experimental-features', 'nix-command flakes', 'build', '-L', '.#dimos-memory-recorder', ...]
c2pread    = -1
c2pwrite   = -1
child_exception_type = <class 'OSError'>
close_fds  = True
creationflags = 0
cwd        = PosixPath('.../experimental/memory/rust')
env        = None
env_list   = None
err_filename = 'nix'
err_msg    = 'No such file or directory'
errno_num  = 2
errpipe_data = bytearray(b'OSError:2:')
errpipe_read = 294
errpipe_write = 295
errread    = -1
errwrite   = -1
exception_name = bytearray(b'OSError')
executable = b'nix'
executable_list = (b'.../dimos/dimos/.venv/bin/nix', b'....../Users/ec2-user/.local.../uv/python/nix', b'/...l/uv/0.12.11/aarch64/nix', b'....../Users/ec2-user/.local/bin/nix', b'.../homebrew/bin/nix', b'.../homebrew/sbin/nix', ...)
fds_to_keep = {295}
gid        = None
gids       = None
hex_errno  = bytearray(b'2')
low_fds_to_close = []
orig_executable = 'nix'
p2cread    = -1
p2cwrite   = -1
part       = b''
pass_fds   = ()
pid        = 80483
preexec_fn = None
process_group = -1
restore_signals = True
self       = <Popen: returncode: 255 args: ['nix', '--extra-experimental-features', 'nix-...>
shell      = False
start_new_session = False
startupinfo = None
sts        = 65280
uid        = None
umask      = -1

../../../../..../uv/python/cpython-3.12.13-macos-aarch64-none/lib/python3.12/subprocess.py:1955: FileNotFoundError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@TomCC7
TomCC7 marked this pull request as ready for review September 8, 2026 22:12
@greptile-apps

greptile-apps Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

Safe to merge: typed lookup works for local and remote modules and handles invalid discovery states safely.

What we checked:

  • Ran a focused typed-spec lookup test suite comparing the parent revision and the PR head; the parent lacked typed Spec lookup while the PR head resolves compatible RPC modules and returns the expected errors for incompatible signatures, unavailable classes, and ambiguous matches; all six tests passed. T-Rex
  • Validated baseline versus PR head behavior for module RPC lookup; baseline raises KeyError on missing typed lookup and TypeError on selection, while the PR head returns LookupError for noncompliance/unavailable class and ValueError for ambiguity. T-Rex
  • Uploaded supporting artifacts to enable inspection of the test approach and results, including the test script and the test logs. T-Rex

Summary

  • Adds typed module discovery through Spec Protocols while preserving name-based lookup.
  • Compatible local and remote RPC modules resolve successfully through the new API.
  • Incompatible signatures, ambiguous deployments, and unavailable client-side classes return the documented errors.

@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Sep 8, 2026
Comment thread dimos/porcelain/dimos.py
def get_module(self, name: str) -> ModuleHandle:
"""Return a module proxy by exact instance name or unique class name."""
@overload
def get_module(self, name: Callable[..., S], *, instance_name: str | None = None) -> S: ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You say "Clients should be able to find a module by its Spec"

But then add find_module_by_spec, don't overload get_module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants