"OSError: [Errno 22] Invalid argument" on Windows with print() and output piped
Asked Answered
P

2

6

I've come across some (to me) weird behaviour when piping the output of a Python script into wc with invalid arguments.

λ python test.py
Hello!
λ python test.py | wc -li
wc: unknown option -- i
Try 'wc --help' for more information.
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='cp1252'>
OSError: [Errno 22] Invalid argument

What is happening here?

My configuration is

  • Windows 10
  • Cmder
  • msysgit 2.5.1.windows.1
Perceive answered 13/9, 2015 at 22:38 Comment(3)
@achampion Right, it doesn't have the option. And that's the reason why the error occurs in Python. I want to know what exactly happens that this OSError is caused and why it has to do with an invalid argument passed to wc.Perceive
wc is exiting when it sees an invalid option which closes the pipe, python continues to write to the pipe and fails. Fortunately, I get a more reasonable error message for the same exception (Mac OSX): BrokenPipeError: [Errno 32] Broken pipe.Grandmother
Don't put stock in the significance of EINVAL. The CRT has to map thousands of Win32 error codes to just about 40 errno values. Probably the Win32 error here is ERROR_NO_DATA because the pipe is being closed. In principle I think this should map to EPIPE (broken pipe), but for some reason the CRT uses the default value of EINVAL (invalid argument).Calva
H
3

As @AChampion noted, this is the Windows version of IOError: [Errno 32] Broken pipe when piping: `prog.py | othercmd` with the added caveat that Python fails to raise the correct exception (i.e. BrokenPipeError).

There's issue 35754 "[Windows] I/O on a broken pipe may raise an EINVAL OSError instead of BrokenPipeError" Python's bug tracker.

Until it's fixed, I don't see a general way to handle it.

How to duplicate sys.stdout to a log file? helps solve this for Python-based output (printing via sys.stdout):

class IgnoreBrokenPipe(object):
    def __init__(self, stream):
        self.stream = stream
        def ignore_einval(fn):
            from functools import wraps
            @wraps(fn)
            def wrapper(*args, **kwargs):
                try:
                    return fn(*args, **kwargs)
                except OSError as exc:
                    if exc.errno != 22:
                        raise exc
                    else:  # mimicking the default SIGPIPE behavior on Windows
                        sys.exit(1)
            return wrapper

        self.write = ignore_einval(lambda data: self.stream.write(data))
        self.flush = ignore_einval(lambda: self.stream.flush())

import sys
sys.stdout = IgnoreBrokenPipe(sys.stdout)
Houseraising answered 30/3, 2021 at 16:29 Comment(0)
C
0

I have faced the same problem while using the gauge test framework.

The problem was, after creating a subprocess.Popen with stdout and stderr parameters set to subprocess.PIPE for test suite (I wanted to capture all output from that process) and an attempt to print something to sys.stderr in tested code, "OSError: [Errno 22] Invalid argument" has been raised.

Complainant answered 2/2, 2022 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.