Bug 2414940

Summary: argparse colorize fails if a tty is not available, like in mod_wsgi
Product: [Fedora] Fedora Reporter: Rob Crittenden <rcritten>
Component: python3.14Assignee: Miro Hrončok <mhroncok>
Status: CLOSED ERRATA QA Contact:
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 43CC: ksurma, mhroncok, python-maint, python-packagers-sig, vashirov
Target Milestone: ---   
Target Release: ---   
Hardware: Unspecified   
OS: Linux   
Whiteboard:
Fixed In Version: python3.14-3.14.2-1.fc43 Doc Type: ---
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2025-12-10 01:33:49 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

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.