extmod/vfs_posix_file: Fix flush handling on macOS.

On macOS, if running micropython from subprocess.check_output, then a
stdout.flush() raises error 45.

Here's a test case.  This will run fine on linux, but crashes on macOS with
error 45.

    import sys
    import subprocess
    import tempfile
    with tempfile.NamedTemporaryFile('w') as fp:
        fp.write('''
    import sys
    sys.stdout.write('hello world')
    sys.stdout.flush()
    print('')
    ''')
        fp.flush()
        print('py3')
        o = subprocess.check_output(f'python3 {fp.name}'.split())
        print(o)
        print('upy')
        o = subprocess.check_output(f'micropython {fp.name}'.split())
        print(o)

On macOS:

    py3
    b'hello world\n'
    upy
    Traceback (most recent call last):
      File "...", line 4, in <module>
    OSError: 45

On unix:

    py3
    b'hello world\n'
    upy
    b'hello world\n'

Signed-off-by: stephanelsmith <stephane.smith@titansensor.com>
This commit is contained in:
stephanelsmith 2023-07-26 02:52:56 +00:00 committed by Damien George
parent 633599cdd5
commit 1c047742a2

View File

@ -153,12 +153,17 @@ STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_
switch (request) { switch (request) {
case MP_STREAM_FLUSH: { case MP_STREAM_FLUSH: {
int ret; int ret;
// fsync(stdin/stdout/stderr) may fail with EINVAL (or ENOTSUP on macos),
// but don't propagate that error out. Because data is not buffered by
// us, and stdin/out/err.flush() should just be a no-op.
#ifdef __APPLE__
#define VFS_POSIX_STREAM_STDIO_ERR_CATCH (err == EINVAL || err == ENOTSUP)
#else
#define VFS_POSIX_STREAM_STDIO_ERR_CATCH (err == EINVAL)
#endif
MP_HAL_RETRY_SYSCALL(ret, fsync(o->fd), { MP_HAL_RETRY_SYSCALL(ret, fsync(o->fd), {
if (err == EINVAL if (VFS_POSIX_STREAM_STDIO_ERR_CATCH
&& (o->fd == STDIN_FILENO || o->fd == STDOUT_FILENO || o->fd == STDERR_FILENO)) { && (o->fd == STDIN_FILENO || o->fd == STDOUT_FILENO || o->fd == STDERR_FILENO)) {
// fsync(stdin/stdout/stderr) may fail with EINVAL, but don't propagate that
// error out. Because data is not buffered by us, and stdin/out/err.flush()
// should just be a no-op.
return 0; return 0;
} }
*errcode = err; *errcode = err;