Bug report
Bug description:
(Some) Errors raised in argparse will be colorized when redirecting stderr to a file.
Requirements:
- Errors redirected to a file are not colorized
- Unittest added to cover the case of error and redirecting of stderr
The most minimal example can be taken from calendar from the stdlib.
Works as expected with the color displayed correctly.
./python.exe -m calendar 2020 01 01
Redirecting stderr
./python.exe -m calendar 2020 01 01 2> err
cat -v err
^[[1;34musage: ^[[0m^[[1;35mpython.exe -m calendar^[[0m [^[[32m-h^[[0m] [^[[32m-w ^[[33mWIDTH^[[0m] [^[[32m-l ^[[33mLINES^[[0m] [^[[32m-s ^[[33mSPACING^[[0m] [^[[32m-m ^[[33mMONTHS^[[0m] [^[[32m-c ^[[33mCSS^[[0m] [^[[32m-L ^[[33mLOCALE^[[0m] [^[[32m-e ^[[33mENCODING^[[0m] [^[[32m-t ^[[33m{text,html}^[[0m] [^[[32m-f ^[[33mFIRST_WEEKDAY^[[0m] ^[[32m[year]^[[0m ^[[32m[month]^[[0m
python.exe -m calendar: error: unrecognized arguments: 01
Redirecting stdout will trigger the "correct" behavior
./python.exe -m calendar 2020 01 01 >out 2> err
(zatters-314) mkocher@zudio cpython % cat -v err
usage: python.exe -m calendar [-h] [-w WIDTH] [-l LINES] [-s SPACING]
[-m MONTHS] [-c CSS] [-L LOCALE] [-e ENCODING]
[-t {text,html}] [-f FIRST_WEEKDAY]
[year] [month]
python.exe -m calendar: error: unrecognized arguments: 01
Observations
- Calling
can_colorize(file=None) assumes that the stdout is used when file=None This implicit use of can_colorize() makes the code hard to reason about.
- Calling
_set_color always assumes stdout.
- Calling
self.print_usage(_sys.stderr) is breaking the assumption that stdout is being used.
- global ENV var design choice make data flow in the code confusing to debug. Depending on how your argparse code is written and the pattern/style of raising errors, some errors raised are being colorized properly by traceback layer. For example, using
parser.error(...) or raise ArgumentTypeError(...) vs a raise ValueError() within your application code.
- I think users are going to be a bit confused when setting
ArgumentParser(color=False) and the error (might) still be colorized (because of the traceback layer determining if it should be colorized). It is documented that configure for traceback is done via ENV vars, but it's not particular clear how the config of these components overlap.
- the specific error is not colorized (
python.exe -m calendar: error: unrecognized arguments: 01) but the usage is?
Stand alone example for debugging:
import sys
from argparse import ArgumentParser, ArgumentTypeError
def validate_lower(sx: str) -> str:
if any(not s.islower() for s in sx):
# this will be handled from within argparse.
# redirecting stderr will still have colorized ascii
raise ArgumentTypeError(f"{sx} contains a lowercase string")
return sx
def get_parser() -> ArgumentParser:
p = ArgumentParser(description="Testing", color=True)
f = p.add_argument
f('-m', '--max-records', type=int, help="Max number of records", required=True)
f('-n', '--name', type=validate_lower, help="User Name", required=True)
f('-t', '--trigger-error', action='store_true', help="Trigger error", required=False)
return p
def main(argv: list[str]) -> int:
p = get_parser()
pa = p.parse_args(argv)
if pa.trigger_error:
# will be colorized by traceback, must be configured by ENV vars to disable
raise ValueError("Trigger an error")
# this is also a common usage to raise an error
#p.error("Error case")
print(f"Completed running {pa}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Linked PRs
Bug report
Bug description:
(Some) Errors raised in argparse will be colorized when redirecting stderr to a file.
Requirements:
The most minimal example can be taken from
calendarfrom the stdlib.Works as expected with the color displayed correctly.
Redirecting stderr
Redirecting stdout will trigger the "correct" behavior
Observations
can_colorize(file=None)assumes that the stdout is used whenfile=NoneThis implicit use ofcan_colorize()makes the code hard to reason about._set_coloralways assumes stdout.self.print_usage(_sys.stderr)is breaking the assumption that stdout is being used.parser.error(...)orraise ArgumentTypeError(...)vs araise ValueError()within your application code.ArgumentParser(color=False)and the error (might) still be colorized (because of the traceback layer determining if it should be colorized). It is documented that configure for traceback is done via ENV vars, but it's not particular clear how the config of these components overlap.python.exe -m calendar: error: unrecognized arguments: 01) but the usage is?Stand alone example for debugging:
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Linked PRs