Skip to content

Instantly share code, notes, and snippets.

@igorsantos07
Last active March 13, 2024 23:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igorsantos07/160dc637eece5957125bb61e4746b424 to your computer and use it in GitHub Desktop.
Save igorsantos07/160dc637eece5957125bb61e4746b424 to your computer and use it in GitHub Desktop.
stderr doesn't get captured when stdout=sys.stdout
#!/usr/bin/env python3
#############################################################################
# Reproducible example for https://stackoverflow.com/q/78152055/102960
# The objective is to see the presentation line, the prompt line, be able to
# answer it, and then have stderr captured and omitted from normal console
# output.
# Run THIS script from CLI, not script.py
#############################################################################
import shlex
import subprocess
import sys
print('Initial setup - the output here is unrelated to the actual problem: downloading MariaDB image')
mariadb = subprocess.run(('docker', 'ps', '-f', 'name=example-mariadb'), capture_output=True, text=True)
if 'example-mariadb' in mariadb.stdout:
print('MariaDB container is already running')
else:
subprocess.run(shlex.split('docker run --detach --name example-mariadb --env MARIADB_DATABASE=db --env MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1 mariadb:10.6.16'))
# Original test script got abandoned, since Python's input() seems q bit buggy, but it's not the actual issue;
# Other than that, captured output behaves as expected, differently from the MariaDB issue
# cmd = './script.py'
cmd = shlex.split('docker exec -it example-mariadb mariadb-admin drop wrong-db')
# For some reason, the prompt doesn't show up in stdout, but in stderr?
print('The same script.py will be called THREE times')
print('First: stdout=sys.stdout, so you should see a presentation line, then the question - answer "y"')
result = subprocess.run(cmd, text=True, stderr=subprocess.PIPE, stdout=sys.stdout)
print(result)
# > CompletedProcess(args='./script.py', returncode=1, stderr='Should I work without throwing errors? [y/N] Cool! Take this error: ERROR ERROR ERROR\n')
print()
print('Second: stderr=PIPE stdout=None, so you should see a presentation line, then the question - answer "y"')
# the same happens if stdout is omitted,
# or if I try to check captured stderr from CalledProcessError instead
try:
subprocess.run(cmd, text=True, check=True, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as error:
print(vars(error))
# > {'returncode': 1, 'cmd': './script.py', 'output': None, 'stderr': 'Should I work without throwing errors? [y/N] Cool! Take this error: ERROR ERROR ERROR\n'}
print()
print('Third: stderr=stdout=PIPE, so you should NOT see the presentation nor the question, but answer "y" anyway')
# However, if I also capture stdout, it works as expected...
# But then, the user cannot really interact, since the question
# is captured and never shown. The same happens if I try..except.
result = subprocess.run(cmd, text=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
print(result)
# > CompletedProcess(args='./script.py', returncode=1, stdout='This is the test script, writing to stdout. Now, the question:\nShould I work without throwing errors? [y/N] ', stderr='Cool! Take this error: ERROR ERROR ERROR\n')
#!/usr/bin/env python3
import sys
print('This is the test script, writing to stdout. Now, the question:')
response = input('Should I work without throwing errors? [y/N] ')
if response.lower() == 'y':
print('Ok, no error then, but no problems either.')
else:
print('Cool! Take this error: ERROR ERROR ERROR', file=sys.stderr)
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment