Bug 2414940 - argparse colorize fails if a tty is not available, like in mod_wsgi
Summary: argparse colorize fails if a tty is not available, like in mod_wsgi
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Fedora
Classification: Fedora
Component: python3.14
Version: 43
Hardware: Unspecified
OS: Linux
unspecified
unspecified
Target Milestone: ---
Assignee: Miro Hrončok
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2025-11-13 21:24 UTC by Rob Crittenden
Modified: 2025-12-10 01:33 UTC (History)
5 users (show)

Fixed In Version: python3.14-3.14.2-1.fc43
Clone Of:
Environment:
Last Closed: 2025-12-10 01:33:49 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Github python cpython issues 141570 0 None closed _colorize.can_colorize() raises when sys.stdout is mod_wsgi logger 2025-11-19 16:52:22 UTC
Github python cpython issues 141571 0 None open argparse color=False calls _set_color(True) first, then _set_color(False) -- leading to needless envirnment lookups 2025-11-19 16:52:23 UTC
Github python cpython pull 141716 0 None Merged gh-141570: can_colorize: Expect fileno() to raise OSError, as documented 2025-11-19 16:52:32 UTC

Description Rob Crittenden 2025-11-13 21:24:59 UTC
argparse enables color by default.

If an argument is added and there is no tty it will fail like:

File "/usr/lib64/python3.14/argparse.py", line 1556, in add_argument
     formatter = self._get_formatter()
File "/usr/lib64/python3.14/argparse.py", line 2725, in _get_formatter
     formatter = self.formatter_class(prog=self.prog)
File "/usr/lib64/python3.14/argparse.py", line 178, in __init__
     self._set_color(color)
     ~~~~~~~~~~~~~~~^^^^^^^
File "/usr/lib64/python3.14/argparse.py", line 198, in _set_color
     if color and can_colorize():
                  ~~~~~~~~~~~~^^    
File "/usr/lib64/python3.14/_colorize.py", line 314, in can_colorize
     return os.isatty(file.fileno())

This is seen trying to issue a certificate using freeIPA. IPA uses lib389 which uses argparse for generating command-line options, which is done on import.

So merely importing this library within mod_wsgi will cause it to fail because it can't detect that there is no tty available.

Reproducible: Always

Comment 1 Rob Crittenden 2025-11-13 21:57:35 UTC
Reproducer not of the crash but that color=False is not honored.

I added an import pdb; pdb.set_trace() to _set_color() in /usr/lib64/python3.14/argparse.py prior to the conditional and color is True.

This is the code I used to create the parser and an option

import argparse

parser = argparse.ArgumentParser(add_help=False, color=False)
arg = parser.add_argument('-v', '--verbose', help='verbose', action='store_true', default=False)

Comment 2 Rob Crittenden 2025-11-13 22:32:58 UTC
For what it's worth this change fixes it for me:

diff -u /usr/lib64/python3.14/argparse.py   /tmp/argparse.py 
--- /usr/lib64/python3.14/argparse.py   2025-10-06 20:00:00.000000000 -0400
+++ /tmp/argparse.py    2025-11-13 17:31:11.678000000 -0500
@@ -2722,7 +2722,7 @@
         return formatter.format_help()
 
     def _get_formatter(self):
-        formatter = self.formatter_class(prog=self.prog)
+        formatter = self.formatter_class(prog=self.prog, color=self.color)
         formatter._set_color(self.color)
         return formatter

Comment 3 Miro Hrončok 2025-11-14 10:15:40 UTC
(In reply to Rob Crittenden from comment #0)
> argparse enables color by default.
> 
> If an argument is added and there is no tty it will fail like:
> 
> File "/usr/lib64/python3.14/argparse.py", line 1556, in add_argument
>      formatter = self._get_formatter()
> File "/usr/lib64/python3.14/argparse.py", line 2725, in _get_formatter
>      formatter = self.formatter_class(prog=self.prog)
> File "/usr/lib64/python3.14/argparse.py", line 178, in __init__
>      self._set_color(color)
>      ~~~~~~~~~~~~~~~^^^^^^^
> File "/usr/lib64/python3.14/argparse.py", line 198, in _set_color
>      if color and can_colorize():
>                   ~~~~~~~~~~~~^^    
> File "/usr/lib64/python3.14/_colorize.py", line 314, in can_colorize
>      return os.isatty(file.fileno())


Is there a part of this traceback missing? What is the exception?

Comment 4 Miro Hrončok 2025-11-14 10:27:12 UTC
(In reply to Rob Crittenden from comment #1)
> Reproducer not of the crash but that color=False is not honored.
> 
> I added an import pdb; pdb.set_trace() to _set_color() in
> /usr/lib64/python3.14/argparse.py prior to the conditional and color is True.
> 
> This is the code I used to create the parser and an option
> 
> import argparse
> 
> parser = argparse.ArgumentParser(add_help=False, color=False)
> arg = parser.add_argument('-v', '--verbose', help='verbose',
> action='store_true', default=False)

I don't really understand this code either. When I run it, what do I do next to see the problem?

Comment 5 Miro Hrončok 2025-11-14 10:34:44 UTC
There's https://github.com/python/cpython/commit/734e15b70dc044f57df4049a22dd769dffdb7d18 which removed the color=self.color argument and replaced it with _set_color(self.color)

I still am not entirely sure what exact problem you see. Perhaps it's related to subinterpreters.

Comment 6 Rob Crittenden 2025-11-14 14:03:43 UTC
Sorry, my paste cut off. The exception raised is:

OSError: Apache/mod_wsgi log object is not associated with a file descriptor.

In order to see the problem you need to instrument argparse.py to display the value of color in _set_color(). Like this:

--- /tmp/argparse.py    2025-11-14 08:46:10.368685441 -0500
+++ /usr/lib64/python3.14/argparse.py   2025-11-14 08:46:43.186354166 -0500
@@ -195,6 +195,8 @@
     def _set_color(self, color):
         from _colorize import can_colorize, decolor, get_theme
 
+        print(color)
+        _sys.exit(1)
         if color and can_colorize():
             self._theme = get_theme(force_color=True).argparse
             self._decolor = decolor

When you run the script you'll get the value of color as True. It should be False as ArgumentParser() was instantiated with color=False.

The code shouldn't get as far as determining if a tty is available or not because color should be False.

I'm seeing this exception in a PR to the freeipa project. I didn't post any direct links because it is full of other stuff that would just muddy the water.

Comment 7 Miro Hrončok 2025-11-14 14:46:51 UTC
So what the current argparse code does is this:

1. It initiates a HelpFormatter instance with no explicit color argument
2. The __init__ calls self._set_color(color), which is self._set_color(True)
3. Only then, it sets the color to False and call self._set_color(False)

I don't know if this is intentional or not. That said, even when color is True, you probably get:

 OSError: Apache/mod_wsgi log object is not associated with a file descriptor.

Right?

So there are two possible fixes here:

 - the code should probably not call self._set_color(True), self._set_color(False)
 - The code should handle the OSError and assume it means not a tty

Do I get that right?

Comment 8 Rob Crittenden 2025-11-14 14:50:36 UTC
Either would be fine.

Comment 9 Miro Hrončok 2025-11-20 12:39:26 UTC
How urgent is this? My upstream fix should be available in Python 3.14.1 scheduled for Tuesday, 2025-12-02.

Comment 10 Rob Crittenden 2025-11-20 13:00:12 UTC
That is fine. I worked around it by modifying imports to not import the thing that invokes argparse.

Comment 11 Fedora Update System 2025-12-07 09:14:55 UTC
FEDORA-2025-e235793f10 (python3.14-3.14.2-1.fc43 and python3-docs-3.14.2-1.fc43) has been submitted as an update to Fedora 43.
https://bodhi.fedoraproject.org/updates/FEDORA-2025-e235793f10

Comment 12 Fedora Update System 2025-12-10 01:33:49 UTC
FEDORA-2025-e235793f10 (python3.14-3.14.2-1.fc43 and python3-docs-3.14.2-1.fc43) has been pushed to the Fedora 43 stable repository.
If problem still persists, please make note of it in this bug report.


Note You need to log in before you can comment on or make changes to this bug.