Skip to content

Flush stdout on shutdown #5521

Closed
Closed

Description

@arihant2math

This is done in cpython, and an error is returned if the flush does not succeed.

Activity

  1. pratheekhassan21 commented on Apr 25, 2025

    @pratheekhassan21

    hey,@arihant2math i thinking of contributing to rustpython.this will be my first contribution.can i work on this issue

  2. arihant2math commented on Apr 25, 2025

    @arihant2math
    ContributorAuthor

    @pratheekhassan21, sure, I think this is not a difficult issue. I think implementing Drop for a Stdout object in a manner that can't panic is the ideal implementation stragtegy. If not just creating a dummy object with a lifetime equivalent to the VM that flushes stdout on it's drop.

  3. pratheekhassan21 commented on Apr 26, 2025

    @pratheekhassan21

    can u explain a little bit more in detail about the issue.As far as i understood if program is terminated it should call the flush before terminating?

  4. arihant2math commented on Apr 27, 2025

    @arihant2math
    ContributorAuthor

    The following test should be fixed if this is implemented:

    def test_stdout_flush_at_shutdown(self):

  5. Ayomided commented on May 12, 2025

    @Ayomided

    Hello @arihant2math is there a bit more context for this issue. I would like to contribute and tackle this as my first issue.

  6. omar-abdelgawad commented on Sep 12, 2025

    @omar-abdelgawad

    I tried to investigate this issue a little bit before making a contribution so maybe I can summarize what I did and my thoughts. First Here is the test that we want to make it pass:

        # TODO: RUSTPYTHON
        @unittest.expectedFailure
        def test_stdout_flush_at_shutdown(self):
            # Issue #5319: if stdout.flush() fails at shutdown, an error should
            # be printed out.
            code = """if 1:
                import os, sys, test.support
                test.support.SuppressCrashReport().__enter__()
                sys.stdout.write('x')
                os.close(sys.stdout.fileno())"""
            rc, out, err = assert_python_failure('-c', code)
            self.assertEqual(b'', out)
            self.assertEqual(120, rc)
            self.assertRegex(err.decode('ascii', 'ignore'),
                             'Exception ignored in.*\nOSError: .*')

    so basically since stdout is normally buffered (unless explicitly told to not be with -u) then when the interpreter flushes right before exit it should trigger an Error like this in Cpython:

    omarabdelgawad@omarabdelgawad:~$ python
    Python 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from test.support.script_helper import assert_python_failure
    >>> code = """if 1:
    ...             import os, sys, test.support
    ...             test.support.SuppressCrashReport().__enter__()
    ...             sys.stdout.write('x')
    ...             os.close(sys.stdout.fileno())"""
    >>> rc,out,err = assert_python_failure('-c',code)
    >>> rc
    120
    >>> out
    b''
    >>> err
    b"Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>\nOSError: [Errno 9] Bad file descriptor\n"

    However the problem is that RustPython's Interpreter exits without an error:

    omarabdelgawad@omarabdelgawad:~/my_workspace/RustPython$ cargo run  --release
        Finished `release` profile [optimized] target(s) in 0.11s
         Running `target/release/rustpython`
    Welcome to the magnificent Rust Python 0.4.0 interpreter 😱 🖖
    RustPython 3.13.0
    Type "help", "copyright", "credits" or "license" for more information.
    >>>>> from test.support.script_helper import assert_python_failure
    >>>>> code = """if 1:
    ..... 		import os, sys, test.support
    ..... 		test.support.SuppressCrashReport().__enter__()
    ..... 		sys.stdout.write('x')
    ..... 		os.close(sys.stdout.fileno())"""
    >>>>> rc, out , err = assert_python_failure('-c',code)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/omarabdelgawad/my_workspace/projects/github_repos/RustPython/pylib/Lib/test/support/script_helper.py", line 177, in assert_python_failure
        return _assert_python(False, *args, **env_vars)
      File "/home/omarabdelgawad/my_workspace/projects/github_repos/RustPython/pylib/Lib/test/support/script_helper.py", line 151, in _assert_python
        res.fail(cmd_line)
      File "/home/omarabdelgawad/my_workspace/projects/github_repos/RustPython/pylib/Lib/test/support/script_helper.py", line 90, in fail
        err))
    AssertionError: Process return code is 0
    command line: ['/home/omarabdelgawad/my_workspace/projects/github_repos/RustPython/target/release/rustpython', '-X', 'faulthandler', '-I', '-c', "if 1:\n\t\timport os, sys, test.support\n\t\ttest.support.SuppressCrashReport().__enter__()\n\t\tsys.stdout.write('x')\n\t\tos.close(sys.stdout.fileno())"]
    
    stdout:
    ---
    
    ---
    
    stderr:
    ---
    
    ---

    Now I think that the interpreter flushes the stdout and stderr before exiting but probably the problem in handling them after they are closed (Bad file descriptor). so in vm/src/vm/interpreter.rs finalize function it should catch an exception in the following line no?

        pub fn finalize(self, exc: Option<PyBaseExceptionRef>) -> u8 {
            self.enter(|vm| {
                vm.flush_std(); 
    
                // See if any exception leaked out:
                let exit_code = if let Some(exc) = exc {
                    vm.handle_exit_exception(exc)
                } else {
                    0
                };
                ... // more stuff here as well
                ...
                exit_code
            })
        }

    Anyways on a side note I found that the REPL crashes after os.close(1) which is the same as os.close(sys.stdout.fileno()) while CPython doesn't crash:

    omarabdelgawad@omarabdelgawad:~$ python
    Python 3.12.3 (main, Aug 14 2025, 17:47:21) [GCC 13.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import os
    >>> os.close(1)
    >>> print("Hello")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 9] Bad file descriptor

    while RustPython crashes

    omarabdelgawad@omarabdelgawad:~/my_workspace/RustPython$ ./target/debug/rustpython
    Welcome to the magnificent Rust Python 0.4.0 interpreter 😱 🖖
    RustPython 3.13.0
    Type "help", "copyright", "credits" or "license" for more information.
    >>>>> import sys,os
    >>>>> os.close(sys.stdout.fileno())
    Readline error: Errno(EBADF)
    omarabdelgawad@omarabdelgawad:~/my_workspace/RustPython$ 

    In order to continue working on this issue I need some help finding where builtin functions like sys.stdout.write and os.close are defined.

  7. omar-abdelgawad commented on Feb 12, 2026

    @omar-abdelgawad

    @Embers-of-the-Fire I apologize but I don't think I will be able to contribute to this issue. I just hope that my comment will help the next one who will tackle it. thanks for the hints.

  8. majiayu000 commented on Mar 25, 2026

    @majiayu000
    Contributor

    Hi, I've been looking into this and traced the root cause.

    Proposed approach: Modify the finalize/shutdown path in vm/src/vm/interpreter.rs to match CPython behavior: when stdout flush fails (e.g. fd closed), print an error message to stderr and return exit code 120 instead of silently succeeding. The existing vm.flush_std() call likely needs error handling added. Validate with the test at Lib/test/test_cmd_line.py#L479.

    I'll open a draft PR shortly. Happy to adjust based on your preference.

  9. majiayu000 commented on Mar 25, 2026

    @majiayu000
    Contributor

    I'd like to work on this. The fix involves:

    1. Changing flush_std() in vm_object.rs to return a status code and use run_unraisable to report stdout flush errors to stderr
    2. Updating finalize() in interpreter.rs to return exit code 120 when stdout flush fails (matching CPython's behavior)
    3. Removing the @unittest.expectedFailure from test_stdout_flush_at_shutdown

    Opening a draft PR shortly.

  10. added a commit that references this issue on Mar 25, 2026
    e1ecb87
  11. added a commit that references this issue on Sep 16, 2026
    44cd8d5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-compatA discrepancy between RustPython and CPythongood first issueGood for newcomers

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions