It generates EBADF only when a file descriptor in the read or write sets isn't open.
If APE's select does the same, it will be fine, without breaking Python's pipes.
In that loop i from 0 to OPEN_MAX-1, on read and write sides, it should check each f in the read and write sets for (f->flags&FD_ISOPEN) != 0, or set EBADF and return -1.